通常使用docker run --name XX image:tag
这样的命令来启动容器后,会遇到两种情况。
- 容器启动后再标准输出打印了一些信息,然后就停在那里了。如果我们使用
ctrl-c
或ctrl-d
退出后容器就结束了。 - 容器启动后很快就自己结束了,然后就没有然后了。
这两种都不熟我们期望的结果。我们希望容器在启动后持续运行,等待我们的再次临幸。对于这个问题网上有很多文章介绍,但能够将清楚的很少。我这里整理了一下希望能够让读者清晰的理解这种现象的原因以及应对方法。
没有耐心读长文的同学可以直接看最后的总结
CMD
执行命令的三种差别
Docker Image
在制作时通过Dockerfile
的CMD
命令指定了容器启动后执行的程序。这个指定程序决定了容器是否结束和如何结束。
我们来看看三种不同的情况:
1. CMD
指定了一个持续运行的程序.
持续运行就是说程序不会主动退出。我们拿postgres
的举栗子。
# 先来看一看postgres的CMD程序是哪个
$ docker pull postgres:11-alpine
...
...
$ docker inspect postgres:11-alpine
...
"Cmd": [
"postgres"
],
...
我们看到容器在启动后会指定
postgres
程序,这个程序会持续运行。我们运行起来看看。
$ docker run --name pp postgres:11-alpine
...[一堆输出信息]...
2019-02-03 13:46:46.117 UTC [1] LOG: database system is ready to accept connections
[停在这里不动了]
[我们该怎么办?]
ctrl-c
[然后就没有然后了]
容器成功启动了
postgres
程序,一旦按下ctrl-c
程序终止了,容器也就退出了。
解决办法
运行时添加
-d
参数$ docker run --name pp -d postgres:11-alpine 1c1e99ac3fd3f18c6558bfc8b0fd08e4bf7aaad80535dd6ea709892f20f06f27
可以看到添加
-d
参数后docker run
命令输出了容器的ID而不是像之前那样输出postgres
程序执行的内容。$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1c1e99ac3fd3 postgres:11-alpine "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 5432/tcp pp
通过
docker ps
命令看到容器确实启动起来,并持续运行。完美。
我们看看
-d
参数的解释$ docker run --help ... -d, --detach Run container in background and print container ID ...
这个参数的含义就是对容器说:"我知道你在努力做事,但我也很忙,你自己到旁边干活去吧,有事儿我再找你"
2. CMD
指定了一个shell
这次我们用node
举栗子
$ docker pull node:10-alpine
$ docker inspect node:10-alpine
...
"Cmd": [
"node"
],
...
node
程序与postgres
程序不太一样。node
并不是我们上面谈的"持续运行"的概念。
它作为一种shell
程序自身并不知道要做什么,而是需要与操作者交流从而执行操作者交付的任务。
而shell
与操作者之间交流的方式是通过tty
进行的。如果tty
没有了那么shell
发现没有人理它了,它就会在孤独和寂寞中睡觉去了。
而当容器启动的时候并不会给程序分配一个tty
,所以这就是我们看到启动shell
程序的容器自动退出的原因。
解决办法
运行时添加
-t
参数$ docker run --name nn -t node:10-alpine > [这是shell提示符]
我们来看看
-t
参数的解释$ docker run --help ... -t, --tty Allocate a pseudo-TTY ...
这个参数的含义是:"瞧这个泰迪熊多可爱,你去和它聊天吧"
现在我们看到了
shell 提示符
,现在的状态与第一种情况类似了,下面我们只需要再加入-d
参数就完美了$ docker run --name nn -dt node:10-alpine bfeead85bf01dd182e4c631ee9077afbb5c989e9c75abdd4776086dd223b9cdc
我们期待的容器ID由出现了,完美。
3. CMD
指定了一个普通程序
这种镜像不好找,我们用参数模拟一下。在启动容器是通过命令行参数指定容器启动的程序,这将替换CMD
指定的程序。
$ docker run --name nn -dt node:10-alpine pwd
412baf5fc0428567bcce3a2cb00fc41a696c10ecda0447d23a9291c2a2b52f20
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
412baf5fc042 node:10-alpine "pwd" 2 minutes ago Exited (0) 2 minutes ago nn
虽然容器启动时返回了容器ID,但当我们查看容器状态时它已经Exited
。
起始这种容器设计之初就不是让它持续运行的,它只是为了完成一项专门的动作,然后去睡觉。
那么对于这样的容器我们改怎么办呢?
♪ Let it go, ♪ let it go
♫ Can't hold it back anymore
♪ Let it go, ♪ let it go
♬ Turn my back and slam the door
### 总结
根据CMD
指定的程序不同,有三种情况需要考虑
- 对于自身持续运行的程序,指定
-d
参数。它就可以自己到角落里画圈圈去了。 - 对于一个
shell
程序,指定-dt
参数。给它一只泰迪熊,它就去角落和和泰迪熊聊天去了。 - 对于一个执行完就结束的程序。我们只能
Let it go
网友评论