使用Dockerfile创建镜像

1、 Dockerfile基本结构

Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。
一般而言,Dockerfile分为四部分:基础镜像信息、维护者信息、镜像
操作指令和容器启动时执行指令。例如:

# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/
sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx

其中,一开始必须指明所基于的镜像名称,接下来一般是说明维护者信息。后面则是镜像操作指令,例如RUN指令,RUN指令将对镜像执行跟随的命令。每运行一条RUN指令,镜像就添加新的一层,并提交。最后是CMD指令,用来指定运行容器时的操作命令。

Dockerfile 官方参考

2、 指令说明

Dockerfile 指令 说明
FROM 指定基础镜像,用于后续的指令构建。
MAINTAINER 指定Dockerfile的作者/维护者。(已弃用,推荐使用LABEL指令)
LABEL 添加镜像的元数据,使用键值对的形式。
RUN 在构建过程中在镜像中执行命令。
CMD 指定容器创建时的默认命令。(可以被覆盖)
ENTRYPOINT 设置容器创建时的主要命令。(不可被覆盖)
EXPOSE 声明容器运行时监听的特定网络端口。
ENV 在容器内部设置环境变量。
ADD 将文件、目录或远程URL复制到镜像中,tar文件会自动解压。
COPY 将文件或目录复制到镜像中。
VOLUME 为容器创建挂载点或声明卷。
WORKDIR 设置后续指令的工作目录。
USER 指定后续指令的用户上下文。
ARG 定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。
ONBUILD 当该镜像被用作另一个构建过程的基础时,添加触发器。
STOPSIGNAL 设置发送给容器以退出的系统调用信号。
HEALTHCHECK 定义周期性检查容器健康状态的命令。
SHELL 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。
  • FROM
    指定所创建镜像的基础镜像,如果本地不存在,则默认会去Docker Hub下载指定镜像。
    任何Dockerfile中的第一条非注释指令必须为FROM指令。并且,如果在同一个Dockerfile中创建多个镜像,可以使用多个FROM指令(每个镜像一次)。
    空镜像: scratch

  • MAINTAINER
    指定维护者信息,格式为MAINTAINER <name>,已弃用,推荐使用LABEL指令

LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"

  • RUN
    运行指定的命令。
    格式为RUN <command>或RUN ["executable","param1","param2"]。注意,后一个指令会被解析为Json数组,因此必须用双引号。
    前者默认将在shell终端中运行命令,即/ bin/sh -c;后者则使用exec执行,不会启动shell环境。
    指定使用其他终端类型可以通过第二种方式实现,例如RUN ["/bin/ bash","-c","echo hello"]。
    每条RUN指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用\来换行。例如:
RUN apt-get update \
&& apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev \
&& rm -rf /var/cache/apt
  • CMD
    CMD指令用来指定启动容器时默认执行的命令。它支持三种格式:
    · CMD ["executable","param1","param2"]使用 exec执行,是推荐使用的方式;
    · CMD command param1 param2在/bin/sh中执行,提供给需要交互的应用;
    · CMD ["param1","param2"]提供给 ENTRYPOINT的默认参数。
    每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。
    如果用户启动容器时手动指定了运行的命令(作为run的参数),则会覆盖掉CMD指定的命令。
  • LABEL
    LABEL指令用来指定生成镜像的元数据标签信息。
LABEL version="1.0"
LABEL description="This text illustrates \ that label-values can span multiple lines."
  • EXPOSE
    声明镜像内服务所监听的端口。

EXPOSE 22 80 8443

注意,该指令只是起到声明作用,并不会自动完成端口映射。
在启动容器时需要使用-P,Docker主机会自动分配一个宿主机的临时端口转发到指定的端口;使用-p,则可以具体指定哪个宿主机的本地端口会映射过来。

  • ENV
    指定环境变量,在镜像生成过程中会被后续RUN指令使用,在镜像启动的容器中也会存在
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

指令指定的环境变量在运行时可以被覆盖掉,如docker run --env <key>=<value> built_image。

  • ADD
    该命令将复制指定的<src>路径下的内容到容器中的<dest>路径下。
    格式为ADD<src><dest>。
    其中<src>可以是Dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL,还可以是一个tar文件(如果为tar文件,会自动解压到<dest>路径下)。<dest>可以是镜像内的绝对路径,或者相对于工作目录(WORKDIR)的相对路径。

ADD *.c /code/

  • COPY
    格式为COPY<src><dest>。
    复制本地主机的<src>(为Dockerfile所在目录的相对路径、文件或目录)下的内容到镜像中的<dest>下。目标路径不存在时,会自动创建。
    路径同样支持正则格式。
    当使用本地目录为源目录时,推荐使用COPY。

  • 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数。
    支持两种格式:

