CMD

cmd给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令。重点就是这个“默认”。意味着,如果不指定启动容器后要执行的命令,那么,就会使用cmd指定的默认的执行命令执行。如果指定了启动容器后要执行的命令,那么就不会再执行cmd中的命令。

那么在哪里可以指定容器启动后要执行的命令呢?

docker run可以指定执行命令

dockerfile用entrypoint指定要执行的命令

docker-compose.yml中的command命令

这也是为什么大多数网上博客论坛说的“cmd会被覆盖”,其实为什么会覆盖?因为cmd的角色定位就是默认,如果你不额外指定,那么就执行cmd的命令,否则呢?只要你指定了,那么就不会执行cmd,也就是cmd会被覆盖。

cmd命令格式:

CMD ["executable","param1","param2"] 

CMD ["param1","param2"] (as default parameters to ENTRYPOINT)

CMD command param1 param2 (shell form)

用法3:shell form,即没有中括号的形式。那么命令command默认是在“/bin/sh -c”下执行。sh -c" 命令,它可以让 bash 将一个字串作为完整的命令来执行

用法1:带有中括号的形式。这时,命令没有再任何shell终端环境下,如果我们要执行shell,必须把shell加入到中括号的参数中。这种用法就像一个c语言的exec函数,意思是我们要执行一个进程。比如CMD ["/bin/bash", "-c", "echo 'hello cmd!'"]

需要注意,采用中括号形式,那么第一个参数必须是命令的全路径才行。而且,一个dockerfile至多只能有一个cmd,如果有多个,只有最后一个生效。

ENTRYPOINT

entrypoint才是正统地用于定义容器启动以后的执行体的,其实我们从名字也可以理解,这个是容器的“入口”。

entrypoint命令格式:

ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)

先看命令行模式,也就是带中括号的。和cmd的中括号形式是一致的,但是这里貌似是在shell的环境下执行的,与cmd有区别。如果run命令后面有东西,那么后面的全部都会作为entrypoint的参数。如果run后面没有额外的东西,但是cmd有,那么cmd的全部内容会作为entrypoint的参数,这同时是cmd的第二种用法。这也是网上说的entrypoint不会被覆盖。当然如果要在run里面覆盖,也是有办法的,使用--entrypoint即可。

第二种是shell模式的。在这种模式下,任何run和cmd的参数都无法被传入到entrypoint里。官网推荐第一种用法。

总结下一般该怎么使用:一般还是会用entrypoint的中括号形式作为docker 容器启动以后的默认执行命令,里面放的是不变的部分,可变部分比如命令参数可以使用cmd的形式提供默认版本,也就是run里面没有任何参数时使用的默认参数。如果我们想用默认参数,就直接run,否则想用其他参数,就run 里面加参数。

 

执行多个执行体

启动镜像执行多个命令可以采用脚本的方式。

Dockerfile:

...
...
CMD source /opt/hrms/run/entrypoint.sh

entrypoint.sh:

#!/bin/bash

celery worker -A celery_tasks.main -l info -f /opt/hrms/logs/celery.log &
celery beat -A celery_tasks.main -l info --pidfile= &
gunicorn -w 4 -b 0.0.0.0:8080 -t 300  hrms.wsgi