サーバーレス COBOL on AWS Lambda

Serverless Advent Calendar 2016 の 24 日目です.

突然ですが COBOL ってご存知でしょうか? Wikipedia によると「common business oriented language = 共通事務処理用言語」の略称らしく、1959年に開発されて以来脈々と仕様が更新されながら現在に至る歴史あるプログラミング言語です. この名前を聞いて目頭が熱くなる方々も数多くいらっしゃるのではないでしょうか.

現在でも COBOL で書かれたプログラムが世界中で数多く動作しているのは有名な話ですが、同 Wikipedia ページによるとガートナー社のレポートでメインフレームやレガシーシステム上で 2000 億行の COBOL コードが存在し、年に 50 億行のペースで増えていると報告されているとかいないとか. 特に金融や保険といったようなお金を取り扱う業界においては今でも現役で当たり前のように動いており、もしかしたらあなたの給与振込を実現しているバッチ処理は数十年前に COBOL で書かれたものかもしれません.

さて、誕生から 50 年以上経った今でも我々の生活を影で支えている偉大な COBOL ですが、正直現代の Web 周りのナウでヤングなエンジニアたちからは忘れられている/知られていないような気がします. 本記事は COBOL と昨今話題のサーバーレスを強引に紐づけることで、皆の目を再び COBOL に向けようじゃないか、という狙いを持っています.

実際は飲み会で話題になって面白かったからやってるだけですが.

以下、COBOL ソースが AWS Lambda 上で動くようになるまでの紆余曲折をざっくりまとめます.

なお、成果物はこちらのリポジトリ toricls/cobolambda にあります. ビルドからデプロイ、実行までできるものをまとめてあり、デプロイは今をときめく AWS SAM を利用しています. 21世紀な COBOL ワールドを体感したい方、記事を読むのは面倒だけど COBOL のことは好きな方、ぜひご覧ください.

1st try: シングルバイナリ

Go 言語で書かれたプログラムを AWS Lambda で動かす、という話をよく見ますが、手始めにそれと同じことに挑戦してみました. 要はバイナリ作って Lambda スクリプトと一緒に zip に突っ込んで、実行時に child_process.spawn とかでバイナリをキックするっていう感じです.

が、昔 COBOL を書いてた時は全然意識してなかったんですが、COBOL って結構たくさんのライブラリに依存してるんですね. 強引に同梱することも考えましたがライセンスのことを考えるのがめんどくさくなったので断念しました. ちなみに利用した COBOL コンパイラは GnuCOBOL (旧 OpenCOBOL) です. NetCOBOL とか Pro*COBOL を期待された方、すみません.

2nd try: IonicaBizau/node-cobol

シングルバイナリを諦め、飛び道具を探してみました.

最初に見つけたのが IonicaBizau/node-cobol - COBOL bridge for NodeJS which allows you to run COBOL code from NodeJS. というライブラリです.

これを利用すると

var Cobol = require("cobol");
Cobol(function () {/*
                   IDENTIFICATION DIVISION.
                   PROGRAM-ID. HELLO.
                   ENVIRONMENT DIVISION.
                   DATA DIVISION.
                   PROCEDURE DIVISION.
                   PROGRAM-BEGIN.
                   DISPLAY "Hello world".
                   PROGRAM-DONE.
                   STOP RUN.
                   */}, function (err, data) {
    console.log(err || data);
});
// => "Hello World"

みたいな感じで COBOL ソースが Node.js 上で実行できるようになります.

かっこいい.

しかし、このライブラリは GnuCOBOL が実行環境に存在していることを要求しているため、AWS Lambda を使ったサーバーレス案件向きではなく、こちらも断念.

ちなみにこのプロジェクトの README にある通り、Node.js 上で COBOL コンパイラを実装してそれを使う、という非常に野心的な青写真もあるようです. 2017年、目が離せないライブラリの一つですね.

3rd try: ajlopez/CobolScript

見つけてしまいました.

ajlopez/CobolScript - COBOL language compiler to JavaScript

このライブラリが、まさに探していたものに違いありません.

README によれば

var cobolscript = require('cobolscript');
var program = cobolscript.compileProgramFile('./hello.cob');
program.run(cobolscript.getRuntime());

たったこれだけで COBOL が js にトランスパイルされ、Node.js 上で実行できてしまいます.

