Anto Subash.

Published on

Deploy ABP Framework dotnet core tiered app to docker swarm. Part 9

Table of contents

Intro

In this post we will see how to deploy your dotnet core app with docker container.

Create ABP Tired application

1abp new AbpDocker -t app -u mvc --tiered -dbms PostgreSQL

Once the app is created update the connection string in all the project and run the DbMigration project to setup the migrations and database seeding.

Create a same site cookies extension

you can find the code for that here: https://community.abp.io/articles/patch-for-chrome-login-issue-identityserver4-samesite-cookie-problem-weypwp3n

Here is the code

1public static class SameSiteCookiesServiceCollectionExtensions
2{
3    public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services)
4    {
5        services.Configure<CookiePolicyOptions>(options =>
6        {
7            options.MinimumSameSitePolicy = SameSiteMode.Lax;
8            options.OnAppendCookie = cookieContext =>
9                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
10            options.OnDeleteCookie = cookieContext =>
11                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
12        });
13
14        return services;
15    }
16
17    private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
18    {
19        if (options.SameSite == SameSiteMode.None)
20        {
21            var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
22            if (!httpContext.Request.IsHttps || DisallowsSameSiteNone(userAgent))
23            {
24                // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1)
25                options.SameSite = SameSiteMode.Lax;
26            }
27        }
28    }
29
30    private static bool DisallowsSameSiteNone(string userAgent)
31    {
32        // Cover all iOS based browsers here. This includes:
33        // - Safari on iOS 12 for iPhone, iPod Touch, iPad
34        // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
35        // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
36        // All of which are broken by SameSite=None, because they use the iOS networking stack
37        if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12"))
38        {
39            return true;
40        }
41
42        // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
43        // - Safari on Mac OS X.
44        // This does not include:
45        // - Chrome on Mac OS X
46        // Because they do not use the Mac OS networking stack.
47        if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
48            userAgent.Contains("Version/") && userAgent.Contains("Safari"))
49        {
50            return true;
51        }
52
53        // Cover Chrome 50-69, because some versions are broken by SameSite=None,
54        // and none in this range require it.
55        // Note: this covers some pre-Chromium Edge versions,
56        // but pre-Chromium Edge does not require SameSite=None.
57        if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
58        {
59            return true;
60        }
61
62        return false;
63    }
64}

Add the following line to ConfigureServices() method in all 3 projects.

1context.Services.AddSameSiteCookiePolicy();

Go to OnApplicationInitialization() method in AcmeBookStoreWebModule.cs add app.UseCookiePolicy(); in all 3 projects.

1app.UseCookiePolicy(); // added this, Before UseAuthentication or anything else that writes cookies.

Create a new client for the production app

Update the appsettings.json from the DbMigrator to add the new client.

1"AbpDocker_Web_Docker": {
2  "ClientId": "AbpDocker_Web_Docker",
3  "ClientSecret": "1q2w3e*",
4  "RootUrl": "http://host.docker.internal:9005"
5},

Update the IdentityServerDataSeedContributor in the Domain project and add the new client to the identity server.

1//webDockerClient Client
2var webDockerClientId = configurationSection["AbpDocker_Web_Docker:ClientId"];
3if (!webClientId.IsNullOrWhiteSpace())
4{
5    var webClientRootUrl = configurationSection["AbpDocker_Web_Docker:RootUrl"].EnsureEndsWith('/');
6
7    await CreateClientAsync(
8        name: webDockerClientId,
9        scopes: commonScopes,
10        grantTypes: new[] { "hybrid" },
11        secret: (configurationSection["AbpDocker_Web_Docker:ClientSecret"] ?? "1q2w3e*").Sha256(),
12        redirectUri: $"{webClientRootUrl}signin-oidc",
13        postLogoutRedirectUri: $"{webClientRootUrl}signout-callback-oidc",
14        frontChannelLogoutUri: $"{webClientRootUrl}Account/FrontChannelLogout",
15        corsOrigins: new[] { webClientRootUrl.RemovePostFix("/") }
16    );
17}

Run the DbMigrator project again to see the new client.

Create production config

Create the appsettings.Production.json in all the three projects.

HttpApi.Host

1{
2  "App": {
3    "CorsOrigins": "https://*.AbpDocker.com"
4  },
5  "ConnectionStrings": {
6    "Default": "Host=host.docker.internal;Port=5432;Database=AbpDocker;User ID=postgres;Password=postgres;"
7  },
8  "Redis": {
9    "Configuration": "host.docker.internal"
10  },
11  "AuthServer": {
12    "Authority": "http://host.docker.internal:9006",
13    "RequireHttpsMetadata": "false",
14    "SwaggerClientId": "AbpDocker_Swagger",
15    "SwaggerClientSecret": "1q2w3e*"
16  },
17  "StringEncryption": {
18    "DefaultPassPhrase": "WBN0szwYr7wL8Dou"
19  }
20}

IdentityServer

1{
2  "App": {
3    "SelfUrl": "http://host.docker.internal:9006",
4    "ClientUrl": "http://localhost:4200",
5    "CorsOrigins": "https://*.AbpDocker.com,http://localhost:4200,https://localhost:44307,https://localhost:44375,http://host.docker.internal:9006,http://host.docker.internal:9005,http://host.docker.internal:9007",
6    "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307,http://host.docker.internal:9006,http://host.docker.internal:9005,http://host.docker.internal:9007"
7  },
8  "ConnectionStrings": {
9    "Default": "Host=host.docker.internal;Port=5432;Database=AbpDocker;User ID=postgres;Password=postgres;"
10  },
11  "Redis": {
12    "Configuration": "host.docker.internal"
13  }
14}

Web

1{
2  "App": {
3    "SelfUrl": "http://host.docker.internal:9005"
4  },
5  "RemoteServices": {
6    "Default": {
7      "BaseUrl": "http://host.docker.internal:9007/"
8    }
9  },
10  "Redis": {
11    "Configuration": "host.docker.internal"
12  },
13  "AuthServer": {
14    "Authority": "http://host.docker.internal:9006",
15    "RequireHttpsMetadata": "false",
16    "ClientId": "AbpDocker_Web_Docker",
17    "ClientSecret": "1q2w3e*"
18  }
19}

Add docker support

Do the following in all 3 projects.

  • Right Click -> Add -> Docker Support

    This will add Docker file into the project.

  • Right Click -> Add -> Container Orchestrator Support

    This will add the docker file to the docker-compose.yml

Add port mapping to the services to the expose the services.

Final docker-compose.yml will look like this.

1version: "3.4"
2
3services:
4  web:
5    image: abpdockerweb
6    ports:
7      - 9005:80
8    build:
9      context: .
10      dockerfile: src/AbpDocker.Web/Dockerfile
11
12  identityserver:
13    image: abpdockeridentityserver
14    ports:
15      - 9006:80
16    build:
17      context: .
18      dockerfile: src/AbpDocker.IdentityServer/Dockerfile
19
20  httpapi:
21    image: abpdockerhttpapihost
22    ports:
23      - 9007:80
24    build:
25      context: .
26      dockerfile: src/AbpDocker.HttpApi.Host/Dockerfile

Build docker containers

Navigate to the directory where the docker-compose.yml file is present and then run the following command.

1docker-compose build

Deploy the docker stack

1docker stack deploy -c .\docker-compose.yml abpdocker

Run the app

Visit the web app in http://host.docker.internal:9005/

Visit the identity server in http://host.docker.internal:9006/

Visit the api in http://host.docker.internal:9007/

Repo: https://github.com/antosubash/AbpWithDocker