从‘它怎么又挂了?’到‘服务稳如狗’:我是如何用Docker给老旧Python项目续命的

凌晨三点,手机警报第7次响起。服务器监控显示那个祖传的Python 2.7项目又双叒叕崩溃了——这次是因为某台机器偷偷升级了openssl库。揉着通红的眼睛,我意识到是时候终结这场持续两年的噩梦了。下面记录的这个改造方案,最终让这个日均崩溃3次的老系统实现了连续180天无故障运行。

1. 解剖老项目的"薛定谔式运行"

那个用着Flask 0.10.1和MySQL-python 1.2.3的项目就像个玄学装置:在我MacBook上跑得好好的,一到CentOS 6服务器就随机崩溃。经过两周的"考古",我整理出这些致命伤:

  • 依赖地狱:requirements.txt里赫然写着numpy>=1.8,<2.0这样的宽泛约束
  • 系统级陷阱:
    # 不同环境下的libssl差异
    $ openssl version
    # 开发机: OpenSSL 1.1.1  11 Sep 2018
    # 生产机: OpenSSL 1.0.1e-fips 11 Feb 2013
    
  • 隐式假设:代码里到处是/home/old_developer/.cache/这样的绝对路径

关键发现:90%的"随机"崩溃都源于环境差异,而非代码逻辑本身

2. 制作时间胶囊:Dockerfile的精准复刻

要让这个2015年的项目焕发新生,Docker化的核心思路不是升级,而是精确克隆原始环境。这是最终奏效的Dockerfile关键部分:

FROM python:2.7.18-slim-stretch  # 锁定Debian 9基础镜像

# 还原原始系统环境
RUN apt-get update && apt-get install -y \
    libssl1.0.0=1.0.2u-1~deb9u4 \
    libmysqlclient20=5.7.33-0debian9.1

# 精确安装依赖(通过pip freeze原始环境)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
    && pip freeze > frozen_requirements.txt  # 生成精确版本锁

# 处理文件权限问题
RUN useradd -ms /bin/bash old_app \
    && mkdir -p /var/log/old_app \
    && chown old_app:old_app /var/log/old_app

这个配置解决了几个关键问题:

问题类型传统方案缺陷Docker方案优势
依赖冲突全局安装导致版本污染隔离环境精准还原
系统库差异需要手动编译旧版本声明式安装特定deb包
文件权限依赖部署人员手工配置容器内预置正确权限

3. 那些教科书不会告诉你的实战坑位

在让容器稳定运行的过程中,我遇到了这些"惊喜",分享解决方案供大家避雷:

时区问题:老代码里大量使用time.localtime(),但容器默认UTC时区

ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime

日志丢失之谜:原项目用RotatingFileHandler写日志,但容器重启就消失

# 改造方案:改用logging.StreamHandler
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
))
app.logger.addHandler(handler)

内存泄漏应对:老代码存在未关闭的数据库连接

# 在docker-compose.yml中添加健康检查
healthcheck:
  test: ["CMD", "python", "check_connections.py"]
  interval: 30s
  timeout: 5s
  retries: 3

4. 从救火到预防:建立容器化工作流

完成基础容器化后,我建立了这些保障措施:

  • 分层构建优化:

    # 基础层(极少变更)
    FROM python:2.7.18-slim-stretch AS base
    RUN apt-get update && apt-get install -y ...
    
    # 依赖层(变更较少)
    FROM base AS deps
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    
    # 应用层(频繁变更)
    FROM deps
    COPY . .
    
  • 黄金镜像管理:

    # 使用Harbor搭建私有镜像仓库
    docker tag old_app:1.0 registry.internal/legacy/old_app:1.0.20230701
    docker push registry.internal/legacy/old_app:1.0.20230701
    
  • 渐进式迁移方案:

    1. 先用Docker跑cron任务
    2. 迁移非核心接口
    3. 最后处理支付等核心业务

现在当新人问起这个项目,我终于可以笑着说:"去拉取legacy/old_app镜像吧,保证和你看到的历史崩溃报告不是同一个项目。" 这个经历让我明白,对待遗留系统,有时候最好的升级就是不升级——用容器技术把它完美封存起来。

更多推荐