書き初めコーディング! Docker + CGI + COBOLな環境を作って温故知新

さて、今年の書き初めコーディングは「温故知新」ということで、古いものと、とても古いものと、最近のものを組み合わせてみました。 というわけで、CGI + COBOL + Dockerという異色組み合わせをしてみました。エンジニアは「最新の技術」ではなく「最適な技術」を押さえる必要があるので、どっちも使ってみないとです。

とりわけ「CGI」は忘れ去られた便利仕様だと思っています。「CGI」と聞くと多くの人は「Perl」「PHPの前の技術」「RailsとかJavaServletのことでしょ?」と色々なイメージがあると思います。 Webアプリケーション黎明期を支えた技術であり、今となってはPHPRailsJavaEEに取って代わられた技術です。

しかし、「CGI=Perl」というわけではありません。CGIは「Common Gateway Interface」。その名の通り、Apacheと外部システムを連携させる「共通仕様」であり、 標準入力と標準出力を備えた言語...いえ、実行可能モジュールなら、なんでも動きます。Perlはもちろんですし、JavaRuby, あるいはC言語Bash、そしてCOBOLおばあちゃんだって大丈夫です。

単なる標準入出力のフックなので、JNIとかより圧倒的に手軽で、LinuxコマンドにWeb I/Fを付けるなど、多くの場面で役立ちます。

そして、今回のターゲットは古代言語の代表格「COBOL」! Linux実装にはOpenCOBOL(現GnuCOBOL)を使います。 さらに、これらをDockerに包んでどこでも使えるポータブルなCOBOLのREST環境を作ります。

まずはこちらに出来たものが。

github.com

環境としてはDocker Toolboxが入ってることを前提としています。Windowsで利用する場合はVolume周りを修正してください。

まずは単純にCOBOLのHello Woldを実行してみましょう。

000100* HELLO.COB GNU Cobol FAQ example
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. hello.
000310 DATA DIVISION.
000320 WORKING-STORAGE SECTION.
000400 PROCEDURE DIVISION.
000500     DISPLAY "Hello, World!".
000600     STOP RUN.

ことごとく最近はやりの言語と語彙が被ってませんね! とは言え読めばわかる通り「DISPLAY」が表示です。あとは全部オマジナイです(ぉ

実行はdockerでやります。

$ git clone https://github.com/koduki/example-cobol.git
$ cd example-cobol
$ docker-compose run app cobc -x -o bin/HELLO ./src/cobol/HELLO.COB 
$ docker-compose run app bin/HELLO 
Hello, World!

Dockerはデーモン(サービス)のコンテナとして使うのが通常だとは思いますが、こういうインタラクティブなコマンド実行にも使えるので、その用途も結構便利だったりします。

続いて、CGIとして実行します。

$ docker-compose run app cobc -x -o bin/APP ./src/cobol/APP.COB 
$ docker-compose up

APP.COBはCGI用に書いたCOBOLで下記のような形をしています。見よう見まねで書いてみたので、COBOLっぽくなかったらごめんなさい。

000100 IDENTIFICATION                     DIVISION.
000200 PROGRAM-ID.                        APP.
000300 DATA                               DIVISION.
000400 WORKING-STORAGE                    SECTION.
000500 01 ARG1                            PIC 9(2).
000600 01 ARG2                            PIC 9(2).
000700 01 RESULT                          PIC 9(4).
000800 PROCEDURE                          DIVISION.
000900 ARGS-INPUT                         SECTION.
001000      ACCEPT ARG1 FROM CONSOLE.
001100      ACCEPT ARG2 FROM CONSOLE.
001200 MAIN                               SECTION.
001300      COMPUTE RESULT = ARG1 + ARG2.
001400 JSON-OUTPUT                        SECTION.
001500      DISPLAY "{arg1:" ARG1 ",arg2:" ARG2 ",result:" RESULT "}" .
001600 JSON-FIN                           SECTION.
001700      STOP RUN.

ACCEPTが標準入力です。標準出力にはJSONを返しています。

ここで、CGIに詳しい方なら「あれ?」と思ったはずです。 そう、このCOBOLCGI仕様ではありません! (ぉ

ここで、少しCGIのI/Fを説明します。CGIはかなり単純で、出力が下記フォーマットなら、なんでもHTTPに乗せることが出来ます。

Content-type: text/html # text/planとかもOK
# 改行
# 本文

Content-typeを書いて、改行して、本文を入れるだけ。実にシンプル。なのですが、何故かCOBOLで書いたらうまく動きませんでした... 謎ですが、仕方が無いので、以下のようなBashで包んで、Conetent-type部分を補完しています。

#!/bin/bash

args=($(echo $QUERY_STRING|sed s/\&/" "/g|sed s/=/" "/g))

### cgi
echo "Content-type:application/json"
echo

echo -e "${args[1]}\n${args[3]}" |/app/bin/APP 

単純に、echoでContent-typeと改行を出力しているだけです。 GETパラメータなどはQUERY_STRINGという環境変数に渡されるので、そちらを分解して標準入力としてCOBOLに渡しています。

これで準備は整ったので、ブラウザで下記URLにアクセスしてみましょう。 ※ docker-machine ip が 192.168.99.100 の場合

http://192.168.99.100/app.cgi?arg1=3&arg2=2 

ちゃんと、足し算の結果が返ってきましたね? ちなみに性能としては特にチューニングしなくても1秒あたり100リクエスト程度の性能でした。 コネクション数を増やしても性能が上がらなかったので、Apache側でシリアライズされてる気がしますが、これはちゃんとチューニングすれば問題ないでしょうし、 Linuxコマンドや特殊な言語で書いたプログラムをAPI的に呼びだすだけなら、まあ十分な性能な気もします。

これで、どこでもCOBOLライフを楽しめるようになりましたね! あれ? 全然嬉しくないのはなんでだろう...

ちなみに今回は試しませんでしたがCGIは単純にApacheの1機能として振る舞いますので、HTTP2とかも動かすことができる気がします。 さすがに、Websocketは難しいでしょうけど。こういう既存のノウハウが活かせるところもCGIの良いところですね。

運用系コマンドのWeb I/FにもBashベースで作ればかなり向いてる気がします。

ただし、1点注意があって、社外からアクセスできる場所に置くときには十分に注意してください。Bashがその筆頭ですが、Webで鍛えられてないので脆弱性の塊です。ご利用は計画的に。

というわけで、古い技術の中にも便利なものはあり、新しいものと組み合わせることで更に生きる、ということを胸に今年も頑張りたいと思います!

それでは今年も、Let's Hacking!