Dockerfile 构建镜像

前两篇跑的都是别人造好的盒子(nginx、hello-world)。自己的项目怎么变成镜像?答案就是 Dockerfile——一份"造盒子说明书",docker build 照着它一步步把你的代码+依赖+启动方式封进镜像。01 篇的盒子比喻到这里闭环:之前是买盒子,现在自己造盒子。

一、第一个 Dockerfile:把 http-server 装进盒子

01 篇里本地跑网页的方式是 python -m http.server 8080,现在把"一个静态网页 + 这个服务"打成镜像。项目目录长这样:

my-site/
├── Dockerfile
├── index.html
└── style.css

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

构建并运行:

docker build -t my-site:1.0 .
docker run -d -p 8080:8080 my-site:1.0

访问 http://localhost:8080——你的网页已经在盒子里了,拷到任何装 Docker 的机器上 run 一下就活。01 篇开头那个"环境不同 依赖不同"的部署难题,到此终结。

二、常用指令全家桶

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 后面的参数覆盖)
💡CMD vs ENTRYPOINT

初学阶段记一条:CMD 就够用了。两者区别是 ENTRYPOINT 写死主程序、CMD 只给默认参数(docker run myimg --port 9000 能覆盖 CMD,覆盖不了 ENTRYPOINT)。细节等踩到再加。

三、分层与缓存:为什么指令顺序很重要

Docker 镜像是一层层叠出来的:Dockerfile 每条指令生成一层,且每层有缓存——指令和上下文没变就直接复用上次的层,没变的部分不用重跑

这就是为什么讲究的 Dockerfile 都把"不容易变的放前面,经常变的放最后":

# 推荐写法:装依赖(不常变)在前,拷代码(天天变)在后
COPY requirements.txt .
RUN pip install -r requirements.txt   # ← 依赖没变时这层永远走缓存
COPY . .                               # ← 只有代码变了这个层才重建

反着写的话,改一行代码就重装一遍依赖,构建从 3 秒变 3 分钟。

配套的 .dockerignoreCOPY . . 别把没用的东西拷进去(作用同 .gitignore):

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 手敲吧?先补一块拼图(数据怎么留住、盒子之间怎么通信):数据卷与网络,然后 Compose 编排实操 把多盒子一键拉起来。


⬅️ 镜像与容器命令实操 🏠 00-网络与项目 ➡️ 数据卷与网络