如何在 Docker compose 中运行一次命令
Posted
技术标签:
【中文标题】如何在 Docker compose 中运行一次命令【英文标题】:How to run a command once in Docker compose 【发布时间】:2016-07-24 06:42:21 【问题描述】:所以我正在开发一个 docker compose 文件来部署我的 Go Web 服务器。我的服务器使用 mongo,所以我在 docker compose 中添加了一个数据卷容器和 mongo 服务。 然后我写了一个 Dockerfile 来构建我的 Go 项目,最后运行它。
但是,还有一个步骤必须完成。编译项目后,我必须运行以下命令:
./my-project -setup
这会将一些必要的信息添加到数据库中,这些信息只需要添加一次。 但是我不能在 Dockerfile 上添加这个步骤(在构建过程中),因为 mongo 必须已经启动。
那么,我该如何实现呢?即使我重新启动服务器然后再次运行docker-compose up
我也不希望再次执行此命令。
我认为我缺少对 Docker 的一些理解,因为我实际上并不了解有关数据卷容器的所有内容(它们只是 停止 挂载卷的容器吗?)。
另外,如果我重新启动服务器,然后运行docker-compose up
,将会运行哪些命令?它会启动现在使用给定 CMD 停止的同一个容器吗?
无论如何,这是我的 docker-compose.yml:
version: '2'
services:
mongodata:
image: mongo:latest
volumes:
- /data/db
command: --break-mongo
mongo:
image: mongo:latest
volumes_from:
- mongodata
ports:
- "28001:27017"
command: --smallfiles --rest --auth
my_project:
build: .
ports:
- "6060:8080"
depends_on:
- mongo
- mongodata
links:
- mongo
这是用于构建项目映像的 Dockerfile:
FROM golang
ADD . /go/src/my_project
RUN cd /go/src/my_project && go get
RUN go install my_project
RUN my_project -setup
ENTRYPOINT /go/bin/my_project
EXPOSE 8080
【问题讨论】:
您可能可以设置一个标志(文件或数据库)来指示脚本是否已运行并且数据是否已初始化。 【参考方案1】:我建议在您的容器中添加一个入口点脚本;在这个入口点脚本中,您可以检查数据库是否已初始化,如果没有,请执行所需的步骤。
正如您在问题中所注意到的,服务/容器的启动顺序不应被视为理所当然,因此您的应用程序容器可能在数据库容器之前启动,因此脚本应该考虑到这一点。
例如,看一下官方的 WordPress 图像,它在其入口点脚本中执行一次数据库初始化。该脚本尝试连接到数据库(如果(还)无法联系到数据库,则重试),并检查是否需要初始化; https://github.com/docker-library/wordpress/blob/df190dc9c5752fd09317d836bd2bdcd09ee379a5/apache/docker-entrypoint.sh#L146-L171
注意
我注意到您创建了一个“仅数据容器”来附加您的卷。从 docker 1.9 开始,docker 有了卷管理,包括命名卷。因此,您不再需要使用“仅数据”容器。
您可以从 compose 文件中删除仅数据容器,并将您的 mongo 服务更改为如下所示;
mongo:
image: mongo:latest
volumes:
- mongodata:/data/db
ports:
- "28001:27017"
command: --smallfiles --rest --auth
这应该创建一个名为mongodata
的新卷(如果它不存在),或者重新使用具有该名称的现有卷。您可以使用docker volume ls
列出所有卷,如果不再需要使用docker volume rm <some-volume>
删除卷
【讨论】:
【参考方案2】:你可以尝试使用ONBUILD
instruction:
ONBUILD
指令向映像添加了一条触发指令,该指令将在以后将映像用作另一个构建的基础时执行。触发器将在下游构建的上下文中执行,就好像它是在下游 Dockerfile
中的 FROM
指令之后立即插入的一样。
任何构建指令都可以注册为触发器。
如果您正在构建一个镜像,该镜像将用作构建其他镜像的基础,例如应用程序构建环境或可以使用用户特定配置自定义的守护程序,这将非常有用。
例如,如果您的图像是可重用的 Python 应用程序构建器,则需要将应用程序源代码添加到特定目录中,并且可能需要在之后调用构建脚本。您现在不能只调用ADD
和RUN
,因为您还没有访问应用程序源代码的权限,而且每个应用程序构建都会有所不同。您可以简单地向应用程序开发人员提供样板文件 Dockerfile
以将其复制粘贴到他们的应用程序中,但这样做效率低、容易出错且难以更新,因为它与特定于应用程序的代码混合在一起。
解决方案是使用ONBUILD
注册高级指令,以便稍后在下一个构建阶段运行。
它是这样工作的:
-
当遇到
ONBUILD
指令时,构建器将触发器添加到正在构建的映像的元数据中。该指令不会影响当前的构建。
在构建结束时,所有触发器的列表都存储在映像清单中,位于键 OnBuild
下。可以使用docker inspect
命令对其进行检查。
稍后可以使用FROM
指令将映像用作新构建的基础。作为处理FROM
指令的一部分,下游构建器查找ONBUILD
触发器,并按照它们注册的相同顺序执行它们。如果任何触发器失败,FROM
指令将被中止,从而导致构建失败。如果所有触发器都成功,则FROM
指令完成,构建照常继续。
触发器在执行后从最终图像中清除。换句话说,它们不会被“孙子”构建继承。
【讨论】:
你建议如何使用它? @Cortwave 它使结构有点复杂,因为需要先使用ONBUILD
指令创建一个“基础”图像,然后再为应用程序创建一个图像。至于我,我最好在应用程序端解决这个问题,或者使用makefile之类的东西。
@Cortwave 我最好让 db 尽可能简单,所以在应用程序端进行编辑。
但据我了解ONBUILD
命令它只是在您将图像用作其他构建的基础图像时执行一些触发器。换句话说,每次你使用你的应用图像作为基础图像时,它都会被执行。
@Cortwave 是的,每次构建镜像时都会执行,但不是每次运行容器时都会执行。【参考方案3】:
您的应用程序需要一些初始状态才能工作。这意味着您应该:
-
检查所需状态是否已存在
取决于第一步结果初始化状态与否
您可以编写检查当前数据库状态的程序(这里我将使用 bash 脚本,但它可以是所有其他语言程序):
RUN if $(./check.sh); then my_project -setup; fi
在我的情况下,如果脚本将返回 0(成功退出状态),则将调用 setup
命令。
【讨论】:
以上是关于如何在 Docker compose 中运行一次命令的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 pycharm 调试在 docker-compose 中运行的进程
如何使用来自其他目录的 docker-compose 环境变量
使用 Docker-Compose 时如何执行 Django 数据库迁移?
如何在 AWS CodeBuild 上运行 docker-compose?