部署与自动化

容器化最实际的好处不是「时髦」,而是把「环境怎么装的」这件事写成文件,换台机器照样跑起来。这篇用一个真实的 Typecho 站点做例子。

目录结构

先把数据目录规划好,容器删了数据也还在:

/opt/typecho/
├── docker-compose.yml
├── data/            # 站点文件(含 usr/uploads)
└── mysql/           # 数据库文件

compose 文件

services:
  db:
    image: mysql:5.7
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: typecho
      MYSQL_USER: typecho
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./mysql:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
      interval: 10s
      retries: 10

  web:
    image: php:8.2-fpm-alpine
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - ./data:/var/www/html
    environment:
      TZ: Asia/Shanghai

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    depends_on: [web]
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./data:/var/www/html:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

密码放在同目录的 .env 里,并加进 .gitignore

两个必须注意的点

1. 别把端口直接暴露到公网。 上面 bind 到 127.0.0.1:8080,由宿主机上的 Nginx 反向代理对外。这样数据库和 PHP 都拿不到公网入口。

2. 时区要对齐。 容器默认 UTC,Typecho 的时间会差 8 小时。两个办法任选:给容器加 TZ 环境变量,或者让站点时区与容器一致。

常用运维命令

docker compose up -d            # 启动
docker compose logs -f web      # 看日志
docker compose exec web sh      # 进容器
docker compose pull && docker compose up -d   # 升级镜像

备份要一起改

容器化之后,备份对象变成两个目录:./data(站点文件)和 ./mysql(数据库)。如果只备份了数据库,恢复时会发现附件全丢——因为 uploads 在文件目录里。

tar -czf typecho-$(date +%F).tar.gz ./data ./mysql

小结

容器化真正解决的是「可复现」。把 compose 文件和 .env 一起存进版本库,下次换服务器时,docker compose up -d 就够了。

参与讨论