2016-11-25 18:02:43 +00:00
# # # ENTRYPOINT 入口点
` ENTRYPOINT ` 的格式和 ` RUN ` 指令格式一样 , 分为 ` exec ` 格式和 ` shell ` 格式 。
` ENTRYPOINT ` 的目的和 ` CMD ` 一样 , 都是在指定容器启动程序及参数 。 ` ENTRYPOINT ` 在运行时也可以替代 , 不过比 ` CMD ` 要略显繁琐 , 需要通过 ` docker run ` 的参数 ` --entrypoint ` 来指定 。
当指定了 ` ENTRYPOINT ` 后 , ` CMD ` 的含义就发生了改变 , 不再是直接的运行其命令 , 而是将 ` CMD ` 的内容作为参数传给 ` ENTRYPOINT ` 指令 , 换句话说实际执行时 , 将变为 :
` ` ` bash
< ENTRYPOINT > "<CMD>"
` ` `
那么有了 ` CMD ` 后 , 为什么还要有 ` ENTRYPOINT ` 呢 ? 这种 ` <ENTRYPOINT> "<CMD>" ` 有什么好处么 ? 让我们来看几个场景 。
# # # # 场景一 : 让镜像变成像命令一样使用
假设我们需要一个得知自己当前公网 IP 的镜像 , 那么可以先用 ` CMD ` 来实现 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2018-12-08 13:47:05 +00:00
FROM ubuntu : 18.04
2016-11-25 18:02:43 +00:00
RUN apt - get update \
&& apt - get install - y curl \
&& rm - rf / var / lib / apt / lists / *
2020-09-06 09:43:21 +00:00
CMD [ "curl" , "-s" , "http://myip.ipip.net" ]
2016-11-25 18:02:43 +00:00
` ` `
假如我们使用 ` docker build -t myip . ` 来构建镜像的话 , 如果我们需要查询当前公网 IP , 只需要执行 :
` ` ` bash
$ docker run myip
当前 IP : 61.148 .226 .66 来自 : 北京市 联通
` ` `
嗯 , 这么看起来好像可以直接把镜像当做命令使用了 , 不过命令总有参数 , 如果我们希望加参数呢 ? 比如从上面的 ` CMD ` 中可以看到实质的命令是 ` curl ` , 那么如果我们希望显示 HTTP 头信息 , 就需要加上 ` -i ` 参数 。 那么我们可以直接加 ` -i ` 参数给 ` docker run myip ` 么 ?
` ` ` bash
$ docker run myip - i
docker : Error response from daemon : invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n" .
` ` `
2020-09-06 09:43:21 +00:00
我们可以看到可执行文件找不到的报错 , ` executable file not found ` 。 之前我们说过 , 跟在镜像名后面的是 ` command ` , 运行时会替换 ` CMD ` 的默认值 。 因此这里的 ` -i ` 替换了原来的 ` CMD ` , 而不是添加在原来的 ` curl -s http://myip.ipip.net ` 后面 。 而 ` -i ` 根本不是命令 , 所以自然找不到 。
2016-11-25 18:02:43 +00:00
那么如果我们希望加入 ` -i ` 这参数 , 我们就必须重新完整的输入这个命令 :
` ` ` bash
2020-09-06 09:43:21 +00:00
$ docker run myip curl - s http : //myip.ipip.net -i
2016-11-25 18:02:43 +00:00
` ` `
这显然不是很好的解决方案 , 而使用 ` ENTRYPOINT ` 就可以解决这个问题 。 现在我们重新用 ` ENTRYPOINT ` 来实现这个镜像 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2018-12-08 13:47:05 +00:00
FROM ubuntu : 18.04
2016-11-25 18:02:43 +00:00
RUN apt - get update \
&& apt - get install - y curl \
&& rm - rf / var / lib / apt / lists / *
2020-09-06 09:43:21 +00:00
ENTRYPOINT [ "curl" , "-s" , "http://myip.ipip.net" ]
2016-11-25 18:02:43 +00:00
` ` `
这次我们再来尝试直接使用 ` docker run myip -i ` :
` ` ` bash
$ docker run myip
当前 IP : 61.148 .226 .66 来自 : 北京市 联通
$ docker run myip - i
HTTP / 1.1 200 OK
Server : nginx / 1.8 .0
Date : Tue , 22 Nov 2016 05 : 12 : 40 GMT
Content - Type : text / html ; charset = UTF - 8
Vary : Accept - Encoding
X - Powered - By : PHP / 5.6 .24 - 1 ~ dotdeb + 7.1
X - Cache : MISS from cache - 2
X - Cache - Lookup : MISS from cache - 2 : 80
X - Cache : MISS from proxy - 2_6
Transfer - Encoding : chunked
Via : 1.1 cache - 2 : 80 , 1.1 proxy - 2_6 : 8006
Connection : keep - alive
当前 IP : 61.148 .226 .66 来自 : 北京市 联通
` ` `
可以看到 , 这次成功了 。 这是因为当存在 ` ENTRYPOINT ` 后 , ` CMD ` 的内容将会作为参数传给 ` ENTRYPOINT ` , 而这里 ` -i ` 就是新的 ` CMD ` , 因此会作为参数传给 ` curl ` , 从而达到了我们预期的效果 。
# # # # 场景二 : 应用运行前的准备工作
启动容器就是启动主进程 , 但有些时候 , 启动主进程前 , 需要一些准备工作 。
比如 ` mysql ` 类的数据库 , 可能需要一些数据库配置 、 初始化的工作 , 这些工作要在最终的 mysql 服务器运行之前解决 。
此外 , 可能希望避免使用 ` root ` 用户去启动服务 , 从而提高安全性 , 而在启动服务前还需要以 ` root ` 身份执行一些必要的准备工作 , 最后切换到服务用户身份启动服务 。 或者除了服务外 , 其它命令依旧可以使用 ` root ` 身份执行 , 方便调试等 。
这些准备工作是和容器 ` CMD ` 无关的 , 无论 ` CMD ` 为什么 , 都需要事先进行一个预处理的工作 。 这种情况下 , 可以写一个脚本 , 然后放入 ` ENTRYPOINT ` 中去执行 , 而这个脚本会将接到的参数 ( 也就是 ` <CMD> ` ) 作为命令 , 在脚本最后执行 。 比如官方镜像 ` redis ` 中就是这么做的 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2016-11-25 18:02:43 +00:00
FROM alpine : 3.4
...
RUN addgroup - S redis && adduser - S - G redis redis
...
ENTRYPOINT [ "docker-entrypoint.sh" ]
EXPOSE 6379
CMD [ "redis-server" ]
` ` `
可以看到其中为了 redis 服务创建了 redis 用户 , 并在最后指定了 ` ENTRYPOINT ` 为 ` docker-entrypoint.sh ` 脚本 。
` ` ` bash
# ! / bin / sh
...
# allow the container to be started with ` --user `
if [ "$1" = ' redis - server ' - a "$(id -u)" = '0' ] ; then
2020-09-06 09:43:21 +00:00
find . \ ! - user redis - exec chown redis ' { } ' +
exec gosu redis "$0" "$@"
2016-11-25 18:02:43 +00:00
fi
exec "$@"
` ` `
该脚本的内容就是根据 ` CMD ` 的内容来判断 , 如果是 ` redis-server ` 的话 , 则切换到 ` redis ` 用户身份启动服务器 , 否则依旧使用 ` root ` 身份执行 。 比如 :
` ` ` bash
$ docker run - it redis id
uid = 0 ( root ) gid = 0 ( root ) groups = 0 ( root )
` ` `