一、当COBOL遇见Docker:一场跨越半个世纪的对话

你可能很难想象,一个诞生于1959年的编程语言,会和2013年火起来的容器技术产生交集。但现实往往比小说更精彩——现在全球仍有超过2200亿行COBOL代码在金融、保险和政府系统中运行,而这些"老古董"正迫切需要进行现代化改造。

把COBOL程序塞进Docker容器,就像教你的爷爷用智能手机。刚开始他可能会抗拒,但一旦掌握就真香。比如某银行的核心交易系统,原本需要在AS/400主机上配置复杂的JCL作业,现在通过容器化后,新来的运维小哥用三条命令就能拉起测试环境:

# 基于IBM官方COBOL镜像构建(技术栈:Docker + GNU Cobol)
FROM ibmcom/cobol:latest

# 将主机目录中的COBOL源码拷贝到容器
COPY ./src/* /usr/src/cobol/

# 编译COBOL程序并指定输出
WORKDIR /usr/src/cobol
RUN cobc -x -free -o app PROGRAM1.CBL PROGRAM2.CBL

# 运行时直接执行编译产物
CMD ["./app"]

这个简单的Dockerfile背后藏着几个关键突破点:

  1. -free参数让编译器接受自由格式的代码(传统COBOL对缩进有变态要求)
  2. IBM官方镜像已经预装了GNU Cobol 3.1.2运行时
  3. 通过卷映射可以把容器内的数据文件挂载到宿主机

二、破解COBOL容器化的三大魔咒

魔咒1:文件系统的时空错乱

老COBOL程序最喜欢直接读写/var/data/transaction.dat这种绝对路径。在容器里这简直是个灾难。我们的解决方案是用环境变量动态注入路径:

       IDENTIFICATION DIVISION.          *> 示例COBOL代码(技术栈:Micro Focus COBOL)
       PROGRAM-ID. FILE-HANDLER.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT TRANS-FILE ASSIGN TO WS-FILE-PATH *> 关键改造点
           ORGANIZATION IS SEQUENTIAL.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WS-FILE-PATH   PIC X(100) VALUE SPACES.
       PROCEDURE DIVISION.
           ACCEPT WS-FILE-PATH FROM ENVIRONMENT "DATA_PATH". *> 从环境变量获取路径
           OPEN INPUT TRANS-FILE.
           *> 后续处理逻辑...

然后在docker-compose.yml里这样配置:

services:
  cobol-app:
    environment:
      - DATA_PATH=/mnt/shared/transactions.dat  # 容器外挂载的实际路径
    volumes:
      - ./host_data:/mnt/shared  # 把宿主机目录映射进来

魔咒2:千年虫的容器版——时间处理

很多COBOL程序还在用YYYYMMDD格式处理日期。在容器里更麻烦的是时区问题。我们得在Dockerfile里预先设置:

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

魔咒3:批处理作业的调度难题

传统COBOL的批处理作业依赖cron,但在容器里最好改用Kubernetes的CronJob:

# K8s调度COBOL批处理作业(技术栈:Kubernetes)
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cobol-nightly
spec:
  schedule: "0 3 * * *"  # 每天凌晨3点执行
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cobol-runner
            image: my-cobol-app:v1.2
            command: ["/usr/src/cobol/run_batch.sh"]
          restartPolicy: OnFailure

三、实战:给80年代的库存系统做微整容

假设我们有个1987年写的库存管理系统,原始架构是这样的:

磁带库 -> COBOL批处理程序 -> 3270终端

改造后的容器化架构:

FTP服务 -> Docker化的COBOL转换器 -> REST API -> Web前端

关键改造代码示例:

       IDENTIFICATION DIVISION.       *> 混合技术栈:COBOL+OpenAPI
       PROGRAM-ID. INVENTORY-API.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 HTTP-RESPONSE   PIC X(1024) VALUE SPACES.
       PROCEDURE DIVISION.
           ACCEPT HTTP-REQUEST FROM SYSIN   *> 从标准输入接收HTTP请求
           *> 解析JSON请求(需要预装COBOL-JSON解析库)
           JSON PARSE HTTP-REQUEST INTO WS-ITEM-CODE
           *> 调用原有库存查询逻辑
           CALL "LEGACY-INQUIRY" USING WS-ITEM-CODE
           *> 返回JSON格式响应
           DISPLAY '{"status":"OK","qty":' WS-QUANTITY '}'
           STOP RUN.

配套的Docker多阶段构建:

# 第一阶段:编译COBOL程序
FROM gnucobol:4.0 AS builder
COPY ./legacy /usr/src/cobol
RUN cobc -x -free -o inventory legacy/*.CBL

# 第二阶段:构建轻量级运行时
FROM alpine:3.15
COPY --from=builder /usr/src/cobol/inventory /app/
COPY ./json-parser /lib/  # 添加JSON支持库
CMD ["/app/inventory"]

四、容器化COBOL的生存指南

适用场景判断矩阵

特征 适合容器化 不适合容器化
使用ISAM文件系统
依赖3270终端模拟 ⚠️需改造
每天运行<5次的批处理
需要硬件加密狗

性能对比测试数据

在某社保系统的迁移中,我们测得:

  • 传统CICS环境:TPS 1200
  • Docker容器(4核8G):TPS 986
  • K8s Pod(2副本):TPS 1823

虽然单容器性能下降18%,但通过横向扩展反而提升了52%吞吐量。

那些年我们踩过的坑

  1. 文件锁问题:COBOL的EXCLUSIVE OPEN在容器间会冲突,解决方案是用分布式锁(如Redis)
  2. 编码问题:EBCDIC转ASCII时,金额符号¤可能变成奇怪的Unicode字符
  3. 调试噩梦:必须给容器添加--cap-add=SYS_PTRACE才能用COBOL-DEBUGGER

未来演进路线

  1. 第一阶段:简单容器化(1-3个月)
  2. 第二阶段:API化改造(3-6个月)
  3. 第三阶段:云原生重构(6-12个月)

就像给老房子做现代化装修,我们既不能全盘否定推倒重来,也不能固步自封拒绝创新。容器化只是COBOL系统现代化长征路上的第一站,接下来还有服务网格、Serverless等更多风景等着我们去探索。毕竟,能让这些"老爷爷级"的代码继续在数字时代发光发热,不正是我们工程师最大的成就感吗?