如何在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的上下文中执行,就像它已经在下游DockerfileFROM指令之后立即插入Dockerfile

任何构build指令都可以注册为触发器。

如果您正在构build将用作构build其他映像的基础的映像,那么这非常有用,例如可以使用用户特定configuration自定义的应用程序构build环境或守护程序。

例如,如果您的映像是可重用的Python应用程序构build器,则需要将应用程序源代码添加到特定目录中,并且可能需要之后调用构build脚本。 你现在不能只调用ADDRUN ,因为你还没有访问应用程序的源代码,每个应用程序的版本都不一样。 您可以简单地向应用程序开发人员提供一个样板化的Dockerfile来复制粘贴到他们的应用程序中,但是效率低,容易出错并且难以更新,因为它与特定于应用程序的代码混合在一起。

解决scheme是使用ONBUILD注册先行指令,以便在以后的构build阶段运行。

这是如何工作的:

  1. 当遇到ONBUILD指令时,构build器会为正在构build的映像的元数据添加一个触发器。 该指令不会影响当前的构build。
  2. 在构build结束时,所有触发器的列表都存储在图像清单中的OnBuildOnBuild 。 可以使用docker inspect命令检查它们。
  3. 稍后,可以使用FROM指令将图像用作新构build的基础。 作为处理FROM指令的一部分,下游构build器会查找ONBUILD触发器,并按照它们所注册的相同顺序执行它们。 如果任何触发器失败,则FROM指令将被中止,从而导致构build失败。 如果所有触发器都成功,则FROM指令完成,并且构build继续照常。
  4. 触发器在执行后从最终图像中清除。 换句话说,他们不是由“大孩子”构build的遗传。

你的应用程序需要一些初始状态来工作。 这意味着你应该:

  1. 检查所需的状态是否已经存在
  2. 取决于第一步结果init的状态与否

你可以编写检查当前数据库状态的程序(这里我将使用bash脚本,但它可以是其他语言程序):

 RUN if $(./check.sh); then my_project -setup; fi 

在我的情况下,如果脚本将返回0(成功退出状态)然后setup命令将被调用。