Dockerfile用于定义单个容器镜像的构建过程,例如安装JDK、复制代码、设置环境变量等。它是镜像构建的蓝图。仅关注镜像构建(如安装依赖、编译代码)。 文件内容分为四个部分,基础镜像信息、维护者信息、镜像操作指令(RUN)、容器启动时执行指令(CMD)。
一、基础镜像信息
使用FROM指令指明所基于的镜像名称,大部分情况下,生成新的镜像都需要通过FROM指令指定父镜像。父镜像是生成镜像的基础,会直接影响到生成镜像的大小和功能。任何dockerfile中第一条指令必须为FROM指令(除了ARG)。并且如果在同一个Dockerfile中创建多个镜像时,可以使用多个FROM指令(每个镜像一次).
ROM
FROM命令格式如下:
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
例:
# 使用官方镜像
FROM ubuntu:20.04
# 多阶段构建中,可以为阶段命名
FROM golang:1.16-alpine AS builder
# 指定平台
FROM --platform=linux/amd64 node:16参数说明:
- --platform:指定平台,如 linux/amd64, linux/arm64
- image:基础镜像名称
- tag:镜像标签,默认为latest
- AS name:为构建阶段命名(多阶段构建)
二、维护者信息
LABEL
MAINTAINER指令已过时,推荐使用LABEL。使用LABEL指令说明维护者信息。可以为生成的镜像添加元数据标签信息,这些信息可用来辅助过滤出特定镜像。LABEL命令格式如下:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
例:
# 单标签
LABEL maintainer="devops@company.com"
# 多标签(推荐)
LABEL version="1.0.0" \
description="Production API Service" \
vendor="My Company Inc."
# 标准标签
LABEL org.opencontainers.image.title="My App" \
org.opencontainers.image.version="2.1.0" \
org.opencontainers.image.created="2023-10-01T12:00:00Z" \
org.opencontainers.image.source="https://github.com/company/repo"
# 分类标签
LABEL com.company.team="platform-engineering" \
com.company.project="customer-api" \
com.company.sla="99.9"参数说明:
- key:标签键,推荐使用反向DNS表示法
- value:标签值
三、镜像操作指令
用于安装软件、复制文件、设置环境变量和工作目录等构建过程中的操作。核心命令:RUN, COPY, ADD, ENV, ARG, WORKDIR。
RUN
在镜像构建过程中执行命令创建新层。合并多条RUN指令可以减少镜像层数,从而减小镜像体积。RUN命令语法如下:
# Exec 形式
RUN ["executable", "param1", "param2"]
# Shell 形式(默认 /bin/sh -c)
RUN <command>
例
# Shell 形式 - 单命令
RUN apt update
# Shell 形式 - 多命令合并(推荐)
RUN apt update && \
apt install -y \
curl \
git \
wget && \
rm -rf /var/lib/apt/lists/* && \
apt clean
# Exec 形式
RUN ["/bin/bash", "-c", "echo 'Building application'"]
RUN ["npm", "ci", "--only=production"]
# 条件执行
RUN if [ "$NODE_ENV" = "production" ]; then \
npm ci --only=production; \
else \
npm ci; \
fi
# 创建目录和设置权限
RUN mkdir -p /app/logs && \
chown -R app:app /app && \
chmod -R 755 /app
# 更新软件包列表并安装软件,最后清理缓存
RUN apt update && apt install -y \
nginx \
php-fpm \
&& rm -rf /var/lib/apt/lists/*COPY & ADD
这两个指令都用于将文件/目录从构建上下文复制到镜像中。
COPY
推荐用于简单的文件复制。命令格式如下:
#复制本机的src(dockerfile所在目录的相对路径,文件或目录)下内容到镜像中的desc,目标路径不存在时,会#自动创建。功能类似add,当使用本地目录为源目录时,推荐使用copy。
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
例
COPY ./src/ /app/src/
COPY requirements.txt /app/
COPY ./app /usr/src/app
# 基本文件复制
COPY package.json /app/
COPY requirements.txt /app/
# 复制目录
COPY src/ /app/src/
COPY config/ /app/config/
# 使用通配符
COPY *.js /app/
COPY data/*.json /app/data/
# 设置文件权限
COPY --chown=node:node app.js /app/
COPY --chown=1001:1001 files/ /app/data/
# 多文件复制
COPY file1.txt file2.txt /app/
COPY ["file with spaces.txt", "/app/"]参数说明:
- --chown:设置文件所有者
- src:源文件或目录,支持通配符
- dest:目标路径
ADD
除了复制,还支持从URL下载文件或自动解压压缩包(如tar、gzip等)到目标路径。除非需要这些额外功能,否则建议使用COPY。ADD语法格式如下:
#将复制指定的src路径下内容到容器中的dest路径下。src可以是dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL。还可以是一个tar文件。dest可以是镜像内绝对路径,或者相对于工作目录workdir的相对路径
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
例
ADD https://example.com/file.tar.gz /tmp/
# 复制本地文件(类似 COPY)
ADD application.jar /app/
# 从URL下载文件
ADD https://example.com/tools/calculator /usr/local/bin/
# 自动解压tar文件
ADD application.tar.gz /opt/
ADD data.tar.xz /app/data/
# 设置权限并从多个源复制
ADD --chown=app:app \
file1.txt \
https://example.com/file2.txt \
/app/
# 解压到特定目录
ADD dataset.tar.gz /app/datasets/ENV & ARG
两者都用于定义变量,但有重要区别:
ARG
定义的是构建时使用的变量,定义构建参数,其值在镜像构建成功后不存在于镜像中。可以通过docker build --build-arg <varname>=<value>覆盖,语法格式如下:
ARG <name>[=<default value>]
例:
ARG APP_VERSION=1.0
RUN echo "Building version $APP_VERSION"
# 基础用法
ARG APP_VERSION=1.0.0
ARG BUILD_NUMBER
# 多阶段构建中的 ARG
FROM alpine AS build
ARG NPM_TOKEN
ARG REGISTRY_URL=https://registry.npmjs.org/
# 条件构建
ARG TARGETARCH
RUN if [ "$TARGETARCH" = "amd64" ]; then \
echo "Building for AMD64"; \
else \
echo "Building for $TARGETARCH"; \
fi
# 与 ENV 配合使用
ARG GIT_COMMIT
ENV GIT_COMMIT=$GIT_COMMITENV
设置的是容器运行时的环境变量,在容器启动后依然存在,语法格式如下:
ENV <key>=<value> ...
例:
# 单变量
ENV NODE_ENV=production
# 多变量
ENV APP_HOME=/app \
APP_USER=app \
APP_PORT=3000
# 使用变量引用
ENV PATH=$APP_HOME/bin:$PATH
ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk
# 应用配置
ENV DB_HOST=localhost \
DB_PORT=5432 \
REDIS_URL=redis://cache:6379
# 语言和区域设置
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
TZ=Asia/ShanghaiWORKDIR
设置后续指令(如RUN、CMD、COPY)的工作目录。如果目录不存在,会自动创建。推荐使用绝对路径。语法格式如下:
WORKDIR /path/to/workdir
例:
# 绝对路径
WORKDIR /app
# 相对路径(基于前一个 WORKDIR)
WORKDIR src
WORKDIR api
# 当前路径:/app/src/api
# 多级目录创建
WORKDIR /usr/local/bin
# 使用环境变量
ENV APP_DIR=/opt/application
WORKDIR $APP_DIRUSER
指定运行容器时的用户名或uid,后续的run等指令也会使用指定的用户身份。命令格式如下:
USER <user>[:<group>]
USER <UID>[:<GID>]
例:
# 创建用户并切换
RUN groupadd -r app && useradd -r -g app app
USER app
# 使用用户名和组
USER node:node
USER nginx:nginx
# 使用 UID/GID(推荐用于生产)
USER 1001:1001
USER 999
# 在特定命令中使用不同用户
USER root
RUN apt-get update && apt-get install -y curl
USER app
# Dockerfile 中切换用户
RUN useradd -m -s /bin/bash developer
USER developer
WORKDIR /home/developerVOLUME
创建一个数据卷挂载点,运行容器时可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存数据等。命令格式如下:
VOLUME ["/path/to/dir"]
VOLUME /path/to/dir
例:
# JSON 数组格式
VOLUME ["/var/lib/mysql", "/var/log/mysql"]
# 多参数格式
VOLUME /data /logs /config
# 应用数据卷
VOLUME ["/app/data", "/app/logs", "/app/uploads"]
# 数据库卷
VOLUME /var/lib/postgresql/data
# 组合使用
RUN mkdir -p /data && chown app:app /data
VOLUME /dataHEALTHCHECK
用于定义健康检查,配置所启动容器如何进行健康检查,命令格式如下:
#根据所执行命令返回值是否为0来判断
HEALTHCHECK [OPTIONS] CMD command
#禁止基础镜像中健康检查
HEALTHCHECK NONE
例:
# HTTP 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 脚本健康检查
HEALTHCHECK --interval=1m --timeout=10s \
CMD /healthcheck.sh
# 数据库健康检查
HEALTHCHECK --interval=15s \
CMD pg_isready -U $POSTGRES_USER -d $POSTGRES_DB || exit 1
# 端口检查
HEALTHCHECK --interval=30s \
CMD netstat -lnt | grep -q :8080 || exit 1
# 禁用健康检查
HEALTHCHECK NONE选项说明:
--interval:检查间隔(默认 30s)
--timeout:检查超时(默认 30s)
--start-period:启动等待时间(默认 0s)
--retries:失败重试次数(默认 3)
SHELL
更改默认shell,格式如下:
SHELL ["executable", "parameters"]
例:
# Linux - 使用 bash
SHELL ["/bin/bash", "-c"]
# Windows - 使用 PowerShell
SHELL ["powershell", "-Command"]
# 影响后续的 RUN 命令
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSL https://get.docker.com/ | sh
# 临时改变 shell
SHELL ["/bin/sh", "-c"]
RUN echo "Using default shell"
SHELL ["/bin/bash", "-c"]
RUN echo "Now using bash"ONBUILD
用于延迟执行指令,指定当基于所生成镜像创建子镜像时,自动执行的操作指令,在创建专门用于自动编译,检查等操作的基础镜像时,十分有用。命令格式如下:
ONBUILD <INSTRUCTION>
例:
# 基础镜像中的 ONBUILD 指令
FROM node:16-alpine
ONBUILD COPY package*.json ./
ONBUILD RUN npm install
ONBUILD COPY . .
ONBUILD RUN npm run build
ONBUILD CMD ["npm", "start"]
# 复杂场景
ONBUILD ARG BUILD_ENV=production
ONBUILD ENV NODE_ENV=$BUILD_ENV
ONBUILD RUN if [ "$BUILD_ENV" = "development" ]; then \
npm install; \
else \
npm ci --only=production; \
fi
# 多指令组合
ONBUILD WORKDIR /app
ONBUILD USER appSTOPSIGNAL
用于设置停止信号,指定所创建镜像启动的容器接受退出的信号值。命令格式如下:
STOPSIGNAL signal
例:
# 使用信号名称
STOPSIGNAL SIGTERM
STOPSIGNAL SIGINT
STOPSIGNAL SIGKILL
# 使用信号编号
STOPSIGNAL 15
STOPSIGNAL 9
# 优雅关闭应用
STOPSIGNAL SIGTERM
CMD ["node", "server.js"]四、容器启动时执行指令
指定容器启动时默认执行的命令及其参数。核心命令:CMD, ENTRYPOINT,EXPOSE。
CMD & ENTRYPOINT
这两个指令都用于指定容器启动时默认执行的命令,但各有侧重:
CMD
提供容器默认的执行命令,可以被docker run后面指定的命令覆盖。如果定义了多个CMD,只有最后一个生效。命令格式如下:
# Exec 形式(推荐)
CMD ["executable", "param1", "param2"]
# Shell 形式,在默认的shell中执行,提供给需要交互的应用
CMD command param1 param2
# 作为 ENTRYPOINT 的参数,提供给entrypoint的默认参数
CMD ["param1", "param2"]
例:
# Exec 形式 - 直接运行应用
CMD ["node", "server.js"]
CMD ["nginx", "-g", "daemon off;"]
CMD ["python", "app.py"]
# Shell 形式 - 通过 shell 执行
CMD npm start
CMD java -jar app.jar
# 为 ENTRYPOINT 提供默认参数
FROM alpine
ENTRYPOINT ["echo"]
CMD ["Hello World"]
# 多参数命令
CMD ["/bin/bash", "-c", "echo $HOME && ls -la"]
# 环境变量扩展
CMD ["sh", "-c", "echo $NODE_ENV && node server.js"]ENTRYPOINT
用于配置容器作为一个可执行程序。用户主程序,指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数。docker run后面的参数会作为参数传递给ENTRYPOINT。每个dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起作用。在运行时可以被--entrypoint参数覆盖 组合使用:CMD可以为ENTRYPOINT提供默认参数,命令格式如下:
# Exec 形式(推荐)
ENTRYPOINT ["executable", "param1", "param2"]
# Shell 形式
ENTRYPOINT command param1 param2
例:
# Exec 形式 - 固定应用
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
# 入口点脚本
ENTRYPOINT ["/docker-entrypoint.sh"]
# 与 CMD 配合使用
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
或
ENTRYPOINT ["java", "-jar"]
CMD ["app.jar"]
# 调试工具入口点
ENTRYPOINT ["/bin/bash"]
# 包装脚本
ENTRYPOINT ["python", "-m", "debugpy", "--listen", "0.0.0.0:5678"]
CMD ["app.py"]
# 或者使用脚本作为入口点
ENTRYPOINT ["/app/start.sh"]
CMD ["--config", "/app/config/prod.yaml"]
# 简单的CMD形式
CMD ["python", "app.py"]
# Shell形式的CMD(不推荐)
CMD nginx -g 'daemon off;'EXPOSE
声明容器运行时监听的网络端口。这只是一个元数据,实际端口映射需要在运行容器时通过-p参数设置。格式如下:
EXPOSE <port> [<port>/<protocol>...]
例:
# 单端口 TCP
EXPOSE 80
# 指定协议
EXPOSE 80/tcp
EXPOSE 53/udp
# 多端口
EXPOSE 80 443 8080
EXPOSE 3000/tcp 5000/udp
# 数据库端口
EXPOSE 5432
EXPOSE 27017
# 微服务端口
EXPOSE 8080 8443 9000Dockerfile命令最佳实践说明
高频使用(每个Dockerfile都应有):
FROM - 必需
RUN - 构建核心
COPY - 文件管理
CMD/ENTRYPOINT - 启动定义
中频使用(生产环境推荐)
ENV - 环境配置
WORKDIR - 目录管理
USER - 安全配置
EXPOSE - 端口声明
LABEL - 元数据管理
低频使用(特定场景)
ARG - 构建参数化
VOLUME - 数据持久化
HEALTHCHECK - 健康监控
ADD - 特殊文件处理
ONBUILD - 基础镜像构建
极少使用(特殊情况)
STOPSIGNAL - 信号定制
SHELL - Shell覆盖
MAINTAINER - 已废弃
生产环境推荐
必需命令
FROM - 每个Dockerfile必须有且只有一个,必须是第一条指令
强烈推荐的生产级命令
LABEL - 元数据管理
RUN - 依赖安装和配置
COPY - 文件复制
ENV - 环境配置
WORKDIR - 工作目录设置
USER - 安全运行
EXPOSE - 端口声明
CMD/ENTRYPOINT - 启动定义
按需使用的命令
ARG - 参数化构建
ADD - 特殊文件处理
VOLUME - 数据持久化
HEALTHCHECK - 健康监控
STOPSIGNAL - 信号处理
特殊场景命令
ONBUILD - 基础镜像构建
SHELL - Shell环境定制
MAINTAINER - 已废弃,用LABEL替代
生产环境分类示例
###############################################
# 模块一:基础镜像信息
###############################################
FROM node:18-alpine AS builder
###############################################
# 模块二:维护者信息
###############################################
LABEL maintainer="platform-engineering@company.com"
LABEL version="2.1.0"
LABEL description="Customer API Service"
LABEL org.opencontainers.image.source="https://github.com/company/api"
###############################################
# 模块三:镜像操作指令
###############################################
# 构建参数
ARG NPM_TOKEN
ARG BUILD_ID=unknown
# 环境配置
ENV NODE_ENV=production
ENV NPM_CONFIG_LOGLEVEL=warn
ENV PORT=3000
# 工作目录
WORKDIR /app
# 安装构建工具
RUN apk add --no-cache \
python3 \
make \
g++ \
&& rm -rf /var/cache/apk/*
# 复制依赖文件
COPY package*.json ./
COPY npmrc .npmrc
# 安装依赖
RUN npm ci --only=production && \
npm cache clean --force
# 复制应用代码
COPY src/ ./src/
COPY config/ ./config/
# 创建非root用户
RUN addgroup -g 1001 -S app && \
adduser -u 1001 -S app -G app
# 设置文件权限
RUN chown -R app:app /app
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node healthcheck.js
# 数据卷
VOLUME ["/app/logs", "/app/uploads"]
###############################################
# 模块四:容器启动时执行指令
###############################################
# 声明端口
EXPOSE 3000
# 切换到非特权用户
USER app
# 启动命令
ENTRYPOINT ["node"]
CMD ["src/server.js"]