【ASP.NET Core分布式项目实战】(五)Docker制作dotnet core控制台程序镜像

时间:2024-01-17 14:37:14

Docker制作dotnet core控制台程序镜像

基于dotnet SDK

  1. 新建控制台程序
    mkdir /home/console
    cd /home/console
    dotnet new console
    dotnet restore
  2. 创建 Dockerfile 文件,参考https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile
    vim /home/console/Dockerfile
    
    # ------
    
    FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
    WORKDIR /app COPY . /app RUN dotnet run
  3. 构建镜像

    docker build -t wyt/console:dev .
  4. 运行容器
    dotnet run --name console-dev wyt/console

基于dotnet Runtime

  1. 新建控制台程序
    mkdir /home/console
    cd /home/console
    dotnet new console
    dotnet restore
    using System;
    using System.Threading; namespace console
    {
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine("Hello World from docker!"); Thread.Sleep(Timeout.Infinite);
    }
    }
    }
  2. 创建 Dockerfile 文件
    FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
    WORKDIR /code COPY *.csproj /code
    RUN dotnet restore COPY . /code
    RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
    WORKDIR /app
    COPY --from=build /code/out /app
    ENTRYPOINT ["dotnet", "console.dll"]
  3. 构建镜像
    docker build -t wyt/console:prod .
  4. 运行容器
    docker run --name=console-prod wyt/console:prod

镜像大小对比

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wyt/console prod d2c683338197 minutes ago 260MB
wyt/console dev 93b346366bc5 minutes ago .74GB
mcr.microsoft.com/dotnet/core/sdk 2.2 155911c343f3 days ago .74GB
mcr.microsoft.com/dotnet/core/aspnet 2.2 c56aab97bc42 days ago 260MB

Mysql EF Core 快速构建 web api

  1. 新建文件夹beta,创建WebAPI项目User.API,添加EFCore引用
    Install-Package MySql.Data.EntityFrameworkCore
  2. 新建文件夹Data、Models 
    新建 AppUser.cs 
    namespace User.API.Models
    {
    public class AppUser
    {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Company { get; set; }
    public string Title { get; set; }
    }
    }

    新建 UserContext.cs

    namespace User.API.Data
    {
    public class UserContext:DbContext
    {
    public UserContext(DbContextOptions<UserContext> options) : base(options)
    { } public DbSet<AppUser> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    modelBuilder.Entity<AppUser>().ToTable(nameof(Users)).HasKey(t => t.Id); base.OnModelCreating(modelBuilder);
    }
    }
    }
  3. 修改 Startup.cs 
    namespace User.API
    {
    public class Startup
    {
    public Startup(IConfiguration configuration)
    {
    Configuration = configuration;
    } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDbContext<UserContext>(options =>
    {
    options.UseMySQL(Configuration.GetConnectionString("MysqlUser"));
    }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    } app.UseMvc(); InitDatabase(app);
    } public void InitDatabase(IApplicationBuilder app)
    {
    using (var scope=app.ApplicationServices.CreateScope())
    {
    var userContext = scope.ServiceProvider.GetRequiredService<UserContext>();
    //userContext.Database.Migrate();
    if (userContext.Users.Count()==)
    {
    userContext.Users.Add(new AppUser()
    {
    Name = "wyt"
    });
    userContext.SaveChanges();
    }
    }
    }
    }
    }
  4. 修改 appsettings.json 添加数据库配置
    "ConnectionStrings": {
    "MysqlUser": "Server=192.168.103.240;Port=3306;Database=beta_user;Uid=root;Pwd=pwd123456"
    }
  5. 生成数据库

    Add-Migration init
    Update-Database
  6. 修改 ValuesController.cs 加入依赖注入
    namespace User.API.Controllers
    {
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : Controller
    {
    private UserContext _userContext; public ValuesController(UserContext userContext)
    {
    _userContext = userContext;
    } // GET api/values
    [HttpGet]
    public async Task<IActionResult> Get()
    {
    return Json(await _userContext.Users.FirstOrDefaultAsync(u => u.Name == "wyt"));
    }
    }
    }
  7. 访问api

    【ASP.NET Core分布式项目实战】(五)Docker制作dotnet core控制台程序镜像

