跳到主要内容

🐳 容器与虚拟化

容器是现代开发的核心技能。无论你做哪类开发,大概率会用到 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?

特性DockerPodman
守护进程需要 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

📚 下一步