最近ようやく開発ツールとして Docker が手に馴染んできたので、タイトルの件も含めていくつか雑多に書きます。

CLI ツールを Go で書いて Docker イメージとしてリリースする

コマンドラインツールを Go で書く、というのは以前からやっていて、主な理由としては「クロスコンパイルができるのでバイナリリリースが簡単」というのがありました。便利なので、クロスコンパイルから GitHub へのリリースを一発でやってくれるラッパーツールを書いたこともありました (一応動くものの、開発は非常に中途半端なところで止まってますが)

その後、2017 年に入って Docker で Multi-Stage Builds という機能が実装されてからは、Dockerfile 内の build ステージで go build したバイナリを最終的なステージから COPY --from=build して使う、ということもやってきました。

この COPY --from ですが、ドキュメントをよく見ると COPY --from=nginx:latest などとすることで、外部のイメージも stage として利用することができると書いてあります。

これを利用すると、Dockerfile 内で必要なツールはこんな感じにインストールすることができるようになります。 (例は前の記事で紹介している guruguru-cache をインストールするもの)

COPY --from=yuyat/guruguru-cache /usr/local/bin/guruguru-cache /usr/local/bin

guruguru-cache は Go で書かれていてシングルバイナリとしてビルドされているので、このように $PATH の通ったところに COPY してくるだけで使えるようになります。

ただし、動的リンクされている場合は動かないことがあるので、静的リンクにする必要があります。以下の記事を参考にしています。

以前に Rust で書いたツール も同じやり方で手元で使ってたりするんですが、この時はその辺に気を使っていたわけではないので、たまたま動いているだけかもしれません。 (ビルドと実行が同じベースイメージで行われていれば普通に動くことも多い)

最近 Webpacker 用の Dockerfile で、ruby のイメージの中から COPY --from=nodenode のバイナリを直接引っ張ってきているのをちょいちょい見ますが、Quipper で一番 Docker に詳しい @mtsmfm さんに聞いたところ、「多分 ruby と node のベースイメージが近いからたまたま動いているだけで、手元で使うのはいいけど本番ではやるべきではないですね」とのような意見をもらって、「なるほど、確かに〜」と思ったのでした。 (確かにどちらも buildpack-deps:stretch がベース。バージョンによっても違うかもですが)

あと言うまでもないとは思いますが、これはあくまでも docker build 内で実行する開発ツール的なやつに有効なテクニックで、サーバやミドルウェアは素直で別コンテナで動かしてネットワークを通じてやり取りするのが良いでしょう (Apache と MySQL の両方を動かすようなイメージを作るべきではない)

Docker Hub よりも Docker Cloud

というわけで最近は前よりも Docker イメージを作ることが増えて、ちょっと前までは Docker Hub を使っていました。

でもビルドの進捗がよくわからんなー、と思って社内の Slack で文句を言っていたところ、これまた @mtsmfm さんから「進捗は Docker Cloud の方がわかりやすい」ということだったのでそちらを使い始めました。

名前はなんとなく知っていたものの、そこで初めて知ったのはどちらも Docker, Inc. が運営するサービスなんですね。しかも内部的にリポジトリのデータは共有されており、docker push すると両方に同じイメージが表れるという不思議な作り。Docker Hub だと他人の public なイメージも閲覧できるのに対して、Docker Cloud は基本的に自分か所属する組織のイメージ以外は見えないので、Hub としての機能は持ってないようです。

使ってみると UI 以外にも Automated Build が少しいい感じになっていて、Git の tag は v1.2.3 だったら Docker イメージの tag は 1.2.3 というように先頭の v を取る、みたいなことも簡単にできるので便利です。