【问题标题】:Bake the codebase into production images将代码库烘焙到生产映像中
【发布时间】:2018-08-25 09:07:14
【问题描述】:

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

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 进行部署,而是依赖 image pull。如果您不这样做以及为什么不建议,请随时告诉我。

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

Dockerfile

# [...]
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 实际上从不改变)?另外,不是每次都依赖 Dockerfile 的一层来提取整个代码库,而不仅仅是 diff(就像 git 一样)?

【问题讨论】:

    标签: git docker production-environment


    【解决方案1】:

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

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

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

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

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

    请注意,在生产环境中使用 latest 标签可能会导致环境不稳定,因为您不知道自己正在部署哪个版本。使用另一个更具体的标签。

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

    【讨论】:

    • 好的,我确实应该在每次部署之前都有一个专门的构建步骤。关于源代码,对于 javascript 或 php 等非编译应用程序,我们必须将它们全部包含到图像中。
    • @PierredeLESPINAY 这不一定是真的,因为即使使用 JS、HTML 和 CSS,您也可能需要执行一些 gulp/grunt 任务来最小化、压缩、源映射等文件
    【解决方案2】:

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

    Docker 镜像被设计成独立的。映像所需的所有依赖项都应打包在映像中。依赖绑定挂载提供源代码或依赖项不是 推荐,因为生成的 Docker 映像不可移植。每当您切换到新机器时,都需要将该源代码和依赖项转移到该新机器上。

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

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

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

    您还可以使用docker pull &lt;image-name&gt;:&lt;version&gt; 提取特定版本。使用这种技术,您可以随时恢复到图像的任何版本。

    【讨论】:

    • 图像可移植性确实是个问题
    【解决方案3】:

    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/

    【讨论】:

    • 我来看看 Docker swarm,我一直认为它只是为了扩展考虑,但似乎做得更多
    • 关于层,对于具有依赖关系的非编译源,它可能是一个很大的......但我不确定我还能怎么做
    • 在这种情况下,您必须添加它们。没有办法解决这个问题。您应该考虑最后添加它们,因为这些遗嘱文件的变化最大。
    猜你喜欢
    • 2013-02-06
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多