Dockerfile 是构建 Docker 镜像的文本文件,由指令(大写关键字)+ 参数组成,指令自上而下顺序执行,每行一条指令,注释以 # 开头。
一、核心基础指令(必掌握)
1. FROM(镜像基础,第一条有效指令)
指定构建镜像的父基础镜像,所有 Dockerfile 必须以 FROM 开头(除 ARG 外)。
语法:
dockerfile
# 格式1:基础镜像:标签
FROM ubuntu:22.04
# 格式2:镜像@哈希(固定版本,防止标签更新)
FROM nginx@sha256:xxxxxx
# 多阶段构建(多个FROM,用于精简镜像)
FROM golang:1.22 AS builder
AS 别名:多阶段构建给阶段命名,后续可复制该阶段文件
2. WORKDIR(设置工作目录)
设定容器内的默认工作路径,后续 RUN/COPY/ADD/CMD 都会基于该目录;目录不存在会自动创建。
dockerfile
# 绝对路径推荐
WORKDIR /app
# 可多层连续设置,自动拼接
WORKDIR src
# 最终目录:/app/src
替代
RUN mkdir -p /app && cd /app,更简洁规范
3. COPY(复制本地文件到镜像,日常首选)
将构建主机本地的文件 / 文件夹复制到镜像内,只支持本地资源,无解压、远程下载功能。
语法:
dockerfile
# 格式:COPY [源路径1,源路径2...] [容器目标路径]
# 1. 普通写法(路径不含空格)
COPY ./target/demo.jar /app/demo.jar
# 2. 数组写法(路径带空格时使用)
COPY ["my app.txt", "/app/data.txt"]
# 复制整个目录,目标目录不存在自动创建
COPY ./static /app/static
特性:
源路径是Docker 构建上下文内的文件(上下文为
docker build .最后的.)自动保留文件权限
不支持 URL、压缩包自动解压
4. ADD(增强版 COPY,仅特殊场景用)
兼容 COPY 所有能力,额外两个特性:
源是
.tar/.gz/.bz2压缩包:自动解压到目标目录源是 HTTP/HTTPS URL:自动下载远程文件到镜像
dockerfile
# 自动解压压缩包
ADD app.tar.gz /app/
# 下载远程文件
ADD https://xxx.com/jdk17.tar.gz /tmp/
使用建议:单纯复制本地文件优先用 COPY,语义清晰;只有需要解压 / 下载远程文件才用 ADD。
5. RUN(构建镜像时执行命令)
镜像构建阶段运行 shell 命令,执行结果写入镜像分层;常用于安装依赖、编译代码。
两种语法:
dockerfile
# 写法1:shell 格式(底层调用 /bin/sh -c)
RUN apt update && apt install -y curl
# 写法2:exec 数组格式(无shell环境,适合无sh的基础镜像如alpine)
RUN ["apt", "update"]
优化技巧:多条命令合并成一个 RUN,减少镜像分层,减小镜像体积
dockerfile
# 不推荐:生成多层
RUN apt update
RUN apt install curl
# 推荐:合并单条RUN
RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/*
6. CMD(容器启动默认执行命令)
容器运行阶段执行,镜像构建时不运行;一个 Dockerfile 只能有一条生效 CMD,多条仅最后一条生效。
作用:容器启动默认程序,docker run 末尾传参可覆盖 CMD。
三种写法:
dockerfile
# 1. exec 数组格式(推荐,无shell,PID1进程)
CMD ["java", "-jar", "/app/demo.jar"]
# 2. shell 格式(底层 /bin/sh -c,程序不是PID1,信号转发异常)
CMD java -jar /app/demo.jar
# 3. 配合ENTRYPOINT,仅传参数
CMD ["--port", "8080"]
7. ENTRYPOINT(容器入口程序,不可被直接覆盖)
和 CMD 类似,容器启动执行,但区别:
docker run后的参数不会覆盖 ENTRYPOINT,而是作为参数传给 ENTRYPOINT适合固定启动脚本,CMD 仅作为默认参数
dockerfile
# exec格式(推荐)
ENTRYPOINT ["/bin/bash", "/app/start.sh"]
# 默认参数,run传参可替换
CMD ["8080"]
执行逻辑:/bin/bash /app/start.sh 8080
二、常用配置指令
8. EXPOSE(声明容器监听端口,仅文档作用)
声明容器内部应用使用的端口,不会自动映射宿主机端口,仅给阅读者、工具做提示。
dockerfile
# 声明TCP 8080端口
EXPOSE 8080
# 声明UDP端口
EXPOSE 9999/udp
实际端口映射仍需 docker run -p 宿主机端口:容器端口
9. ENV(设置环境变量,构建 + 运行阶段都生效)
定义镜像内环境变量,后续所有指令(RUN/CMD/ENTRYPOINT)均可引用。
dockerfile
# 单变量
ENV APP_NAME=demo
# 多变量
ENV JAVA_HOME=/usr/local/jdk \
PORT=8080
# 使用变量
CMD ["java", "-jar", "/app/${APP_NAME}.jar"]
运行时可通过 docker run -e PORT=9090 覆盖环境变量
10. ARG(构建参数,仅镜像构建阶段生效)
构建镜像时传入的临时变量,容器运行阶段失效;配合 docker build --arg KEY=VALUE 使用。
dockerfile
# 定义参数,可设置默认值
ARG JDK_VERSION=17
# 使用ARG参数
FROM openjdk:${JDK_VERSION}
区分 ENV / ARG:
ARG:构建镜像时临时参数,容器运行看不到
ENV:写入镜像,容器启动后环境永久存在
三、进阶运维指令
11. VOLUME(声明数据卷,持久化存储)
声明容器内目录为数据卷,容器删除后数据保留;自动挂载匿名卷,避免容器写入数据随容器销毁丢失。
dockerfile
# 声明多个持久化目录
VOLUME ["/app/logs", "/app/data"]
注意:Dockerfile 无法指定宿主机挂载路径,宿主机绑定挂载只能在 docker run -v 指定。
12. USER(切换执行用户)
切换后续 RUN/CMD/ENTRYPOINT 指令的执行用户,默认 root;生产环境禁止容器以 root 运行,安全规范必备。
dockerfile
# 创建普通用户
RUN useradd -m appuser
# 切换用户
USER appuser
# 后续命令都以 appuser 执行
CMD ["java", "-jar", "/app/demo.jar"]
13. LABEL(镜像元数据标签)
给镜像添加自定义元数据,如作者、版本、描述,使用 docker inspect 镜像名 查看。
dockerfile
# 多标签写法
LABEL author="zhangsan" \
version="1.0" \
description="SpringBoot demo image"
14. HEALTHCHECK(容器健康检查)
定义健康检测命令,Docker 定期执行判断容器是否正常,支持 HTTP / 命令 / TCP 检测。
dockerfile
# 基础语法:检测命令,间隔30s,超时5s,失败3次标记不健康
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://127.0.0.1:8080/actuator/health || exit 1
15. SHELL(自定义 shell 解释器,极少使用)
修改 RUN shell 格式默认使用的解释器,默认 /bin/sh -c,alpine 可改为 ash。
dockerfile
# 替换shell为bash
SHELL ["/bin/bash", "-c"]
RUN source /app/env.sh
四、生命周期对比总结表
五、完整极简示例 Dockerfile
dockerfile
# 构建参数
ARG JDK_VER=17
# 基础镜像
FROM openjdk:${JDK_VER}
# 镜像标签
LABEL author="demo" version="1.0"
# 环境变量
ENV APP_PORT=8080
# 工作目录
WORKDIR /app
# 复制jar包
COPY ./target/demo.jar /app/app.jar
# 声明端口
EXPOSE ${APP_PORT}
# 健康检查
HEALTHCHECK --interval=20s CMD curl -f http://127.0.0.1:${APP_PORT}/health || exit 1
# 容器启动命令
CMD ["java", "-jar", "/app/app.jar", "--server.port=${APP_PORT}"]