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"]
),确保信号处理正确。