ASPNETCORE WEB API与MYSQL互联

  • 修改 appsettings.json 与 appsettings.Development.json ,采用不同配置
    //appsettings.json
    
    {
    "Logging": {
    "LogLevel": {
    "Default": "Warning"
    }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
    "MysqlUser": "Server=db;Port=3306;Database=beta_user;Uid=root;Pwd=pwd123456"
    }
    } //appsettings.Development.json {
    "Logging": {
    "LogLevel": {
    "Default": "Debug",
    "System": "Information",
    "Microsoft": "Information"
    }
    },
    "ConnectionStrings": {
    "MysqlUser": "Server=47.111.84.191;Port=3306;Database=beta_user;Uid=root;Pwd=pwd123456"
    }
    }
  • 修改 Program.cs 设置80端口

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .UseUrls("http://*:80")
    .UseStartup<Startup>();
  • 创建 Dockerfile

    FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
    WORKDIR /code
    COPY *.csproj ./
    RUN dotnet restore COPY . ./
    RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
    WORKDIR /app
    COPY --from=build /code/out ./ EXPOSE ENTRYPOINT ["dotnet", "User.API.dll"]
  • 拷贝到Linux服务器上,并执行下方命令创建镜像

    docker build -t wyt/aspnetcore:pred .
    [root@localhost User.API]# docker build -t wyt/aspnetcore:pred .
    Sending build context to Docker daemon .265MB
    Step / : FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
    ---> 155911c343f3
    Step / : WORKDIR /code
    ---> Using cache
    ---> 3e2cb7223b3b
    Step / : COPY *.csproj ./
    ---> 6f6d88b83c75
    Step / : RUN dotnet restore
    ---> Running in c538c0a59636
    Restore completed in 3.85 sec for /code/User.API.csproj.
    Removing intermediate container c538c0a59636
    ---> 6e45bd786a9c
    Step / : COPY . ./
    ---> 50ac66ac3f97
    Step / : RUN dotnet publish -c Release -o out
    ---> Running in 9febf9972a3d
    Microsoft (R) Build Engine version 16.1.+g14b0a930a7 for .NET Core
    Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 667.65 ms for /code/User.API.csproj.
    User.API -> /code/bin/Release/netcoreapp2./User.API.dll
    User.API -> /code/out/
    Removing intermediate container 9febf9972a3d
    ---> a7c92c3fd98b
    Step / : FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
    ---> c56aab97bc42
    Step / : WORKDIR /app
    ---> Using cache
    ---> 12d1df98dc50
    Step / : COPY --from=build /code/out ./
    ---> Using cache
    ---> b901e53b64f8
    Step / : EXPOSE
    ---> Using cache
    ---> c61ad551fa76
    Step / : ENTRYPOINT ["dotnet", "User.API.dll"]
    ---> Running in 36c66859c548
    Removing intermediate container 36c66859c548
    ---> 063fc4fe64ed
    Successfully built 063fc4fe64ed
    Successfully tagged wyt/aspnetcore:pred
  • 运行容器,进行端口映射和容器关联

    docker run -d -p : --name aspnetcore --link mysql01:db wyt/aspnetcore:pred
  • 成功访问
    【ASP.NET Core分布式项目实战】(五)Docker制作dotnet core控制台程序镜像

Docker Network

  1. 创建新的桥接网段
    # 创建mybridge桥接网段
    docker network create -d bridge mybridge
    # 显示网络信息
    docker network ls
  2. 通过桥接网络创建新的 aspnetcore 容器
    # 删除之前创建的容器
    docker rm aspnetcore -f
    # 使用mybridge桥接网段创建新的容器
    docker run -d -p : --net mybridge --name aspnetcore wyt/aspnetcore:pred

    我们可以通过命令查看容器 aspnetcore 的ip信息,为172.17.0.4

    docker inspect aspnetcore
  3. 将 mybridge 桥接网段与 mysql01 进行桥接
    # 将mybridge网段与mysql01所在网段进行桥接
    docker network connect mybridge mysql01

    这时可以查看 mysql01 的ip,然后进入 aspnetcore 容器内尝试是否可以ping通

    docker exec -it aspnetcore bash
    apt-get update -y
    apt-get install iputils-ping -y
    apt-get install net-tools -y
    ping 172.18.0.3
  4. 由于我们没有进行数据卷映射,所以配置文件无法更改,备注: appsettings.json 中使用的数据库为db,所以我们只能将 mysql01 改为 db

    # 容器重命名
    docker rename mysql01 db

    方法二

    # 进入容器
    docker exec -it aspnetcore bash
    ls
    apt-get install vim -y
    # 修改配置
    vim appsettings.json
    exit
    # 重启容器
    docker restart aspnetcore
  5. 完成
    curl http://47.111.84.191:8002/api/values

    或者访问

    【ASP.NET Core分布式项目实战】(五)Docker制作dotnet core控制台程序镜像

