qianlei90 / Blog

那些该死的文字呦
https://qianlei.notion.site
103 stars 20 forks source link

深入Dockerfile(二):最佳实践 #36

Open qianlei90 opened 7 years ago

qianlei90 commented 7 years ago

深入Dockerfile(二):最佳实践

Tags: 印象笔记 Docker

docker官方文档Best practices for writing Dockerfiles的笔记。

[toc]


一、Dockerfile

1.1 原则与建议

1.2 Dockerfile指令的最佳实践

针对单条指令的建议。

1.2.1 FROM

尽可能的使用官方镜像,推荐使用Debian Image。如果追求镜像的大小,可以考虑alpine或scratch。

1.2.2 LABEL

在一个Label指令内添加多个标签。如:

LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

1.2.3 RUN

为了增加Dockerfile的可读性和可维护性,将复杂的RUN指令分行写,不要全部写在一行。

  1. 避免使用apt-get upgradedist-upgrade,这会更新大量不必要的系统包,增加了镜像大小。如果需要更新包,简单的使用RUN apt-get update && apt-get install -y package就好。记得,如果package很多的话,分行并按字母表排序。
  2. apt-get upgradeapt-get install -y package要在一个RUN指令内,如果在多个RUN指令内,docker会使用缓存,具体的参考之前的<Dockerfile语法指南>
  3. Debian和Ubuntu的官方镜像会自动执行apt-get clean语句。如果是其他镜像,手动执行该指令,或删除/var/lib/apt/lists下的文件。

带管带的命令,其返回值是最后一条命令的返回值。所以如果管道前的命令出错而管道后的指令正常执行,则docker不会认为这条指令有问题。如果需要所有的管道命令都正常执行,可以增加set -o pipefail,如:

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

部分shell不支持set -o pipefail,所以需要指定shell。如:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

1.2.4 CMD

使用exec格式,如CMD ["executable", "param1", "param2"]。 如果用户不清楚镜像的ENTRYPOINT是什么,不要和ENTRYPOINT配合使用,不要使用CMD ["param1", "param2"]。

1.2.5 EXPOSE

如果你的镜像是个服务,如Apache这样的,使用正常的、通用的端口,如80端口。

1.2.6 ENV

可以修改PATH环境变量来优先使用自己的可执行文件。 可以当做变量来使用,控制Dockerfile中的其他指令,使Dockerfile更易维护。如:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

1.2.7 ADD和COPY

这两个指令类似,但如果只是复制文件到镜像内,仍然推荐使用COPY,因为COPY在功能上更单一,而ADD指令有更多的特性(如远程文件、自动解压等)。 ADD指令使用的最好时机是想镜像内添加压缩包,ADD会自动解压。强烈不建议使用ADD来添加远程文件。如果确实需要远程文件,应该使用RUN wgetRUN curl

1.2.8 ENTRYPOINT

ENTRYPOINT指令的主要用途是把容器当做命令来用,此时CMD指令来指定默认参数。可以自己写脚本来当做ENTRYPOINT。

1.2.9 VOLUME

强烈建议使用该指令为用户数据创建数据卷,如数据的存储位置、配置文件的存储位置或用户自己创建的文件/目录等。

1.2.10 USER

如果镜像的服务不用授权就能使用,那应该新增用户和组,以新用户来执行,如:

RUN groupadd -r postgres && useradd -r -g postgres postgres

需要注意的是,UID/GID是顺着镜像中已存在的UID/GID创建的,如果对这个有严格要求,应该自己显式定义UID/GID。 不要在镜像内安装或使用sudo,如果需要类似的功能,可以使用gosu。 也不要来来回回的切换用户,这样会增加图层层数。

1.2.11 WORKDIR

只使用绝对路径。切换工作目录只使用WORKDIR而不是RUN cd ... && do-something

1.2.12 ONBUILD

在运行docker build命令时,在执行任何指令前,先执行父Dockerfile中定义的ONBUILD指令。 在打标签时应该添加这些信息,如:ruby:1.9-onbuild。 谨慎使用COPYADD,因为有可能在子构建中并不存在对应的文件或目录。

二、创建自己的新镜像

两种方式:

  1. 通过debootstrap类似的工具来生成一个基础的OS,然后导入到docker中:
$ sudo debootstrap raring raring > /dev/null
$ sudo tar -C raring -c . | docker import - raring

a29c15f1bf7a

$ docker run raring cat /etc/lsb-release

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=13.04
DISTRIB_CODENAME=raring
DISTRIB_DESCRIPTION="Ubuntu 13.04"
  1. 从scratch镜像重新开始构建。scratch是docker中最小的镜像。如:
FROM scratch
ADD hello /
CMD ["/hello"]

- 完 - 2017/03/10