抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

dockerfile

Dockerfile 是一个用来构建 Docker 镜像的脚本文件,里面按一定语法规则写好了一系列的指令(instructions),每个指令会执行一个操作,从而逐步构建出一个镜像。

dockerfile的作用

自动化构建镜像:不需要手动安装、配置环境,执行一次 docker build 就能复现相同镜像。

可移植性强:换服务器、换电脑,Dockerfile 还能构建出同样的镜像。

版本可控:和代码一起放在 Git 里,方便回滚和更新。

dokcerfile相关命令

指令(Instruction) 描述
ADD 添加本地或远程文件和目录。
ARG 使用构建时变量。
CMD 指定默认命令。
COPY 复制文件和目录。
ENTRYPOINT 指定默认可执行文件。
ENV 设置环境变量。
EXPOSE 描述应用程序正在侦听哪些端口。
FROM 从基础映像创建新的生成阶段。
HEALTHCHECK 在启动时检查容器的运行状况。
LABEL 将元数据添加到镜像。
MAINTAINER 指定镜像的作者。
ONBUILD 指定在生成中使用映像的说明。
RUN 执行构建命令。
SHELL 设置镜像的默认外壳。
STOPSIGNAL 指定退出容器的系统调用信号。
USER 设置用户和组 ID。
VOLUME 创建卷挂载。
WORKDIR 更改工作目录。

ARG

ARG是 Dockerfile 中唯一可以开在FROM前的指令,ARG可以在构建镜像提供一个dockerfile内部可使用的变量。当ARG在FROM前时,FROM的基础镜像指令可以使用ARG的变量值,若后续没有再次声明该变量,则无法使用FROM前定义的ARG变量,举例如下:

全局变量

1
2
3
4
ARG VERSION=latest           #定义一个版本变量
FROM busybox:$VERSION #FROM使用该变量
ARG VERSION #为下面继续使用之前的变量再次声明,不需要赋值,会沿用上面的值,若不声明则VERSION变量只能在FROM里使用
RUN echo $VERSION > image_version

变量传入赋值

1
2
3
4
FROM ubuntu            
ARG CONT_IMG_VER #声明变量为空,可以在docker build --build-arg 变量名 来赋值
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0} #bash的变量替换法,如果CONT_IMG_VER为空则用v1.0.0,不为空则用变量值。
RUN echo $CONT_IMG_VER

多阶段构建使用变量

1
2
3
4
5
6
ARG  CODE_VERSION=latest    #定义版本为最新
FROM base:${CODE_VERSION} #自动下载base最新latest版本镜像
CMD /code/run-app

FROM extras:${CODE_VERSION} #自动下载extras最新版本latest镜像
CMD /code/run-extras

FROM

FROM时构建镜像需要的基础镜像,有效的 Dockerfile 必须以FROM指令开头,该镜像可以是任何有效的镜像,例如docker官方源的,第三方源的,个人的。

多阶段构建FROM可以在单个 Dockerfile 中多次出现,以 创建多个映像或使用一个生成阶段作为另一个构建阶段的依赖项。

1
FROM [--platform=<platform>] <image> [AS <name>]

使用AS定义镜像作为下一个阶段的可用依赖,在所有阶段完成时,没有使用的镜像将会消失,不会为最终生成镜像产生额外的资源消耗和增大体积,常用于精简和优化镜像体积的方法。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
# ========== 阶段 1:构建阶段 ==========
FROM python:3.10 AS builder #该阶段以builder命名
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt
COPY . .

# ========== 阶段 2:运行阶段 ==========
FROM python:3.10-slim AS final
WORKDIR /app
COPY --from=builder /install /usr/local #使用builder阶段的文件,复制到指定路径
COPY --from=builder /app /app
CMD ["python", "main.py"]

ENV

ENV设置环境变量,这些变量会在 镜像构建阶段容器运行阶段 都生效。

1
2
3
4
5
6
7
ENV MY_NAME="John Doe"    #引号赋值
ENV MY_DOG=Rex\ The\ Dog #反斜杠转义空格,使环境变量在不用引号下也能赋值空格
ENV MY_CAT=fluffy

#单行声明多个变量,反斜杠转义空格和回车
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy

env声明与使用(可配合ARG变量)

