将代码库烘焙到生产图像中

Bake the codebase into production images

我目前正在使用 git 在我的不同主机上部署我的应用程序。我的代码库被安装到 运行 Docker images:

docker-compose.yml

services:
    app:
        image: my.registry.net:5000/my/app
        volumes:
            - .:/app
# [...]

所以我的部署是这样的:

$ git pull
$ docker-compose build && docker-compose up -d
$ docker-compose exec app some_dependencies_installation_command
$ docker-compose exec app some_cache_clearing_command

在我看来错误,原因有很多,您可以想象。虽然如果出现问题,我仍然可以 恢复 到我以前的 git 发布标签。

我想通过将我的代码库烘焙到映像中来简化我的部署,所以我不再使用 git 进行部署,而是依赖 镜像拉取。如果您不建议这样做,请不要犹豫,告诉我为什么。

我很想简单地将我的代码库添加到我的图像的末尾,并在主机中只安装所需的东西:

Docker文件

# [...]
ADD . /app

docker-compose.prod.yml

services:
    app:
        image: my.registry.net:5000/my/app
        volumes:
            - ./config.yml:/app/config.yml
            - ./logs:/app/logs
            - ./cache:/app/cache
            - ./sessions:/app/sessions
# [...]

所以一旦构建完成,我只需要使用以下命令部署我的图像:

$ docker-compose pull && docker-compose up -d

这是个好方法吗?我如何对我的图像进行版本化,以便在出现问题时可以 还原 它(因为 Dockerfile 实际上 永远不会改变 )?另外,是不是依赖于 Docker 文件的一层来 每次拉取整个代码库 而不是仅 diff(如 git)?

docker-compose 是 docker 的一种编排工具,但还有其他工具,例如 kubernetes 或 docker swarm。您应该亲自检查一下,考虑在哪里可以使用这些工具。

要对图像进行版本控制,您可以标记它们(使用 -t )。这样您就可以推出特定版本 my.registry.net:5000/my/app:1.1.0,等等

构建可以在您的机器上本地完成,也可以在 CI/CD 管道中自动完成。

Docker 不提取代码库,只提取文件层。如果您编译您的程序,则只应添加二进制文件和一些资产。图层已缓存,仅在需要时才拉出。

如果您想最小化注册表中的图像,可以在 docker 中使用多阶段构建。这使用多个阶段来构建最后一个容器,它只为您的应用程序提供服务,因此需要更少的文件。例如。您可以在一个阶段使用 javac 编译您的二进制文件,在另一个阶段使用 nodejs 和 webpack 编译资产并将这些生成的文件复制到最后一个只有 java 运行时和静态生成的容器资产。官方文档在这里:https://docs.docker.com/develop/develop-images/multistage-build/

你是对的,你的方法有很多问题,包括:

  • Your containers are not ephemeral
  • 您的应用程序的每个实例都必须等待构建完成才能开始
  • 如果构建失败会怎样?排除手动测试,您只知道 "deploy time"
  • 处的失败
  • 构建过程将在每个 运行 上生成相同的工件(前提是您没有更改源)。为什么要浪费时间重复这个过程?

与其在容器中烘焙应用的源代码并在每次部署时重建您的应用,您可以直接构建您的应用(如果您想让构建环境可移植,可以在另一个 "build" 容器中构建也,即一个仅用于构建的容器)并将构建生成的工件添加到将部署在生产环境中的另一个映像中(在测试和 QA 之后,因为您确实有多个环境,对吧?:)。

Docker 图像标记可以这样工作:在每次提交(可能在最新的图像标签下)和每个 git 标签(在相应的图像标签下)上构建一个图像。示例:对于主分支上的每个提交,您 "update" your-image:latest 并且当您标记 1.0.0 版本时,您会生成 your-image:1.0.0.

如果您有某种 CI 服务,您也可以自动执行此过程。

请注意,在生产环境中使用最新标签可能会导致环境不稳定,因为您不知道自己部署的是哪个版本。使用另一个 more-specific 标签。

更新:正如其他人所指出的,将源代码放入容器是一种可以应用于开发阶段的策略。但这不是你的情况,因为我假设你在谈论(预)生产部署。

将源代码绑定到容器上,通常是为了在开发时有一个快速的反馈循环。这是通过避免在代码更改完成后重建图像来实现的。此技术主要用于本地开发目的。对于生产环境,不应使用此技术。

Docker 图片设计为 self-contained。镜像需要的所有依赖都应该打包在镜像内部。依赖 bind mounts 提供源码或依赖不是 推荐,因为生成的 Docker 图像不可移植。每当您切换到一台新机器时,您都需要将该源代码和依赖项转移到那台新机器上。

因此将源代码添加到图像中是推荐的方法。您还应该受益于标准 Docker 工作流程,您可以在其中构建图像并为其指定特定标签(通常是版本),然后推送 它进入 Docker 存储库。从那时起,您只需拉取映像并在您拥有的任何机器上启动它。

至于版本控制:构建图像时,您使用源代码版本(或 git 标记或提交 ID)对其进行标记。

docker build -t <image-name>:<version> .

并且您还使用 docker pull <image-name>:<version> 提取特定版本。使用此技术,您可以随时恢复到图像的任何版本。