制作 docker compose

  1. 修改User.API项目
    # Startup.cs
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }
    app.UseMvc(); UserContextSeed.SeedAsync(app,loggerFactory);
    } # /Data/UserContextSeed.cs
    public class UserContextSeed
    { private ILogger<UserContextSeed> _logger;
    public UserContextSeed(ILogger<UserContextSeed> logger)
    {
    _logger = logger;
    } public static async Task SeedAsync(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
    using (var scope = app.ApplicationServices.CreateScope())
    {
    var userContext = scope.ServiceProvider.GetRequiredService<UserContext>();
    var logger = (ILogger<UserContextSeed>)scope.ServiceProvider.GetService(typeof(ILogger<UserContextSeed>));
    logger.LogDebug("Begin UserContextSeed SeedAsync"); userContext.Database.Migrate(); if (userContext.Users.Count() == )
    {
    userContext.Users.Add(new AppUser()
    {
    Name = "wyt"
    });
    userContext.SaveChanges();
    }
    }
    }
    }
  2. 安装docker-compose
    # 下载Docker Compose的当前稳定版本
    sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    # https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64
    # 建议迅雷下载后进行重命名,这样速度快
    # 对二进制文件应用可执行权限
    sudo chmod +x /usr/local/bin/docker-compose
    # 测试安装
    docker-compose --version
  3. 新建文件 docker-compose.yml 
    version: ''
    
    services:
    db:
    image: mysql/mysql-server
    container_name: db
    command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
    restart: always
    ports:
    - '3306:3306' #host物理直接映射端口为3306
    environment:
    MYSQL_ROOT_PASSWORD: pwd123456 #root管理员用户密码
    MYSQL_USER: jeese #创建jeese用户
    MYSQL_PASSWORD: pwd123456 #设置jeese用户的密码
    MYSQL_ROOT_HOST: '%'
    volumes:
    - "/home/wyt/beta/mysql-init:/docker-entrypoint-initdb.d" #设置数据库自动执行脚本目录,目录要存在 web:
    build: .
    container_name: aspnetcore
    ports:
    - '8003:80' #host物理直接映射端口为3306
    depends_on:
    - db
    # 初始化脚本mysql-init/init.sql
    use mysql;
    ALTER USER 'jeese'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd123456';
    ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd123456';
    FLUSH PRIVILEGES;
  4. docker-compose构建

    docker-compose build
    [root@localhost User.API]# docker-compose build
    db uses an image, skipping
    Building web
    Step / : FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
    ---> 155911c343f3
    Step / : WORKDIR /code
    ---> Using cache
    ---> 7525de38c042
    Step / : COPY *.csproj ./
    ---> Using cache
    ---> 397affedf1a6
    Step / : RUN dotnet restore
    ---> Using cache
    ---> 964ce7a0de36
    Step / : COPY . ./
    ---> Using cache
    ---> 5d18774ff1df
    Step / : RUN dotnet publish -c Release -o out
    ---> Using cache
    ---> 3353849a8dd8
    Step / : FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
    ---> c56aab97bc42
    Step / : WORKDIR /app
    ---> Using cache
    ---> 12d1df98dc50
    Step / : COPY --from=build /code/out ./
    ---> Using cache
    ---> 4e6819b010fe
    Step / : EXPOSE
    ---> Using cache
    ---> 2ee374887860
    Step / : ENTRYPOINT ["dotnet", "User.API.dll"]
    ---> Using cache
    ---> 2b06acc1b707
    Successfully built 2b06acc1b707
    Successfully tagged userapi_web:latest
    docker-compose up
    [root@localhost User.API]# docker-compose up
    Creating network "userapi_default" with the default driver
    Creating db ... done
    Creating aspnetcore ... done
    Attaching to db, aspnetcore
    db | [Entrypoint] MySQL Docker Image 8.0.-1.1.
    db | [Entrypoint] Initializing database
    aspnetcore | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[]
    aspnetcore | No XML encryptor configured. Key {67f35cc4-a4c3--ba07-7a0753ed0d09} may be persisted to storage in unencrypted form.
    aspnetcore | Hosting environment: Production
    aspnetcore | Content root path: /app
    aspnetcore | Now listening on: http://[::]:80
    aspnetcore | Application started. Press Ctrl+C to shut down.
    db | --05T09::.358708Z [System] [MY-] [Server] /usr/sbin/mysqld (mysqld 8.0.) initializing of server in progress as process
    db | --05T09::.360043Z [Warning] [MY-] [Server] --character-set-server: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
    db | --05T09::.360052Z [Warning] [MY-] [Server] --collation-server: 'utf8_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead.
    db | --05T09::.943704Z [Warning] [MY-] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
    db | --05T09::.131450Z [System] [MY-] [Server] /usr/sbin/mysqld (mysqld 8.0.) initializing of server has completed
    db | [Entrypoint] Database initialized
    db | --05T09::.902213Z [System] [MY-] [Server] /usr/sbin/mysqld (mysqld 8.0.) starting as process
    db | --05T09::.903376Z [Warning] [MY-] [Server] --character-set-server: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
    db | --05T09::.903389Z [Warning] [MY-] [Server] --collation-server: 'utf8_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead.
    db | --05T09::.456492Z [Warning] [MY-] [Server] CA certificate ca.pem is self signed.
    db | --05T09::.477501Z [System] [MY-] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.16' socket: '/var/lib/mysql/mysql.sock' port: MySQL Community Server - GPL.
    db | --05T09::.558190Z [System] [MY-] [Server] X Plugin ready for connections. Socket: '/var/run/mysqld/mysqlx.sock'
    db | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
    db | Warning: Unable to load '/usr/share/zoneinfo/leapseconds' as time zone. Skipping it.
    db | Warning: Unable to load '/usr/share/zoneinfo/tzdata.zi' as time zone. Skipping it.
    db | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
    db | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
    db |
    db | [Entrypoint] running /docker-entrypoint-initdb.d/init.sql
    db |
    db |
    db | --05T09::.092496Z [System] [MY-] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.).
    db | --05T09::.191280Z [System] [MY-] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.) MySQL Community Server - GPL.
    db | [Entrypoint] Server shut down
    db |
    db | [Entrypoint] MySQL init process done. Ready for start up.
    db |
    db | [Entrypoint] Starting MySQL 8.0.-1.1.
    db | --05T09::.417600Z [System] [MY-] [Server] /usr/sbin/mysqld (mysqld 8.0.) starting as process
    db | --05T09::.419490Z [Warning] [MY-] [Server] --character-set-server: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
    db | --05T09::.419504Z [Warning] [MY-] [Server] --collation-server: 'utf8_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead.
    db | --05T09::.858661Z [Warning] [MY-] [Server] CA certificate ca.pem is self signed.
    db | --05T09::.880107Z [System] [MY-] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.16' socket: '/var/lib/mysql/mysql.sock' port: MySQL Community Server - GPL.
    db | --05T09::.066024Z [System] [MY-] [Server] X Plugin ready for connections. Socket: '/var/run/mysqld/mysqlx.sock' bind-address: '::' port:
    aspnetcore | Application is shutting down...
    aspnetcore exited with code
    aspnetcore | Hosting environment: Production
    aspnetcore | Content root path: /app
    aspnetcore | Now listening on: http://[::]:80
    aspnetcore | Application started. Press Ctrl+C to shut down.
    # 停止docker-compose
    # docker-compose down

启动问题解决方式:由于docker-compose.yml文件中存在db依赖,所以要修改/Data/UserContextSeed.cs进行延迟数据库自动初始化

public static async Task SeedAsync(IApplicationBuilder app, ILoggerFactory loggerFactory,int? retry=)
{
var retryForAvaiability = retry.Value;
try
{
using (var scope = app.ApplicationServices.CreateScope())
{
var userContext = scope.ServiceProvider.GetRequiredService<UserContext>();
var logger = (ILogger<UserContextSeed>)scope.ServiceProvider.GetService(typeof(ILogger<UserContextSeed>));
logger.LogDebug("Begin UserContextSeed SeedAsync"); userContext.Database.Migrate(); if (userContext.Users.Count() == )
{
userContext.Users.Add(new AppUser()
{
Name = "wyt"
});
userContext.SaveChanges();
}
}
}
catch (Exception ex)
{
if (retryForAvaiability<)
{
retryForAvaiability++;
var logger = loggerFactory.CreateLogger(typeof(UserContextSeed));
logger.LogError(ex.ToString());
await Task.Delay(TimeSpan.FromSeconds());
await SeedAsync(app, loggerFactory, retryForAvaiability);
}
}
}