Common Lispで実装したWebアプリを動かすdockerイメージを作っただけの話

[Nextremer Advent Calendar 2017](https://qiita.com/advent-calendar/2017/nextremer) 14日目の記事です

Lispという言語に皆さんは触れたことがあるでしょうか？
「神の言語」とも言われるLisp、プログラマーであれば一度は気になったことがあるんじゃないでしょうか

NextremerにはLisperのメンバーが若干名おり、そのお陰で最近はたまに[NextremerのオフィスでLispの勉強会であるShibuya.lispが開催](https://lisp.connpass.com/event/73796/)されたりしています

私自身は今まで一度もLisp系の言語をちゃんと勉強したことがなかったんですが、会社のslackでうっかりCommon Lispやってみたいてきなことを呟いたらLisperの方々が反応してくれました

このやり取りをきっかけに、最近じわじわと社内でCommon Lisp熱が高まりつつあります

# Common Lisp を使おう！
Common Lisp環境のセットアップ、Hello world的な解説や基本的な構文などを@t-sinさんが記事にまとめられています
とてもわかりやすく参考にさせていただきました！
[いまから始めるCommon Lisp](https://qiita.com/t-sin/items/054c2ff315ec3b9d3bdc)

# Dockerイメージを作ろう！
Nextremer内の自称Dockerエバンジェリストとしては、何か新しいアプリケーションを開発するときに何でもDockerにしてしまいたい欲望があります

RoswellとSBCLをインストール済みのDockerイメージを作ってDocker Hubにアップしました
・Docker Hub
https://hub.docker.com/r/chanmoro/docker-roswell/
・githubリポジトリ
https://github.com/KazukiMorozumi/docker-roswell

# WebAPIを作ってみよう！
先ほども紹介した@t-sinさんの記事で紹介されているWebAPIと同じものを実装して、Dockerコンテナ内で動かしてみます

ソースコード一式は以下に置きました
https://github.com/Chanmoro/commom-lisp-webapp-example

このリポジトリをクローンして `$ docker-compose up` を実行するとWebサーバーが起動します
初回の実行は各種ダウンロードで少し時間がかかります

起動した後は localhost:5000 で以下のようにアクセスできます

```
$ curl localhost:5000
Hello CL with Docker!
```

```
$ curl localhost:5000/post/list
[{"ID":1,"TITLE":"記事1","DATE":"2017-01-01","TAGS":["tag1","tag2","tag3"]},{"ID":2,"TITLE":"記事2","DATE":"2017-01-02","TAGS":["tag2","tag3"]},{"ID":3,"TITLE":"記事3","DATE":"2017-01-03","TAGS":["tag1","tag3"]}]
```

```
$ curl localhost:5000/post/id/1
{"ID":1,"TITLE":"記事1","DATE":"2017-01-01","TAGS":["tag1","tag2","tag3"],"BODY":"本文1本文1本文1本文1"}
```

コード自体は特に困ったところはなかったんですが、Dockerイメージを作るのにちょこちょこ細かいところでハマったので以下にまとめます


# Dockerコンテナ化する際にハマった点
## WARNINGが出る
ビルド時や `ros` コマンドの実行時に以下のようなWarningが出ます

```
WARNING:
Couldn't re-execute SBCL with proper personality flags (/proc isn't mounted? setuid?)
```
以下のwikiにこれについての対応方法が書かれていました
https://github.com/dimitri/pgloader/wiki/Running-in-Docker-(SBCL-warning)

どうやらなんらかのシステムコールを実行する必要があり、Dockerのセキュリティ機構の `seccomp` を無効にするか、システムコールを実行する許可の設定をすることで該当のWarningが消えます
https://github.com/daewok/slime-docker/blob/master/resources/docker-sbcl-seccomp.json

seccompを設定する際は以下のように `--security-opt` オプションで指定します
`docker run -it --security-opt seccomp=./docker-sbcl-seccomp.json chanmoro/docker-roswell bash`

seccompの設定をしなくてもビルドは成功しますし、その後の処理も動くので無視したままでも動作はするようでした
※そもそものWarningの内容をよく理解していないので、今後問題になるようだったら詳細調べてみるかもです・・・

## Common Lispのソース内に日本語があるとエラーになる
無事Dockerイメージがビルドできたと思ったら、今度はAPIサーバーの実行時に以下のようなエラーが出ました

```
  :ASCII stream decoding error on
  #<SB-SYS:FD-STREAM for "file /app/app.lisp" {1003857E83}>:
    the octet sequence #(232) cannot be decoded.
```

何らか文字コードが関係していそうで、いろいろとググるとどうやらlocaleが関係していそうでした
今回ベースに利用しているubuntu 16.04のイメージは、デフォルトの状態だとlocaleはこのように `POSIX` が設定されています

```
# locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="
```

この状態だとエラーとなるので、aptで`locales`をインストールしDockerfile内で以下のようにlocaleに`en_US.UTF-8`をセットすることで日本語のエラーが解決しました

```
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
```

## Common Lispの各種パッケージをどのタイミングでインストールしたらいいかわからない
コンテナを気にせず普通に実行する場合は`app.lisp`内の以下のように`quickload`を利用するとライブラリのダウンロードが自動で行われます

```
(ql:quickload '(:ningle) :silent t)
(ql:quickload '(:jonathan) :silent t)
```

しかし、Dockerイメージ化する上では各種ライブラリがダウンロード済みの状態のイメージを作りたいです
結果として以下のようにDockerfile内でライブラリを読み込んでダウンロードするようにしました

```
RUN echo 'install libs' \
  && ros -s ningle \
  && ros -s jonathan
```

この悩みを弊社のLisperである@gos_kさんに聞いてみたところ、以下のリポジトリを教えてくれました
https://github.com/Rudolph-Miller/dockerfile-clack/blob/master/Dockerfile
さらに`ros -e "(ql:quickload :hoge)"`のようにコードを実行する方法ではなく`ros -s hoge`としてquickloadだけ実行するのがナウいと教えてくれたのでこの方法を使いました

# みんなでCommon Lispしよう！
さて、今回はCommon Lispで実装したWebアプリケーションをDockerで動かそう、というのにトライしました
これでポータビリティーも最強になったので、Common Lispで実装したソフトウェアを思う存分投入できますね！

まだまだCommon Lispを触り始めたばかりなので分からないことだらけですが、今後も引き続き勉強させていただきます！

あまりLisp系言語やCommon Lispについてゆるふわなことばかり言っていると、どこかからマサカリが飛んで首を持っていかれそうな気がするので、初心者はこのあたりで黙っておきます