你写的代码在自己电脑上跑得好好的,一到同事机器就报错——这就是"我这里能跑"问题。Docker 的解法:把代码 + 运行时 + 系统库打包成一个标准交付物(镜像),到哪都跑一样。这一页不讲概念名词,直接教你写 Dockerfile、起容器、挂数据卷、用 compose 一键拉起整个服务栈。
别上来就背命令,先把"镜像 vs 容器"这个核心类比建立起来:
docker run -d -p 8080:80 nginx,浏览器访问 localhost:8080。以前部署一个 Python Web 服务,你得在服务器上:装对应版本 Python、装 pip、pip install 一堆依赖、配环境变量、开端口。换台机器再来一遍,版本稍不匹配就崩。
Docker 的思路:别再"在目标机器上现装环境",而是把整个运行环境(操作系统精简层 + Python 解释器 + 依赖库 + 你的代码)切成只读的层,叠在一起就是镜像。镜像拷到任何装了 Docker 的机器,都能原封不动跑起来。
镜像 vs 容器:镜像是死的模板(像一个类、一张安装光盘);容器是活的进程(new 出来的实例、开机后的系统)。同一个镜像 docker run 两次,就是两个互相隔离的容器。
为什么是"分层":基础镜像(如 python:3.11-slim)所有人共享,你只在上面加一层"拷贝代码+pip install"。改一行代码只重建最后那层,这就是构建缓存快的原因。
| 命令 | 作用 | 常用示例 |
|---|---|---|
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 |
# 阶段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"]
| 指令 | 作用 | 注意点 |
|---|---|---|
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
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 连卷一起删)。
docker run -d -p 8080:80 --name my-nginx nginx。docker ps 看到 my-nginx 状态 Up。http://localhost:8080,看到 Nginx 欢迎页即成功。COPY . . 再 RUN pip install。改一行代码→COPY 层变了→pip install 缓存失效,重装所有依赖。COPY requirements.txt . 再 RUN pip install -r requirements.txt,最后才 COPY . . 拷代码。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 删容器。-v pgdata:/容器内数据目录;不加 -v,删容器数据即丢。-p 宿主机:容器。访问宿主机 IP:左边端口,流量进容器右边端口。写反了(如 -p 80:8080)就会连不上。python app.py 而不是 nohup ... &)。排查用 docker logs 容器名。-p 80:8080 又去访问 localhost:8080——宿主机根本没监听 8080。记住"宿主机端口:容器端口",访问的是宿主机端口。pip install --no-cache-dir、合并 RUN 并清理。| 考法 | 出题形式 | 应对 |
|---|---|---|
| 镜像 vs 容器 | 问二者关系 | 镜像=只读模板,容器=运行实例 |
| 端口映射 | 给 -p 判断能否访问 | 左宿主机右容器,访问左边 |
| 构建缓存 | 问 build 变慢原因 | 变动多的层放后面 |
| 数据卷 | 删容器数据丢失原因 | 有状态服务必须 -v 挂卷 |
真题基础1. 关于 Docker 镜像和容器,正确的是?
真题中档2. 执行 docker run -p 8080:80 nginx,应通过哪个地址访问?
真题中档3. Dockerfile 里为什么把 COPY requirements.txt 和 pip install 放在 COPY 代码之前?
真题拔高4. 起了 Postgres 容器但没挂卷,删容器重建后数据库表都没了,原因是?
requirements.txt 写 fastapi、uvicorn[standard]。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 页面。docker compose up -d,不用装 Python、不用 pip install。-a 显示全部含已停止的。docker logs -f --tail 100 容器名。CMD。-p。docker exec -it 容器名 bash(没 bash 用 sh)。db:5432,compose 内置 DNS 会解析。docker logs 看报错;再确认前台进程是否真的启动、端口是否冲突、配置/环境变量是否缺、挂载路径权限是否对。USER appuser。仓库地址/名:标签,docker login 后 docker push。| 天 | 任务 | 自检 |
|---|---|---|
| 第 1 天 | 读②③,跑通 nginx 容器 | 能访问 localhost:8080 |
| 第 2 天 | 背命令卡 + 做基础 1-5 | 基础全对 |
| 第 3 天 | 手写一个 FastAPI 的 Dockerfile 并 build | 镜像成功生成 |
| 第 4 天 | 做中档 6-10,挂卷起 Postgres | 删容器数据还在 |
| 第 5 天 | 做拔高 11-15 + 真题 4 题 | 排错思路清晰 |
| 第 6-7 天 | 写 compose 一键起 web+db,口述交付流程 | 不看资料全跑通 |