Docker7-使用 Dockerfile 创建镜像

Dockerfile 是一个文本格式的配置文件, 用户可以使用 Dockerfile 来快速创建自定义的镜像.

基本结构

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

FROM centos:latest
MAINTAINER docker_user docker_user@email.com
RUN yum -y update
RUN yum -y install systemd systemd-libs
RUN yum clean all;
CMD ["/usr/sbin/init"]

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


命令详解

1.FROM
指定所创建镜像的基础镜像, 如果本地不存在, 则默认会去 Docker Hub 下载指定镜像.

格式为 FROM<image> 或 FROM<image>:<tag> 或 FROM<image>@<digest>

任何 Dockerfile 中的第一条指令必须为 FROM 指令. 并且, 如果在同一个 Dockerfile 中创建多个镜像, 可以使用多个 FROM 指令(每个镜像一次).

2. MAINTAINER
指定维护者信息, 格式为 MAINTAINER<name>. 例如:

MAINTAINER docker_user@email.com

该信息会写入生成镜像的 Author 属性域中.

3. 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

4.CMD
CMD 指令用来指定启动容器时默认执行的命令. 它支持三种格式:
1.CMD ["executable", "param1", "param2"] 使用 exec 执行, 是推荐使用的方式.
2.CMD command param1 param2/bin/sh 中执行, 提供给需要交互的应用.
3.CMD ["param1", "param2"] 提供给 ENTRYPOINT 的默认参数.

每个 Dockerfile 只能一个 CMD 命令. 如果指定了多条命令, 只有最后一条会被执行.

如果用户启动容器时, 手动指定了运行的命令(作为 run 的参数), 则会覆盖掉 CMD 指定的命令.

5.LABEL
LABEL 指令用来指定生成镜像的元数据标签信息.
格式为 LABEL <key>=<value> <key>=<value> <key>=<value>...
例如:

LABEL version="1.0"
LABEL description=""

6.EXPOSE
声明镜像内服务所监听的端口.
格式为EXPOSE <port> [<port>...]

EXPOSE 22 80 8080

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

7.ENV
指定环境变量, 在镜像生成过程中会被后续 RUN 指令使用, 在镜像启动的容器中也会存在.
格式为: ENV<key><value> 或 <key>=<value>....
例如:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN .....
ENV ....

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

8.ADD
该指令将复制指定的 <src> 路径下的内容到容器中的 <dest> 路径下.
格式为 ADD <src> <dest>

其中 <src> 可以是 Dockerfile 所在目录的一个相对路径(文件或目录), 也可以是 URL, 还可以是一个 tar 文件(如果为 tar 文件, 会自动解压到 <dest> 路径下). <dest> 可以是镜像内的绝对路径, 或者相对于工作目录(WORKDIR) 的相对路径.
路径支持正则格式, 例如:

ADD *.c /code/

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

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

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

每个 Dockerfile 中只能有一个 ENTRYPOINT, 当指定多个时, 只有最后一个有效.

在运行时, 可以被 --entrypoint 参数覆盖掉, 如 docker run --entrypoint.

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

12.USER
指定运行容器时的用户名或 UID, 后续的 RUN 等指令也会使用指定的用户身份.
格式为 USER daemon

当服务不需要管理员权限时, 可以通过该命令指定运行用户, 并且可以在之前创建需要的用户. 例如:

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

要临时获取管理员权限可以使用 gosu 或 sudo.

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

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

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

14.ARG
指定一些镜像内使用的参数(例如版本号信息等), 这些参数在执行 docker build 命令时才以 --build-arg<varname>=<value> 格式传入.
格式为 ARG<name>[=<defaults value>]
则可以用 docker build --build-arg<varname>=<value>. 来指定参数值.

15.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
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

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

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

STOPSIGNAL signal

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

HEALTHCHECK [OPTIONS] CMD command: 根据所执行命令返回值是否为0来判断.
HEALTHCHECK NONE: 禁止基础镜像中的健康检查.

OPTION 支持:

--interval=DURATION(默认为: 30s): 过多久检查一次;
--timeout=DURATION(默认为: 30s): 每次检查等待结果的超时;
--retries=N(默认为: 3): 如果失败了, 重试几次才最终确定失败.

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

注意: 对于 Windows 系统, 建议在 Dockerfile 开头添加 # escape =`来指定转义信息.


创建镜像

编写完成 Dockerfile 之后, 可以通过 docker build命令来创建镜像.
基本的格式为 docker build[选项] 内容路径, 该命令将读取指定路径下(包括子目录)的 Dockerfile, 并将该路径下的所有内容发送给 Docker 服务器, 由服务器来创建镜像.

因为除非生成镜像需要, 否则一般建议放置在 Dockerfile 的目录为空目录. 有两点经验:
1.如果使用非内容路径下的 Dockerfile, 可以通过 -f 选项来指定其路径.
2.要指定生成镜像的标签信息, 可以使用 -t 选项.

例如, 指定 Dockerfile 所在路径为 /tmp/docker_builder/, 并且希望生成镜像标签为 build_repo/first_image, 可以使用下面的命令:

docker build -t build_repo/first_image /tmp/docker_builder/

使用 .dockerignore 文件

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

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

最佳实践

所谓最佳实践, 实际上是从需求出发, 来定制适合自己 高效方便的镜像.

首先, 要尽量吃透每个指令的含义和执行结果, 自己多编写一些简单的例子进行测试, 弄清除了在撰写正式的 Dockerfile. 此外, Docker Hub 官方仓库中提供了大量的优秀镜像和对应的 Dockerfile, 通过阅读他们来学习如何撰写高效的 Dockerfile.

建议初学者在生成镜像过程中, 尝试从如下角度进行思考, 完善所生成的镜像.

**精简镜像用途: **尽量让每个镜像的用途都比较集中 单一, 避免构造大而复杂 多功能的镜像.

**选用合适的基础镜像: **过大的基础镜像会造成臃肿的镜像, 一般推荐比较小巧的 debian 镜像.

**提供足够清晰的命令注释和维护者的信息: ** Dockerfile 也是一种代码, 需要考虑方便后续扩展和他人使用.

**正确使用版本号: **使用明确的版本号信息, 如1.0, 2.0, 而非 latest, 将避免内容不一致可能引发的惨案.

**减少镜像层数: **如果希望所生成镜像层数尽量少, 则要尽量合并命令, 例如多个 RUN 指令可以合并为一条.

**及时删除临时文件和缓存文件: **特别是在执行 apt-get 指令后, /var/cache/apt 下面会缓存一些安装包.

**高度生成速度: **合理使用缓存, 减少内容目录下的文件, 或使用 .dockerignore 文件指定等.

**调整合理的指令顺序: **在开启缓存的情况下, 内容不变的指令尽量放在前面, 这样可以尽量复用.

**减少外部源的干扰: **如果确实要从外部引入数据, 需要指定持久的地址, 并带有版本信息, 让他人可以重复而不出错.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容