mongodb 如何在 Docker compose 中运行一次命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36407477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to run a command once in Docker compose
提问by Ivan
So I'm working on a docker compose file to deploy my Go web server. My server uses mongo, so I added a data volume container and the mongo service in docker compose. Then I wrote a Dockerfile in order to build my Go project, and finally run it.
所以我正在使用 docker compose 文件来部署我的 Go Web 服务器。我的服务器使用的是 mongo,所以我在 docker compose 中添加了一个数据卷容器和 mongo 服务。然后我写了一个 Dockerfile 来构建我的 Go 项目,最后运行它。
However, there is another step that must be done. Once my project has been compiled, I have to run the following command:
./my-project -setup
但是,还有一个步骤必须完成。编译我的项目后,我必须运行以下命令:
./my-project -setup
This will add some necessary information to the database, and the information only needs to be added once. I can't however add this step on the Dockerfile (in the build process) because mongo must already be started.
这会向数据库添加一些必要的信息,并且信息只需要添加一次。但是,我无法在 Dockerfile 上添加此步骤(在构建过程中),因为 mongo 必须已经启动。
So, how can I achieve this? Even if I restart the server and then run again docker-compose up
I don't want this command to be executed again.
那么,我怎样才能做到这一点?即使我重新启动服务器然后再次运行,docker-compose up
我也不希望再次执行此命令。
I think I'm missing some Docker understanding, because I don't actually understand everything about data volume containers (are they just stoppedcontainers that mount a volume?).
Also, if I restart the server, and then run docker-compose up
, which commands will be run? Will it just start the same container that was now stopped with the given CMD?
我想我缺少对 Docker 的一些理解,因为我实际上并不了解有关数据卷容器的所有内容(他们是否只是停止了装载卷的容器?)。另外,如果我重新启动服务器,然后运行docker-compose up
,将运行哪些命令?它会启动现在用给定 CMD 停止的同一个容器吗?
In any case, here is my docker-compose.yml:
无论如何,这是我的 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
And here is my Dockerfile to build my project image:
这是我的 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
采纳答案by thaJeztah
I suggest to add an entrypoint-script to your container; in this entrypoint-script, you can check if the database has been initialized, and if it isn't, perform the required steps.
我建议在你的容器中添加一个入口点脚本;在这个入口点脚本中,您可以检查数据库是否已初始化,如果没有,请执行所需的步骤。
As you noticed in your question, the order in which services / containers are started should not be taken for granted, so it's possible your application container is started beforethe database container, so the script should take that into account.
正如您在问题中所注意到的,服务/容器的启动顺序不应被视为理所当然,因此您的应用程序容器可能在数据库容器之前启动,因此脚本应考虑到这一点。
As an example, have a look at the official WordPress image, which performs a one-time initialization of the database in it's entrypoint-script. The script attempts to connect to the database (and retries if the database cannot be contacted (yet)), and checks if initialization is needed; https://github.com/docker-library/wordpress/blob/df190dc9c5752fd09317d836bd2bdcd09ee379a5/apache/docker-entrypoint.sh#L146-L171
举个例子,看看官方的 WordPress 图像,它在它的入口点脚本中执行数据库的一次性初始化。该脚本尝试连接到数据库(如果无法访问数据库(还),则重试),并检查是否需要初始化;https://github.com/docker-library/wordpress/blob/df190dc9c5752fd09317d836bd2bdcd09ee379a5/apache/docker-entrypoint.sh#L146-L171
NOTE
笔记
I notice you created a "data-only container" to attach your volume to. Since docker 1.9, docker has volume management, including naming volumes. Because of this, you no longer need to use "data-only" containers.
我注意到您创建了一个“仅数据容器”来附加您的卷。从 docker 1.9 开始,docker 有了卷管理,包括命名卷。因此,您不再需要使用“仅数据”容器。
You can remove the data-only container from your compose file, and change your mongo service to look something like this;
您可以从撰写文件中删除仅数据容器,并将您的 mongo 服务更改为如下所示;
mongo:
image: mongo:latest
volumes:
- mongodata:/data/db
ports:
- "28001:27017"
command: --smallfiles --rest --auth
This should create a new volume, named mongodata
if it doesn't exist, or re-use the existing volume with that name. You can list all volumes using docker volume ls
and remove a volume with docker volume rm <some-volume>
if you no longer need it
这应该创建一个新卷,mongodata
如果它不存在则命名,或者重新使用具有该名称的现有卷。如果您不再需要它,您可以使用它列出所有卷docker volume ls
并删除docker volume rm <some-volume>
它
回答by Petr Shevtsov
You could try to use ONBUILD
instruction:
你可以尝试使用ONBUILD
指令:
The ONBUILD
instruction adds to the image a trigger instruction to be executed at a later time, when the image is used as the base for another build. The trigger will be executed in the context of the downstream build, as if it had been inserted immediately after the FROM
instruction in the downstream Dockerfile
.
该ONBUILD
指令将一个触发指令添加到图像中,以便稍后在该图像用作另一个构建的基础时执行。触发器将在下游构建的上下文中执行,就好像它是FROM
在下游的指令之后立即插入的Dockerfile
。
Any build instruction can be registered as a trigger.
任何构建指令都可以注册为触发器。
This is useful if you are building an image which will be used as a base to build other images, for example an application build environment or a daemon which may be customized with user-specific configuration.
如果您正在构建将用作构建其他镜像的基础的镜像,例如应用程序构建环境或可以使用用户特定配置自定义的守护程序,这将非常有用。
For example, if your image is a reusable Python application builder, it will require application source code to be added in a particular directory, and it might require a build script to be called afterthat. You can't just call ADD
and RUN
now, because you don't yet have access to the application source code, and it will be different for each application build. You could simply provide application developers with a boilerplate Dockerfile
to copy-paste into their application, but that is inefficient, error-prone and difficult to update because it mixes with application-specific code.
例如,如果您的映像是可重用的 Python 应用程序构建器,则需要将应用程序源代码添加到特定目录中,并且可能需要在此之后调用构建脚本。您不能只调用ADD
and RUN
now,因为您还没有访问应用程序源代码的权限,而且每个应用程序构建都会有所不同。您可以简单地为应用程序开发人员提供一个样板Dockerfile
以将其复制粘贴到他们的应用程序中,但这效率低下、容易出错且难以更新,因为它与特定于应用程序的代码混合在一起。
The solution is to use ONBUILD
to register advance instructions to run later, during the next build stage.
解决方案是ONBUILD
在下一个构建阶段使用注册高级指令以便稍后运行。
Here's how it works:
这是它的工作原理:
- When it encounters an
ONBUILD
instruction, the builder adds a trigger to the metadata of the image being built. The instruction does not otherwise affect the current build. - At the end of the build, a list of all triggers is stored in the image manifest, under the key
OnBuild
. They can be inspected with thedocker inspect
command. - Later the image may be used as a base for a new build, using the
FROM
instruction. As part of processing theFROM
instruction, the downstream builder looks forONBUILD
triggers, and executes them in the same order they were registered. If any of the triggers fail, theFROM
instruction is aborted which in turn causes the build to fail. If all triggers succeed, theFROM
instruction completes and the build continues as usual. - Triggers are cleared from the final image after being executed. In other words they are not inherited by “grand-children” builds.
- 当遇到
ONBUILD
指令时,构建器会向正在构建的图像的元数据添加触发器。该指令不会以其他方式影响当前构建。 - 在构建结束时,所有触发器的列表存储在图像清单中的键下
OnBuild
。可以使用docker inspect
命令检查它们。 - 稍后,可以使用该
FROM
指令将该映像用作新构建的基础。作为处理FROM
指令的一部分,下游构建器查找ONBUILD
触发器,并按照它们注册的顺序执行它们。如果任何触发器失败,FROM
指令就会中止,从而导致构建失败。如果所有触发器都成功,则FROM
指令完成并且构建照常继续。 - 触发器在执行后从最终图像中清除。换句话说,它们不是由“孙子”构建继承的。
回答by Cortwave
Your application need some initial state for working. It means that you should:
您的应用程序需要一些初始状态才能工作。这意味着你应该:
- Check if required state already exists
- Depends on first step result init state or not
- 检查所需状态是否已存在
- 取决于第一步结果初始化状态与否
You can write program for checking current database state (here I will use bash script but it can be every other language program):
您可以编写用于检查当前数据库状态的程序(这里我将使用 bash 脚本,但它可以是所有其他语言程序):
RUN if $(./check.sh); then my_project -setup; fi
In my case if script will return 0 (success exit status) then setup
command will be called.
在我的情况下,如果脚本将返回 0(成功退出状态),则将setup
调用命令。