1
2
3
4
5
6
7
FROM ubuntu:22.04
# 声明环境变量
ARG VERSION="1.0.0"
ENV APP_NAME="MyApp" \
APP_VERSION=${VERSION}
# 容器启动时执行脚本
CMD echo "Starting $APP_NAME, version $APP_VERSION"

ADD

ADD可以从构建上下文(本地目录)复制文件,从远程 URL下载并添加文件,自动解压 tar 压缩包.tar, .tar.gz 等),从Git 仓库(实验性功能,需要 BuildKit)拉取文件。

当使用本地 tar 存档作为 的源时,并且存档位于可识别的压缩格式( 或 uncompressed),则存档被解压缩并提取到指定的目标中。只 提取本地 tar 档案。如果tar存档是远程URL,则存档不会被解压,而是下载并放置在目标位置。(支持格式.tar,.tar.gz,.tgz ,.tar,.bz2, .tar.xz)

使用语法:

1
2
ADD [OPTIONS] <src> ... <dest>
ADD [OPTIONS] ["<src>", ... "<dest>"] #当路径有空格时,推荐使用这个语法

ADD使用举例:

1
2
3
4
5
6
ADD file1.txt file2.txt /usr/src/things/ #将两个文件推送到目标路径,最后的参数必须要目标路径
ADD git@github.com:user/repo.git /usr/src/things/ #下载网址内容到目标路径
ADD *.png /dest/ #模糊匹配,匹配后缀为.png的文件
ADD index.?s /dest/ #单字匹配,可以匹配index.js index.ts
ADD package.tar.gz /app/ #自动解压到app下

COPY

该命令与ADD相似,有些许不同,具体下面讲解。COPY 有两种形式。 对于包含空格的路径,需要后一种形式。

  • 如果目标路径不是以前导斜杠开头,则将其解释为相对于构建容器的工作目录(即当前shell路径的相对路径)。
  • 如果目标不存在,则会创建它以及所有缺少的目录 在它的路径上。
  • 如果源是一个文件,并且目标没有以尾随斜杠结尾,源文件将作为文件写入目标路径。
1
2
COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]

多阶段构建容器间引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
COPY file1.txt file2.txt /usr/src/things/ #将两个文件推到指定路径
#如果直接或使用通配符指定多个源文件,则目标必须是目录(必须以斜杠结尾)。/
COPY *.txt /usr/src/things/

# 多阶段构建依赖文件
# COPY [--from=<image|stage|context>] <src> ... <dest> 引用阶段名称来使用该容器里的内容
# ========== 阶段 1:构建阶段 ==========
FROM python:3.10 AS builder #该阶段以builder命名
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt
COPY . .

# ========== 阶段 2:运行阶段 ==========
FROM python:3.10-slim AS final
WORKDIR /app
COPY --from=builder /install /usr/local #使用builder阶段的文件或目录,复制到指定路径
COPY --from=builder /app /app #使用builder阶段的文件或目录,复制到指定路径
CMD ["python", "main.py"]

MAINTAINER

MAINTAINER 用于指定镜像的维护作者信息,已被官方弃用,推荐使用下面的LABEL维护镜像作者信息。

1
MAINTAINER <name>

LABEL

该指令将元数据以键值对形式添加到镜像中,要在值中包含空格,需要使用引号和反斜杠,使用语法:

MAINTAINER 已被官方弃用,目前使用LABEL标签替换,更加灵活且便洁。

1
LABEL <key>=<value> [<key>=<value>...]

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#逐行添加
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

#单行添加
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"

#镜像添加的信息可以使用inspect查看
docker image inspect myimage

EXPOSE

该指令通知 Docker 容器监听运行时指定的网络端口。您可以指定端口是否侦听 TCP 或 UDP,如果未指定协议,则默认值为 TCP。

1
2
3
4
5
6
EXPOSE <port> [<port>/<protocol>...]

EXPOSE 80/udp
#若要开启TCP与udp,需要两行
EXPOSE 80/tcp
EXPOSE 80/udp

下面列出了EXPOSE和docker run -p 80:80 的区别

场景 结果
只写 EXPOSE 8080,运行不加 -p 容器 8080 端口仅容器内部可访问
写了 EXPOSE 8080,运行 -p 80:8080 宿主机 80 → 容器 8080
不写 EXPOSE,运行 -p 80:8080 一样可以映射,EXPOSE 不是必须的
多个 -p 映射同一容器端口 后运行的 -p 会报错端口占用

