官网:https://docs.docker.com/engine/reference/builder/
Docker的CMD 和 ENTRYPOINT
CMD 和 ENTRYPOINT 指令都是用来指定容器启动时运行的命令。大多数情况下基本功能相同。
exec 模式和 shell 模式
CMD 和 ENTRYPOINT 指令都支持 exec 模式和 shell 模式的写法,所以要理解 CMD 和 ENTRYPOINT 指令的用法,就得先区分 exec 模式和 shell 模式。这两种模式主要用来指定容器中的不同进程为 1 号进程。
exec 模式
使用 exec 模式时,容器中的任务进程就是容器内的 1 号进程
exec 模式是建议的使用模式,因为当运行任务的进程作为容器中的 1 号进程时,我们可以通过 docker 的 stop 命令优雅的结束容器。
exec 模式的特点是不会通过 shell 执行相关的命令,所以像 $HOME 这样的环境变量是取不到的:
CMD [ "echo", "$HOME" ]
通过 exec 模式执行 shell 可以获得环境变量:
CMD [ "sh", "-c", "echo $HOME" ]
shell 模式
使用 shell 模式时,docker 会以 /bin/sh -c “task command” 的方式执行任务命令。也就是说容器中的 1 号进程不是任务进程而是 bash 进程
CMD top
1 号进程执行的命令居然是 /bin/sh -c top。而指定的 top 命令的进程 ID 为 7。这是由 docker 内部决定的,目的是让我们执行的命令或者脚本可以取到环境变量。
CMD 指令
CMD 指令的目的是:为容器提供默认的执行命令。
CMD 指令有三种使用方式,其中的一种是为 ENTRYPOINT 提供默认的参数:
CMD ["param1","param2"]
另外两种使用方式分别是 exec 模式和 shell 模式:
CMD ["executable","param1","param2"] # 这是 exec 模式的写法,注意需要使用双引号。
CMD command param1 param2 # 这是 shell 模式的写法。
注意命令行参数(即通过docker run 指定的)可以覆盖 CMD 指令的设置,但是只能是重写,却不能给 CMD 中的命令通过命令行传递参数。实际上,命令行上的命令同样会覆盖 shell 模式的 CMD 指令。
ENTRYPOINT 指令
ENTRYPOINT 指令的目的也是为容器指定默认执行的任务。
ENTRYPOINT 指令有两种使用方式,就是我们前面介绍的 exec 模式和 shell 模式:
ENTRYPOINT ["executable", "param1", "param2"] # 这是 exec 模式的写法,注意需要使用双引号。
ENTRYPOINT command param1 param2 # 这是 shell 模式的写法。
exec 模式和 shell 模式的基本用法和 CMD 指令是一样的。
指定 ENTRYPOINT 指令为 exec 模式时,命令行(docker run)上指定的参数会作为参数添加到 ENTRYPOINT 指定命令的参数列表中。所以写entrypoint文件时,要考虑输入各种参数问题。
覆盖默认的 ENTRYPOINT 指令
ENTRYPOINT 指令也是可以被命令行覆盖的,只不过不是默认的命令行参数,而是需要显式的指定 --entrypoint 参数。
docker run --rm --entrypoint command
Dockerfile 中至少要有一个
如果镜像中既没有指定 CMD 也没有指定 ENTRYPOINT 那么在启动容器时会报错。这不算是什么问题,因为现在能见到的绝大多数镜像都默认添加了 CMD 或 ENTRYPOINT 指令。
同时使用 CMD 和 ENTRYPOINT 的情况
对于 CMD 和 ENTRYPOINT 的设计而言,多数情况下它们应该是单独使用的。当然,有一个例外是 CMD 为 ENTRYPOINT 提供默认的可选参数。
大概可以总结出下面几条规律:
• 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略。
• 如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的内容被追加为 ENTRYPOINT 指定命令的参数。
• 如果 ENTRYPOINT 使用了 exec 模式,CMD 也应该使用 exec 模式。
CMD和ENTRYPOINT差异:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
entrypoint入口文件详解
#!/bin/bash
set -eo pipefail
shopt -s nullglob
# if command starts with an option, prepend mysqld
if [ "${1:0:1}" = '-' ]; then
set -- mysqld "$@"
fi
- set -e
bash如果任何语句的执行结果不是true则应该退出 。 set -o errexit 等同
- set -o pipefail
管道执行过程中出现错误立即退出
- shopt -s nullglob
在使用 Linux 中的通配符时 * ?等 如果没有匹配到任何文件, 不会报 No such file or directory 而是将命令后面的参数去掉执行
- set –
set --会将他后面所有以空格区分的字符串, 按顺序分别存储到$1, $2, $3 变量中
- exec "$@"
执行了你没有预料到的可执行命令时, 将会走到脚本的这最后一行, 去执行用户新的可执行命令
ADD和COPY的区别
ADD
ADD指令的功能是将主机构建环境(上下文)目录中的文件和目录、以及一个URL标记的文件 拷贝到镜像中。
其格式是: ADD 源路径 目标路径
1、如果源路径是个文件,且目标路径是以 / 结尾, 则docker会把目标路径当作一个目录,会把源文件拷贝到该目录下。如果目标路径不存在,则会自动创建目标路径。
2、如果源路径是个文件,且目标路径是不是以 / 结尾,则docker会把目标路径当作一个文件。
如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件;
如果目标文件是个存在的文件,会用源文件覆盖它,当然只是内容覆盖,文件名还是目标文件名。
如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。 注意,这种情况下,最好显示的以 / 结尾,以避免混淆。
3、如果源路径是个目录,且目标路径不存在,则docker会自动以目标路径创建一个目录,把源路径目录下的文件拷贝进来 ( 如果有子目录,不会拷贝子目录,仅拷贝子目录下的文件,不分层级)。如果目标路径是个已经存在的目录,则docker会把源路径目录下的文件拷贝到该目录下。
4、如果源文件是个归档文件(压缩文件),则docker会自动帮解压。
5、如果源路径是url,则会下载文件到目标。
COPY
COPY是ADD的简化版本,只是拷贝文件到指定位置。
COPY/ADD 复制文件夹的诡异行为
1、使用 *
作为 COPY/ADD
命令的源时候表示的是 ./*
2、cp
命令在执行 cp * target
时会把文件夹当成文件一股脑的复制到目标路径下,可以认为复制了文件本身,而 docker 的 COPY/ADD
在复制文件夹时复制的是其内容。
3、如果要递归拷贝目录(包含子目录),不要带*
COPY ./scripts/ /workspace/scripts/
这样scripts下的目录递归拷贝到目标目录下。
.dockerignore
忽略在构造docker镜像文件时,不发送给 docker daemon 的文件。用于减少传送量。
Docker 维护
yum install docker
vi /etc/docker/daemon.json
{
"insecure-registries": ["192.168.1.XX"],
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
systemctl start docker
systemctl stop docker
systemctl restart docker
systemctl status docker
systemctl enable docker
Docker问题
挂载宿主机已存在目录后,在容器内对其进行操作,报“Permission denied”。
可通过两种方式解决:
1> 关闭selinux。
临时关闭:# setenforce 0
永久关闭:修改/etc/sysconfig/selinux文件,将SELINUX的值设置为disabled。
2> 以特权方式启动容器
指定–privileged参数
如:# docker run -it --privileged=true -v /test:/soft centos /bin/bash
network被占用
提示1:service endpoint with name xxx already exists
docker network list
docker network inspect xxx
docker network disconnect --force XXX-network api-gateway