現状
M1 macでDockerのApple M1 Tech preview 7 *1 を動かしています。
golang:1.15 imageがたまたま手元で動いていたのでこれで試します。
entrykitはv0.4.0のバイナリを配布していますが、これを実行しようとすると以下のようにgoレベルで落ちてしまい実行できません。
$ docker run --rm -it golang:1.15 bash root@aa08b9d02add:/go# wget https://github.com/progrium/entrykit/releases/download/v0.4.0/entrykit_0.4.0_Linux_x86_64.tgz root@aa08b9d02add:/go# tar -xvzf entrykit_0.4.0_Linux_x86_64.tgz root@aa08b9d02add:/go# ./entrykit runtime: failed to create new OS thread (have 2 already; errno=22) fatal error: newosproc runtime stack: runtime.throw(0x84a820, 0x9) /usr/local/go/src/runtime/panic.go:527 +0x90 runtime.newosproc(0xc820026000, 0xc820035fc0) /usr/local/go/src/runtime/os1_linux.go:150 +0x1ab runtime.newm(0x8e5980, 0x0) /usr/local/go/src/runtime/proc1.go:1105 +0x130 runtime.main.func1() /usr/local/go/src/runtime/proc.go:48 +0x2c runtime.systemstack(0xa2b6e0) /usr/local/go/src/runtime/asm_amd64.s:262 +0x79 runtime.mstart() /usr/local/go/src/runtime/proc1.go:674 goroutine 1 [running]: runtime.systemstack_switch() /usr/local/go/src/runtime/asm_amd64.s:216 fp=0xc820020770 sp=0xc820020768 runtime.main() /usr/local/go/src/runtime/proc.go:49 +0x62 fp=0xc8200207c0 sp=0xc820020770 runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc8200207c8 sp=0xc8200207c0
あまりパソコンに詳しくないのですが、 downloadしたものはx86_64(amd64)環境のbuildと思われるので、aarch64(arm64)環境では動かないのかなと推測します。
また、entrykitは長らくreleaseされておらず、作者も興味を失っている気がする(憶測)。
界隈で一瞬流行ったので、なんとなく使われているということが多いのではないでしょうか。
考えうる解決法
解決方法はいくつか考えられます。
1. entrykitを捨てる
そもそもprehook
だけの利用の場合、entrykitは必要ありません。
prehookコマンドの正体は、
$ prehook ① -- ②
となっている場合、
- ①を外部コマンドとして実行する(複数可)。その後②でexecする。
--
以降がなく①だけの場合は①でexecする。
ただこれだけです。
よって
Dockerfile
ENTRYPOINT[ \ "prehook", "ruby -v", "--", \ "prehook", "bundle install", "--" ]
は
Dockerfile
ENTRYPOINT[ "./entrypoint.rb" ]
entrypoint.rb
#! /usr/bin/env ruby system("ruby -v") system("bundle install") exec *ARGV
と別ファイルに切り出せます。
もちろんrubyじゃなくshellでもいいです。
Multi stage build
これだけなら話は早いですが、entrykitを使うからには、おそらくprehook以外のentrykitコマンドと組み合わせたいという使い方が多いと思います。
そんなときの対処方法としてMulti stage buildがおすすめです。
FROM golang:1.15 AS entrykit RUN go get -v -ldflags "-s -w" github.com/progrium/entrykit/cmd
とすると、Docker環境内でdownloadとbuildが始まり、/go/bin/cmd
という実行ファイルが生成されます。これがentrykitの実体です。
cmd
じゃなくentrykit
という名前でbuildする方法がいまいち分からなかったので誰か教えて下さい……。-o
はgo getでは使えないらしい。
ともかくこれで、M1環境でしかもgolang version 1.15でbuildしたentrykitのbinaryが手に入ります。簡単。
次にentrykitを使う場所で、entrykitの実体だけCOPYしてきます。
Dockerfile
COPY --from=entrykit /go/bin/cmd entrykit RUN entrykit --symlink
これで各種コマンドが生成されます。symlinkはCOPYできないのでCOPY後に--symlinkするようにしましょう。 また、使うコマンドが1つだけなら、
COPY --from=entrykit /go/bin/cmd render
だけでもいいです。お好みで。
Cross buildしてbinaryをプロジェクトリポジトリに置いとく
手元でCross buildして複数環境用にbuildしておき、生成されたbinaryが使われるようにuname -m
とかで切り替える。。。
面倒そうな上にMulti stage build以上のメリットはなさそうです。
結論
- entrykit本当にいるのかどうかよく考えよう。
- Multi stage build便利。
課題
Multi stage buildの場合、entrykitのファイル配置がおかしいのか、go getでversion指定する方法がよく分からない……。go歴0秒なのでご教授いただけると幸いです。
逆に言うとversion指定しなくてもいいのでメンテナンスレス!
感想
バイナリ配布型のプロダクトの場合、fat gemなどと同じく継続的なreleaseが必要でメンテナ負担が大きいのではないかなと推測します。(作ったこと無いのでなんとも言えないけど)
その点、スクリプト型言語なら再buildなどが必要ないので、 ミニマムに使えるけどメンテナンス負担が大きいバイナリ配布と、 導入は大げさだけどメンテナンス負担が小さいスクリプト配布と使い分けがありそうですね。
とはいえ今回のように都度buildすりゃいいじゃんと言われればまあそうなのかも。