WORKDIR

该指令为 Dockerfile 中后面的任何指令设置工作目录,设置在镜像中的当前路径。

该指令可以在 Dockerfile 中多次使用。如果写的是相对路径,它将相对于上一条指令的路径。列如:

1
2
3
4
5
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#输入的路径会是/a/b/c

如果未设置工作目录,那么当前的路径会由FROM的基础镜像设置。

USER

为当前构建的镜像设置用户名(或 UID)和可选的用户组(或 GID)用作默认用户和组,用于当前阶段。

1
USER <user>[:<group>]

若指定的用户不存在时,需要先创建。(容器里直接创建用户不需要密码交互即可直接使用该身份)

1
2
3
4
5
FROM ubuntu:22.04
# -m 创建 home 目录
# -s 指定 shell
RUN useradd -m -s /bin/bash temp
USER temp

SHELL

该指令用于指定shell形式的解释器,默认使用的/bin/sh,可以切换为/bin/bash。用于解决可能出现不同解释器的命令问题。

1
2
3
4
5
6
7
8
9
10
SHELL ["/bin/bash", "-c"]

FROM ubuntu:22.04

# 默认 /bin/sh,换成 bash
SHELL ["/bin/bash", "-c"]

RUN echo "Using bash now"
RUN for i in {1..3}; do echo "line $i"; done

如果不换成 bash,{1..3} 这种写法 /bin/sh 不支持,会报错。

ENTRYPOINT

该指令允许你将容器配置成像一个可执行文件那样运行,多行ENTRYPOINT只有最后一行生效

1
2
3
4
5
#exec格式,推荐使用,可以避免shell转义错误的情况
ENTRYPOINT ["executable", "param1", "param2"]

#shell格式
ENTRYPOINT command param1 param2

ENTRYPOINT在运行容器时不会被显示的替换,如果需要替换需要docker run –entrypoint来指定的替换命令。

无入口点 入口点exec_entry p1_entry(shell格式) 入口点 [“exec_entry”, “p1_entry”](exec格式)
无CMD 错误,不允许 /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

如上图,ENTRYPOINT与CMD存在一定的关联交互,当ENTRYPOINT与CMD同时存在时,ENTRYPOINT会作为命令,CMD作为命令的参数结合使用,如RNTRYPOINT [“top”],CMD[“-b”],两个命令在没有覆盖命令运行容器的情况会执行top -b。如果单独只有一个,则会将那一个当完整命令执行。

CMD

该命令指定容器运行时需要执行的命令,多行CMD命令只有最后一行生效。CMD命令不会在构建镜像时生成新的镜像层。

使用形式:

1
2
3
CMD ["executable","param1","param2"]   #exec形式
CMD ["param1","param2"] #与ENTRYPOINT配合使用的参数形式
CMD command param1 param2 #shell形式

CMD运行容器时可以被显示覆盖,如docker run -it images command。这里的command命令就会替换CMD里的命令。

RUN

RUN命令在构建镜像时会生成新的镜像层,每一行RUN命令都会。

1
2
3
4
5
6
7
8
9
# Shell 形式
RUN [OPTIONS] <command> ...
# Exec 形式
RUN [OPTIONS] [ "<command>", ... ]
#heredocs形式 多行命令一次执行
RUN <<EOF
apt-get update
apt-get install -y curl
EOF

多阶段运行

使用--mount=type=bind from=阶段名称 来绑定多阶段的源,使用其构建的资源

1
2
3
4
5
6
7
8
9
10
# 第一阶段:生成一些文件
FROM ubuntu:22.04 AS stage1
RUN mkdir /data && echo "Hello from stage1" > /data/file.txt

# 第二阶段:用 from= 挂载 stage1 的 /data
FROM ubuntu:22.04 AS final
RUN --mount=type=bind,from=stage1,source=/data,target=/mnt/data \
cat /mnt/data/file.txt > /result.txt

CMD ["cat", "/result.txt"]

ENTRYPOINT CMD RUN同样都是执行命令,他们之间有着区别,如下表:

指令 执行阶段 覆盖规则 是否生成镜像层 用途
RUN 构建阶段 无覆盖 ✅ 每条 RUN 都生成镜像层 安装软件、生成文件、编译
CMD 运行阶段 容器启动时可被命令覆盖 ❌ 不生成镜像层 容器默认启动命令或默认参数
ENTRYPOINT 运行阶段 可用 --entrypoint 覆盖 ❌ 不生成镜像层 固定容器执行程序,让容器像可执行文件

