跳转至

历史应用迁移指南:Docker & Python

本指南用于指导如何将存量的旧版 Docker 应用和 Python 脚本/服务迁移到新的 Hybrid Mesh (Swarm + Tailscale) 架构中。


1. 迁移核心策略

在旧架构中,应用可能以“单机 Docker Compose”甚至“宿主机 Python 进程”的方式运行。迁移到新架构(伦敦 Swarm 集群)时,需要遵循以下原则:

维度 旧模式 (Legacy) 新模式 (Hybrid Mesh) 改造动作
运行方式 docker-compose uppython 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 关键检查点

  1. 移除 ports: 除非你需要 UDP 或非 HTTP 协议,否则不要映射端口到宿主机,全部通过 Traefik 路由。
  2. 数据目录规范: volumes 挂载必须写绝对路径,统一放在 /opt/infra/stacks/<stack-name>/ 下(如 /opt/infra/stacks/app-payment/logs),Swarm 无法解析相对路径 ./
  3. 约束 (Placement):
    • 无状态 (Stateless): 随意调度,replicas: 2
    • 有状态 (Stateful): 必须锁定节点!添加 constraints: [node.hostname == london3],否则重启后漂移到另一台机器,数据就找不到了。

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

  1. 访问 Portainer: 登录 https://portainer.120224.xyz
  2. 选择环境: 点击 London-Swarm (或目标 Swarm 集群)
  3. 新建 Stack:
    • 导航: Stacks → Add Stack
    • Name: app-payment (遵循 app- 前缀命名规范)
    • Build Method: Web Editor
    • 粘贴上述转换后的 Compose 内容
  4. 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