--- title: "05-Dockerfile-构建镜像" created: 2026-09-01 tags: - 项目筑基 --- # Dockerfile 构建镜像 前两篇跑的都是**别人造好的盒子**(nginx、hello-world)。自己的项目怎么变成镜像?答案就是 Dockerfile——一份"造盒子说明书",`docker build` 照着它一步步把你的代码+依赖+启动方式封进镜像。01 篇的盒子比喻到这里闭环:**之前是买盒子,现在自己造盒子。** ## 一、第一个 Dockerfile:把 http-server 装进盒子 01 篇里本地跑网页的方式是 `python -m http.server 8080`,现在把"一个静态网页 + 这个服务"打成镜像。项目目录长这样: ```text my-site/ ├── Dockerfile ├── index.html └── style.css ``` Dockerfile 内容: ```dockerfile FROM python:3.12-slim WORKDIR /app COPY . . EXPOSE 8080 CMD ["python", "-m", "http.server", "8080"] ``` 五行,每行一个动作: | 指令 | 作用 | 备注 | | --- | --- | --- | | `FROM` | 基础镜像,一切从它出发 | `slim` 是精简版(约 50MB),完整版 1GB——基础镜像直接决定成品体积 | | `WORKDIR` | 设定工作目录 | 后续指令都在这个目录里执行 | | `COPY . .` | 把宿主机当前目录拷进镜像 | 第一个 `.` 是宿主机,第二个是容器内 | | `EXPOSE 8080` | 声明容器监听的端口 | 只是"说明书注释",真正开放端口还要靠 run 时的 `-p` | | `CMD` | 容器启动时执行的命令 | 一个 Dockerfile 只有一条生效的 CMD | 构建并运行: ```bash docker build -t my-site:1.0 . docker run -d -p 8080:8080 my-site:1.0 ``` 访问 `http://localhost:8080`——你的网页已经在盒子里了,拷到任何装 Docker 的机器上 `run` 一下就活。01 篇开头那个"环境不同 依赖不同"的部署难题,到此终结。 ## 二、常用指令全家桶 ```dockerfile FROM ubuntu:22.04 # 基础镜像(起点) ENV TZ=Asia/Shanghai # 环境变量(构建期+运行期都在) ARG VER=1.0 # 仅构建期的变量 RUN apt-get update && apt-get install -y curl # 构建时执行命令(装依赖) COPY app/ /app/ # 拷贝文件/目录 ADD dist.tar.gz /opt/ # 类似 COPY,但会自动解压 tar(少用,语义混) WORKDIR /app # 切换工作目录 USER appuser # 切换运行用户(安全加分项) EXPOSE 8000 # 声明端口 ENTRYPOINT ["python"] # 固定的主命令 CMD ["-m", "http.server"] # 默认参数(可被 docker run 后面的参数覆盖) ``` > [!tip] CMD vs ENTRYPOINT > 初学阶段记一条:**写 `CMD` 就够用了**。两者区别是 ENTRYPOINT 写死主程序、CMD 只给默认参数(`docker run myimg --port 9000` 能覆盖 CMD,覆盖不了 ENTRYPOINT)。细节等踩到再加。 ## 三、分层与缓存:为什么指令顺序很重要 Docker 镜像是**一层层叠出来的**:Dockerfile 每条指令生成一层,且每层有缓存——指令和上下文没变就直接复用上次的层,**没变的部分不用重跑**。 这就是为什么讲究的 Dockerfile 都把"不容易变的放前面,经常变的放最后": ```dockerfile # 推荐写法:装依赖(不常变)在前,拷代码(天天变)在后 COPY requirements.txt . RUN pip install -r requirements.txt # ← 依赖没变时这层永远走缓存 COPY . . # ← 只有代码变了这个层才重建 ``` 反着写的话,改一行代码就重装一遍依赖,构建从 3 秒变 3 分钟。 配套的 `.dockerignore` 让 `COPY . .` 别把没用的东西拷进去(作用同 `.gitignore`): ```text node_modules .git *.log ``` ## 四、镜像瘦身两板斧 1. **换小基础镜像**:`python:3.12`(1GB 级)→ `python:3.12-slim`(百 MB 级)→ `python:3.12-alpine`(几十 MB 级); 2. **RUN 合并+清理**:`RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*` 一行写完——每条 RUN 是独立一层,中间产生的缓存不会被下一条清掉,必须在同一条里删。 --- 到这一步,单个盒子从造到跑已经全通。但真实服务往往不止一个盒子——网站 + 数据库 + 缓存,总不能一条条 `docker run` 手敲吧?先补一块拼图(数据怎么留住、盒子之间怎么通信):[[06-数据卷与网络|数据卷与网络]],然后 [[07-Compose-编排实操|Compose 编排实操]] 把多盒子一键拉起来。 --- ⬅️ [[04-镜像与容器命令实操|镜像与容器命令实操]] 🏠 [[00-网络与项目|00-网络与项目]] ➡️ [[06-数据卷与网络|数据卷与网络]]