Docker Compose 保持容器运行

227

我想使用docker-compose启动一个服务并保持容器运行,以便我可以通过'docker inspect'获取其IP地址。但是,容器在启动后总是立即退出。

我尝试将"command: ['sleep', '60']"等内容添加到docker-compose.yml文件中,但是每次添加了"command:..."行后,我就无法调用"docker-compose up"命令,因为会收到消息"Cannot start container.....System error: invalid character 'k' looking for beginning of value"

我还尝试在Dockerfile本身中添加"CMD sleep 60"等内容,但这些命令似乎没有被执行。

是否有一种简单的方法来保持容器处于活动状态或解决我的问题?

编辑: 这是我想要运行的Compose文件:

version: '2'
services:
  my-test:
    image: ubuntu
    command: bash -c "while true; do echo hello; sleep 2; done"

如果我在OS X上使用docker-compose启动它,它可以正常工作,但是如果我在Ubuntu 16.04下尝试相同的操作,就会出现上述错误信息。

如果我尝试使用Dockerfile的方法,Dockerfile看起来像这样:

FROM ubuntu:latest
CMD ["sleep", "60"]

似乎什么也没做

编辑2: 我需要更正,原来Dockerfile和docker-compose.yml出了同样的问题: 每次在Dockerfile中添加“CMD ...”或在compose文件中添加“command ...”,就会出现上述无效字符的错误。如果删除这两个命令,它就可以无缝运行。


1
请包括docker-compose.yml、Dockerfile以及您尝试调试的任何脚本。 - BMitch
对于那些感兴趣的人,这里有一个相关的讨论:使用Docker Compose开发服务器和客户端的工作流程? - blong
15个回答

275
为了在使用 docker-compose 启动容器时保持其运行状态,请使用以下命令:command: tail -F anything。在上述命令中,最后一部分的 anything 应该被文字原样包含,假设在容器中不存在这样的文件,但是使用 -F 选项(大写的 -F 不要与小写的 -f 混淆,后者如果找不到文件将立即终止)tail 命令会永远等待文件 anything 出现。一个永久等待的进程基本上就是我们所需要的。因此,您的 docker-compose.yml 就变成了这样:
version: '2'
services:
  my-test:
    image: ubuntu
    command: tail -F anything

您可以使用以下命令运行shell进入容器:

docker exec -i -t composename_my-test_1 bash

其中composenamedocker-compose为您的容器添加的名称。


1
你如何在使用完容器后停止它?有类似于 Ctrl+C、Ctrl+Z 的命令吗?目前我必须关闭终端才能退出。 - mac10688
这对我没有用,当我执行 docker ps -a 时,容器状态显示为 Restarting (0) 5 seconds ago,当我尝试执行 docker exec -it data-quality-test-db-sqlite sh 时,它会显示 Error response from daemon: Container [...] is restarting, wait until the container is running - Alexis.Rolland
2
@Alexis.Rolland 如果您愿意提出一个新的SO问题并分享更多细节,那么我非常乐意帮忙查看。我的猜测是,您的错误与其中一个容器的内部有关,而不是Docker或主机操作系统的问题。 - Nick Settje
1
@mac10688 如果你的附加容器会话中没有提示符,则尝试使用Ctrl-D进行分离。 - Stark
29
在 "tail" 命令中,将 anything 替换为 /dev/null 会更好。参考链接:https://dev59.com/OlkT5IYBdhLWcg3wefbR#48732671。 - Nam G VU
显示剩余4条评论

247

你可以使用tty配置选项。

version: '3'

services:
  app:
    image: node:8
    tty: true           # <-- This option

注意:如果您在 Dockerfile 中使用 CMD,则此选项将无法工作;但是,您可以在组合文件中使用 entrypoint 选项来清除 Dockerfile 中的 CMD


21
这个方法可行,看起来比 tail -f /dev/null 更加正式。有了这个方法,我可以通过 docker-compose up 启动带有附加 postgres 数据库的容器化开发环境,并且可以在另一个终端上使用 docker exec 运行 shell。 - Psiloc
1
虽然目前文档不是很完善,但这是一个官方选项,可以实现与 tail -f /dev/nulltail -f anything 相同的效果,请参见此处:https://docs.docker.com/compose/compose-file/。 - b01
6
只有在docker-compose.yml文件中不使用"command"时,此方法才有效。当使用"command"时,你需要另一个技巧——因此tail -F技巧在这里非常适合。 - asafel
1
如果您在 Dockerfile 中使用 entrypoint,则这将是最佳答案。 - DDKV587
3
对我来说不起作用,容器仍然会死机。如果我在我的入口bash脚本中添加 tail -F /dev/null,它就能够工作,但是这样依靠一个活着的bash脚本似乎有些奇怪,因为我只想在容器启动时运行一些初始命令。 - XCS
适用于我,非常干净。 - progonkpa

88
根据2015年8月26日GitHub上@aanand的评论,可以在docker-compose中使用tail -f /dev/null来保持容器运行。 docker-compose.yml示例:
version: '3'
services:
  some-app:
    command: tail -f /dev/null

