容器化最实际的好处不是「时髦」,而是把「环境怎么装的」这件事写成文件,换台机器照样跑起来。这篇用一个真实的 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 就够了。