← 返回 FDE 培养总览 FDE 培养 · 知识点深化 · Docker 容器化实战
知识点深化 · Docker

Docker 容器化实战:镜像、Dockerfile、网络、数据卷与 Compose

你写的代码在自己电脑上跑得好好的,一到同事机器就报错——这就是"我这里能跑"问题。Docker 的解法:把代码 + 运行时 + 系统库打包成一个标准交付物(镜像),到哪都跑一样。这一页不讲概念名词,直接教你写 Dockerfile、起容器、挂数据卷、用 compose 一键拉起整个服务栈。

① 小白第一课怎么学(4 步走,约 90 分钟)

别上来就背命令,先把"镜像 vs 容器"这个核心类比建立起来:

1建立类比(15 分钟)
读②③:镜像=类/安装光盘,容器=运行起来的实例/开机后的电脑。一个镜像能起多个容器。
2跑通第一个容器(20 分钟)
读④:执行 docker run -d -p 8080:80 nginx,浏览器访问 localhost:8080。
3写一个自己的 Dockerfile(30 分钟)
跟着⑤把一个 Python 小服务打包成镜像并跑起来。
4排错+刷题(25 分钟)
读⑥高频错误,做⑦⑩。重点搞懂端口、卷、构建缓存三个坑。
本课小目标学完你要能:① 说清镜像/容器/仓库三者关系;② 手写一个多阶段 Dockerfile;③ 解释数据卷为什么能持久化;④ 用 docker-compose 同时起 Web+数据库。

② 一图看懂:Docker 全景

Docker 容器 镜像 Image 只读模板,分层构建 容器 Container 镜像的运行实例 Dockerfile 逐条指令 build 成镜像 网络 & 数据卷 容器互通 + 持久化 Compose 编排 一条命令起多容器 易错:端口/卷/缓存 -p 映射错、数据丢失
读法:中心 Docker,分四块——镜像(静态模板)、容器(运行实例)、Dockerfile(怎么造镜像)、网络与卷(容器怎么通信、数据怎么存);下方是 Compose 编排和高频坑。

③ 本质直觉:把"环境"一起打包带走

以前部署一个 Python Web 服务,你得在服务器上:装对应版本 Python、装 pip、pip install 一堆依赖、配环境变量、开端口。换台机器再来一遍,版本稍不匹配就崩。

Docker 的思路:别再"在目标机器上现装环境",而是把整个运行环境(操作系统精简层 + Python 解释器 + 依赖库 + 你的代码)切成只读的层,叠在一起就是镜像。镜像拷到任何装了 Docker 的机器,都能原封不动跑起来。

镜像 vs 容器:镜像是死的模板(像一个类、一张安装光盘);容器是活的进程(new 出来的实例、开机后的系统)。同一个镜像 docker run 两次,就是两个互相隔离的容器。

为什么是"分层":基础镜像(如 python:3.11-slim)所有人共享,你只在上面加一层"拷贝代码+pip install"。改一行代码只重建最后那层,这就是构建缓存快的原因。

你的代码 + 配置(最上层) pip install 依赖 Python 3.11 运行时 Debian 精简系统层 镜像 = 只读层叠起来; run 时在顶部加一层可写层=容器
容器不是虚拟机容器共用宿主机内核,只隔离进程、文件系统、网络,所以毫秒级启动、几百 MB;VM 要装完整 Guest OS,启动分钟级、几个 GB。这就是容器"轻量"的本质。

④ 完整体系:命令、Dockerfile、网络、卷、Compose

核心命令速查

命令作用常用示例
docker build用 Dockerfile 构建镜像docker build -t myapp:1.0 .
docker run起一个容器docker run -d -p 8080:80 --name web nginx
docker ps看运行中容器docker ps -a 看全部(含停止的)
docker logs看容器日志docker logs -f --tail 100 web
docker exec进容器执行命令docker exec -it web bash
docker images列出本地镜像docker images
docker stop/rm停止/删除容器docker rm -f web
docker volume管理数据卷docker volume create pgdata

一个生产级 Python 服务的 Dockerfile(多阶段构建)

# 阶段1:装依赖(用大镜像,构建完丢弃)
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt --target=/app/deps

# 阶段2:运行镜像(只带必要文件,体积小)
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /app/deps /usr/local/lib/python3.11/site-packages
COPY app.py .
ENV PORT=8000
EXPOSE 8000
# 用非 root 用户跑,更安全
RUN useradd -m appuser && chown -R appuser /app
USER appuser
CMD ["python", "app.py"]

Dockerfile 常用指令对照

指令作用注意点
FROM基础镜像优先选 slim/alpine 减小体积
WORKDIR设定工作目录等价 cd,避免绝对路径
COPY拷文件进镜像比 ADD 可控(ADD 会自动解压)
RUN构建时执行命令每一条生成一层,尽量合并
ENV设环境变量运行时可读
EXPOSE声明端口仅声明,真正映射靠 -p
CMD容器启动默认命令一个 Dockerfile 只有一个生效

