如何在Docker中运行一个命令
所以我正在编写Docker撰写文件来部署我的Go Web服务器。 我的服务器使用mongo,所以我添加了一个数据卷容器和docker中的mongo服务。 然后我写了一个Dockerfile来构build我的Go项目,最后运行它。
但是,还有一个步骤是必须完成的。 一旦我的项目被编译,我必须运行以下命令: ./my-project -setup
这将添加一些必要的信息到数据库,并且只需要添加一次信息 。 我不能在Dockerfile上添加这个步骤(在构build过程中),因为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来build立我的项目图像:
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
我build议添加一个入口点脚本到你的容器; 在此入口点脚本中,可以检查数据库是否已经初始化,如果不是,请执行所需的步骤。
正如你在你的问题中注意到的那样,服务/容器的启动顺序不应该被认为是理所当然的,所以你的应用程序容器可能在数据库容器之前启动,所以脚本应该考虑到这一点。
作为一个例子,看看官方的WordPress图像,它在入口点脚本中执行数据库的一次初始化。 该脚本尝试连接到数据库(如果数据库不能联系(但仍然)),并检查是否需要初始化; https://github.com/docker-library/wordpress/blob/df190dc9c5752fd09317d836bd2bdcd09ee379a5/apache/docker-entrypoint.sh#L146-L171
注意
我注意到你创build了一个“数据专用容器”来附加你的卷。 由于docker1.9,docker有卷pipe理,包括命名卷。 因此,您不再需要使用“仅数据”容器。
您可以从撰写文件中删除仅限数据的容器,并将您的mongo服务更改为如下所示;
mongo: image: mongo:latest volumes: - mongodata:/data/db ports: - "28001:27017" command: --smallfiles --rest --auth
这应该创build一个新卷,如果它不存在,则将其命名为mongodata
,或者重新使用具有该名称的现有卷。 您可以使用docker volume ls
列出所有卷,如果不再需要,可以使用docker volume rm <some-volume>
删除卷
您可以尝试使用ONBUILD
指令 :
ONBUILD
指令为图像添加一个触发器指令,稍后将该图像用作另一个构build的基础。 触发器将在下游构build的上下文中执行,就像它已经在下游Dockerfile
的FROM
指令之后立即插入Dockerfile
。
任何构build指令都可以注册为触发器。
如果您正在构build将用作构build其他映像的基础的映像,那么这非常有用,例如可以使用用户特定configuration自定义的应用程序构build环境或守护程序。
例如,如果您的映像是可重用的Python应用程序构build器,则需要将应用程序源代码添加到特定目录中,并且可能需要在此之后调用构build脚本。 你现在不能只调用ADD
和RUN
,因为你还没有访问应用程序的源代码,每个应用程序的版本都不一样。 您可以简单地向应用程序开发人员提供一个样板化的Dockerfile
来复制粘贴到他们的应用程序中,但是效率低,容易出错并且难以更新,因为它与特定于应用程序的代码混合在一起。
解决scheme是使用ONBUILD
注册先行指令,以便在以后的构build阶段运行。
这是如何工作的:
- 当遇到
ONBUILD
指令时,构build器会为正在构build的映像的元数据添加一个触发器。 该指令不会影响当前的构build。 - 在构build结束时,所有触发器的列表都存储在图像清单中的
OnBuild
键OnBuild
。 可以使用docker inspect
命令检查它们。 - 稍后,可以使用
FROM
指令将图像用作新构build的基础。 作为处理FROM
指令的一部分,下游构build器会查找ONBUILD
触发器,并按照它们所注册的相同顺序执行它们。 如果任何触发器失败,则FROM
指令将被中止,从而导致构build失败。 如果所有触发器都成功,则FROM
指令完成,并且构build继续照常。 - 触发器在执行后从最终图像中清除。 换句话说,他们不是由“大孩子”构build的遗传。
你的应用程序需要一些初始状态来工作。 这意味着你应该:
- 检查所需的状态是否已经存在
- 取决于第一步结果init的状态与否
你可以编写检查当前数据库状态的程序(这里我将使用bash脚本,但它可以是其他语言程序):
RUN if $(./check.sh); then my_project -setup; fi
在我的情况下,如果脚本将返回0(成功退出状态)然后setup
命令将被调用。