1. 问题
使用镜像alpine
起个容器,使其保持后台运行,正常情况有如下的效果,可以发现容器保持运行状态。
bash
[root@k8s-master helloWorld]# docker run -dit docker.io/alpine /bin/sh
8d39d7579d5e4f1a560aef16ba57ab5cae2506ea9105e21cbc06342a4d4fe17f
[root@k8s-master helloWorld]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d39d7579d5e docker.io/alpine "/bin/sh" 6 seconds ago Up 5 seconds loving_shannon
但是有时候一些容器镜像按照上述方法却达不到预期效果。比如下面这个容器,一创建完就退出了。
bash
[root@k8s-master helloWorld]# docker run -dit helloapp:v1 /bin/sh
8b654e4dc44c9a30544099bf360a4d410cfa81ad9bc14e73c0f384a166bf2420
[root@k8s-master helloWorld]# docker ps --all |grep 8b65
8b654e4dc44c helloapp:v1 "./hello /bin/sh" 14 seconds ago Exited (0) 13 seconds ago fervent_hoover
那么问题出在哪个环节呢?
2. 分析
首先明确一个Docker容器的特性,docker容器运行必须有一个进程, 如果没有进程执行,容器认为空闲,就会自行退出 。
那么我们使用docker inspect <id>
看看上述两个容器启动时分别执行了什么命令
- 成功后台运行的容器
bash
[root@k8s-master helloWorld]# docker inspect 938 | head
[
{
"Id": "938cf5ba3fe61e30265e6b44b5493d6d9e60909f77dd4c72da6ee3395e593e55",
"Created": "2023-07-31T14:41:02.155416227Z",
"Path": "/bin/sh",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
[root@k8s-master helloWorld]#
可以看到Path
值对应的/bin/sh
就是容器创建时执行的命令
- 退出的容器
bash
[root@k8s-master helloWorld]# docker inspect 8b654e4dc44c | head
[
{
"Id": "8b654e4dc44c9a30544099bf360a4d410cfa81ad9bc14e73c0f384a166bf2420",
"Created": "2023-07-31T16:49:46.95505723Z",
"Path": "./hello",
"Args": [
"/bin/sh"
],
"State": {
"Status": "exited",
[root@k8s-master helloWorld]#
可以看到Path
对应的值是./hello
,Args
对应的值就是./hello
的参数
现在问题基本明朗,就是docker run
指定的/bin/sh
并不是容器创建时真正执行的命令,而是作为了Path
值的参数。当Path
值对应的命令执行结束后,容器也就退出了
根本原因:
如果容器镜像制作时,DockerFile
中通过ENTRYPOINT
指定了容器运行时执行的命令,那么docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
中的COMMAND
不生效,直接作为ARG
如果IMAGE
后带多个参数,效果也是一样,全部作为了ARG
bash
[root@k8s-master helloWorld]# docker run -dit helloapp:v1 /bin/sh first second
dc0f72e6a8c20915aa31fb325c25e32a0c7230e5596e8747cac3e5c147d47e49
[root@k8s-master helloWorld]# docker inspect dc | head -12
[
{
"Id": "dc0f72e6a8c20915aa31fb325c25e32a0c7230e5596e8747cac3e5c147d47e49",
"Created": "2023-07-31T17:37:09.251912669Z",
"Path": "./hello",
"Args": [
"/bin/sh",
"first",
"second"
],
"State": {
"Status": "exited",
3. 解决方法
通过--entrypoint
参数指定容器创建时执行的命令,覆盖DockerFile
中指定的ENTRYPOINT
例如:
bash
[root@k8s-master helloWorld]# docker run -dit --entrypoint /bin/sh helloapp:v1
f00a30b58cf15246ecec2e9089a96a1ebfe57110313f3e45e7b1cd6b12d04536
[root@k8s-master helloWorld]#
[root@k8s-master helloWorld]# docker inspect f0 | head
[
{
"Id": "f00a30b58cf15246ecec2e9089a96a1ebfe57110313f3e45e7b1cd6b12d04536",
"Created": "2023-07-31T17:42:52.001675371Z",
"Path": "/bin/sh",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
[root@k8s-master helloWorld]#
补充:
如果通过指定--entrypoint
还是不行,建议docker logs <id>
检查下报错。如下面这个,就属于一些常见错误(指定的ARG不是--entrypoint对应的命令能执行的)
bash
[root@k8s-master helloWorld]# docker run -dit --entrypoint /bin/sh helloapp:v1 today
edf83a787563a541cf53a0c0cf569307cc9f7f22e440ca0fb49980d23f181d11
[root@k8s-master helloWorld]#
[root@k8s-master helloWorld]# docker inspect edf | head -12
[
{
"Id": "edf83a787563a541cf53a0c0cf569307cc9f7f22e440ca0fb49980d23f181d11",
"Created": "2023-07-31T17:47:21.330346122Z",
"Path": "/bin/sh",
"Args": [
"today"
],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
[root@k8s-master helloWorld]# docker logs edf
/bin/sh: can't open 'today': No such file or directory
[root@k8s-master helloWorld]#