使用容器编排处理 EF Core 迁移

Posted

技术标签:

【中文标题】使用容器编排处理 EF Core 迁移【英文标题】:Handle EF Core migrations with container orchestration 【发布时间】:2019-09-26 06:40:51 【问题描述】:

我正在使用 ASP.Net Core、Entity Framework Core 和 PostgreSQL 构建一个普通的 Web 应用程序,我想使用 Docker 之上的容器编排器(无论是 Docker Swarm 还是 Kubernetes)进行部署。

我想知道如何在部署过程中集成我的数据库迁移。

我目前的Dockerfile(我正在使用多阶段构建)是:

FROM mcr.microsoft.com/dotnet/core/sdk:2.2-alpine as builder
COPY . /app
WORKDIR /app
RUN dotnet publish -c Release -o publish

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
COPY --from=builder /app/publish /app/
WORKDIR /app
CMD dotnet MyApp.dll

我的docker-compose.yml(我想通过docker stack 命令使用它,所以它不包括build 选项的使用):

version: '3'

services:
    app:
        image: edouardberthe/myapp
        ports:
            - 80:80
        depends_on:
            - db
    db:
        image: postgres:latest
        ports:
            - 5432:5432
        volumes:
            - data:/var/lib/postgresql/data
        environment:
            POSTGRES_USER: myapp_user
            POSTGRES_PASSWORD: myapp_pass
            POSTGRES_DB: myapp_db
volumes:
    data:

现在我想在每次部署时启动我的迁移(由 Entity Framework 核心通过 dotnet ef migrations add 生成)。

在开发过程中,我使用dotnet ef database update。 但是,我不能这样做,因为这意味着我在运行时需要 .Net Core SDK(此外,我从 SO 帖子和 Microsoft 文档中看到它被认为是一种不好的做法)。

我从this discussion on github 看到,最好使用dotnet ef migrations script --idempotent,它会在构建时生成纯SQL 迁移文件,然后在运行时运行此脚本。但是(如帖子中所说)“唯一的问题是我还需要一个命令行客户端来用于运行时映像上使用的数据库。”

如果我遵循这个过程,我的 Dockerfile 会变成这样的东西(更大更复杂):

FROM mcr.microsoft.com/dotnet/core/sdk:2.2-alpine as builder
COPY . /app
WORKDIR /app
RUN dotnet publish -c Release -o publish \
    && dotnet ef migrations script --output publish/migrate.sql --idempotent 

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
COPY --from=builder /app/publish /app/
WORKDIR /app
RUN apt-get update \
    && mkdir -p /usr/share/man/man1 \
    && mkdir -p /usr/share/man/man7 \
    && apt-get install --no-install-recommends -y postgresql-client \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean
CMD psql -f migrate.sql && dotnet MyApp.dll

mkdir 这两条线来自this post 和this fix) 除了增加了很大的复杂性之外,最后一行 CMD 不起作用,因为我无权访问数据库! 我确实知道数据库名称、用户和密码是什么(即使我认为将其放在 Dockerfile 中并不是一个好主意),但我还不知道 host 会是什么。

在这样的设置中是否有更好的方法来处理迁移?

谢谢

【问题讨论】:

这不是编排甚至容器的工作。迁移数据库模式应该是 CI/CD 管道的一部分。将最终产品部署到 Kubernetes/Swarm 将是此类管道的最后一步。 您好,谢谢您的回答。但是,即使我使用的是 CI/CD 管道,正如您所说,在(最后)时刻我确实需要在 K8s/Swarm 上进行部署。此时,我的生产数据库确实必须更新,因此必须启动迁移。你能详细说明一下你的意思吗? 假设您在容器中运行数据库,实际数据文件在容器外部,并且可以在实际部署新容器之前进行修改,即使有新容器。关键是它在实际集群部署之外。有各种 CI/CD 工具可以处理数据库迁移。你只需要做一些研究并选择一个。 好的,但是拥有一个撰写文件有什么意义呢?我认为 docker Swarm 一个持续部署工具。 ..部署工具。基本上,“docker stack deploy -c compose.file”命令不是应该处理整个部署过程吗? 【参考方案1】:

我不确定这是否是最佳做法,但您可以从 C# 代码触发架构升级。您必须在 Startup.cs 的 Configure 方法中添加以下代码:

using (var serviceScope = app.ApplicationServices
    .GetRequiredService<IServiceScopeFactory>()
    .CreateScope())

    using (var context = serviceScope.ServiceProvider.GetService<EasyAdminContext>())
    
        context.Database.Migrate();
    

这样数据库会在应用启动时更新

【讨论】:

多容器运行时会出现问题 你的意思是什么问题?我认为第一个容器启动会更新架构,其他容器不会因为数据库中的 __EFMigrationsHistory 表而开始更新。 __EFMigrationsHistory 表周围没有锁定/事务,因此 2 个容器可以尝试同时执行相同的迁移。这往往会失败:) 太好了,他们把它留在这里了。我还是不知道该怎么办。 @dystopiandev,对此感到抱歉 :) 答案实际上取决于您的设置。如果您的应用只有一个副本,那么您可以选择我建议的选项。

以上是关于使用容器编排处理 EF Core 迁移的主要内容,如果未能解决你的问题,请参考以下文章

.net core docker容器编排部署(linux)

搭建 kubernetes 容器编排平台

一文带你了解K8S 容器编排(下)

如何从Rancher 1.6迁移到Rancher 2.0?这份清单可以帮助你!

为啥在编排的容器化架构中,我们需要一个 Web 服务器和一个应用服务器?

Docker Compose容器编排