書き初めコーディング! Docker + CGI + COBOLな環境を作って温故知新
さて、今年の書き初めコーディングは「温故知新」ということで、古いものと、とても古いものと、最近のものを組み合わせてみました。 というわけで、CGI + COBOL + Dockerという異色組み合わせをしてみました。エンジニアは「最新の技術」ではなく「最適な技術」を押さえる必要があるので、どっちも使ってみないとです。
とりわけ「CGI」は忘れ去られた便利仕様だと思っています。「CGI」と聞くと多くの人は「Perl」「PHPの前の技術」「RailsとかJavaServletのことでしょ?」と色々なイメージがあると思います。 Webアプリケーション黎明期を支えた技術であり、今となってはPHPやRails、JavaEEに取って代わられた技術です。
しかし、「CGI=Perl」というわけではありません。CGIは「Common Gateway Interface」。その名の通り、Apacheと外部システムを連携させる「共通仕様」であり、 標準入力と標準出力を備えた言語...いえ、実行可能モジュールなら、なんでも動きます。Perlはもちろんですし、JavaやRuby, あるいはC言語やBash、そしてCOBOLおばあちゃんだって大丈夫です。
単なる標準入出力のフックなので、JNIとかより圧倒的に手軽で、LinuxコマンドにWeb I/Fを付けるなど、多くの場面で役立ちます。
そして、今回のターゲットは古代言語の代表格「COBOL」! Linux実装にはOpenCOBOL(現GnuCOBOL)を使います。 さらに、これらをDockerに包んでどこでも使えるポータブルなCOBOLのREST環境を作ります。
まずはこちらに出来たものが。
環境としては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に詳しい方なら「あれ?」と思ったはずです。 そう、このCOBOLはCGI仕様ではありません! (ぉ
ここで、少し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!