容器网络与端口映射

-p 主机端口:容器端口。比如 -p 8080:80:宿主机 8080 收到的流量转发到容器的 80 端口。容器之间用 Compose 时,可直接用服务名当域名互相访问(DNS 解析)。

数据卷:容器删了数据还在

容器内文件默认写在可写层,容器一删就全没。数据库这类有状态服务必须挂卷:

# 命名卷:docker 管理生命周期
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16

# 绑定挂载:直接挂宿主机目录,改代码即时生效(开发用)
docker run -d -v $(pwd):/app -p 8000:8000 myapp

docker-compose.yml:一键起多容器

services:
  web:
    build: .
    ports: ["8000:8000"]
    environment:
      - DATABASE_URL=postgresql://app:secret@db:5432/app
    depends_on: [db]
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes: ["pgdata:/var/lib/postgresql/data"]
volumes:
  pgdata:

docker compose up -d 一条命令把 web + db 全拉起来;docker compose down 停止(加 -v 连卷一起删)。

⑤ 用法场景与典型例题

例1(基础·跑第一个 Nginx)用一条命令后台起 Nginx,并把宿主机 8080 映射到容器 80
关键是 -d 后台、-p 端口映射、--name 起名。
① 执行 docker run -d -p 8080:80 --name my-nginx nginx。
② docker ps 看到 my-nginx 状态 Up。
③ 浏览器访问 http://localhost:8080,看到 Nginx 欢迎页即成功。
解析:-d=detach 后台;-p 8080:80=宿主机8080→容器80;容器内 Nginx 默认监听 80,正好对上。
例2(镜像构建缓存)为什么我只改了一行代码,build 却从 pip install 那步重来、特别慢?
Docker 缓存按 Dockerfile 指令顺序逐层判断,前面任何一层变了,后面全部失效。
① 错误写法:先 COPY . . 再 RUN pip install。改一行代码→COPY 层变了→pip install 缓存失效,重装所有依赖。
② 正确写法:先 COPY requirements.txt . 再 RUN pip install -r requirements.txt,最后才 COPY . . 拷代码。
③ 这样只要 requirements.txt 没变,pip install 这层就命中缓存,改代码只重建最后一层。
答案:把"变动少的依赖安装"放前面,"频繁变动的代码拷贝"放后面。
例3(数据持久化)起一个 Postgres 容器,要求删容器后数据不丢
Postgres 数据写在容器内 /var/lib/postgresql/data,必须挂卷。
① docker volume create pgdata。
② docker run -d --name pg -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres:16。
③ 写入一些表后,docker rm -f pg 删容器。
④ 再用同样命令 + 同一个 pgdata 卷起新容器,数据还在。
答案:关键就是 -v pgdata:/容器内数据目录;不加 -v,删容器数据即丢。
端口映射记忆法"左边是家门,右边是院门":-p 宿主机:容器。访问宿主机 IP:左边端口,流量进容器右边端口。写反了(如 -p 80:8080)就会连不上。

⑥ 高频错误诊断(4 条)

错误 1:容器秒退(Exited 0/1)容器不是虚拟机,前台进程一结束容器就停。跑后台服务务必让进程以前台方式运行(如 python app.py 而不是 nohup ... &)。排查用 docker logs 容器名。
错误 2:端口映射写反或没加 -p容器内服务监听 8080,但你只写了 -p 80:8080 又去访问 localhost:8080——宿主机根本没监听 8080。记住"宿主机端口:容器端口",访问的是宿主机端口。
错误 3:容器删了数据库全没忘了挂数据卷。Postgres/Redis/Mongo 这类有状态服务,第一次启动就必须规划卷,事后补卷救不回已有数据。
错误 4:镜像体积巨大(几百 MB 起)用了 python:3.11 完整版、没多阶段构建、apt 装完没清理缓存。对策:用 slim/alpine 基础镜像、多阶段构建、pip install --no-cache-dir、合并 RUN 并清理。

⑦ 考点真题演练(4 题)

考点分布

考法出题形式应对
镜像 vs 容器问二者关系镜像=只读模板,容器=运行实例
端口映射给 -p 判断能否访问左宿主机右容器,访问左边
构建缓存问 build 变慢原因变动多的层放后面
数据卷删容器数据丢失原因有状态服务必须 -v 挂卷

真题基础1. 关于 Docker 镜像和容器,正确的是?

真题中档2. 执行 docker run -p 8080:80 nginx,应通过哪个地址访问?

真题中档3. Dockerfile 里为什么把 COPY requirements.txt 和 pip install 放在 COPY 代码之前?

真题拔高4. 起了 Postgres 容器但没挂卷,删容器重建后数据库表都没了,原因是?

