将 Yii 应用部署到 Docker Swarm 和 Traefik
本指南将引导你从空白服务器开始,使用 Traefik 作为反向代理,从容器注册表(Forgejo 或 Gitea)部署 Yii 应用程序到 Docker Swarm。
graph LR
A[Internet] --> B[Reverse Proxy: Traefik]
B --> C[app1.example.com]
B --> D[app2.example.com]
subgraph Docker Swarm Cluster
B
C
D
end前提条件
- 一台全新安装 Linux 发行版的服务器(推荐 Ubuntu 22.04 LTS 或更高版本)
- 指向服务器 IP 地址的域名
- 对服务器的 SSH 访问权限
- Docker 和命令行工具的基础知识
服务器准备
安装 Docker
有关安装说明,请参阅官方 Docker 文档。
初始化 Docker Swarm
将服务器初始化为 Docker Swarm 管理器:
docker swarm init --advertise-addr <YOUR_SERVER_IP>将 <YOUR_SERVER_IP> 替换为服务器的公共 IP 地址。
设置反向代理网络
创建一个专用的覆盖网络,供反向代理与服务通信:
docker network create --driver=overlay reverse_proxy_public设置 Traefik 作为反向代理
要将 Traefik 部署为反向代理,请创建文件 traefik-stack.yml:
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "traefik_certificates:/letsencrypt"
networks:
- reverse_proxy_public
deploy:
placement:
constraints:
- node.role == manager
volumes:
traefik_certificates:
networks:
reverse_proxy_public:
external: true部署 Traefik:
docker stack deploy -c traefik-stack.yml traefik设置容器注册表
你需要一个容器注册表来存储 Docker 镜像。选择以下选项之一。
选项 1:使用 Forgejo
要部署 Forgejo,请创建文件 forgejo-stack.yml:
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.21
ports:
- "3000:3000"
volumes:
- forgejo_data:/data
networks:
- reverse_proxy_public
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`git.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=3000"
volumes:
forgejo_data:
networks:
reverse_proxy_public:
external: true部署 Forgejo:
docker stack deploy -c forgejo-stack.yml forgejo将 git.example.com 替换为所需的子域名。
部署后,访问 https://git.example.com 上的 Forgejo 并完成初始设置。确保在设置中启用容器注册表。
选项 2:使用 Gitea
要部署 Gitea,请创建文件 gitea-stack.yml:
services:
gitea:
image: gitea/gitea:latest
ports:
- "3000:3000"
volumes:
- gitea_data:/data
networks:
- reverse_proxy_public
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`git.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=3000"
volumes:
gitea_data:
networks:
reverse_proxy_public:
external: true部署 Gitea:
docker stack deploy -c gitea-stack.yml gitea将 git.example.com 替换为所需的子域名。
部署后,访问 https://git.example.com 上的 Gitea 并完成初始设置。确保在设置中启用容器注册表。
配置 Yii 应用程序
更新 Makefile 配置
Yii 应用程序模板包含一个带有部署命令的 Makefile。更新项目中的 docker/.env 文件:
STACK_NAME=myapp
#
# Production
#
PROD_HOST=app.example.com
PROD_SSH="ssh://user@your-server-ip"
IMAGE=git.example.com/username/myapp
IMAGE_TAG=latest替换以下值:- STACK_NAME:应用程序堆栈的唯一名称 - PROD_HOST:应用程序可访问的域名 - PROD_SSH:到服务器的 SSH 连接字符串(格式:ssh://user@host)- IMAGE:注册表中容器镜像的完整路径 - IMAGE_TAG:镜像标签,通常为 latest 或版本号
配置生产环境
使用生产环境变量更新 docker/prod/.env:
APP_ENV=prod
YII_DEBUG=false
YII_ENV=prod
# Database configuration
DB_HOST=db
DB_NAME=myapp
DB_USER=myapp
DB_PASSWORD=secure_password_here
# Add other environment-specific variablesWARNING
切勿将敏感凭据提交到版本控制。对敏感值使用 docker/prod/override.env 并将其添加到 .gitignore。
查看生产环境 Docker Compose 配置
使用 Traefik 时,你需要修改 Yii 应用程序的 docker/prod/compose.yml 以使用 Traefik 标签而不是 Caddy 标签:
services:
app:
image: ${IMAGE}:${IMAGE_TAG}
networks:
- reverse_proxy_public
volumes:
- runtime:/app/runtime
- caddy_data:/data
- caddy_config:/config
env_file:
- path: ./prod/.env
- path: ./prod/override.env
required: false\
environment:
CADDY_EXTRA_CONFIG: 'auto_https off'
deploy:
replicas: 2
update_config:
delay: 10s
parallelism: 1
order: start-first
failure_action: rollback
monitor: 10s
rollback_config:
parallelism: 0
order: stop-first
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`${PROD_HOST:-app.example.com}`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=80"此配置:
- 运行 2 个副本以实现高可用性
- 使用滚动更新策略,失败时自动回滚
- 配置
labels以在反向代理上自动启用 HTTPS - 禁用容器本身获取 HTTPS 证书 因为代理通过 HTTP 与容器通信。即
auto_https off。
如果需要数据库,请将其添加到堆栈中:
services:
app:
# ... existing configuration ...
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- reverse_proxy_public
deploy:
placement:
constraints:
- node.role == manager
secrets:
- db_password
volumes:
runtime:
db_data:
secrets:
db_password:
external: true在服务器上创建数据库 password secret:
echo "your_secure_password" | docker secret create db_password -构建并推送镜像
在本地机器上设置 Docker 登录
配置 Docker 以向容器注册表进行身份验证:
docker login git.example.com在提示时输入用户名和 password。
构建生产镜像
使用 Makefile 构建生产镜像:
make prod-build这将运行 Makefile 中定义的命令:
docker build --file docker/Dockerfile --target prod --pull -t ${IMAGE}:${IMAGE_TAG} .Dockerfile 使用多阶段构建:1. 在构建器阶段安装 Composer 依赖项 2. 创建仅包含必要文件的最小生产镜像 3. 以非 root 用户(www-data)身份运行
将镜像推送到注册表
将构建的镜像推送到容器注册表:
make prod-push这将执行:
docker push ${IMAGE}:${IMAGE_TAG}部署到 Docker Swarm
Configure SSH access
Set up SSH key-based authentication to your server:
# Generate SSH key (if you don't have one)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Copy the key to your server
ssh-copy-id user@your-server-ip
# Add the SSH host to your SSH config (~/.ssh/config)
cat >> ~/.ssh/config << EOF
Host docker-web
HostName your-server-ip
User user
IdentityFile ~/.ssh/id_ed25519
EOF设置 Docker 上下文
创建用于远程部署的 Docker 上下文:
docker context create swarm-prod --docker "host=ssh://docker-web"或者,配置 DOCKER_HOST 环境变量:
export DOCKER_HOST=ssh://docker-web部署应用程序
将应用程序堆栈部署到 Docker Swarm:
make prod-deploy这将执行:
docker -H ${PROD_SSH} stack deploy --prune --detach=false --with-registry-auth -c docker/compose.yml -c docker/prod/compose.yml ${STACK_NAME}--with-registry-auth 标志确保 Swarm 节点可以从私有注册表拉取镜像。
验证部署
检查服务状态:
docker -H ssh://docker-web service ls
docker -H ssh://docker-web service ps ${STACK_NAME}_app查看日志:
docker -H ssh://docker-web service logs ${STACK_NAME}_app监控和维护
查看服务日志
# View all logs
docker -H ssh://docker-web service logs -f ${STACK_NAME}_app
# View logs from the last 100 lines
docker -H ssh://docker-web service logs --tail 100 ${STACK_NAME}_app
# View logs with timestamps
docker -H ssh://docker-web service logs -t ${STACK_NAME}_app扩展应用程序
调整副本数量:
docker -H ssh://docker-web service scale ${STACK_NAME}_app=3或者更新 docker/prod/compose.yml 中的 replicas 值并重新部署。
资源限制
添加资源限制以防止容器消耗所有服务器资源。更新 docker/prod/compose.yml:
services:
app:
# ... existing configuration ...
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M安全注意事项
对敏感数据使用 Docker secrets
对于敏感信息,使用 Docker secrets 而不是环境变量:
# 创建 secrets
echo "database_password" | docker secret create db_password -
echo "api_key" | docker secret create api_key -更新 docker/prod/compose.yml:
services:
app:
secrets:
- db_password
- api_key
secrets:
db_password:
external: true
api_key:
external: true在应用程序中通过 /run/secrets/secret_name 访问 secrets。
设置防火墙
在服务器上配置 UFW(简单防火墙):
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow Docker Swarm ports (if you plan to add more nodes)
sudo ufw allow 2377/tcp
sudo ufw allow 7946/tcp
sudo ufw allow 7946/udp
sudo ufw allow 4789/udp
# Enable the firewall
sudo ufw enable保持系统更新
定期更新服务器和 Docker:
# 更新系统包
sudo apt-get update && sudo apt-get upgrade -y
# 更新 Docker 镜像
docker -H ssh://docker-web service update --image ${IMAGE}:${IMAGE_TAG} ${STACK_NAME}_app故障排除
服务无法启动
检查服务事件和日志:
docker -H ssh://docker-web service ps ${STACK_NAME}_app --no-trunc
docker -H ssh://docker-web service logs ${STACK_NAME}_app常见问题:- 镜像拉取错误:使用 docker -H ssh://docker-web login 验证注册表身份验证 - 端口冲突:确保没有其他服务使用端口 80/443 - 资源约束:使用 docker -H ssh://docker-web node ls 检查可用资源
SSL 证书问题
如果 Traefik 无法获取证书:- 验证 DNS 是否指向服务器 - 检查端口 80 和 443 是否可从互联网访问 - 确保 Let's Encrypt 配置中的电子邮件有效 - 检查日志:docker -H ssh://docker-web service logs traefik_traefik
容器注册表连接问题
测试注册表连接:
# 从本地机器
docker pull git.example.com/username/myapp:latest
# 从服务器
docker -H ssh://docker-web pull git.example.com/username/myapp:latest总结
你已成功将 Yii 应用程序部署到 Docker Swarm,包括:- 容器注册表(Forgejo 或 Gitea)- 通过 Traefik 自动启用 HTTPS - 通过滚动更新实现零停机部署 - 通过多个副本实现高可用性
Makefile 命令简化了部署工作流程:- make prod-build - 构建生产镜像 - make prod-push - 推送到注册表 - make prod-deploy - 部署到 Docker Swarm
有关更多信息,请参阅:- Yii 应用程序模板 - Docker Swarm 文档 - Traefik 文档