在 kubernetes 的 asp.net 个核心容器中登录 reset/lost

Login reset/lost in asp.net core containers in kubernetes

容器的新手并在将近十年后回到 asp.net,我认为这已经在某处得到解答,但似乎无法找到它! 我在 3 个节点上有一个简单的 asp.net 应用程序 运行,登录显然是每个节点的,并且当请求由不同的节点提供服务时会丢失。我以为会有分布式会话管理教程,但这里很短,任何 pointers/how-to ?

仅供参考 - 目前在开发中,但针对 public 面向云的解决方案 azure/linode/... 运行 3 pods

下面附上configuration/logging资料

Redis 在本地主机上工作 - 不在 docker 容器中

行为如下

似乎还需要其他东西才能使会话使用 redis !!!


public void ConfigureServices(IServiceCollection services)
{
    var cx = Configuration["RedisCache:ConnectionString"];
var redis = ConnectionMultiplexer.Connect(cx); 
 services.AddDataProtection().PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys");

    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(20);
    });
    services.AddControllersWithViews();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseDeveloperExceptionPage();
        //app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    if (env.IsDevelopment())
    {
        app.UseHttpsRedirection();
    }

    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    // Adds session middleware to pipeline
    app.UseSession();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapRazorPages();
    });
}

APPSETTINGS.JSON

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=host.docker.internal;Database=SAMPLE;Trusted_Connection=false;user id=XXX;password=XXX;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "RedisCache": {
    "ConnectionString": "host.docker.internal:6379,ssl=True,abortConnect=False"
  },
  "AllowedHosts": "*"
}

我在 Index.cshtml.cs

中有以下日志记录信息
public void OnGet()
{
    var str = $"({Environment.MachineName}) || {HttpContext.Session.Id} || {DateTime.UtcNow.ToString("hh:mm:ss")}: Current({User.Identity.Name})";
    _logger.LogWarning(str);

    string sessionKey = "testKey1";

    HttpContext.Session.SetString(sessionKey, str);
}

使用当前代码,会话存储在本地,因此每次负载均衡器重定向到新 pod 时,您将丢失先前请求的任何上下文。

您必须启用分布式缓存,以便在您的服务器应用程序的多个实例之间共享会话。参见 IDistributedCache interface

您还必须确保所有应用程序实例 shared the same keys for Data Protection related workflows.

这是一个使用 Redis 作为 DistributedCache 的工作示例:

public void ConfigureServices(IServiceCollection services)
{
    // ensure that several instances of the same application can share their session
    services.AddDataProtection()
        .SetApplicationName("myapp_session")
        .PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect("redis:6379,password=cE3nNEXHmvGCwdq7jgcxxxxxxxxxx"),
            "DataProtection-Keys");

    services.AddControllersWithViews();
    
    // add distributed caching based on Redis
    services.AddStackExchangeRedisCache(action => {
        action.InstanceName = "redis";
        action.Configuration = "redis:6379,password=cE3nNEXHmvGCwdq7jgcxxxxxxxxxx";
    });

    services.AddSession(options => {
        options.Cookie.Name = "myapp_session";
        options.IdleTimeout = TimeSpan.FromMinutes(60 * 24);
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseSession();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

这里是对应的csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="5.0.3" />
  </ItemGroup>
</Project>

此处提供完整示例:https://github.com/Query-Interface/SO-Answers/tree/master/dotNET/AspWithSessionOnMultipleNodes

另一种选择是在您的 Kubernetes 服务中启用 SessionAffinity。它允许 LoadBalancer 将来自一个客户端的流量始终指向同一个 Pod。它可以帮助您,但不建议这样做,因为一旦 Pod 被删除(失败的 Pod 或由于缩放操作),LoadBalancer 将无法将您的请求路由到该特定的 Pod。在这个问题中有解释: