赞
踩
一、先看最简单的例子,定制nginx镜像,打印出
Dockerfile文件:
FROM nginx
RUN echo'
在该Dockerfile目录下运行
docker build -t nginx:v1 .
则将生成一个 nginx:v1 的新镜像,运行该镜像
docker run -p 80:80 nginx:v1
在浏览器直接访问地址 localhost,可以看到nginx的主页已被修改
二、RUN 命令关键解析
Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个 RUN 的行为,新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。
FROM debian:jessie
RUN apt-getupdate
RUN apt-get install -y gcc libc6-dev make
RUN wget-O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"RUN mkdir-p /usr/src/redis
RUN tar-xzf redis.tar.gz -C /usr/src/redis --strip-components=1RUN make-C /usr/src/redis
RUN make-C /usr/src/redis install
如上dockerfile所示,创建了 7 层镜像,这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。
正确的写法:
FROM debian:jessie
RUN buildDeps='gcc libc6-dev make'\&& apt-getupdate \&& apt-get install -y $buildDeps \&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"\&& mkdir -p /usr/src/redis \&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1\&& make -C /usr/src/redis \&& make -C /usr/src/redis install \&& rm -rf /var/lib/apt/lists/*\
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
三、CMD 与 ENTRYPOINT对比
场景1、让镜像变成像命令一样使用
假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:
FROM ubuntu:16.04RUN apt-getupdate \&& apt-get install -y curl \&& rm -rf /var/lib/apt/lists/*CMD [ "curl", "-s", "http://ip.cn" ]
使用docker build -t myip . 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:
docker run myip
可以打印出当前的ip地址
这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到实质的命令是 curl ,那么如果我们希望显示 HTTP头信息,就需要加上 -i 参数。那么我们可以直接加 -i 参数给 docker run myip 么?
$ docker run myip -i
docker: Error responsefrom daemon: invalid header field value "oci runtime error: con
tainer_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable
file not found in $PATH\"\n".
可以看到可执行文件找不到的报错, executable file not found 。之前我们说过,跟在镜像名后面的是 command ,运行时会替换 CMD 的默认值。因此这里的 -i 替换了远了的CMD ,而不是添加在原来的 curl -s http://ip.cn 后面。而 -i 根本不是命令,所以自然找不到。
那么如果我们希望加入 -i 这参数,我们就必须重新完整的输入这个命令:
$ docker run myip curl -s http://ip.cn -i
这显然不是很好的解决方案,而使用 ENTRYPOINT 就可以解决这个问题。现在我们重新用ENTRYPOINT 来实现这个镜像:
FROM ubuntu:16.04RUN apt-getupdate \&& apt-get install -y curl \&& rm -rf /var/lib/apt/lists/*ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
这次我们再来尝试直接使用 docker run myip -i
成功!这是因为当存在 ENTRYPOINT 后, CMD 的内容将会作为参数传给ENTRYPOINT ,而这里 -i 就是新的 CMD ,因此会作为参数传给 curl ,从而达到了我们预期的效果
场景2:应用运行前的准备工作
启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。
比如 mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的mysql 服务器运行之前解决。
此外,可能希望避免使用 root 用户去启动服务,从而提高安全性,而在启动服务前还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 root 身份执行,方便调试等。
这些准备工作是和容器 CMD 无关的,无论 CMD 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 )作为命令,在脚本最后执行。
四、HEALTHCHECK 健康检查
HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12引入的新指令
HEALTHCHECK 支持下列选项:
--interval= :两次健康检查的间隔,默认为 30 秒;
--timeout= :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被
视为失败,默认 30 秒;
--retries= :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3
次。
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fshttp://localhost/|| exit 1
这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。
构建镜像,并且启动容器
$ docker build -t myweb:v1 .
$ docker run -d --name web -p 80:80 myweb:v1
当运行该镜像后,可以通过 docker ps 看到最初的状态为 (health: starting)
在等待几秒钟后,再次 docker ps ,就会看到健康状态变化为了 (healthy)
如果健康检查连续失败超过了重试次数,状态就会变为 (unhealthy) 。
为了帮助排障,健康检查命令的输出(包括 stdout 以及 stderr )都会被存储于健康状态里,可以用 docker inspect 来查看。
$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{"FailingStreak": 0,"Log": [
{"End": "2016-11-25T14:35:37.940957051Z","ExitCode": 0,"Output": "\n\n
\nWelcome to nginx!e>\n
amily: Tahoma, Verdana, Arial, sans-serif;\n }\n\n\n
\nome to nginx!\n
If you see this page, the nginx web server issuccessfully inst
alled and\nworking. Further configurationis required.
\n\nFor online documentat
ion and support please refer to\nnginx.org.
\nCo
mmercial support is available at\nnginx.com.
\n\nThank you for using nginx.
\n\n\n","Start": "2016-11-25T14:35:37.780192565Z"}],"Status": "healthy"}
五、COPY与ADD区别
在 Docker 官方的最佳实践文档中要求,尽可能的使用 COPY ,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用ADD 的场合,就是所提及的需要自动解压缩的场合。
另外需要注意的是, ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY 指令,仅在需要自动解压缩的场合使用 ADD 。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。