Edited at
NextremerDay 14

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

More than 1 year has passed since last update.

Nextremer Advent Calendar 2017 14日目の記事です

Lispという言語に皆さんは触れたことがあるでしょうか?

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

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

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

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


Common Lisp を使おう!

Common Lisp環境のセットアップ、Hello world的な解説や基本的な構文などを@t-sinさんが記事にまとめられています

とてもわかりやすく参考にさせていただきました!

いまから始めるCommon Lisp


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についてゆるふわなことばかり言っていると、どこかからマサカリが飛んで首を持っていかれそうな気がするので、初心者はこのあたりで黙っておきます