opensource COBOLをAWS Lambdaで動かしてみた

# 概要
AWS LambdaがCOBOLをサポートすると発表し、バズった時からかなり遅れていますが、Lambdaを扱う機会があったため、概要をまとめたいとおもいます。

公式でサポートすると発表されていたCOBOLについては、Lambdaの一般メニューからは選択できず、サポート元のBluAge社に問い合わせないと使えないようなので、先人の知恵を借りて、カスタムランタイムとレイヤーを利用した構築を行います。

今回は、Open COBOLの日本語対応版であるopensource COBOLを利用します。

# COBOLビルド環境
COBOLのビルド環境の作成ですが、オープンCOBOLソリューション部会のdocker hubにてイメージを作成しているため、そちらを利用します。

[dockerイメージ](https://hub.docker.com/r/opensourcecobol/opensource-cobol)

# Lambdaレイヤーの作成
まずはAWS Lambdaのレイヤーを作成する必要があります。
レイヤーの作成には、必要なライブラリを集めてアップロードする必要があります。
ビルドしたCOBOLアプリケーションに対し、lddコマンドで依存性を確認して必要なファイルを選定します。

```
[root@2a4317c788de /]# ldd lambda/cobol/hello.so
        linux-vdso.so.1 =>  (0x00007fff3b5fd000)
        libcob.so.1 => not found
        libm.so.6 => /lib64/libm.so.6 (0x00007f79179a0000)
        libvbisam.so.1 => not found
        libgmp.so.10 => /lib64/libgmp.so.10 (0x00007f7917728000)
        libncurses.so.5 => /lib64/libncurses.so.5 (0x00007f7917501000)
        libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f79172d7000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f79170d3000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f7916d06000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f7917ea5000)
```

上記のファイル群を`lib`に、コンパイラと実行用ファイルである`cobc`および`cobcru`をbinに格納し、レイヤーとしてアップロードします。

# Lambda関数の作成

### bootstrapの作成

カスタムランタイムを利用しているため、bootstrapを公式のサンプルをもとに編集します。

```sh:bootstrap
#!/bin/sh
set -euo pipefail

# Handler format: <cobol program id>.<dummy>
#
# The script file <cobol program id>  must be located at the root of your
# function's deployment package, alongside this bootstrap executable.

while true
do
    # Request the next event from the Lambda runtime
    HEADERS="$(mktemp)"
    EVENT_DATA=$(curl -v -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

    # Execute the COBOL function 
    RESPONSE=$(echo $EVENT_DATA | cobcrun $(echo $_HANDLER | cut -d. -f1))

    # Send the response to Lambda runtime
    curl -v -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response" -d "$RESPONSE"
done
```
ここでは`RESPONSE=`部分を修正し、`cobcrun`にてCOBOLアプリケーションを呼び出すようにしています。
`$_HANDLER`はLambdaの関数で設定する関数名のようなもの。ここでは`hello.handler`が入力される想定としています。

### COBOLプログラム
今回の検証では、実行してレスポンスを返すだけのCOBOLプログラムを作成します。
レスポンス内容は公式サンプルのスクリプトを参考にしています。
また、入力された`EVENT_DATA`が扱えるよう、標準入力を読み込むようにしています。

```cobol:hello.cbl
      ******************************************************************
      *  opensource COBOL and AWS Lambda sample program
      ******************************************************************
       IDENTIFICATION              DIVISION.
      ******************************************************************
       PROGRAM-ID.                 hello.
       AUTHOR.                     nor51010.
       DATE-WRITTEN.               2019-06-11.
      ******************************************************************
       ENVIRONMENT                 DIVISION.
      ******************************************************************
       INPUT-OUTPUT                SECTION.
       FILE-CONTROL.
      ******************************************************************
       DATA                        DIVISION.
      ******************************************************************
       FILE                        SECTION.
      ******************************************************************
       WORKING-STORAGE             SECTION.
       01  HELLO-STR               PIC  X(256).
       01  ACCEPT-STR              PIC  X(128).
       01  SPACE-IDX               PIC  9(005).
      ******************************************************************
       PROCEDURE                   DIVISION.
      ******************************************************************
       MAIN-RTN.
           STRING  '{"statusCode": 200, '         DELIMITED BY SIZE
                   '"body": "Hello from opensource COBOL!", '
                                                  DELIMITED BY SIZE
                   '"input": [;'                  DELIMITED BY SIZE
                   INTO HELLO-STR.
       MAIN-001.
           ACCEPT  ACCEPT-STR FROM CONSOLE.
           PERFORM VARYING SPACE-IDX FROM FUNCTION LENGTH(ACCEPT-STR)
                                     BY   -1
                                     UNTIL SPACE-IDX = 0
              IF   ACCEPT-STR(SPACE-IDX:) NOT = SPACE
                   EXIT  PERFORM
              END-IF
           END-PERFORM.
           STRING  HELLO-STR                      DELIMITED BY ";"
                   ACCEPT-STR(1:SPACE-IDX)        DELIMITED BY SIZE
                   ';'                            DELIMITED BY SIZE
                   INTO HELLO-STR.
       MAIN-002.
           STRING  HELLO-STR                      DELIMITED BY ";"
                   ']}'                           DELIMITED BY SIZE
                   INTO HELLO-STR.
           DISPLAY HELLO-STR.
       MAIN-EXT.
           STOP RUN.
```

# ビルド
Dockerfileを利用して、一括でビルドできるようにします。

以下、今回使用したDockerfileとビルド用のスクリプトです。
dockerhubからイメージを取得し、必要なライブラリを`lambda-oss-cobol-layer.zip`に、関数用のスクリプトとCOBOLアプリケーションを`lambda-oss-cobol-hello.zip`にまとめます。

```dockerfile:Dockerfile
FROM opensourcecobol/opensource-cobol

# add zip command
RUN yum install -y zip

# create lambda layer
RUN mkdir -p /lambda/layer/lib
RUN mkdir -p /lambda/layer/bin
RUN cp /usr/lib/libcob.so.1 \
       /usr/lib/libvbisam.so.1 \
       /lib64/libm.so.6 \
       /usr/lib64/libgmp.so.10 \
       /lib64/libncurses.so.5 \
       /lib64/libtinfo.so.5 \
       /lib64/libdl.so.2 \
       /lib64/libc.so.6 \
       /lib64/ld-linux-x86-64.so.2 \
       /lambda/layer/lib
RUN cp /usr/bin/cobc \
       /usr/bin/cobcrun \
       /lambda/layer/bin
RUN cd /lambda/layer && zip -r /lambda-oss-cobol-layer.zip ./lib ./bin

# create lambda function
RUN mkdir -p /lambda/cobol
COPY bootstrap /lambda/cobol
COPY hello.cbl /lambda/cobol
RUN cd /lambda/cobol && cobc hello.cbl
RUN cd /lambda/cobol && zip /lambda-oss-cobol-hello.zip ./bootstrap ./hello.sh ./hello.so

WORKDIR /

ENTRYPOINT [""]
```

```shell:build.sh
#!/bin/bash
docker build -t lambda-oss-cobol:latest .
docker run -it --rm -v `pwd`:/tmp lambda-oss-cobol cp /lambda-oss-cobol-hello.zip /lambda-oss-cobol-layer.zip /tmp/

```
# AWSへの適用

### レイヤー登録
先ほど作成したZIPファイルからレイヤーを作成します。AWS Lambdaのコンソールからレイヤーの作成にすすみ、ZIPファイルをアップロードします。
![layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/fc3b1b4f-9212-4d88-35c0-1f565b5f4f21.png)

アップロードが完了すると、Layer画面に表示されるため、ここのバージョンARNをメモしておきます。
![layer_new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/fff58656-bfd5-069f-f5a6-9d7452c00010.png)


### 関数の作成
引き続き関数の作成を行っていきます。一から作成でランタイムを、「ユーザー独自のブートストラップを提供する」にします。アクセス権限は必要に応じて設定してください。
![func-new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/aa770a13-e7c6-cb35-dccc-d2ea30cd27e8.png)

関数ができたらレイヤーの追加を行います。DesignerのLayersを選択し、レイヤーの追加を行います。
レイヤーの追加では、先ほどメモしたバージョンARNを入力して追加します。

![func-layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/b523cd86-7599-fb6a-2f3c-b37bfbde6667.png)


続いて、関数コードをアップロードします。Designerの関数名を選択し、コードエントリタイプで「.zipファイルをアップロード」を選択し、先ほど作成した`lambda-oss-cobol-hello.zip`をアップロードします。

![func-cobol.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/08e83be5-6bb0-5ba2-5159-5485d0f94c49.png)

ファイル選択後、保存を実行するとコードが展開します。
![func-boot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/fd54afb2-59bb-f433-ee16-4c03af0cf9e0.png)

# 動作確認
テストを利用して、動作確認を行います。新しいテストイベントを作成します。今回はテンプレートの「Hello World」を利用します。
![func-test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/1b1ac53c-cda7-035a-32c1-16e97bb91cb4.png)

作成したテストを実行すると、レスポンスが確認できます。

![func-res3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420363/b86d8ca2-1552-9ae0-c709-60d78057a8e2.png)


実行結果から、opensource COBOLがLambda上で動作し、引数としてイベント情報のJSONを読み込めていることがわかります。

# まとめ
AWS Lambdaのカスタムランタイムを利用することで、容易にopensource COBOLのCOBOLアプリケーションをサーバーレス化することができました。
作りこまれている業務ロジックの一部をマイクロサービス化し、新しい基盤システムと連携するなどといった活用が見込まれます。

# 参考資料
opensource COBOLの提供元とか参考にしたサイトなど
- [OSSコンソーシアム](https://www.osscons.jp/osscobol/)
- [opensource COBOLリポジトリ](https://github.com/opensourcecobol/opensource-cobol)
- [AWS Lambda超入門](https://qiita.com/s_s_k/items/b584435120e99d63975b)
- [AWS LambdaのCustom RuntimeでCOBOLを動かしてみた #reinvent](https://dev.classmethod.jp/cloud/aws/lambda-custom-runtimes-cobol/)


今回作成したファイルはgithubで公開しています。
https://github.com/nor51010/lambda-oss-cobol