9

Docker中的ENTRYPOINT与CMD

 3 years ago
source link: https://note.qidong.name/2017/11/docker-entrypoint-cmd/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Docker中的ENTRYPOINT与CMD

2017-11-10 10:18:10 +08  字数:1891  标签: Docker

记住两点,就可以完全理解这两个关键字相关的所有用法与现象。

  1. ENTRYPOINT的默认值相当于是/bin/sh。(只是『相当于』。)
  2. 容器的执行入口为ENTRYPOINT CMD

相关概念

每个Dockerfile写到最后,都需要指定一个需要执行的命令,及其参数。 一个Docker容器,需要有且仅有一个前台进程在运行。 一旦这个进程结束了,那么容器本身的运行也就终结了。 而Dockerfile最后指定的这个命令,就是Docker容器的前台进程。

指定的方法,一般有两种,ENTRYPOINTCMD。 二者表面上看不出区别,实际上却有巨大差异,也有内在联系。

每个Docker容器,都必须要有一个可执行(executable)文件或命令、及相关参数,作为执行入口(entrypoint)。 所以,ENTRYPOINTCMD,二者在Dockerfile与docker run里,至少指定一个。

在执行docker run args... <image> cmd...时,镜像名称<image>后的所有内容,都被看做CMD。 它会覆盖镜像中写好的CMD(如果有的话)。

如果使用docker run --entrypoint /bin/bash <image> cmd,可以复写ENTRYPOINT为/bin/bash

使用区别

使用ENTRYPOINT和CMD的方式,大概分两种。 一是Dockerfile,二是docker run。 还有docker-compose.yml,其实和docker run差不多。

Dockerfile

No ENTRYPOINT ENTRYPOINT entry arg0 ENTRYPOINT [“entry”, “arg0”] No CMD error, not allowed /bin/sh -c entry arg0 entry arg0 CMD [“cmd”, “arg1”] cmd arg1 /bin/sh -c entry arg0 entry arg0 cmd arg1 CMD cmd arg1 /bin/sh -c cmd arg1 /bin/sh -c entry arg0 entry arg0 /bin/sh -c cmd arg1

上表源于官方文档中的Understand how CMD and ENTRYPOINT interact,有所简化。 (原文应该是个中国人写的,竟然还用中文的引号!)

  1. ENTRYPOINT和CMD至少要有一个。
  2. 使用ENTRYPOINT entry arg0形式,与CMD将没有任何配合。因此,除非特定需求,否则不推荐这种使用方式。
  3. 右下角entry arg0 /bin/sh -c cmd arg1这种形式,几乎没有什么使用场景,反而是常见错误,应该尽量避免。

本质上,其实可以理解为ENTRYPOINT是真正的Docker可执行入口,而CMD则是可选参数。 之所以在很多情况下直接写CMD也能生效,是因为ENTRYPOINT就相当于是指定Shell,而CMD则是指定Shell中执行的命令。 注意,只是『相当于』。

docker run与docker-compose.yml

docker run <image> cmd arg1时,总是相当于CMD的["cmd", "arg1"]形式。 此外,docker-compose.yml的情况与docker run类似。

docker run中使用--entrypoint参数时,限制非常大。 仅仅只能指定一个命令、或可执行文件,不能指定一个参数列表。 例如:

$ docker run --entrypoint ls alpine /etc/passwd
/etc/passwd
$ docker run --entrypoint ls alpine -l /etc/passwd
-rw-r--r--    1 root     root          1224 Apr 24  2017 /etc/passwd

这时,如果希望把-l参数放到--entrypoint中,则无法做到。 (一些早期版本似乎是可以的。)

$ docker run --entrypoint 'ls -l' alpine /etc/passwd
container_linux.go:262: starting container process caused "exec: \"ls -l\": executable file not found in $PATH"
docker: Error response from daemon: oci runtime error: container_linux.go:262: starting container process caused "exec: \"ls -l\": executable file not found in $PATH".
ERRO[0001] error waiting for container: context canceled

--entrypoint中,无法指定一个列表。 不过,docker-compose.yml文件中,倒是可以。

    image: alpine
    entrypoint:
      - ls
      - -l
    command: /etc/passwd

注意PID

原则上,一个Docker容器里应该只有一个进程,其PID为1。 Docker外部的操作,比如docker stop,就是向这个进程发送信号。 如果那个唯一的前台进程PID不为1,那么就会收不到信号,只能在超时(默认约10秒)后被kill。

在Dockerfile中使用ENTRYPOINT entry arg0这种形式时,entry的位置总是应该使用exec,后面再接其它内容。 比如,ENTRYPOINT exec top,这可以确保top命令是PID为1的进程。 否则,ENTRYPOINT top的形式,PID为1的进程就是/bin/sh -c top,而top则被挤到了另外一个进程。

例如,对一个如下的Dockerfile:

FROM alpine
ENTRYPOINT top

执行以下的build与run操作:

docker build -t top .
docker run --rm -it --name test top

在另一个Shell中,可以查看到以下结果:

$ docker exec test ps
PID   USER     TIME   COMMAND
    1 root       0:00 /bin/sh -c top
    7 root       0:00 top
    8 root       0:00 ps
$ time docker stop test

real	0m10.774s
user	0m0.008s
sys	0m0.004s

如果换成ENTRYPOINT exec top,或者ENTRYPOINT ["top"],就可以避免这个问题。

Docker版本

本文中描述的部分内容,在早期的Docker版本中并不正确。 撰写本文时,测试用的Docker版本信息如下。

$ docker version
Client:
 Version:      17.10.0-ce
 API version:  1.33
 Go version:   go1.8.3
 Git commit:   f4ffd25
 Built:        Tue Oct 17 19:02:43 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.10.0-ce
 API version:  1.33 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   f4ffd25
 Built:        Tue Oct 17 19:01:22 2017
 OS/Arch:      linux/amd64
 Experimental: false

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK