docker 实践六:dockerfile 详解
本篇开始来学习关于 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 详解的更多相关文章
- Docker系列07—Dockerfile 详解
本文收录在容器技术学习系列文章总目录 1.认识Dockerfile 1.1 镜像的生成途径 基于容器制作 dockerfile,docker build 基于容器制作镜像,已经在上篇Docker系列 ...
- Docker 学习7 Dockerfile详解
一.镜像的生成途径 1.使用当前进程替换上一个进程 exec 2.生成方式 3.dockerfile制作镜像要求 a.要有专有的工作目录. b.要有专门的制作文件,文件名首字母大写 c.如果要打包很多 ...
- Docker 学习8 Dockerfile详解2
一.继续上章节Docker学习7 CMD命令后. 11.ENTRYPOINT a.容器启动后相当于会启动ENTRYPOINT + CMD 命令,CMD相当于参数传给entrypoint的 [root@ ...
- Docker入门02——Dockerfile详解
基本示例 FROM MAINTAINER LABEL RUN ADD COPY CMD ENTRYPOINT ENV EXPOSE VOLUME WORKDIR USER ARG 基本示例 # Thi ...
- Dockerfile详解
Dockerfile详解 利用Dockerfile文件,可以构建docker的image镜像 命令使用 通过-f参数指定Dockerfile路径,进行构建image docker build -f / ...
- Dockerfile详解及优化
Dockerfile详解 0. Dockerfile的作用 docker可以根据Dockerfile中的指令来构建docker镜像.Dockerfile是一个文本文件,其应当包含用户想要构建一个镜像的 ...
- 最佳实战Docker持续集成图文详解
最佳实战Docker持续集成图文详解 这是一种真正的容器级的实现,这个带来的好处,不仅仅是效率的提升,更是一种变革:开发人员第一次真正为自己的代码负责——终于可以跳过运维和测试部门,自主维护运行环境( ...
- docker entrypoint入口文件详解
docker entrypoint入口文件详解 pasting Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较 [k8s]args指令案例-彻底理解docke ...
- 【转】Dockerfile详解
Dockerfile详解 https://blog.csdn.net/wo18237095579/article/details/80540571 --------------------- 作者:大 ...
- Docker Swarm 负载均衡详解 or 模式选择
Docker Swarm 负载均衡详解 Swarm模式内置DNS组件,可以自动为集群中的每个服务分配DNS记录. Swarm manager使用内部负载均衡,根据服务的DNS名称在集群内的服务之间分发 ...
随机推荐
- Spring 中开启Mybatis缓存
mybatis的一级缓存默认是开启的,二级缓存开启的方法: 在每个Mapper.xml文件中加入一个
- 常忘知识点三-使用选择器继承来精简CSS --- @extend
推荐一个很详细的sass教程:https://www.sass.hk/docs/ 在设计网页的时候常常遇到这种情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式.通常会在 HTML 中 ...
- leetcode 576. Out of Boundary Paths 、688. Knight Probability in Chessboard
576. Out of Boundary Paths 给你一个棋盘,并放一个东西在一个起始位置,上.下.左.右移动,移动n次,一共有多少种可能移出这个棋盘 https://www.cnblogs.co ...
- ECMAScript 6.0基础入门教程
ECMAScript 6.0基础入门教程 转:https://blog.csdn.net/hexinyu_1022/article/details/80778727 https://blog.csdn ...
- SpringBoot启动嵌入式tomcat源码解读
一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...
- 泡泡一分钟:Fast and Robust Initialization for Visual-Inertial SLAM
张宁 Fast and Robust Initialization for Visual-Inertial SLAM链接:https://pan.baidu.com/s/1cdkuHdkSi9x7l ...
- SIEBEL BIP报表平台端口被占用
管理-服务器配置S 找到对应的服务名称: CRMSS2 组件输入:XMLP Report Server 选择[高级]后,查找 Static Port Number 即可修改端口号. 端口号可查询测试环 ...
- 报错:ModuleNotFoundError: No module named '_ctypes'
报错背景: CentOS 7 Python3.7 安装 setuptools 插件的时候报错. 报错现象: [root@master setuptools-]# python3. setup.py b ...
- 看烦了VS2012的黑白调调了吗?换
VS2012的默认深色主题的确让整个IDE看起来很有气场,而且深色的主题保护眼睛,还是蛮不错的.但是看久了也会烦啊.虽然说重要的不是IDE看起来怎么样,而是写出来的代码质量怎么样,但一个好的环境也是会 ...
- [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 ...