为什么选择这个命令?

选择这个选项的唯一原因是它在GitHub上得到了很多点赞,但是得票最高的答案并不意味着它是最好的答案。第二个原因是实用性,因为由于截止日期问题必须尽快解决。


4
为什么要使用这个命令?它有什么优点胜过其他命令? 在我的情况下,只需启动一个Bash也能达到同样的效果... - N4ppeL
@N4ppeL 很好的问题。我选择这个选项的唯一原因是它在Github上收到了很多赞,但得票最高的答案并不意味着它是最好的答案。第二个原因是实用主义的,因为我必须尽快解决问题,以避免错过截止日期。 - 030
这个程序相关的内容翻译成中文:这将覆盖原始入口点,未执行原始入口点。 - david valentino

27
创建一个名为docker-compose.yml的文件, 将以下内容添加到文件中。
version: "3"

services:
  ubuntu:
    image: ubuntu:latest
    tty: true
  • 在同一目录下,从终端运行docker-compose up -d
  • 运行docker ps以获取容器id或名称
  • 您可以运行docker inspect $container_id
  • 您可以进入容器并运行一个bash shell:docker-compose exec ubuntu /bin/bash docker-compose exec ubuntu /bin/sh
  • 完成后,请确保您在容器之外并运行docker-compose down

这里有一个小型的bash脚本(my-docker-shell.sh)用于创建docker compose文件、运行容器、登录容器,最后在您注销时清理docker容器和docker compose文件。

#!/bin/bash

cat << 'EOF' > ./docker-compose.yml
---

version: "3"

services:
  ubuntu:
    image: ubuntu:latest
    command: /bin/bash
    # tty: true

...
EOF

printf "Now entering the container...\n"
docker-compose run ubuntu bash
docker-compose down

rm -v ./docker-compose.yml

1
虽然这不是实际问题的答案,但我发现它非常有价值!我进一步使用了全局bashrc函数来进行这种方法:https://gist.github.com/loopmode/4d59a4e9a0a2ffacaec2dd14db4ae8bd - loopmode
它将覆盖当前目录中的任何现有的 docker-compose.yml 文件;有一点危险。 - henrikstroem

18

虽然我来晚了,但你可以简单地使用:stdin_open: true

version: '2'
services:
  my-test:
    image: ubuntu
    stdin_open: true

15

这里有些人谈论覆盖entrypoint,以便command也能发挥作用。但没有人给出例子。然后我:

docker-compose.yml

version: '3'

services:

    etfwebapp:
        # For messed up volumes and `sudo docker cp`:
        command: "-f /dev/null"
        entrypoint: /usr/bin/tail
        tty: true

# ...

我不确定此时是否需要 tty。重复执行两次是否更好?在我的情况下,这样做没有任何问题,并且完美地运行。如果没有 entrypoint,那么 command 将没有效果,所以我想对于这个解决方案来说,tty 是可选的。

要了解启动时执行的命令,请先读取 entrypoint,然后再读取 command(用空格连接):/usr/bin/tail -f /dev/null


12

在Dockerfile中,您可以使用命令:

{CMD sleep infinity}

11

只需要阻塞命令。

我已经为这个问题苦苦挣扎了半天。下面有很多答案,但不是很清楚。而且没有人说原因。

简而言之,有两种方法,但也可以说只有一种,在后台运行一个阻塞进程。


第一种方法使用COMMAND

version: '3'
services:
  some-app:
    command: ["some block command"]

在命令中可以加入一些类似于 sleep infinitytail -f /dev/nullwatch anythingwhile true 的阻塞命令。

这里我推荐使用 sleep infinity


第二步是启用 tty=true,然后在命令中打开一个 shell,例如 /bin/bash

services:
  ubuntu:
    image: ubuntu:latest
    tty: true
    command: "/bin/bash"

由于tty已启用,bash将在后台持续运行,如果需要,您可以在其前面加入其他一些命令块。

请注意,必须在最后执行shell命令,例如:

command: /bin/bash -c "/root/.init-service && /bin/bash"

如您所见,您只需要使用阻塞命令即可。

5

简要说明:

我已经测试了基于golang的单个镜像,因此当我在这里调用docker-compose down时,我得到以下结果:

version: "3.1"
...
command: tail -f /dev/null   # stopping container takes about 10 sec.
tty: true                    # stopping container takes about 2 sec.

我的系统信息:

Ubuntu 18.04.4 LTS (64-bit)
Docker version 19.03.6, build 369ce74a3c
docker-compose version 1.26.0, build d4451659

3
正如评论者所说,我们需要看到相关的Dockerfile才能给你一个完整的答案,但这是一个非常普遍的错误。我可以几乎保证你尝试运行的命令是启动后台进程的命令。在非Docker环境中,这可能是你要运行的命令,但在Dockerfile中却是错误的做法。例如,如果你要运行的是通常定义为系统服务的内容,你可能会使用类似"systemctl start"的命令。这将在后台启动进程,这是不起作用的。你必须在前台运行进程,以便整个进程将被阻塞。

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