⑧ 必背命令/知识点卡

核心三对象:镜像 Image / 容器 Container / 仓库 Registry 模板/实例/仓库
构建:docker build -t 名:标签 . -t 打标签,末尾是上下文
起容器:docker run -d -p 主机:容器 --name 名 镜像 -d 后台 -p 映射
排错三件套:ps / logs -f / exec -it ... bash 看状态、看日志、进容器
持久化:-v 卷名:容器内目录 有状态服务必挂
缓存原则:变动少的层在前,频繁改的代码在后 build 快
编排:docker compose up -d / down 多容器一键起停

⑨ 应用输出:从零把一个 FastAPI 服务容器化上线

场景:把本地写的一个 Python API 打包,同事 docker compose up 就能跑
① 写依赖:项目里 requirements.txt 写 fastapi、uvicorn[standard]。
② 写 Dockerfile:FROM python:3.11-slim,先 COPY requirements.txt 再 pip install,最后 COPY app.py,CMD 用 uvicorn app:app --host 0.0.0.0 --port 8000。
③ 构建:docker build -t demo-api:1.0 .,看到每一步 Successfully built。
④ 本地验证:docker run -d -p 8000:8000 demo-api:1.0,访问 http://localhost:8000/docs 看到 Swagger 页面。
⑤ 配 Compose:再加一个 Postgres 服务,web 用 depends_on 等 db,DATABASE_URL 用服务名 db 做主机名。
⑥ 交付:把项目(含 Dockerfile、compose.yml)交给同事,他只需 docker compose up -d,不用装 Python、不用 pip install。
口述交付思路合上课本说:"把运行时和代码打包成镜像,用端口映射暴露服务,用卷持久化数据库,用 compose 把多容器编排起来,别人一条命令就能起。"

⑩ 分层练习 15 题(基础 5 + 中档 5 + 拔高 5)

▍基础 5 题

基础1镜像和容器哪个是只读模板?
镜像是只读模板;容器是它运行起来的实例,顶部有可写层。
基础2docker ps 默认显示什么?加 -a 呢?
默认只显示运行中容器;-a 显示全部含已停止的。
基础3看容器日志的命令是?
docker logs -f --tail 100 容器名。
基础4Dockerfile 里声明启动命令的指令是?
CMD。
基础5EXPOSE 80 等于把端口映射到宿主机了吗?
不是。EXPOSE 只是声明,真正映射要 run 时加 -p。

▍中档 5 题

中档6-p 8080:3000,容器内服务监听哪个端口?访问宿主机哪个端口?
容器内监听 3000;访问宿主机 8080。
中档7怎么进运行中的容器执行 bash?
docker exec -it 容器名 bash(没 bash 用 sh)。
中档8多阶段构建解决什么问题?
用大镜像装编译依赖,只把产物拷进小运行镜像,显著减小最终镜像体积。
中档9绑定挂载和命名卷的区别?
绑定挂载挂宿主机具体目录(开发改代码即时生效);命名卷由 Docker 管理(生产存数据更稳)。
中档10compose 里两个服务如何互访数据库?
用服务名当主机名,如 db:5432,compose 内置 DNS 会解析。

▍拔高 5 题

拔高11容器启动后立刻 Exited (139),如何系统排查?
先 docker logs 看报错;再确认前台进程是否真的启动、端口是否冲突、配置/环境变量是否缺、挂载路径权限是否对。
拔高12为什么生产容器建议用非 root 用户运行?
容器若被攻破,root 权限可能逃逸影响宿主机;非 root 可缩小攻击面。Dockerfile 里 USER appuser。
拔高13镜像 push 到私有仓库前要做什么?
镜像 tag 成 仓库地址/名:标签,docker login 后 docker push。
拔高14docker compose down 加 -v 会怎样?
除删容器网络外,连命名卷一起删除,数据清空。生产执行要非常小心。
拔高15.dockerignore 文件的作用?
类似 .gitignore,排除 node_modules、.env、.git 等不进构建上下文,加快 build、避免把密钥打进镜像。

⑪ 记忆口诀 + 7 天复习计划

三句口诀 ① 镜像是模板容器是实例,一层一层叠出来。
② 端口映射左家右院,有状态服务必挂卷。
③ 变动少的层放前面吃缓存,前台进程别用 nohup。
天任务自检
第 1 天读②③,跑通 nginx 容器能访问 localhost:8080
第 2 天背命令卡 + 做基础 1-5基础全对
第 3 天手写一个 FastAPI 的 Dockerfile 并 build镜像成功生成
第 4 天做中档 6-10,挂卷起 Postgres删容器数据还在
第 5 天做拔高 11-15 + 真题 4 题排错思路清晰
第 6-7 天写 compose 一键起 web+db,口述交付流程不看资料全跑通

← 返回 FDE 培养总览