Docker 与 Compose 上手:把应用装进盒子里

Docker 的价值对个人站点来说不是「微服务」,而是环境可复现:换台机器、重装系统,docker compose up -d 就能回到原来的状态。

一、安装

bash
# 官方脚本(Debian/Ubuntu)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER   # 免 sudo,需重新登录生效

docker version
docker run --rm hello-world

国内机器拉镜像慢,配镜像加速:

json
{
  "registry-mirrors": ["https://docker.m.daocloud.io"]
}

写入 /etc/docker/daemon.jsonsudo systemctl restart docker

二、最高频的命令

bash
docker ps -a                     # 看容器(不加 -a 只看运行中的)
docker logs -f --tail 100 web     # 实时看日志
docker exec -it web sh            # 进容器
docker stats                      # 实时资源占用

docker images
docker rmi $(docker images -f "dangling=true" -q)   # 清理悬空镜像
docker system prune -a            # 大扫除(会删未使用的镜像,谨慎)

三、Dockerfile 的几个要点

yaml
# 多阶段构建:构建产物只带走需要的东西,镜像能小一个数量级
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

容易踩的点:

  1. 不要用 root 跑(生产环境):USER node
  2. .dockerignore 必须有,否则 node_modules.git 全进镜像;
  3. COPY package*.json ./ 单独一行,能利用缓存层,改代码不重装依赖;
  4. 基础镜像优先 -alpine,体积小很多。

四、一份能直接改的 compose 文件

yaml
services:
  web:
    image: nginx:alpine
    container_name: web
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    networks:
      - app

  app:
    build: ./app
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - DB_HOST=db
    depends_on:
      - db
    networks:
      - app

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_PASSWORD=change_me_please
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - app

volumes:
  pgdata:

networks:
  app:
bash
docker compose up -d         # 后台启动
docker compose ps
docker compose logs -f app
docker compose down          # 停止并移除容器(数据卷保留)
docker compose pull && docker compose up -d   # 更新镜像

容器之间用服务名互相访问,比如 app 连数据库写 db:5432,不要写 127.0.0.1——容器内的 localhost 是它自己。

五、数据持久化:volume 是重点

容器删了数据就没了,所以数据库一定要挂 volume:

bash
docker volume ls
docker volume inspect pgdata

# 备份:起个临时容器把卷打包
docker run --rm -v pgdata:/data -v $(pwd):/backup alpine \
  tar czf /backup/pgdata-$(date +%F).tar.gz -C /data .

六、小内存机器上限制资源

1G 内存的小鸡跑多个容器很容易 OOM,加上限制:

yaml
services:
  app:
    image: myapp
    mem_limit: 256m
    cpus: 0.5
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

最后一条很重要:不限制日志大小,一个跑飞的容器能把磁盘写满。

七、什么时候不用 Docker

说句实话:只跑一个静态站就没必要上 Docker。直接 Nginx 托管,少一层就少一层要维护的东西。

值得用的场景:

  • 需要数据库、缓存、后端多个组件协同;
  • 想在一台机器上跑多个互不干扰的应用;
  • 应用依赖特定运行环境(老版本 Python/Node);
  • 需要频繁换机器、要求环境可复现。