🐳 容器与虚拟化
容器是现代开发的核心技能。无论你做哪类开发,大概率会用到 Docker/容器。
开发者视角
本章不深入 Kubernetes/容器编排(那是 DevOps/SRE 的领域)。我们聚焦开发者本地用 Docker 开发的实用技能。
🐳 Docker 日常开发流
安装 Docker
# Ubuntu/Debian
sudo apt update
sudo apt install docker.io
# 或者用 Docker 官方脚本(推荐,版本更新)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 让非 root 用户也能用 Docker(避免每次 sudo)
sudo usermod -aG docker $USER
# 退出重新登录后生效
# 验证安装
docker --version
docker run hello-world
核心命令速查
# 镜像操作
docker images # 列出本地镜像
docker pull nginx:alpine # 拉取镜像
docker rmi nginx:alpine # 删除镜像
docker build -t myapp:1.0 . # 构建镜像
# 容器操作
docker ps # 列出运行中的容器
docker ps -a # 列出所有容器(含已停止)
docker run -d -p 8080:80 --name mynginx nginx:alpine
# 启动容器(后台运行)
docker stop mynginx # 停止容器
docker start mynginx # 启动已停止的容器
docker rm mynginx # 删除容器
docker logs -f mynginx # 查看容器日志
# 进入容器
docker exec -it mynginx bash # 在运行中的容器里执行 bash
docker exec -it mynginx sh # Alpine 镜像用 sh(没有 bash)
# 清理(开发环境常用)
docker system prune -a # 删除所有未使用的镜像/容器/网络
docker volume prune # 删除未使用的卷
开发调试技巧
# 场景 1:本地开发,代码改了自动重启
docker run -v $(pwd):/app -p 3000:3000 myapp:dev
# -v 挂载本地目录到容器,代码改了容器内也改了
# 场景 2:查看容器内文件变化
docker diff mynginx
# 场景 3:复制文件进出容器
docker cp mynginx:/etc/nginx/nginx.conf ./
docker cp ./new-config.conf mynginx:/etc/nginx/nginx.conf
# 场景 4:查看容器资源使用
docker stats mynginx
# 场景 5:查看容器进程
docker top mynginx
📝 编写高效 Dockerfile
基础 Dockerfile 结构
# 阶段 1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 阶段 2:运行(多阶段构建,镜像更小)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
最佳实践
# ✅ 好的做法
# 1. 用 .dockerignore 排除 node_modules/(构建更快)
# .dockerignore 内容:
# node_modules
# .git
# dist
# 2. 合并 RUN 减少层数(镜像更小)
RUN apt update && apt install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# 3. 用 ARG 传递构建参数
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine
# 4. 用多阶段构建减小最终镜像
# (见上面示例)
# 5. 非 root 用户运行(安全)
RUN addgroup -S app && adduser -S app -G app
USER app
CMD ["./app"]
# ❌ 坏的做法
# 1. 用 latest 标签(不可复现)
FROM node:latest # 不好!
# 2. 把 secrets 写进 Dockerfile
ENV AWS_SECRET=xxx # 危险!
# 3. 不清理 apt 缓存(镜像变大)
RUN apt update && apt install -y curl # 缺少 rm -rf
调试 Dockerfile 构建失败
# 构建到某一步失败了,想看看那一步发生了什么?
# 技巧:在失败的 RUN 命令后加 && bash,让容器停在那一步
# Dockerfile 里临时改成:
RUN failing-command && bash
# 然后构建,进入容器调试
docker run -it <image-id> bash
🎼 docker-compose 编排
本地开发多个服务(Web + API + DB + Redis)→ 用 docker-compose.yml 一键启动。
基础 docker-compose.yml
version: '3.8'
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- '6379:6379'
app:
build: .
ports:
- '3000:3000'
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/myapp
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
volumes:
- .:/app
- /app/node_modules # 匿名卷,保护容器内的 node_modules
volumes:
pgdata:
常用命令
# 启动所有服务(后台)
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看某个服务日志
docker-compose logs app
docker-compose logs -f app # 实时追踪
# 重启某个服务
docker-compose restart app
# 停止所有服务
docker-compose down
# 停止并删除卷(清理数据)
docker-compose down -v
# 重新构建并启动
docker-compose up -d --build
🦭 Podman(Docker 替代品)
Podman 是 Red Hat 开发的无守护进程容器引擎,兼容 Docker 命令。
为什么用 Podman?
| 特性 | Docker | Podman |
|---|---|---|
| 守护进程 | 需要 dockerd | 不需要(无守护进程) |
| root 权限 | 需要(docker 组有风险) | 不需要(rootless) |
| Docker Compose | 支持 | 支持(podman-compose) |
| Kubernetes | 需额外工具 | 原生支持(podman generate kube) |
| 商业授权 | 企业版收费 | 完全免费 |
基本使用(和 Docker 命令几乎一样)
# 安装(Ubuntu)
sudo apt install podman
# 基本命令(和 docker 几乎一样)
podman pull nginx:alpine
podman run -d -p 8080:80 nginx:alpine
podman ps
podman stop <container>
# 用 docker-compose 替代方案
pip install podman-compose
podman-compose up -d
🚨 常见场景
场景 1:清理 Docker 磁盘空间
# 查看磁盘占用
docker system df
# 一键清理(危险!会删除所有未使用资源)
docker system prune -a --volumes
# 选择性清理
docker image prune -a # 只删除未使用的镜像
docker container prune # 只删除已停止的容器
docker volume prune # 只删除未使用的卷
场景 2:容器网络不通
# 排查步骤:
# 1. 容器是否在运行?
docker ps
# 2. 端口映射是否正确?
docker port <container>
# 3. 容器内能否访问外部网络?
docker exec -it <container> ping 8.8.8.8
# 4. 容器间能否互通?(在同一网络下)
docker network ls
docker network inspect mynetwork
docker exec -it app ping db
📚 下一步
- 下一章:🔒 安全实践 — 文件权限、SSH 密钥、secrets 处理
- 进阶:🔍 故障排查与性能优化 — 日志分析、性能监控、常见故障