「COBOL to JavaScript」という圧倒的未来感. メインフレームの世界で活躍を続ける名エンジニアたちが Web 界に殴り込んでくる日も近いかもしれません.

以下、このライブラリを使って実現した COBOL on AWS Lambda について簡単に説明します.

構成 & 実装

toricls/cobolambda をデプロイするとこんな感じの構成が出来上がります.

Overview

API Gateway はなくても良かったんですが、curl コマンド一発で試せるお手軽感が欲しかったので追加しました.

$ curl https://<your-rest-api-id>.execute-api.<your-aws-region>.amazonaws.com/Prod/cobolambda

を叩くと

Hello Serverless World!

って返ってくる感じです.

COBOL はこんなソース

こんな感じです.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COBOLAMBDA.
       ENVIRONMENT DIVISION.
       DATA DIVISION.
       PROCEDURE DIVISION.
           DISPLAY "Hello Serverless World!".

“Hello Serverless World!” と出力するだけのコードです.

非常にシンプルですが、まずは動くことが大事です.

(おなじみの STOP RUN. が最後にいない理由は後述しますね)

Lambda ファンクションはこんなソース

'use strict'

let cobolscript = require('cobolscript')
let SCRIPT = './serverless.cbl'

const hookConsoleLog = (logs) => {
    console.log = (d) => logs.push(d)
}

exports.handler = (event, context, callback) => {
    var logs = []
    hookConsoleLog(logs)

    var program = cobolscript.compileProgramFile(SCRIPT)
    program.run(cobolscript.getRuntime())
    callback(null, { statusCode: 200, body: logs.join('\n') })
}

./serverless.cbl を読み込んでトランスパイルし、実行、という感じです.

hookConsoleLog てなに?

ajlopez/CobolScript は COBOL の DISPLAY コマンドを console.log にトランスパイルするという実装になっています. 今回は COBOL からの出力を API Gateway に返せるようにしたかったので、hookConsoleLog 関数を書いて console.log への出力をフックし、最終的に callback の引数として渡せるようにしました. まずは動くことが大事です.

STOP RUN が COBOL ソースにないのなんで?

STOP RUN は同ライブラリによって process.exit(0) にトランスパイルされます. お察しの通り、こいつが含まれていると Lambda ファンクションが意志半ばで終了してしまい、Lambda でおなじみの Process exited before completing request エラーが出ます. というわけで今回は STOP RUN を書かないという選択をしました. まずは動くことが大事ですからね.

ajlopez/CobolScript

夢に溢れる ajlopez/CobolScript ですが、プロジェクトの説明文にある通りまだまだ開発途上というステータスのようです. いわゆる1枚のソースファイルに数十万行とかあるレジェンド級の COBOL たちと戦うにはまだまだといった感じですが、DISPLAY コマンド以外にも既に対応済みのコマンドは色々とあるようなので、ちょっとした分岐や繰り返し、計算処理程度なら十分動きそうです.

久々に COBOL を書きたくなったら

この記事を読んで久々に COBOL を書きたくなったあなたに朗報です. cobolambda リポジトリの Dockerfile をビルドすると一発であなたのマシンに COBOL 環境が作れます. ご自身でビルドしなくても Docker Hub にプッシュ済みのイメージをご利用いただくことも可能です. 以下のような感じでどうぞ.

$ docker run -it --rm -v $(pwd):/src toricls/gnucobol:latest bash
root@xxxxxxxxxxxx:/src# cobc --help

Docker はちょっと… という方も、もしあなたの手元のマシンが macOS なのであれば、Homebrew を使って brew install open-cobol でインストールすることもできます. ぜひお試しください.

COBOL を勉強したくなったら

この記事を読んで COBOL を勉強したくなったあなたに朗報です. ドットインストールさんが COBOL入門(全16回) を用意してくれているので、今日から勉強を始められます. ちなみに Indeed さんによれば、記事執筆時点の COBOL Developer の求人平均年収は $88,000 らしいです.

まとめ

この仕組みを使えば異常にスケーラブルな COBOL アプリケーションを書けますね. 異常にスケーラブルな COBOL 帳票出力システム on AWS Lambda とかを誰かがやり始めてもおかしくありません.

ちなみに “COBOL 2002” という仕様からはオブジェクト指向もいけるそうです. すごいぞ COBOL.

リンク