Dockerfile 指令详解

第 12 章学会了 Dockerfile 骨架,本章把常用指令逐条讲透,重点辨析 CMD 与 ENTRYPOINT、COPY 与 ADD、shell 形式与 exec 形式的差别,让构建文件既正确又可维护。

指令总览

FROM 必须是第一个可执行指令,其余指令汇总如下:

指令作用典型写法
FROM指定基础镜像FROM node:20-alpine
RUN构建期执行命令,每个 RUN 生成一层RUN apt-get update && apt-get install -y curl
COPY把构建上下文内的文件拷入镜像COPY package.json /app/
ADD类似 COPY,可自动解压 tar、下载远程 URLADD jdk-17.tar.gz /opt/
CMD启动时默认命令,可被 docker run 覆盖CMD ["node", "server.js"]
ENTRYPOINT固定主程序,配合 CMD 传默认参数ENTRYPOINT ["docker-entrypoint.sh"]
ENV环境变量,构建期与运行期都可见ENV NODE_ENV=production
ARG仅构建期变量,不写入镜像ARG APP_VERSION=2.4
EXPOSE声明监听端口,仅文档作用,不真开端口EXPOSE 8080
WORKDIR工作目录,不存在会自动创建WORKDIR /app
USER切换后续指令与运行用户USER node
VOLUME声明匿名卷,便于持久化VOLUME /data
ONBUILD子镜像构建时自动触发的指令ONBUILD COPY . /app
HEALTHCHECK定义容器健康检查命令HEALTHCHECK CMD wget -q http://localhost:80/

ENV 与 ARG

两者都定义变量,差别在生命周期:ARG 只在构建期存在,构建时用 docker build --build-arg 覆盖;ENV 会写进镜像,容器运行时可读(运行时 echo $APP_HOME 输出 /opt/myapp)。

ARG APP_VERSION=2.4          # 构建期默认值,可被命令行覆盖
ENV APP_HOME=/opt/myapp      # 运行期可见;可用 \ 续行一次定义多个
WORKDIR $APP_HOME            # 用 $ 引用已定义的变量

CMD 与 ENTRYPOINT

CMD 提供默认命令或参数,会被 docker run 末尾追加的命令整体替换;ENTRYPOINT 定义固定主程序。标准组合是 ENTRYPOINT 写程序、CMD 写默认参数;同一 Dockerfile 出现多个 CMD 只有最后一个生效:

ENTRYPOINT ["nginx", "-g", "daemon off;"]
CMD ["-t"]   # 默认参数;docker run 之后可替换成别的参数
docker run nginx-img -t   # 实际执行:nginx -g "daemon off;" -t
docker run --entrypoint bash nginx-img -c "echo ok"   # 用 --entrypoint 整体替换

COPY 与 ADD

COPY 只做拷贝;ADD 额外支持从远程 URL 下载、自动解压本地 tar 包,行为多、易出意外,官方建议能 COPY 就不用 ADD,自动解压 tar 是它不可替代的场景:

COPY ./conf/nginx.conf /etc/nginx/nginx.conf   # 拷单个文件
COPY ./html/ /usr/share/nginx/html/            # 拷整个目录
ADD jdk-17_linux-x64_bin.tar.gz /opt/jdk/      # 本地 tar 自动解压到 /opt/jdk/

RUN 层与构建缓存

每个 RUN 与 COPY 都生成一个只读层,构建时按「指令 + 输入文件内容」逐条比对缓存。把依赖安装放前面、源码拷贝放后面,改源码就不会撞翻整条缓存链;需要干净构建时加 --no-cache:

FROM python:3.12-slim
COPY requirements.txt /app/                      # 依赖清单先拷
RUN pip install -r /app/requirements.txt         # 清单没变就命中缓存
COPY . /app                                      # 源码变动只影响本层及之后

shell 形式与 exec 形式

命令类指令(RUN/CMD/ENTRYPOINT)有 shell 与 exec 两种写法:shell 形式由 /bin/sh -c 包一层执行;exec 形式是 JSON 数组、必须用双引号,进程直接成为容器 1 号进程:

CMD echo "hello world"             # shell 形式
CMD ["echo", "hello world"]        # exec 形式
CMD ["sh", "-c", "echo hello $NAME"]   # exec 中要展开变量须显式套 sh -c

docker stop 默认发 SIGTERM:exec 形式下信号直达主进程可优雅退出;shell 形式下信号常被 sh 吞掉、只能等超时强杀,生产镜像优先 exec 形式。

综合示例

FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
USER node                # 以非 root 运行更安全
EXPOSE 3000
CMD ["node", "server.js"]
docker build -t my-node . && docker run -d -p 3000:3000 --name node-app my-node

小结

指令组合是 Dockerfile 的核心功力:FROM 选底、COPY/ADD 组装、RUN 装依赖、ENV/ARG 传参、CMD+ENTRYPOINT 定启动、USER/HEALTHCHECK 定契约;多用 exec 形式与 COPY,缓存和信号问题就少一半。

笔记加载中…