Dockerfile 是 Docker 用来定义容器镜像构建过程的脚本文件,包含一系列指令来描述如何构建镜像。以下是对 WORKDIR、COPY、RUN、ENV 和 CMD 指令的详细介绍,包括其功能、语法、用法和注意事项。
1. WORKDIR
功能
WORKDIR 指令用于设置 Dockerfile 中后续指令的工作目录(类似于 Linux 的 cd 命令)。它指定了在容器内执行命令或程序时的当前目录。如果指定的目录不存在,Docker 会自动创建。
语法
WORKDIR /path/to/workdir
用法
WORKDIR定义的路径会影响后续的RUN、CMD、ENTRYPOINT、COPY和ADD等指令的执行环境。- 可以多次使用
WORKDIR,每次都会切换到新的工作目录。 - 支持相对路径和绝对路径:
- 绝对路径:推荐使用,例如
WORKDIR /app。 - 相对路径:相对于上一个
WORKDIR设置的目录。 - 如果没有设置
WORKDIR,默认工作目录是/(容器根目录)。
示例
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
- 上述代码将工作目录设置为
/app,COPY和RUN指令会在/app目录下执行。 - 如果再次设置
WORKDIR /data,后续指令会在/data下执行。
注意事项
- 建议使用绝对路径以提高可读性和避免路径混淆。
- 多次使用
WORKDIR不会影响镜像的层级,但会影响指令的执行环境。 WORKDIR设置的工作目录在容器运行时会成为默认的工作目录,除非CMD或ENTRYPOINT覆盖。
2. COPY
功能
COPY 指令用于将构建上下文(本地文件或目录)中的文件或目录复制到镜像中的指定路径。
语法
COPY [--chown=<user>:<group>] <src>... <dest>
用法
<src>:源文件或目录的路径,相对于构建上下文(通常是 Dockerfile 所在目录)。<dest>:目标路径,可以是镜像内的绝对路径或相对于WORKDIR的相对路径。- 支持复制多个源文件到目标路径(用空格分隔)。
--chown=<user>:<group>:(可选)设置复制文件的所有者和组(仅在ADD和COPY支持)。
示例
WORKDIR /app
COPY package.json .
COPY src/ ./src/
COPY --chown=user:group config.ini /config/config.ini
- 第一行:将本地的
package.json复制到镜像的/app目录。 - 第二行:将本地
src目录复制到镜像的/app/src目录。 - 第三行:将
config.ini复制到/config/config.ini,并设置文件的所有者为user:group。
注意事项
- 源路径必须在构建上下文中(即
docker build指定的目录)。无法访问父目录(../)。 - 如果目标路径不存在,Docker 会自动创建。
- 如果目标路径以
/结尾,表示目标是一个目录;否则,Docker 会根据源类型决定目标是文件还是目录。 COPY不会解压文件(与ADD不同)。
3. RUN
功能
RUN 指令在构建镜像时执行指定的命令,并在镜像中创建新的层。常用于安装软件、更新系统或执行其他构建时需要的操作。
语法
RUN 有两种形式:
- Shell 形式(在 shell 中运行,默认为
/bin/sh -c):
RUN <command>
- Exec 形式(直接执行命令,不通过 shell):
RUN ["executable", "param1", "param2"]
用法
- Shell 形式:适合简单的命令,运行时会启动一个 shell 环境。
RUN apt-get update && apt-get install -y python3
- 上述命令在 shell 中运行
apt-get update和apt-get install。 - Exec 形式:直接调用可执行文件,适合需要精确控制参数或避免 shell 开销的场景。
RUN ["/bin/bash", "-c", "echo hello > /tmp/file"]
- 直接调用
/bin/bash执行命令。
示例
RUN apt-get update && apt-get install -y curl
RUN ["pip", "install", "--no-cache-dir", "requests"]
- 第一行:在 shell 中更新包索引并安装
curl。 - 第二行:直接执行
pip install安装 Python 的requests库。
注意事项
- 每条
RUN指令都会创建一个新的镜像层,建议将相关命令合并(如使用&&)以减少层数。
# 优化:合并命令
RUN apt-get update && apt-get install -y curl && apt-get clean
- Shell 形式会启动一个 shell 进程,可能增加开销,且信号处理(如
SIGTERM)可能不直接传递给命令。 - Exec 形式不会启动 shell,信号处理更直接,但不支持 shell 的管道(
|)或重定向(>)。 - 在
RUN中执行的命令不会在容器运行时重复执行,仅在构建时运行。
4. ENV
功能
ENV 指令用于设置环境变量,这些变量在构建时和容器运行时都可用。环境变量可以被后续的 RUN、CMD 和 ENTRYPOINT 指令使用。
语法
ENV <key>=<value> ...
用法
- 可以设置单个或多个环境变量。
- 环境变量在容器运行时可以通过
$key或${key}引用。 - 支持多行设置或单行多个变量。
示例
ENV APP_HOME=/app
ENV PORT=8080 LOG_LEVEL=info
WORKDIR $APP_HOME
RUN echo "Port is $PORT"
- 第一行:设置
APP_HOME环境变量为/app。 - 第二行:设置
PORT和LOG_LEVEL两个环境变量。 - 第三行:
WORKDIR使用环境变量$APP_HOME切换目录。 - 第四行:
RUN命令引用$PORT。
注意事项
- 环境变量在构建时和运行时都有效,容器运行时可以通过
docker run -e覆盖。 - 使用
ENV设置的变量优先级低于docker run的-e或--env参数。 - 避免在
ENV中存储敏感信息(如密码),建议使用 Docker Secrets 或运行时环境变量。 - 支持
${key:-default}形式,提供默认值。
5. CMD
功能
CMD 指令指定容器启动时默认执行的命令。它定义了容器运行时的默认行为(仅在容器启动时执行)。一个 Dockerfile 中只能有一个有效的 CMD 指令(如果有多个,只有最后一个生效)。
语法
CMD 有三种形式:
- Exec 形式(推荐):
CMD ["executable", "param1", "param2"]
- Shell 形式:
CMD command param1 param2
- 参数形式(为
ENTRYPOINT提供默认参数):
CMD ["param1", "param2"]
用法
- Exec 形式:直接执行命令,推荐用于启动应用程序。
CMD ["nginx", "-g", "daemon off;"]
- 启动 Nginx 作为前台进程。
- Shell 形式:在 shell 中运行命令,默认使用
/bin/sh -c。
CMD python app.py
- 在 shell 中运行
python app.py。 - 参数形式:与
ENTRYPOINT配合使用,提供默认参数。
ENTRYPOINT ["/bin/echo"]
CMD ["Hello", "World"]
- 容器启动时执行
/bin/echo Hello World。
示例
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]
- 容器启动时在
/app目录下执行python app.py。
注意事项
CMD指定的命令可以在docker run时被覆盖。例如:
docker run myimage bash
- 会忽略
CMD中的命令,改为运行bash。 - Exec 形式:推荐使用,因为它直接运行命令,信号处理更可靠,且不启动额外的 shell 进程。
- Shell 形式:会启动 shell,可能导致信号(如
SIGTERM)无法直接传递给进程,容器可能无法优雅退出。 - 如果定义了
ENTRYPOINT,CMD会作为其参数;否则,CMD提供默认的容器启动命令。 - 如果没有
CMD或ENTRYPOINT,容器启动会失败。
总结与对比
| 指令 | 功能 | 执行时机 | 语法形式 | 注意事项 |
|---|---|---|---|---|
| WORKDIR | 设置工作目录 | 构建时和运行时 | WORKDIR /path | 使用绝对路径,避免多次切换导致混乱。 |
| COPY | 复制文件/目录到镜像 | 构建时 | COPY src dest | 源路径限于构建上下文,支持 --chown 设置权限。 |
| RUN | 执行构建时的命令,生成镜像层 | 构建时 | RUN command 或 RUN ["exec"] | 合并命令减少层数,Exec 形式更高效。 |
| ENV | 设置环境变量 | 构建时和运行时 | ENV key=value | 可被 docker run -e 覆盖,避免存储敏感信息。 |
| CMD | 指定容器启动时的默认命令 | 运行时 | CMD ["exec"] 或 CMD command | 推荐 Exec 形式,可被 docker run 覆盖,与 ENTRYPOINT 配合使用更灵活。 |
综合示例
以下是一个完整的 Dockerfile 示例,展示这些指令的协同使用:
# 设置基础镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app
# 复制文件到镜像
COPY requirements.txt .
COPY app.py .
# 设置环境变量
ENV PORT=8000
ENV LOG_LEVEL=debug
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 指定容器启动命令
CMD ["python", "app.py"]
说明
WORKDIR /app:设置工作目录为/app。COPY:将requirements.txt和app.py复制到/app。ENV:设置PORT和LOG_LEVEL环境变量。RUN:安装 Python 依赖。CMD:容器启动时运行python app.py。
最佳实践
- WORKDIR:始终使用绝对路径,保持路径清晰。
- COPY:只复制必要的文件,使用
.dockerignore排除无关文件。 - RUN:合并命令减少镜像层,使用 Exec 形式提高效率。
- ENV:合理使用环境变量,方便配置复用。
- CMD:优先使用 Exec 形式(
["command", "arg1"]),确保信号处理正确。