`__\ 。
.. code:: docker
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
``HEALTHCHECK``
---------------------
格式:
- ``HEALTHCHECK [选项] CMD <命令>``\ :设置检查容器健康状况的命令
- ``HEALTHCHECK NONE``\ :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
``HEALTHCHECK`` 指令是告诉 Docker
应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。
在没有 ``HEALTHCHECK`` 指令前,Docker
引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在
1.12 以前,Docker
不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。
而自 1.12 之后,Docker 提供了 ``HEALTHCHECK``
指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。
当在一个镜像指定了 ``HEALTHCHECK`` 指令后,用其启动容器,初始状态会为
``starting``\ ,在 ``HEALTHCHECK`` 指令检查成功后变为
``healthy``\ ,如果连续一定次数失败,则会变为 ``unhealthy``\ 。
``HEALTHCHECK`` 支持下列选项:
- ``--interval=<间隔>``\ :两次健康检查的间隔,默认为 30 秒;
- ``--timeout=<时长>``\ :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认
30 秒;
- ``--retries=<次数>``\ :当连续失败指定次数后,则将容器状态视为
``unhealthy``\ ,默认 3 次。
和 ``CMD``, ``ENTRYPOINT`` 一样,\ ``HEALTHCHECK``
只可以出现一次,如果写了多个,只有最后一个生效。
在 ``HEALTHCHECK [选项] CMD`` 后面的命令,格式和 ``ENTRYPOINT``
一样,分为 ``shell`` 格式,和 ``exec``
格式。命令的返回值决定了该次健康检查的成功与否:\ ``0``\ :成功;\ ``1``\ :失败;\ ``2``\ :保留,不要使用这个值。
假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web
服务是否在正常工作,我们可以用 ``curl`` 来帮助判断,其 ``Dockerfile`` 的
``HEALTHCHECK`` 可以这么写:
.. code:: dockerfile
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
这里我们设置了每 5
秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过
3 秒没响应就视为失败,并且使用 ``curl -fs http://localhost/ || exit 1``
作为健康检查命令。
使用 ``docker build`` 来构建这个镜像:
.. code:: shell
$ docker build -t test:v1 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM nginx:latest
......
---> 666c5320c9d3
Successfully built 666c5320c9d3
Successfully tagged test:v1
构建好了后,我们启动一个容器:
.. code:: shell
$ docker run -d --name web -p 8001:80 test:v1
48118ab055f56f3805153f90566115f0e7c1b3dcf8d9f67394b18f5deb942543
当运行该镜像后,可以通过 ``docker container ls`` 看到最初的状态为
``(health: starting)``\ :
.. code:: shell
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
在等待几秒钟后,再次 ``docker container ls``\ ,就会看到健康状态变化为了
``(healthy)``\ :
.. code:: shell
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
如果健康检查连续失败超过了重试次数,状态就会变为 ``(unhealthy)``\ 。
为了帮助排障,健康检查命令的输出(包括 ``stdout`` 以及
``stderr``\ )都会被存储于健康状态里,可以用 ``docker inspect`` 来查看。
.. code:: shell
$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
"FailingStreak": 0,
"Log": [
{
"End": "2021-05-31T14:57:02.822820903+08:00",
"ExitCode": 0,
"Output": "\n\n\nWelcome to nginx!\n\n\n\nWelcome to nginx!
\nIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.
\n\nFor online documentation and support please refer to\nnginx.org.
\nCommercial support is available at\nnginx.com.
\n\nThank you for using nginx.
\n\n\n",
"Start": "2021-05-31T14:57:02.739461919+08:00"
},
{
"End": "2021-05-31T14:57:23.159938424+08:00",
"ExitCode": 0,
"Output": "\n\n\nWelcome to nginx!\n\n\n\nWelcome to nginx!
\nIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.
\n\nFor online documentation and support please refer to\nnginx.org.
\nCommercial support is available at\nnginx.com.
\n\nThank you for using nginx.
\n\n\n",
"Start": "2021-05-31T14:57:23.082654997+08:00"
}
],
"Status": "healthy"
}
``ONBUILD``
----------------------------
格式:\ ``ONBUILD <其它指令>``\ 。
``ONBUILD`` 是一个特殊的指令,它后面跟的是其它指令,比如 ``RUN``,
``COPY`` 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
``Dockerfile`` 中的其它指令都是为了定制当前镜像而准备的,唯有
``ONBUILD`` 是为了帮助别人定制自己而准备的。
编写父镜像``Dockerfile``\ :
.. code:: docker
FROM ubuntu:latest
MAINTAINER father-image
ONBUILD RUN apt-get update && apt-get install tree -y && rm -rf /var/lib/apt/lists/*
ONBUILD RUN mkdir mydir
构建镜像为father:
.. code:: shell
$ docker build -t father:v1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
345e3491a907: Pull complete
57671312ef6f: Pull complete
5e9250ddb7d0: Pull complete
Digest: sha256:adf73ca014822ad8237623d388cedf4d5346aa72c270c5acc01431cc93e18e2d
Status: Downloaded newer image for ubuntu:latest
---> 7e0aa2d69a15
Step 2/4 : MAINTAINER father-image
---> Running in 87b6c6596149
Removing intermediate container 87b6c6596149
---> 3de0b4ab00e7
Step 3/4 : ONBUILD RUN apt-get update && apt-get install tree -y && rm -rf /var/lib/apt/lists/*
---> Running in a0ff4fab64db
Removing intermediate container a0ff4fab64db
---> 731c21056063
Step 4/4 : ONBUILD RUN mkdir mydir
---> Running in 5b59a0d0a00d
Removing intermediate container 5b59a0d0a00d
---> ee17bc9c194a
Successfully built ee17bc9c194a
Successfully tagged father:v1
并创建容器:
.. code:: bash
$ docker run -it --rm father:v1
root@012b7f578bae:/# tree
bash: tree: command not found
root@012b7f578bae:/# ls /
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
发现此时由父镜像启动的容器是没有tree命令的,根目录下也没有mydir目录。说明ONBUILD后面的指令并不会在自己的构建中执行。
编写子镜像 ``Dockerfile`` :
.. code:: dockerfile
FROM father:v1
MAINTAINER son
构建son镜像:
.. code:: bash
$ docker build -t son:v1 .
根据镜像启动容器:
.. code:: bash
$ docker run --rm -it son:v1
root@733473ae9daa:/# ls
bin dev home lib32 libx32 mnt opt root sbin sys usr
boot etc lib lib64 media mydir proc run srv tmp var
root@733473ae9daa:/# tree /opt
/opt
0 directories, 0 files
在实际工作中,利用ONBUILD指令,通常用于创建一个模板镜像,后续可以根据该模板镜像创建特定的子镜像,需要在子镜像构建过程中执行的一些通用操作就可以在模板镜像对应的dockerfile文件中用ONBUILD指令指定。 从而减少dockerfile文件的重复内容编写。
``LABEL``
----------------------------
``LABEL`` 指令用来给镜像以键值对的形式添加一些元数据(metadata)。
.. code:: docker
LABEL = = = ...
我们还可以用一些标签来申明镜像的作者、文档地址等:
.. code:: docker
LABEL org.opencontainers.image.authors="yeasy"
LABEL org.opencontainers.image.documentation="https://yeasy.gitbooks.io"
具体可以参考
https://github.com/opencontainers/image-spec/blob/master/annotations.md
``SHELL``
----------------------------
格式:\ ``SHELL ["executable", "parameters"]``
SHELL` 指令可以指定 `RUN``ENTRYPOINT``CMD` 指令的 shell,Linux 中默认为 `["/bin/sh", "-c"]
SHELL ["/bin/sh", "-c"]
.. code::
RUN lll ; ls
SHELL ["/bin/sh", "-cex"]
RUN lll ; ls
两个 ``RUN`` 运行同一命令,第二个 ``RUN``
运行的命令会打印出每条命令并当遇到错误时退出。
当 :literal:`ENTRYPOINT``CMD` 以 shell 格式指定时,\ ``SHELL``
指令所指定的 shell 也会成为这两个指令的 shell
.. code:: docker
SHELL ["/bin/sh", "-cex"]
# /bin/sh -cex "nginx"
ENTRYPOINT nginx
SHELL ["/bin/sh", "-cex"]
# /bin/sh -cex "nginx"
CMD nginx
.. NOTE::
参考资料
- ``Dockerfie``
官方文档:\ https://docs.docker.com/engine/reference/builder/
- ``Dockerfile``
最佳实践文档:\ https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- ``Docker`` 官方镜像
``Dockerfile``\ :\ https://github.com/docker-library/docs
.. include:: ../../../comment.rst