🐳 Docker 入门教程(八):Dockerfile
一、Dockerfile 是什么?
Dockerfile 是构建 Docker 镜像的“说明书”,它告诉 Docker:
- 从哪开始构建(FROM)
- 要做哪些修改(RUN / COPY / ADD)
- 最终如何启动(CMD / ENTRYPOINT)
构建命令:
docker build -t myapp:1.0 .
二、Dockerfile 指令详解
这里列出常用指令,并提供格式、示例、说明、最佳实践。
FROM
:指定基础镜像(必须是第一行)
FROM ubuntu:22.04
- 所有构建都必须有个基础镜像
- 可以使用官方、私有或自定义镜像
- 可以通过
AS
给阶段命名(多阶段构建)
多阶段构建:
FROM golang:1.20 AS builder
WORKDIR /app
RUN go build -o mybin
FROM scratch
COPY --from=builder /app/mybin /mybin
ENTRYPOINT ["/mybin"]
RUN
:执行命令生成新镜像层
RUN apt update && apt install -y curl
- 用于安装依赖、配置环境
- 每条 RUN 都创建一个新层
合并多个命令,减少镜像层数
RUN apt update && \
apt install -y curl git && \
rm -rf /var/lib/apt/lists/*
COPY
:将本地文件复制进镜像
COPY . /app
- 第一个参数是宿主机相对路径(构建上下文)
- 第二个参数是容器内路径
指定复制文件:
COPY requirements.txt /app/
ADD
:类似 COPY,但更智能
ADD app.tar.gz /app/
- 可以自动解压
.tar
文件 - 支持远程 URL(不推荐)
- 对本地文件请尽量用 COPY,行为更可控
WORKDIR
:设置容器内的工作目录
WORKDIR /app
- 相当于进入了
/app
目录 - 所有后续命令(RUN、CMD、ENTRYPOINT)都在这个目录中执行
- 多次使用将自动拼接:
WORKDIR /a
→WORKDIR b
→/a/b
CMD
:容器默认执行的命令(可被覆盖)
CMD ["python", "app.py"]
容器启动时默认执行(docker run 不指定命令时)
只能有一条,写多条只保留最后一条
支持两种写法:
- JSON 数组:首选方式,精确执行命令
- Shell 字符串:通过
/bin/sh -c
执行
ENTRYPOINT
:固定的启动命令(不建议覆盖)
ENTRYPOINT ["python"]
CMD ["app.py"]
ENTRYPOINT
是主程序,不会被 run 覆盖CMD
是参数,可以在 run 里改写- 两者合用,实现默认行为可改、主程序不可改
示例:
docker run myapp # python app.py
docker run myapp test.py # python test.py
ENV
:设置环境变量
ENV APP_ENV=production
- 可用于 RUN、CMD 中引用:
RUN echo $APP_ENV
- 容器启动后这些变量也会存在
EXPOSE
:声明容器要用的端口(仅声明,不映射)
EXPOSE 80
- 不会自动映射端口
- 只是标记,供文档/编排系统参考(如 Compose)
VOLUME
:声明挂载点
VOLUME ["/data"]
- 建议容器外部挂载这个路径
- 自动创建匿名卷
- 可由用户显式绑定实际路径
ARG
:构建时变量(不同于 ENV)
ARG VERSION=1.0
RUN echo "Building version $VERSION"
- 使用
--build-arg
提供参数:
docker build --build-arg VERSION=2.0 -t app:2.0 .
三、RUN / CMD / ENTRYPOINT 的本质区别
指令 | 用于何时 | 作用对象 | 是否写入镜像 | 会不会被 docker run 覆盖 |
---|---|---|---|---|
RUN |
构建镜像时 | 构建阶段行为 | 写进镜像层 | 不能被覆盖 |
CMD |
容器运行时 | 默认启动命令 | 写进镜像元数据 | 会被 run 的命令替换 |
ENTRYPOINT |
容器运行时 | 强制主程序入口 | 写进镜像元数据 | 不会被 run 替换主程序,只能追加参数 |
时间线类比:三者分别出现在 Docker 生命周期的哪一刻?
① docker build 时
└── RUN:安装、配置、打包(一次性的“做菜”过程)
② docker run 时
└── ENTRYPOINT:一定要执行的“主程序”或“框架”
└── CMD:主程序的默认参数(可以临时替换)
之后区分ENTRYPOINT
和CMD
ENTRYPOINT
是你必须执行的程序,
CMD
是你建议的默认参数。
示例 A:CMD 单独使用
FROM ubuntu
CMD ["echo", "hello"]
- 默认执行:
echo hello
docker run image
→ 输出 hellodocker run image hi
→ 执行hi
(完全覆盖)
示例 B:ENTRYPOINT 单独使用
FROM ubuntu
ENTRYPOINT ["echo"]
docker run image hi
→ 执行echo hi
docker run image ls
→ 执行echo ls
- 想执行别的(如
ls
),你做不到,除非:
docker run --entrypoint ls image
示例 C:ENTRYPOINT + CMD 联用(推荐)
FROM ubuntu
ENTRYPOINT ["echo"]
CMD ["hello"]
执行命令 | 实际执行 |
---|---|
docker run image |
echo hello |
docker run image world |
echo world |
docker run --entrypoint date image |
date |
CMD 是默认参数,ENTRYPOINT 是主程序。