ENTRYPOINT ["executable", "param1", "param2"](exec调用执行);
ENTRYPOINT command param1 param2(shell中执行)。

此时,CMD指令指定值将作为根命令的参数。
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个有效。
在运行时,可以被--entrypoint参数覆盖掉,如docker run --entrypoint。

  • VOLUME
    创建一个数据卷挂载点,格式为VOLUME["/data"]。
    可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存的数据等。

  • USER
    指定运行容器时的用户名或UID,后续的RUN等指令也会使用指定的用户身份。
    格式为USER daemon。
    当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户。例如:

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

  • WORKDIR
    为后续的RUN、CMD和ENTRYPOINT指令配置工作目录。
    格式为WORKDIR/path/to/workdir。
    可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为/ a/b/c。

  • ARG
    指定一些镜像内使用的参数(例如版本号信息等),这些参数在执行docker build命令时才以--build-arg <varname>=<value>格式传入。
    格式为ARG<name>[=<default value>]。
    则可以用docker build --build-arg <name>=<value>.来指定参数值。
    它与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

  • ONBUILD
    配置当所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令。
    格式为ONBUILD[INSTRUCTION]。
    例如,Dockerfile使用如下的内容创建了镜像 image-A:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于image-A创建新的镜像时,新的Dockerfile中使用FROM image-A指定基础镜像,会自动执行ONBUILD指令的内容,等价于在后面添加了两条指令:

FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用ONBUILD指令的镜像,推荐在标签中注明,例如ruby:1.9-onbuild。

  • STOPSIGNAL
    指定所创建镜像启动的容器接收退出的信号值。例如:

STOPSIGNAL signal

  • HEALTHCHECK
    配置所启动容器如何进行健康检查(如何判断健康与否),自Docker1.12开始支持。
    格式有两种:

HEALTHCHECK [OPTIONS] CMD command:根据所执行命令返回值是否为0来判断;

·HEALTHCHECK[OPTIONS]CMD command:根据所执行命令返回值是否为0来判断;
·HEALTHCHECK NONE:禁止基础镜像中的健康检查。
OPTION支持:
·--interval=DURATION(默认为:30s):过多久检查一次;
·--timeout=DURATION(默认为:30s):每次检查等待结果的超时;
·--retries=N(默认为:3):如果失败了,重试几次才最终确定失败。

  • SHELL
    指定其他命令使用shell时的默认shell类型。
    默认值为["/bin/sh","-c"]。

3、创建镜像

编写完成Dockerfile之后,可以通过docker build命令来创建镜像。

$ docker build -t test-image:1.1 /tmp/docker_builder/

4、使用.dockerignore文件

可以通过.dockerignore文件(每一行添加一条匹配模式)来让 Docker忽略匹配模式路径下的目录和文件。例如:

# comment
*/temp*
*/*/temp*
tmp?
~*

5、最佳实践

所谓最佳实践,实际上是从需求出发,来定制适合自己、高效方便的镜像。
首先,要尽量吃透每个指令的含义和执行效果,自己多编写一些简单的例子进行测试,弄清楚了再撰写正式的Dockerfile。此外,Docker Hub官方仓库中提供了大量的优秀镜像和对应的Dockefile,可以通过阅读它们来学习如何撰写高效的Dockerfile。
笔者在应用过程中也总结了一些实践经验。建议读者在生成镜像过程中,尝试从如下角度进行思考,完善所生成的镜像。

  • 精简镜像用途:尽量让每个镜像的用途都比较集中、单一,避免构造大而复杂、多功能的镜像;
  • 选用合适的基础镜像:过大的基础镜像会造成生成臃肿的镜像,一般推荐较为小巧的debian镜像;
  • 提供足够清晰的命令注释和维护者信息:Dockerfile也是一种代码,需要考虑方便后续扩展和他人使用;
  • 正确使用版本号:使用明确的版本号信息,如1.0,2.0,而非latest,将避免内容不一致可能引发的惨案;
  • 减少镜像层数:如果希望所生成镜像的层数尽量少,则要尽量合并指令,例如多个RUN指令可以合并为一条;
  • 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下面会缓存一些安装包;
  • ·提高生成㏿度:如合理使用缓存,减少内容目录下的文件,或使用.dockerignore文件指定等;
  • 调整合理的指令顺序:在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以尽量复用;
  • 减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带有版本信息,让他人可以重复而不出错。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容