LoginSignup
61
36

More than 3 years have passed since last update.

Haskellで超簡単にシングルバイナリを作る

Last updated at Posted at 2020-07-04

はじめに

Haskellをローカル開発環境で勉強していろいろな抽象的な知識を得たものの、実際活用するにあたってデプロイとかその辺どうするんだろう?やっぱりdocker使って簡単にやりたいな〜

でも、Goとかだと簡単に100%静的なシングルバイナリになるし、Rustとかもmuslを利用していい感じに行けそうだけどHaskellだとわからない>< というお気持ちが強かったので、簡単にデプロイできる100%静的リンクのシングルバイナリ作るぞ!と思って調べたお話です。

追記

下記の紹介しているものとほぼ同じような手順で、自分で拡張しやすいDockerfileでのみ構築されたDockerイメージを自分で作成したので、是非参考にしてみてください。

GitHub
Docker Hub

対象読者

Haskellを本番で使ったことない人向け

大雑把に説明

概要

  1. 静的リンクされたバイナリを作るためのDockerコンテナ上でプロジェクトをビルドしてバイナリを手に入れる
  2. 動作用のDockerイメージにそのバイナリを乗っける
  3. そのイメージを動かす!

環境

OS : MacOS Catalina
パッケージマネージャ : Stack

今回はHaskellのパッケージマネージャであるStackを利用してやっていきますが、cabalでもなんでもできそうなので調べてみてください。

Stack Docker integration

StackにはDockerと協調していい感じにバイナリを作ってくれる機能があります。

stack.yaml
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を利用してビルド時のイメージを設定

stack.yaml
docker:
  repo: utdemir/ghc-musl:v7-libgmp-ghc883 # 正しいhaskellコンパイラを指定しよう
  enable: false

2. 静的リンク用の設定を追加

package.yaml
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の方がいいかもしれないです)

Dockerfile
FROM scratch
COPY ./bin/docker/{ バイナリの名前入れてね } /
CMD ["./{ バイナリの名前入れてね }"]

ビルドします

$ docker build . -t {任意のイメージの名前}

4. そして動かす!

$ docker run --rm --init {イメージ名}

おわり

Servantのシンプルなプロジェクトで動作確認をしたけれど、他のDB操作系のライブラリとかでHaskell以外の他の依存がある場合はどうなるんやろな?ってところはやってないので、大規模プロジェクトでは未知数ですが、試してみてコメントいただけるとありがたいです〜。

もっといい方法があればコメントにて教えてください!
あと、聖人様のリポジトリにスターをつけてたくさん開発してもらえるように促してください!

61
36
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
61
36