本篇开始来学习关于 dockerfile 的知识。

注:环境为 CentOS7,docker 19.03。

dockerfile 是⼀个⽂本格式的配置⽂件, ⽤户可以使⽤ dockerfile 来快速创建⾃定义的镜像。

指令系统

dockerfile 主要是通过一个指令来实现想要的功能的。dockerfile 支持的指令如下

ARG

定义创建镜像过程中使⽤的变量。格式为:

ARG<name>[=<default value>]

在执⾏docker build时, 可以通过 -build-arg[=] 来为变量赋值。 当镜像编译成功后, ARG指定的变量将不再存在(ENV指定的变量将在镜像中保留)。

Docker内置了⼀些镜像创建变量, ⽤户可以直接使⽤⽽⽆须声明, 包括(不区分⼤⼩写) HTTP_PROXY、 HTTPS_PROXY、 FTP_PROXY、NO_PROXY。`

FROM busybox
ARG user1
ARG buildno=1
USER &user1
...

注:不要使用构建时变量来传递 github 密钥,用户凭据等重要信息。使用 docker history 命令可以查看构建时的变量信息。

FROM

指定所创建镜像的基础镜像。格式为:

  • FROM[AS]
  • FROM: [AS]
  • FROM@[AS]

任何 dockerfile中 第⼀条指令必须为FROM指令。 并且, 如果在同⼀个 dockerfile 中创建多个镜像时, 可以使⽤多个FROM指令(每个镜像⼀次)。

为了保证镜像精简, 可以选⽤体积较⼩的镜像如Alpine或Debian作为基础镜像。 例如:

ARG VERSION=9.3
FROM debian:${VERSION}

LABEL

LABEL指令可以为⽣成的镜像添加元数据标签信息。 这些信息可以⽤来辅助过滤出特定镜像。格式为 LABEL<key>=<value><key>=<value><key>=<value>

LABEL version="1.0.0-rc3"
LABEL author="yeasy@github" date="2020-01-01"
LABEL description="This text illustrates \
that label-values can span multiple lines."

EXPOSE

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

EXPOSE 22 80 8443/udp

注意该指令只是起到声明作⽤, 并不会⾃动完成端⼜映射

ENV

指定环境变量, 在镜像⽣成过程中会被后续RUN指令使⽤, 在镜像启动的容器中也会存在。格式为ENV<key><value>ENV<key>=<value>...

ENV APP_VERSION=1.0.0
ENV APP_HOME=/usr/local/app
ENV PATH $PATH:/usr/local/bin

指令指定的环境变量在运⾏时可以被覆盖掉, 如docker run --env <key>=<value> built_image。注意当⼀条ENV指令中同时为多个环境变量赋值并且值也是从环境变量读取时, 会为变量都赋值后再更新。

ENTRYPOINT

指定镜像的默认⼊⼜命令, 该⼊⼜命令会在启动容器时作为根命令执⾏, 所有传⼊值作为该命令的参数。⽀持两种格式:

  • 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

当服务不需要管理员权限时, 可以通过该命令指定运⾏⽤户, 并且可以在Dockerfile中创建所需要的⽤户。 例如:

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

要临时获取管理员权限可以使⽤gosu命令。

WORKDIR

为后续的RUN、 CMD、 ENTRYPOINT指令配置⼯作⽬录。格式为 WORKDIR/path/to/workdir

可以使⽤多个WORKDIR指令, 后续命令如果参数是相对路径, 则会基于之前命令指定的路径。 例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

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

注:为了避免出错, 推荐WORKDIR指令中只使⽤绝对路径。

ONBUILD

指定当基于所⽣成镜像创建⼦镜像时, ⾃动执⾏的操作指令。格式为 ONBUILD [INSTRUCTION]

例如, 使⽤如下的Dockerfile创建⽗镜像ParentImage, 指定 ONBUILD 指令:

# Dockerfile for ParentImage[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

使⽤ docker build 命令创建⼦镜像 ChildImage 时(FROM ParentImage),会⾸先执⾏ParentImage中配置的ONBUILD指令:

# Dockerfile for ChildImage
FROM ParentImage

等价于在 ChildImage 的 dockerfile 中添加了如下指令:

#Automatically run the following when building ChildImage
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
...

由于ONBUILD指令是隐式执⾏的, 推荐在使⽤它的镜像标签中进⾏标注, 例如ruby: 2.1-onbuild。

ONBUILD指令在创建专门⽤于⾃动编译、 检查等操作的基础镜像时,⼗分有⽤。

STOPSIGNAL

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

STOPSIGNAL signal

HEALTHCHECK

配置所启动容器如何进⾏健康检查(如何判断健康与否),⾃ docker1.12 开始⽀持。

格式有两种:

  • HEALTHCHECK[OPTIONS]CMD command: 根据所执⾏命令返回是否为0来判断;
  • HEALTHCHECK NONE: 禁⽌基础镜像中的健康检查。

OPTION⽀持如下参数:

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

SHELL

指定其他命令使⽤ shell 时的默认 shell 类型:

SHELL `["executable", "parameters"]`

默认值为 ["/bin/sh", "-c"]

对于Windows系统,Shell路径中使⽤了“\”作为分隔符, 建议在 dockerfile 开头添加 #escape='来指定转义符。

RUN

运⾏指定命令。格式为

  • RUN
  • 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 \
&& rm -rf /var/lib/apt/lists/*

CMD

CMD 指令⽤来指定启动容器时默认执⾏的命令。

⽀持三种格式:

  • CMD ["executable", "param1", "param2"]: 相当于执⾏ executableparam1 param2, 推荐⽅式;
  • CMD command param1 param2: 在默认的Shell中执⾏, 提供给需要交互的应⽤;
  • CMD ["param1", "param2"]: 提供给ENTRYPOINT的默认参数。

每个 Dockerfile 只能有⼀条CMD命令。 如果指定了多条命令, 只有最后⼀条会被执⾏。如果⽤户启动容器时候⼿动指定了运⾏的命令(作为run命令的参数) , 则会覆盖掉CMD指定的命令。

ADD

添加内容到镜像。格式为 ADD <src> <dest>

该命令将复制指定的 路径下内容到容器中的路径下。其中可以是Dockerfile所在⽬录的⼀个相对路径(⽂件或⽬录);也可以是⼀个URL; 还可以是⼀个tar⽂件(⾃动解压为⽬录)可以是镜像内绝对路径, 或者相对于⼯作⽬录(WORKDIR) 的相对路径。

路径⽀持正则格式, 例如:

ADD *.c /code/

COPY

复制内容到镜像。格式为 COPY <src> <dest>

复制本地主机的 (为Dockerfile所在⽬录的相对路径, ⽂件或⽬录) 下内容到镜像中的。 ⽬标路径不存在时, 会⾃动创建。路径同样⽀持正则格式。

COPY与ADD指令功能类似, 当使⽤本地⽬录为源⽬录时, 推荐使⽤COPY。

创建镜像

创建镜像使用命令 docker [image] build, 格式为 docker build [OPTIONS] PATH | URL| -

该命令将读取指定路径下(包括⼦⽬录) 的Dockerfile, 并将该路径下所有数据作为上下⽂(Context) 发送给Docker服务端。 Docker服务端在校验Dockerfile格式通过后, 逐条执⾏其中定义的指令, 碰到ADD、 COPY和RUN指令会⽣成⼀层新的镜像。 最终如果创建镜像成功, 会返回最终镜像的ID。

docker build 支持以下的选项:





关于父镜像

dockerfile 使用 FROM 指令指定父镜像,一般我们使用系统镜像来作为父镜像,但是我们也可以使用基础镜像(scratch)来作为父镜像:

FROM scratch
ADD binary /
CMD ["/binary"]

docker 中镜像存在继承关系,用一张图来说明:

.docerignore 文件

如同 git 有 .gitignore 文件一样,docker 也有 .dockerignore 文件来让Docker忽略匹配路径或⽂件, 在创建镜像时候不将⽆关数据发送到服务端

*/temp*
*/*/temp*
tmp?
~*
Dockerfile
!README.md

dockerignore⽂件中模式语法⽀持Golang风格的路径正则格式:

  • “*”表⽰任意多个字符;
  • “? ”代表单个字符;
  • “!”表⽰不匹配(即不忽略指定的路径或⽂件) 。

多步骤创建

⾃17.05版本开始, Docker⽀持多步骤镜像创建(Multi-stage build) 特性, 可以精简最终⽣成的镜像⼤⼩。例如我们先指定一个编译的环境镜像进行编译,在指定一个运行的镜像来运行编译的代码。

以 Go 语言为例,创建一个空目录,在目录中创建 Go 代码文件 main.go

package main

import "fmt"

func main() {
fmt.Println("Hello World")
}

编写 dockerfile

FROM golang:1.9 as builder
RUN mkdir -p /go/src/test
WORKDIR /go/src/test
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o app . FROM alpine:latest
WORKDIR /root/
COPY --from=builder /go/src/test/app .
CMD ["./app"]

执行创建镜像

# docker build -t xingyys/test -f dockerfile .
Sending build context to Docker daemon 38.91kB
Step 1/9 : FROM golang:1.9 as builder
---> ef89ef5c42a9
...
Successfully built 73b66909a48d
Successfully tagged xingyys/test:latest
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xingyys/test latest 73b66909a48d 8 minutes ago 7.44MB

使用多步骤发现生成的镜像很精简,当也可以用多个 dockerfile 实现这个功能,但需要两个 dockerfile,维护成本提高了。

dockerfile 要点

  • 编写 dockerfile 存在注意的点,遵守这些点可以生成更精简的镜像:
  • 精简镜像⽤途: 尽量让每个镜像的⽤途都⽐较集中单⼀, 避免构造⼤⽽复杂、 多功能的镜像;
  • 选⽤合适的基础镜像: 容器的核⼼是应⽤。 选择过⼤的⽗镜像(如Ubuntu系统镜像) 会造成最终⽣成应⽤镜像的臃肿, 推荐选⽤瘦⾝过的应⽤镜像(如node: slim) , 或者较为⼩巧的系统镜像(如alpine、 busybox或debian) ;
  • 提供注释和维护者信息: Dockerfile也是⼀种代码, 需要考虑⽅便后续的扩展和他⼈的使⽤;
  • 正确使⽤版本号: 使⽤明确的版本号信息, 如1.0, 2.0, ⽽⾮依赖于默认的latest。 通过版本号可以避免环境不⼀致导致的问题;
  • 减少镜像层数: 如果希望所⽣成镜像的层数尽量少, 则要尽量合并RUN、 ADD和COPY指令。 通常情况下, 多个RUN指令可以合并为⼀条RUN指令;
  • 恰当使⽤多步骤创建(17.05+版本⽀持) : 通过多步骤创建, 可以将编译和运⾏等过程分开, 保证最终⽣成的镜像只包括运⾏应⽤所需要的最⼩化环境。 当然, ⽤户也可以通过分别构造编译镜像和运⾏镜像来达到类似的结果, 但这种⽅式需要维护多个Dockerfile。
  • 使⽤.dockerignore⽂件: 使⽤它可以标记在执⾏docker build时忽略的路径和⽂件, 避免发送不必要的数据内容, 从⽽加快整个镜像创建过程。
  • 及时删除临时⽂件和缓存⽂件: 特别是在执⾏apt-get指令后, /var/cache/apt下⾯会缓存了⼀些安装包;
  • 提⾼⽣成速度: 如合理使⽤cache, 减少内容⽬录下的⽂件, 或使⽤.dockerignore⽂件指定等;
  • 调整合理的指令顺序: 在开启cache的情况下, 内容不变的指令尽量放在前⾯, 这样可以尽量复⽤;
  • 减少外部源的⼲扰: 如果确实要从外部引⼊数据, 需要指定持久的地址, 并带版本信息等, 让他⼈可以复⽤⽽不出错。

docker 实践六:dockerfile 详解的更多相关文章

  1. Docker系列07—Dockerfile 详解

    本文收录在容器技术学习系列文章总目录 1.认识Dockerfile 1.1 镜像的生成途径 基于容器制作  dockerfile,docker build 基于容器制作镜像,已经在上篇Docker系列 ...

  2. Docker 学习7 Dockerfile详解

    一.镜像的生成途径 1.使用当前进程替换上一个进程 exec 2.生成方式 3.dockerfile制作镜像要求 a.要有专有的工作目录. b.要有专门的制作文件,文件名首字母大写 c.如果要打包很多 ...

  3. Docker 学习8 Dockerfile详解2

    一.继续上章节Docker学习7 CMD命令后. 11.ENTRYPOINT a.容器启动后相当于会启动ENTRYPOINT + CMD 命令,CMD相当于参数传给entrypoint的 [root@ ...

  4. Docker入门02——Dockerfile详解

    基本示例 FROM MAINTAINER LABEL RUN ADD COPY CMD ENTRYPOINT ENV EXPOSE VOLUME WORKDIR USER ARG 基本示例 # Thi ...

  5. Dockerfile详解

    Dockerfile详解 利用Dockerfile文件,可以构建docker的image镜像 命令使用 通过-f参数指定Dockerfile路径,进行构建image docker build -f / ...

  6. Dockerfile详解及优化

    Dockerfile详解 0. Dockerfile的作用 docker可以根据Dockerfile中的指令来构建docker镜像.Dockerfile是一个文本文件,其应当包含用户想要构建一个镜像的 ...

  7. 最佳实战Docker持续集成图文详解

    最佳实战Docker持续集成图文详解 这是一种真正的容器级的实现,这个带来的好处,不仅仅是效率的提升,更是一种变革:开发人员第一次真正为自己的代码负责——终于可以跳过运维和测试部门,自主维护运行环境( ...

  8. docker entrypoint入口文件详解

    docker entrypoint入口文件详解 pasting Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较 [k8s]args指令案例-彻底理解docke ...

  9. 【转】Dockerfile详解

    Dockerfile详解 https://blog.csdn.net/wo18237095579/article/details/80540571 --------------------- 作者:大 ...

  10. Docker Swarm 负载均衡详解 or 模式选择

    Docker Swarm 负载均衡详解 Swarm模式内置DNS组件,可以自动为集群中的每个服务分配DNS记录. Swarm manager使用内部负载均衡,根据服务的DNS名称在集群内的服务之间分发 ...

随机推荐

  1. Spring 中开启Mybatis缓存

    mybatis的一级缓存默认是开启的,二级缓存开启的方法: 在每个Mapper.xml文件中加入一个

  2. 常忘知识点三-使用选择器继承来精简CSS --- @extend

    推荐一个很详细的sass教程:https://www.sass.hk/docs/ 在设计网页的时候常常遇到这种情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式.通常会在 HTML 中 ...

  3. leetcode 576. Out of Boundary Paths 、688. Knight Probability in Chessboard

    576. Out of Boundary Paths 给你一个棋盘,并放一个东西在一个起始位置,上.下.左.右移动,移动n次,一共有多少种可能移出这个棋盘 https://www.cnblogs.co ...

  4. ECMAScript 6.0基础入门教程

    ECMAScript 6.0基础入门教程 转:https://blog.csdn.net/hexinyu_1022/article/details/80778727 https://blog.csdn ...

  5. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...

  6. 泡泡一分钟:Fast and Robust Initialization for Visual-Inertial SLAM

    张宁  Fast and Robust Initialization for Visual-Inertial SLAM链接:https://pan.baidu.com/s/1cdkuHdkSi9x7l ...

  7. SIEBEL BIP报表平台端口被占用

    管理-服务器配置S 找到对应的服务名称: CRMSS2 组件输入:XMLP Report Server 选择[高级]后,查找 Static Port Number 即可修改端口号. 端口号可查询测试环 ...

  8. 报错:ModuleNotFoundError: No module named '_ctypes'

    报错背景: CentOS 7 Python3.7 安装 setuptools 插件的时候报错. 报错现象: [root@master setuptools-]# python3. setup.py b ...

  9. 看烦了VS2012的黑白调调了吗?换

    VS2012的默认深色主题的确让整个IDE看起来很有气场,而且深色的主题保护眼睛,还是蛮不错的.但是看久了也会烦啊.虽然说重要的不是IDE看起来怎么样,而是写出来的代码质量怎么样,但一个好的环境也是会 ...

  10. [LintCode] 619 Binary Tree Longest Consecutive Sequence III 二叉树最长连续序列 III

    Given a k-ary tree, find the length of the longest consecutive sequence path. The path could be star ...