Сброс / потеря входа в основные контейнеры asp.net в кубернетах

новичок в контейнерах и вернулся на asp.net почти через десять лет, и я предполагал, что на этот вопрос уже где-то ответят, но, похоже, не могу его найти! У меня есть простое приложение asp.net, работающее на 3 узлах, логин, по-видимому, выполняется на каждый узел и теряется, когда запрос обслуживается другим узлом. Я предполагал, что будет учебник по распределенному управлению сеансом, но здесь нет никаких указателей / инструкций?

К вашему сведению - пока в разработке, но для общедоступного облачного решения azure / linode / ... работает 3 модуля

Информация о конфигурации / регистрации прилагается ниже.

Redis работает на локальном хосте, а не в контейнере докеров

поведение выглядит следующим образом

  • скажем, пользователь вошел в pod1, всякий раз, когда запрос попадает в pod1, он показывает, что он вошел в систему,
  • если запрос попадает в pod2 / pod3, он показывает, что не авторизован !!!

Похоже, что для того, чтобы сессия использовала 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();
    });
}

ПРИЛОЖЕНИЯ.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);
}

person Kumar    schedule 07.02.2021    source источник
comment
Можете ли вы вставить свои файлы конфигурации и спецификацию среды?   -  person Malgorzata    schedule 08.02.2021
comment
@Malgorzata, наклеено   -  person Kumar    schedule 10.02.2021


Ответы (1)


С текущим кодом сеансы хранятся локально, поэтому каждый раз, когда балансировщик нагрузки перенаправляет на новый модуль, вы теряете любой контекст предыдущего запроса.

Вы должны включить распределенное кэширование, которое позволит совместно использовать сеансы между несколькими экземплярами вашего серверного приложения. См. интерфейс IDistributedCache

Вы также должны убедиться, что все экземпляры приложения использует одни и те же ключи для рабочих процессов, связанных с защитой данных.

Вот рабочий пример с 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

Другой вариант - включить SessionAffinity в вашем сервисе Kubernetes. Это позволяет LoadBalancer всегда направлять трафик от одного клиента к одному и тому же поду. Это может помочь вам, но не рекомендуется, поскольку как только модуль будет удален (отказавший модуль или в результате операции масштабирования), LoadBalancer не сможет направить ваш запрос на этот конкретный модуль. Это объясняется в этом вопросе: https://stackoverflow.com/questions/56323438/session-affinity-settings-for-multiple-pods-exposed-by-a-single-service

person Arnaud Develay    schedule 10.02.2021
comment
Я столкнулся с этим, но это не похоже на старое ядро ​​asp.net 2.2, в частности, пропал AddStackExchangeRedisCache, вот этот вопрос здесь stackoverflow.com/questions/ 59292159 /, но этой функции нет в пакете v3 !! также я бы предпочел не использовать sessionAffinity, как вы говорите - person Kumar; 10.02.2021
comment
Кажется, все еще действует: docs.microsoft.com/en-us/dotnet/api/. - person Arnaud Develay; 11.02.2021
comment
Вы добавили ссылку на пакет Microsoft.Extensions.Caching.StackExchangeRedis в свой проект? Что-то вроде этого (с версией, которую нужно обновить в зависимости от целевой платформы): <ItemGroup><PackageReference include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" /> </ItemGroup> - person Arnaud Develay; 11.02.2021
comment
ссылка docs.microsoft.xxxx предназначена для версии 3.1. последняя доступная, да, я добавил версии 5.0.1, но их там нет! - person Kumar; 11.02.2021
comment
Привет, Кумар, я обновил свой ответ рабочим примером github. com / Query-Interface / SO-Answers / tree / master / dotNET /. Также размещен на Civo Kubernetes в развертывании с 3 репликами приложения ASP Net: webapp-service.f3c7dcb8-4774-4a05-be30-e760c7f0eea6.k8s.civo.co - person Arnaud Develay; 16.02.2021
comment
Спасибо, быстрое обновление, меня втянули в другую срочную проблему, скоро посмотрю образец, С уважением - person Kumar; 17.02.2021