Dockerfile基础
一、Dockerfile概念
Dockerfile 定义镜像,依赖镜像来运行容器,因此 Dockerfile 是镜像和容器的关键。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。
二、Dockerfile与Docker commit
(1)Docker commit创建镜像
搭建过程:
- 启动一个基础容器;
- 在容器中进行软件安装、环境配置等操作;
- 使用docker commit 命令将修改后的容器保存为镜像。
优点:
- 简单直观,不需要太复杂的操作;
- 快速上手,适合初学者或临时需求,快速创建镜像;
缺点:
- 缺乏可重复性,手动操作无法保证每次构建出的镜像完全一致;
- 不可追踪,容器中的修改过程没有记录,他人难以理解镜像的构建过程;
- 不可维护,若需要更新镜像,必须手动操作,难以维护;
- 体积臃肿,docker commit 创建的镜像通常是一个整体快照,无法充分利用 docker 的分层存储机制,导致镜像体积较大;
(2)Dockerfile创建镜像
搭建过程:
- 编写dockerfile文件;
- 使用docker build 命令依据dockerfile构建镜像。
优点:
- 可重复性,dockerfile明确记录了镜像的构建过程,便于在不同环境重复构建相同的镜像;
- 透明性,可以查看dockerfile文件,了解镜像是如何搭建的;
- 体积小,dockerfile的每一行都会生成一个新的镜像层,有利于优化镜像大小和构建速度;
缺点:
- 上手难度高,需熟悉dockerfile的指令语法。
三、Dockerfile创建镜像
(1)dockerfile目录
首先需要有一个制作镜像的目录,该目录下有个文件,名称必须为Dockerfile,docker build读取Dockerfile是按顺序依次Dockerfile里的配置。可以在docker build命令中使用 -f 标志指向文件系统中任何位置的Dockerfile。
把文件路径写入到.dockerignore,对应的路径将不会被打包到新镜像。
(2)dockerfile文件格式
Dockerfile有指定的格式,#号开头为注释,指令默认用大写字母来表示,以区分指令和参数,且第一条非注释指令必须是FROM 开头,表示基于哪个基础镜像来构建新镜像。
下面通过一个示例来介绍其文件格式:
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1 # Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
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
# 4、暴露端口,启动执行指令
EXPOSE 80
CMD /usr/sbin/nginx
Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。
(3)根据dockerfile构建镜像
编写完成 Dockerfle 之后,可以通过 docker build 命令来创建镜像。
该命令将读取指定路径下(包括子目录)的Dockerfle,并将该路径下所有数据作为上下文(Context)发送给 Docker 服务端。Docker 服务端在校验 Dockerfle 格式通过后,逐条执行其中定义的指令,碰到 ADD、COPY 和 RUN 指令会生成一层新的镜像。最终如果创建镜像成功,会返回最终镜像的 ID。
如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。因此除非是生成镜像所必需的文件,不然不要放到上下文路径下。如果使用非上下文路径下的Dockerfle,可以通过-f选项来指定其路径。
要指定生成镜像的标签信息,可以通过-t选项。该选项可以重复使用多次为镜像一次添加多个名称。例如,上下文路径为 /tmp/docker builder/,并且希望生成镜像标签为 builder/rst_image:1.0.0,可以使用下面的命令:
$ docker build -t builder/first_image:1.0.0 /tmp/docker_builder
四、dockerfile指令
(1)FROM
指定所创建镜像的基础镜像。
# 示例
FROM ubuntu:20.04
(2)ARG和ENV
定义创建镜像过程中使用的变量。
# 示例
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
注意:使用 ARG
定义构建时参数(如镜像版本号);使用 ENV
设置运行时环境变量。其中ARG的值仅在构建时有效,而ENV的值会保留在最终镜像中。
(3)LABEL和MAINTAINER
为镜像添加元数据,用于描述镜像的信息,便于管理和追踪。
# 示例
LABEL version="1.0" maintainer="example@example.com"
MAINTAINER docker_user docker_user@email.com
(4)WORKDIR
定义容器内的工作目录,后续的指令都会在这个目录下执行。
# 示例
WORKDIR /app
(5)COPY
复制本地主机的<src> (为Dockerfile所在目录的相对路径)下内容到镜像中的<dest>。目标路径不存在时,会自动创建。
# 示例
COPY app.py /app/app.py
(6)ADD
ADD功能更强大,除了可以复制文件外,还支持从远程 URL 下载文件以及自动解压压缩文件。
# 示例
ADD https://example.com/config.json /app/config.json
(7)RUN
运行命令以安装依赖或配置环境。
# 示例
RUN apt-get update && apt-get install -y nginx
(8)EXPOSE
声明容器运行时监听的端口。
# 示例
EXPOSE 80 443
(9)ENTRYPOINT
ENTRYPOINT 用于配置容器作为一个可执行程序运行,提供固定入口点,其在运行容器时不可以被覆盖。
语法:
ENTRYPOINT ["executable", "param1", "param2"] # JSON 数组格式(推荐)
ENTRYPOINT command param1 param2 # Shell 格式
示例:
ENTRYPOINT ["/start.sh"]
(10)CMD
CMD 指令用来指定启动容器时默认执行的命令,其在运行容器时可以被覆盖。
格式:
CMD ["executable", "param1", "param2"] # JSON 数组格式(推荐)
CMD command param1 param2 # Shell 格式
CMD ["param1", "param2"] # 默认参数形式(补充 ENTRYPOINT)
示例:
CMD ["python", "app.py"]
# 也可以与ENTRYPOINT组合使用
ENTRYPOINT ["python"]
CMD ["app.py"]
注意:每个Dockerfile 只能有一条 CMD命令。 如果指定了多条命令,只有最后一条会被执行。
五、dockerfile构建镜像原则
dockerfile构建镜像需要遵循以下原则:
- 单一职责,每个层级只做每个层级的事。
- 提供注释信息,以便他人理解。
- 保持容器最小化。
- 合理选择基础镜像,尽量选择成熟易用的基础镜像版本。
- 最小化镜像层数,尽量精简,否则容易出错,可能回影响加载速度。