文章目录
在云原生时代,容器化是应用交付的基石,而 Dockerfile 则是构建容器镜像的蓝图。本文整理自实战文档,详细解析如何编写 Dockerfile、常用指令的深层机制、多架构镜像构建方法以及镜像瘦身的最佳实践。
什么是 Dcokerfile?
Dockerfile 是一个文本文件,它包含了一系列指令。Docker 能够读取这些指令,自动构建出一个定制化的镜像。每一条指令通常对应镜像中的一层。
Dockerfile 核心指令详解
基础与元数据
FROM: 指定基础镜像。构建必须基于一个已存在的镜像(如 centos:7, alpine)。
- 建议:生产环境建议使用轻量级镜像(如 Alpine, Busybox)或包含基础环境的运行时镜像(如 OpenJDK)。
- 示例
# 使用官方 Python 3.9 Slim 版本作为基础镜像
FROM python:3.9-slim
LABEL (替代 MAINTAINER): 用于添加镜像的元数据,如作者信息、版本等。
- 示例
LABEL maintainer="devops@example.com"
LABEL version="1.0"
LABEL description="This is a sample image"
USER: 指定运行容器时的用户名或 UID。
- 注意:该用户必须在容器内提前存在。
- 最佳实践:出于安全考虑,避免使用 root,推荐使用 UID(如 1001)。
- 示例
# 创建用户并切换
RUN useradd -m tomcat -u 1001
USER 1001
-u 指定用户id
-m 创建用户的时候创建家目录
文件操作
WORKDIR: 设置工作目录。
- 如果目录不存在,Docker 会自动创建。后续的
RUN,CMD,COPY等指令都会在该目录下执行。 - 示例
# 设置工作目录为 /app
WORKDIR /app
# 此时 copy 会将文件复制到 /app/config.json
COPY config.json ./
COPY: 将宿主机文件复制到容器内。
- 特性:
COPY src dest若 src 是目录,只会复制目录下的内容,不会复制目录本身。 - 权限:支持在复制时修改权限(避免先复制再 chmod 导致镜像体积增加)。当前使用什么用户去 build 镜像,那么 COPY 的文件就属于什么用户或者组
- 示例
#复制文件并修改文件权限与属组
COPY --chown=huiye:huiye file /mnt/file
COPY --chmod=644 file /mnt/file
COPY --chown=huiye:huiye --chmod=644 file /mnt/file
ADD: 与 COPY 类似,但功能更高级。
- 区别:如果源文件是压缩包(如 tar),ADD 会自动解压
- 建议:明确仅需复制文件时使用 COPY,需要解压时使用 ADD。
- 示例:
# 自动解压 nginx.tar.gz 到 /usr/share/nginx/html/
ADD nginx.tar.gz /usr/share/nginx/html/
环境变量
ENV: 设置环境变量。
- 持久化:该变量在容器运行时依然有效。
- 优先级:
docker run -e的优先级高于 Dockerfile 中的 ENV。 - 示例
ENV APP_ENV=production
ENV APP_PORT=8080
# 在后续指令中使用变量
CMD ["java", "-jar", "app.jar", "--port=${APP_PORT}"]
ARG: 构建参数。
- 仅在
docker build过程中有效,容器运行时不生效 - 可以通过
docker build --build-arg name=value动态传入。 - 示例
FROM centos:7
LABEL maintainer="huiye"
#设置一个参数为username
ARG username
#设置一个有默认值的参数path1,如果没有传入这个参数的值,则这个参数使用默认值"/home/hiuye"
ARG path1="/home/huiye"
ENV NAME=test1
#使用这个参数username
RUN useradd -m $username -u 1001
USER 1001
WORKDIR $path1
CMD ["/bin/bash","-c","whoami && pwd"]
#构建命令
docker build --build-arg username=huiyex --build-arg path1=/home/huiyex -t centos:arg .
执行命令
RUN: 在构建镜像时执行的 Shell 命令。
- 每一条 RUN 指令都会生成新的一层。
- 优化:建议使用
&&连接多条命令,并清理缓存以减小体积。 - 示例
RUN yum install -y net-tools && \
yum install -y iproute && \
yum clean all
容器启动
CMD: 容器启动时的默认命令。
- 格式:推荐使用 JSON 数组格式
["executable", "param1", "param2"]。 - 覆盖:
docker run后面的命令会覆盖 Dockerfile 中的 CMD。 - 注意:如果存在多个 CMD,仅最后一个生效。
- 示例
FROM centos:7
LABEL maintainer="huiye"
ENV NAME=test1
RUN useradd -m huiye -u 1001
USER 1001
WORKDIR /home/huiye
CMD ["/bin/bash","-c","echo \"NAME=$NAME\""]
ENTRYPOINT: 也可以指定容器默认运行的程序
- 覆盖:启动容器时会执行的命令,会被docker run –entrypoint XX覆盖
- 与 CMD 区别:
docker run后面的命令会被追加到 ENTRYPOINT 之后,作为参数传递,而不是覆盖它。例:比如 docker run -it –rm –entrypoint ls centos:latest /tmp ,就是将 CMD 的 /tmp 作为参数传给 –entrypoint ls,那么容器内执行的就是 ls /tmp - 示例
# 定义固定命令
ENTRYPOINT ["/bin/ping"]
# 定义默认参数(可被覆盖)
CMD ["--help"]
网络与存储
EXPOSE: 声明端口。仅仅是声明,方便阅读和映射,不会自动开启端口映射。
- 示例
FROM centos:7
LABEL maintainer="huiye"
ENV NAME=test1
RUN useradd -m huiye -u 1001
USER 1001
WORKDIR /home/huiye
CMD ["/bin/bash","-c","echo \"NAME=$NAME\""]
EXPOSE 80
EXPOSE 443
VOLUME: 定义匿名数据卷。即使启动时不指定 -v,容器也会自动挂载该目录。
- 示例
# 在创建容器的时候,即使没有指定卷,默认就会有 -v /data
VOLUME ["/data"]
# 指定多个卷
VOLUME ["/var/log", "/var/db"]
易混淆指令解析
CMD vs ENTRYPOINT
| 指令 | 作用 | 被 docker run [command] 覆盖? |
| CMD | 指定默认命令 | 是,完全覆盖 |
| ENTRYPOINT | 指定入口程序 | 否,[command] 会作为参数传给 ENTRYPOINT |
实战用法: 将 ENTRYPOINT 设置为固定程序,CMD 设置为默认参数。
ENTRYPOINT ["/bin/my-app"]
CMD ["--help"]
# 运行 docker run my-image /tmp
# 实际执行:/bin/my-app /tmp
COPY vs ADD
- COPY:语义清晰,仅用于复制本地文件。
- ADD:包含解压功能。如果你需要将
nginx.tar.gz放入容器并解压,使用ADD nginx.tar.gz /usr/share/nginx/html/一步到位。
构建命令与实战
标准构建
docker build -t my-app:v2 -f Dockerfile3 .
# -t: 指定镜像名和标签
# -f: 指定 Dockerfile 文件路径(不指定时默认为当前目录下的 Dockerfile)
# .: 上下文目录(COPY 命令寻找文件的根目录)
动态传参构建
配合 Dockerfile 中的 ARG 指令:
docker build --build-arg username=admin -t my-app:args .
进阶:构建多架构镜像 (ARM/AMD)
随着 ARM 架构服务器的普及,构建支持多平台(x86_64/arm64)的镜像变得尤为重要。这需要使用 docker buildx。但是用来构建镜像的基础镜像也需要是 支持 ARM 架构的。可以在 hub 官方查看该镜像是否支持 ARM