HEALTHCHECK

健康检查,使用该命令指定的判断容器是否正常健康运行。该命令需要配合

  • HEALTHCHECK [OPTIONS] CMD command(通过在容器内运行命令来检查容器运行状况)
  • HEALTHCHECK NONE(禁用从基础映像继承的任何运行状况检查)

该命令需要配合CMD命令使用,使用CMD前可以添加如下参数配合检查:

  • --interval=DURATION(默认值:30s) #间隔
  • --timeout=DURATION(默认值:30s) #超时
  • --start-period=DURATION(默认值:0s) #开始期
  • --start-interval=DURATION(默认值:5s) #开始间隔
  • --retries=N(默认值:3) #重试次数

–interval:运行状况检查将在容器 started,然后在每次前一次检查完成后再次间隔秒。

–timeout:如果单次运行检查所需的时间超过超时秒数,则检查被认为失败了。

–retries:它会重试容器的运行状况检查连续失败

–start-period:开始期为需要时间引导的容器提供初始化时间。 在此期间的探测失败将不计入最大重试次数。 但是,如果运行状况检查在开始期间成功,则会考虑容器已启动,所有连续失败都将计入最大重试次数。

–start-interval:开始间隔是开始期间健康检查之间的时间。 此选项需要 Docker 引擎版本 25.0 或更高版本。

命令的退出状态指示容器的运行状况。 可能的值为:

  • 0:成功 - 容器运行正常并可供使用
  • 1:不正常 - 容器无法正常工作
  • 2:保留 - 不要使用此退出代码
1
2
3
#每五分钟检查一次 Web 服务器是否能够 在三秒钟内投放网站主页:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1

STOPSIGNAL

STOPSIGNAL 用来指定容器接收到 docker stop 时发送给主进程(PID 1)的 系统信号,默认发送SIGTERM 。有些第三方应用不适用该默认值,例如nginx使用SIGQUIT来优雅退出。

1
2
3
FROM ubuntu:22.04
STOPSIGNAL SIGQUIT
CMD ["sleep", "1000"]

VOLUME

该指令创建具有指定名称的挂载点 并将其标记为保存来自本机主机或其他主机的外部挂载卷,该命令使用任何数据初始化新创建的卷存在于基础映像中的指定位置,但是无法从在 Dockerfile 中挂载主机目录。该指令不支持指定参数。您必须在创建或运行容器时指定挂载点,如docker run

1
2
3
4
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

如果docker run没有显示的指定卷名,则docker会自动创建随机名称的卷挂载/myvol。

运行容器时显示指定卷名mydata,docker run -v mydata:/data ubuntu

volume优点:

  • 容器删除,数据还在
  • 多个容器可以共享数据
  • 可以备份、迁移
  • 不依赖宿主机目录结构
  • IO 性能通常比 bind mount 好

备份与迁移恢复

备份

创建一个新容器dbstore,挂载容器的/dbdata

1
docker run -v /dbdata --name dbstore ubuntu /bin/bash
  1. 启动新容器并从容器挂载卷dbstore
  2. 将本地主机目录挂载为/backup
  3. 将卷内容 tar 传递到目录内的文件的命令。dbdata backup/backup.tar
1
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

当命令完成且容器停止时,它会创建卷dbdata的备份数据到/backup/backup.tar包

--rm

  • 容器执行完命令后 自动删除,不会在 docker ps -a 留下记录。
  • 适合临时任务,比如备份或一次性操作。

--volumes-from dbstore

  • 挂载另一个容器 dbstore 的卷到当前容器。
  • 如果 dbstore 里有 /dbdata 卷,这里会把它挂载到本容器同路径 /dbdata
  • 常用于 数据备份访问别的容器数据

ubuntu

  • 该临时运行的容器镜像

迁移与恢复

使用刚刚创建的备份,您可以将其恢复到同一个容器, 或您在其他地方创建的另一个容器。

例如,创建一个名为 dbstore2的新容器

1
docker run -v /dbdata --name dbstore2 ubuntu /bin/bash

对新容器数据卷中的备份文件进行tar解压

1
docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

评论