本文介绍了kubernetes中asp.net核心容器中的登录重置/丢失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

容器的新手,并在近十年后回到asp.net,我认为这已经可以在某个地方得到解决,但似乎找不到它!我有一个在3个节点上运行的简单asp.net应用程序,登录名显然是每个节点的,当请求由另一个节点提供服务时会丢失.我以为会有分布式的会话管理教程,但是在这里简短介绍一下,任何指针/操作方法?

FYI-目前处于开发阶段,但针对面向公众的云解决方案azure/linode/...运行3个豆荚

配置/日志记录信息附在下面

Redis正在localhost上运行-不在Docker容器中

行为如下

  • 说用户登录到pod1,只要请求到达pod1,它就会显示已登录,
  • 如果请求到达pod2/pod3,则表明未登录!!!

要使会话使用redis,似乎需要采取其他措施!!!


  public void ConfigureServices(IServiceCollection服务){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();}公共无效配置(IApplicationBuilder应用程序,IWebHostEnvironment env){如果(env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseMigrationsEndPoint();}别的{app.UseDeveloperExceptionPage();//app.UseExceptionHandler(""/Error");app.UseHsts();}如果(env.IsDevelopment()){app.UseHttpsRedirection();}app.UseStaticFiles();app.UseRouting();app.UseAuthentication();app.UseAuthorization();//将会话中间件添加到管道app.UseSession();app.UseEndpoints(endpoints =>{endpoints.MapControllerRoute(名称:默认",模式:"{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"},记录":{"LogLevel":{默认":警告","Microsoft":警告","Microsoft.Hosting.Lifetime":信息".}},"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")}:当前({User.Identity.Name})";_logger.LogWarning(str);字符串sessionKey ="testKey1";HttpContext.Session.SetString(sessionKey,str);} 
解决方案

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

您必须启用分布式缓存,这将允许在服务器应用程序的多个实例之间共享会话.请参见 IDistributedCache接口

还必须确保所有应用程序实例共享了与数据保护相关的工作流程相同的密钥..

这是Redis作为DistributedCache的有效示例:

  public void ConfigureServices(IServiceCollection服务){//确保同一应用程序的多个实例可以共享其会话services.AddDataProtection().SetApplicationName("myapp_session").PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect("redis:6379,password = cE3nNEXHmvGCwdq7jgcxxxxxxxxxx")),"DataProtection-Keys");services.AddControllersWithViews();//添加基于Redis的分布式缓存services.AddStackExchangeRedisCache(action => {action.InstanceName ="redis";配置="redis:6379,密码= cE3nNEXHmvGCwdq7jgcxxxxxxxxxx";});services.AddSession(options => {options.Cookie.Name ="myapp_session";options.IdleTimeout = TimeSpan.FromMinutes(60 * 24);});}公共无效配置(IApplicationBuilder应用程序,IWebHostEnvironment env){如果(env.IsDevelopment()){app.UseDeveloperExceptionPage();}别的{app.UseExceptionHandler("/Home/Error");//HSTS的默认值为30天.您可能要针对生产方案更改此设置,请参见https://aka.ms/aspnetcore-hsts.app.UseHsts();}app.UseHttpsRedirection();app.UseRouting();app.UseSession();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllerRoute(名称:默认",模式:"{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";版本="5.0.1";/>< PackageReference Include ="Microsoft.AspNetCore.DataProtection.StackExchangeRedis";版本="5.0.3";/></ItemGroup></Project> 

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

另一种选择是在Kubernetes服务中启用 SessionAffinity .它允许LoadBalancer始终将来自一个客户端的流量引导到同一Pod.它可以为您提供帮助,但是不建议您这样做,因为一旦Pod被删除(失败的Pod或由于缩放操作而导致),LoadBalancer将无法将您的请求路由到该特定Pod.在以下问题中对此进行了解释: https://stackoverflow.com/questions/56323438

newbie to containers and coming back to asp.net in nearly a decade and i assumed this would be answered somewhere already but can't seem to locate it !I have a simple asp.net app running on 3 nodes, the login is per node apparently and is lost when the request is served by a different node. i assumed there would be distributed session management tutorial but coming up short here, any pointers/how-to ?

FYI - in dev for now but for a public facing cloud solution azure/linode/...running 3 pods

The configuration/logging information is attached below

Redis is working on the localhost - not in a docker container

the behaviour is as follows

  • say user is logged into pod1, whenever request hits pod1, it shows logged in,
  • if the request hits pod2/pod3, it shows not logged in !!!

It looks like something else is required to make the session use 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": "*"
}

I have the following logging information In 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);
}
解决方案

With the current code, the sessions are stored locally so each time the load balancer redirect to a new pod, you will lose any context of the previous request.

You have to enable distributed caching that will allow to share sessions across multiple instances of your server application. See IDistributedCache interface

You must also ensure that all application instances shared the same keys for Data Protection related workflows.

Here is a working sample with Redis as 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?}");
    });
}

Here is the corresponding 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>

Full sample available here: https://github.com/Query-Interface/SO-Answers/tree/master/dotNET/AspWithSessionOnMultipleNodes

Another option, is to enable SessionAffinity in your Kubernetes Service. It allows the LoadBalancer to root traffic from one client always to the same Pod. It can help you but it is not recommended since as soon as a Pod will be deleted (failing pod or as a result of scaling operation) the LoadBalancer will not be able to route your request to this specific Pod. It is explained in this question: https://stackoverflow.com/questions/56323438

这篇关于kubernetes中asp.net核心容器中的登录重置/丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-18 19:01