はじめに
Haskellをローカル開発環境で勉強していろいろな抽象的な知識を得たものの、実際活用するにあたってデプロイとかその辺どうするんだろう?やっぱりdocker使って簡単にやりたいな〜
でも、Goとかだと簡単に100%静的なシングルバイナリになるし、Rustとかもmuslを利用していい感じに行けそうだけどHaskellだとわからない>< というお気持ちが強かったので、簡単にデプロイできる100%静的リンクのシングルバイナリ作るぞ!と思って調べたお話です。
追記
下記の紹介しているものとほぼ同じような手順で、自分で拡張しやすいDockerfileでのみ構築されたDockerイメージを自分で作成したので、是非参考にしてみてください。
対象読者
Haskellを本番で使ったことない人向け
大雑把に説明
概要
- 静的リンクされたバイナリを作るためのDockerコンテナ上でプロジェクトをビルドしてバイナリを手に入れる
- 動作用のDockerイメージにそのバイナリを乗っける
- そのイメージを動かす!
環境
OS : MacOS Catalina
パッケージマネージャ : Stack
今回はHaskellのパッケージマネージャであるStackを利用してやっていきますが、cabalでもなんでもできそうなので調べてみてください。
Stack Docker integration
StackにはDockerと協調していい感じにバイナリを作ってくれる機能があります。
docker:
repo: {{docker imageの名前}}
enable: {{ true or false }}
こんな感じで追記すると、
$ stack build
としたときにrepo
に記載しているimageを使ってbuildをしてくれます。
enable
をfalseにした場合は$ docker build --docker
ってコマンドになるので僕はfalseにしてます。
local-bin-path
stackにはinstall機能もありますね。
$ stack install
とすると、ローカルのプロジェクトのソースをビルドして、~/.local/bin
この辺(つまりグローバル)に実行可能ファイルを置いてくれます。
でもグローバルはやじゃん!
$ stack install --local-bin-path=./bin
こうすると、現在のディレクトリの bin
配下にいい感じにビルドしてくれるのでバイナリが手に入ります!
ghc-musl
musl-libcを使って静的リンクされたバイナリを簡単に作ることができるimageをHaskell界の聖人様が作ってくれているので、利用します。
参考リポジトリ: https://github.com/utdemir/ghc-musl
やっていき!
上で説明した三つの項目を利用していい感じに動作用imageを作る作業をやっていきます。
1. まずはHaskellのdocker integrationを利用してビルド時のイメージを設定
docker:
repo: utdemir/ghc-musl:v7-libgmp-ghc883 # 正しいhaskellコンパイラを指定しよう
enable: false
2. 静的リンク用の設定を追加
executables:
example-exe:
main: Main.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
- -static -optl-static -optl-pthread -fPIC # <- これを追加!
dependencies:
- example
3. バイナリを手に入れよう
これで ./bin/docker/
配下に静的リンクされたバイナリが手に入ります
$ stack install --local-bin-path=./bin/docker --docker
4. 動作用のDockerイメージをビルドする
ベースイメージは本当に何にも含まれていないscratchを利用することによって、静的リンクされていることを確認します。
こうすると、イメージのサイズもバイナリと同じサイズになってめっちゃちっちゃくて最高です。
(実用上いろいろ考えるとAlpineの方がいいかもしれないです)
FROM scratch
COPY ./bin/docker/{ バイナリの名前入れてね } /
CMD ["./{ バイナリの名前入れてね }"]
ビルドします
$ docker build . -t {任意のイメージの名前}
4. そして動かす!
$ docker run --rm --init {イメージ名}
おわり
Servantのシンプルなプロジェクトで動作確認をしたけれど、他のDB操作系のライブラリとかでHaskell以外の他の依存がある場合はどうなるんやろな?ってところはやってないので、大規模プロジェクトでは未知数ですが、試してみてコメントいただけるとありがたいです〜。
もっといい方法があればコメントにて教えてください!
あと、聖人様のリポジトリにスターをつけてたくさん開発してもらえるように促してください!