在Docker容器中自动运行一个服务

40
我正在设置一个简单的镜像:其中包含Riak(一种NoSQL数据库)。该镜像使用riak start作为CMD启动Riak服务。现在,如果我使用docker run -d quintenk/riak-dev命令将其作为守护进程运行,它确实会启动Riak进程(我可以在日志中看到)。然而,它会在几秒钟后自动关闭。如果我使用docker run -i -t quintenk/riak-dev /bin/bash命令运行它,则不会启动riak进程(更新:请参见答案以获取解释)。事实上,根本没有任何服务在运行。我可以在终端上手动启动它,但我希望Riak能够自动启动。我认为这种行为也会发生在其他服务上,Riak只是一个例子。
因此,运行/重新启动容器应自动启动Riak。如何正确设置?
下面是Dockerfile,可用于创建该镜像(更新:使用所选答案进行了修改):
FROM ubuntu:12.04
RUN apt-get update
RUN apt-get install -y openssh-server curl 
RUN curl http://apt.basho.com/gpg/basho.apt.key | apt-key add -
RUN bash -c "echo deb http://apt.basho.com precise main > /etc/apt/sources.list.d/basho.list"
RUN apt-get update
RUN apt-get -y install riak
RUN perl -p -i -e 's/(?<=\{http,\s\[\s\{")127\.0\.0\.1/0.0.0.0/g' /etc/riak/app.config
EXPOSE 8098 
CMD /bin/riak start && tail -F /var/log/riak/erlang.log.1

编辑:根据sesm的建议,CMD中的-f更改为-F。


我的回答

在使用Docker一段时间后,我养成了使用supervisord运行进程的习惯。如果您需要示例代码,请查看https://github.com/Krijger/docker-cookbooks。我将我的supervisor映像用作所有其他映像的基础。我在这里写了关于使用supervisor的博客here


顺便说一下,我现在(为了开发目的)使用容器启动它,附加到它,然后启动Riak命令行。 - qkrijger
6个回答

45
为了让Docker容器持续运行,您需要在前台保持一个活动进程。
所以您可以用以下内容替换Dockerfile中的最后一行。
CMD /bin/riak console

甚至更多

CMD /bin/riak start && tail -F /var/log/riak/erlang.log.1

请注意,您不能拥有多行CMD语句,只有最后一行会被执行。


我现在正在使用第二个选项。谢谢你的反馈! - qkrijger

32
使用tail命令来保持容器运行是一种hack方法。此外,请注意,使用-f选项时,容器会在日志轮换发生时终止(可以通过使用-F选项来避免这种情况)。
更好的解决方案是使用supervisor。请查看这个tutorial,了解如何在Docker容器中运行Riak。

3
看起来很有前途。 如果你已经有使用它与Docker结合的经验,你愿意在这里分享一个简短的例子吗? - qkrijger

5
如果我使用“docker run -i -t quintenk/riak-dev /bin/bash”运行它,那么Riak进程不会启动。
看起来你只想在附加到容器时监视日志。我的用例略有不同,我想自动启动命令,但是我想能够附加到容器并处于bash shell中。我解决了我们两个问题,方法如下:
在镜像/容器中,在/etc/bash.bashrc文件的末尾添加要自动启动的命令。
在你的情况下,只需添加一行“/bin/riak start && tail -F /var/log/riak/erlang.log.1”,或根据所需功能将“/bin/riak start”和“tail -F /var/log/riak/erlang.log.1”放在不同的行上。
现在提交你对容器的更改,并再次使用“docker run -i -t quintenk/riak-dev /bin/bash”运行它。你会发现你在bashrc中放置的命令已经在你附加时运行。

3
那其实是一个非常好的解决方案 :) 请注意,0.6.5版本添加了docker run -a选项以支持监管程序的兼容性。您可能也想检查一下。 - qkrijger
不是一个好的解决方案。如果进程退出,tailf 将继续运行,容器将不会退出,你会怎么做? - Daniel Andrei Mincă
@MincăDanielAndrei 你说的有一定道理。这不是一个可供生产使用的解决方案,正如你所提到的那样,它是不可持续的。最好的方法是尽可能让你的镜像的 entrypointcmd 成为你想要运行的进程本身。通常这意味着你需要找到该进程的 --no-daemon 选项,以防止 entrypoint 后台自身并退出容器,并将其日志记录到 stderr/stdout。 - damick
1
另一个选择是使用 dumb-init,容器的 init 系统,如此所述 https://engineeringblog.yelp.com/2016/01/dumb-init-an-init-for-docker.html - Daniel Andrei Mincă

5

"docker run -i -t quintenk/riak-dev /bin/bash" 没有启动 riak 进程的原因是:在 Dockerfile 中使用 CMD 实际上与使用 docker run {image} {command} 启动容器是一样的。正如 Gigablah 所指出的那样,只有最后一个 CMD 被使用,因此在这种情况下,Dockerfile 中编写的 CMD 将被覆盖。

通过在 Buildfile 中使用 CMD /bin/riak start && tail -f /var/log/riak/erlang.log.1,您可以像使用 docker run -d {image} 一样将容器作为后台进程启动,这将非常顺利。


同样地,我正在尝试将CDH容器化。在这种情况下,我使用RUN命令启动服务。它在运行完RUN命令后启动并退出。可能的原因是什么? - Gibbs
@GopsAB 可能存在某种错误导致主进程无法持续运行。请检查您的日志或尝试通过使用 /bin/bash 启动容器手动运行 CMD 并查看发生了什么。 - qkrijger
如果进程崩溃,tailf 会继续运行。和 @damick 的情况一样。 - Daniel Andrei Mincă

3
因为我希望有一种干净的方式来延迟进程退出,所以我将最后一个命令调用shell的read ,使该进程阻塞直到我稍后附加并按下回车键。
arthur@macro:~/docker$ sudo docker run -d -t -i -v /raid:/raid -p 4040:4040 subsonic /bin/bash -c 'service subsonic start && read -p "waiting"'
WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: [8.8.8.8 8.8.4.4]
f27229a260c9

arthur@macro:~/docker$ sudo docker ps                                                                                                                                     
[sudo] password for arthur: 
ID                  IMAGE               COMMAND                CREATED              STATUS              PORTS
35f253bdf45a        subsonic:latest     /bin/bash -c service   2 days ago          Up 2 days           4040->4040

arthur@macro:~/docker$ sudo docker attach 35f253bdf45a

arthur@macro:~/docker$ sudo docker ps                                                                                                                                     
ID                  IMAGE               COMMAND             CREATED             STATUS              PORTS

正如您所看到的,在连接到容器并解除阻塞读取后,容器将退出。

当然,如果您需要进行其他清理操作,例如停止服务和保存日志等,您可以使用比read -p更复杂的脚本。


0
每当我开始构建新的Docker容器时,我都会使用一个简单的技巧来保持其运行。为了保持其活跃状态,我在入口脚本中使用ping命令。
因此,在Dockerfile中,例如使用Debian时,我确保可以进行ping操作。 顺便说一下,这样做总是很好的,可以检查容器内部可以访问哪些内容。
...
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
 && apt-get install -y iputils-ping 
...
ENTRYPOINT ["entrypoint.sh"]

在 entrypoint.sh 文件中

#!/bin/bash
...
ping 10.10.0.1 >/dev/null 2>/dev/null

我使用这个命令代替 CMD bash,因为我总是需要使用启动文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接