历史应用迁移指南:Docker & Python¶
本指南用于指导如何将存量的旧版 Docker 应用和 Python 脚本/服务迁移到新的 Hybrid Mesh (Swarm + Tailscale) 架构中。
1. 迁移核心策略¶
在旧架构中,应用可能以“单机 Docker Compose”甚至“宿主机 Python 进程”的方式运行。迁移到新架构(伦敦 Swarm 集群)时,需要遵循以下原则:
| 维度 | 旧模式 (Legacy) | 新模式 (Hybrid Mesh) | 改造动作 |
|---|---|---|---|
| 运行方式 | docker-compose up 或 python main.py |
Portainer Stack | 编写 Compose 文件,通过 Portainer 界面部署 |
| 网络暴露 | ports: - 8080:80 (直接映射) |
networks: - proxy-uk (内网路由) |
移除端口映射,添加 Traefik 标签 |
| 可访问性 | IP:Port 直连 | https://app.domain.com |
配置 Traefik Host 规则 |
| 持久化 | 本地路径 bind mount (./data:/data) |
命名卷或绝对路径绑定 | 确保路径在目标节点存在,或使用 NFS |
| 高可用 | 单点故障 | 多副本 (Replicas) | 无状态应用开启多副本 |
2. Python 应用容器化改造¶
如果您的 Python 脚本之前是直接在服务器上运行的 (nohup python app.py &),首先需要将其容器化。
2.1 标准 Dockerfile 模板¶
在项目根目录创建 Dockerfile:
# 使用官方轻量级镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量 (防止生成 pyc 文件,并在日志中实时输出)
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# 复制依赖文件并安装 (利用缓存层)
COPY requirements.txt .
# 如需国内源可加上: -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install --no-cache-dir -r requirements.txt
# 复制业务代码
COPY . .
# 非 Root 用户运行 (可选,增强安全性)
# RUN useradd -m myuser
# USER myuser
# 启动命令
# 生产环境建议使用 gunicorn/uvicorn 而非 python manage.py runserver
CMD ["python", "app.py"]
2.2 环境变量外置¶
修改代码,不要在代码中硬编码所有配置(如数据库密码、API Key)。
旧代码 (Bad):
db_password = "my_secret_password"
新代码 (Good):
import os
db_password = os.getenv("DB_PASSWORD", "default_pass")
3. Docker Compose 转换 (Stack 规范)¶
将旧的 docker-compose.yml 转换为适配 Swarm 的 Stack 文件。
3.1 转换对照表¶
假设我们有一个名为 payment-service 的旧应用。
❌ 旧版 (docker-compose.yml)¶
version: '3'
services:
payment:
image: my-python-app:v1
restart: always
ports:
- "5000:5000" # 直接暴露端口
volumes:
- ./logs:/app/logs # 相对路径
✅ 新版 (stack-deploy.yml)¶
version: '3.8'
services:
payment:
image: registry.gitlab.com/me/my-python-app:v1 # 建议使用私有镜像库
# restart: always <-- Swarm 中由 deploy.restart_policy 接管,可省略
# 环境变量 (推荐使用 configs/secrets,此处仅为示例)
environment:
- DB_PASSWORD=xxxx
deploy:
replicas: 2 # 双副本高可用
placement:
constraints: [node.role == worker] # 调度到工作节点
restart_policy:
condition: on-failure
# Traefik 自动发现配置
labels:
- "traefik.enable=true"
- "traefik.http.routers.payment.rule=Host(`pay.example.com`)"
- "traefik.http.routers.payment.entrypoints=websecure"
- "traefik.http.routers.payment.tls.certresolver=letsencrypt"
- "traefik.http.services.payment.loadbalancer.server.port=5000" # 容器内部端口
networks:
- proxy-uk # 加入网关网络
- default # 这个是 stack 内部网络
volumes:
# 路径规范: /opt/infra/stacks/<stack-name>/<子目录>
- /opt/infra/stacks/app-payment/logs:/app/logs
networks:
proxy-uk:
external: true
3.2 关键检查点¶
- 移除
ports: 除非你需要 UDP 或非 HTTP 协议,否则不要映射端口到宿主机,全部通过 Traefik 路由。 - 数据目录规范:
volumes挂载必须写绝对路径,统一放在/opt/infra/stacks/<stack-name>/下(如/opt/infra/stacks/app-payment/logs),Swarm 无法解析相对路径./。 - 约束 (Placement):
- 无状态 (Stateless): 随意调度,
replicas: 2。 - 有状态 (Stateful): 必须锁定节点!添加
constraints: [node.hostname == london3],否则重启后漂移到另一台机器,数据就找不到了。
- 无状态 (Stateless): 随意调度,
4. 迁移操作流程¶
Step 1: 镜像推送¶
将本地构建的镜像推送到镜像仓库(Docker Hub 或 私有 Registry)。Swarm 的不同节点需要能拉取到同一个镜像。
docker build -t my-app:v1 .
docker tag my-app:v1 my-registry/my-app:v1
docker push my-registry/my-app:v1
Step 2: 准备数据目录¶
在目标服务器(如 london3)上创建持久化目录,路径需遵循 Stack 命名规范。
# 格式: /opt/infra/stacks/<stack-name>/<子目录>
mkdir -p /opt/infra/stacks/app-payment/logs
Step 3: 通过 Portainer 部署 Stack¶
- 访问 Portainer: 登录
https://portainer.120224.xyz - 选择环境: 点击
London-Swarm(或目标 Swarm 集群) - 新建 Stack:
- 导航: Stacks → Add Stack
- Name:
app-payment(遵循app-前缀命名规范) - Build Method: Web Editor
- 粘贴上述转换后的 Compose 内容
- Deploy: 点击 "Deploy the stack"
5. 常见问题 (FAQ)¶
Q1: 我的 Python 脚本需要一直运行且没有 HTTP 端口,怎么办?¶
A: 不需要配置 Traefik 标签。直接部署为一个后台服务即可。
如果需要监控它是否存活,可编写简单的 Healthcheck 或集成 Beszel 监控。
Q2: 多个容器之间如何通信?¶
A: 在同一个 Stack 内的服务,直接通过 服务名 访问(例如 redis:6379)。
如果跨 Stack,确保它们都加入了同一个 Overlay 网络(如 proxy-uk 或自定义的 shared-net)。
Q3: 数据库迁移怎么办?¶
A:
1. 停止旧数据库容器。
2. 将数据文件 (/var/lib/mysql) 打包 tar。
3. SCP 传输到新节点对应目录。
4. 部署新 Stack,挂载该目录。
5. 启动并验证数据完整性。
6. 附录:旧环境清理 (Uninstall)¶
在完成迁移并确认新服务运行正常后,需要彻底清理旧环境的残留,以释放资源。
6.1 定位旧项目目录¶
如果忘记了旧应用部署在哪里,可以通过正在运行的容器反向查找:
# 查看工作目录
docker inspect <container_name> | grep "com.docker.compose.project.working_dir"
# 或查看挂载源
docker inspect <container_name> | grep Source
6.2 优雅卸载 (推荐)¶
进入找到的项目目录,执行标准卸载命令:
cd /path/to/old/project
# 停止并移除容器、网络
docker compose down
# [可选] 同时移除数据卷 (彻底删除数据)
docker compose down -v
6.3 暴力清理 (强制)¶
如果找不到目录或 Compose 文件丢失,可直接通过 Docker 命令清理:
1. 停止容器: docker rm -f <container_id>
2. 清理遗留卷: docker volume ls 找到相关卷 (如 project_db_data) 并 docker volume rm <volume_name>。
3. 清理悬空镜像: docker image prune -a。