环境准备
查看当前构建器支持的架构:
docker builder ls

建议安装一个新的构建器
docker buildx create --name mybuilder --use --platform linux/amd64,linux/arm64 --driver docker-container
参数解析
--name 自定义构建器的名称
--platform 指定构建器支持的目标平台
--driver docker-container 指定构建器的底层驱动模式为“容器化”
--use 创建完成后,立即切换到这个构建器。省去了单独执行 docker buildx use mybuilder 的步骤。也就是说,接下来的 docker buildx build 命令都会默认使用这个新创建的 builder。
如需要删除 builder,使用以下命令
docker buildx rm mybuilder
此时看到使用 docker builder ls 可以看到这个builder是inactive 未启用的状态

激活构建器
docker buildx inspect --bootstrap

注:该命令会拉取 一个镜像 moby/buildkit:buildx-stable-1,如果网络不好的情况,可以提前把这个镜像下载到本地上
安装所有架构的仿真环境,用于构建ARM镜像
docker run --privileged --rm tonistiigi/binfmt --install all
可以看到增加了一些架构类型

构建并推送
构建并推送到镜像仓库
docker buildx build --platform linux/arm64 -t xxxhub/huiye-private/buildx:armv1 --push -f ./Dockerfile .
参数解析
--push 构建好了就直接推送到镜像仓库
若需要同时构建多种架构,可以把 linux/arm64 修改为 linux/arm64,linux/amd64
只构建不推送到镜像仓库
docker buildx build --platform linux/arm64/v8 -t xxxhub/huiye-private/buildx:armv2 -f ./Dockerfile . --load
镜像优化最佳实践
选择合适的基础镜像
- 避免:直接使用臃肿的 OS 镜像(如完整的 Ubuntu/CentOS)。
- 推荐:Alpine(极小,是基于 Alpine Linux 发行版构建的轻量级 Docker 基础镜像,特别适合容器化环境)、或着一些官方的基础环境镜像 如 python 特定版本的镜像。
多阶段构建
这是减少镜像体积最有效的方法。将编译环境和运行环境分离。
示例:
FROM centos:7
RUN rm -rf /etc/yum.repos.d/*
COPY CentOS-7.repo /etc/yum.repos.d/
RUN yum install -y net-tools && \
yum install -y iproute
#第一次构建临时容器,编号为0
FROM centos:7
COPY --from=0 /usr/sbin/ifconfig /sbin/
COPY --from=0 /usr/sbin/ip /sbin/
CMD ["/bin/bash"]
#第二次构建临时容器,编号为1。
#COPY --from=0 意为从第一次构建的临时容器中拷贝文件到本次构建的临时容器内
也可以
FROM centos:7 AS builder
#给第一阶段起名为 builder
RUN rm -rf /etc/yum.repos.d/*
COPY CentOS-7.repo /etc/yum.repos.d/
RUN yum install -y net-tools && \
yum install -y iproute
FROM centos:7
COPY --from=builder /usr/sbin/ifconfig /sbin/
COPY --from=builder /usr/sbin/ip /sbin/
CMD ["/bin/bash"]
#COPY --from=builder 意为从命名为builder构建的临时容器中拷贝文件到本次构建的临时容器内
减少镜像层数
合并 RUN 指令,参考如下,将多个命令放到一个 RUN 里面,一个 RUN 就是一层,官方不建议镜像层数过多
FROM registry.cn-beijing.aliyuncs.com/dotbalo/alpine:3.20
#RUN sed -i "s@dl-cdn.alpinelinux.org@mirrors.aliyun.com@g" /etc/apk/repositories
#RUN apk update
#RUN apk add nginx curl
RUN sed -i "s@dl-cdn.alpinelinux.org@mirrors.aliyun.com@g" /etc/apk/repositories && \
apk update && \
apk add nginx curl
权限与文件操作优化
不要这样做:
COPY file /app/
RUN chmod 755 /app/file # 导致多一层,文件实际存了两份(一份原权限,一份新权限)
推荐做法:
COPY --chmod=755 file /app/
# 或者
COPY --chown=user:group file /app/
© 版权声明
文章版权归作者所有,未经允许请勿转载。