今更感はありますが、最近Docker始めました🐳
これまで何となく難しそう的な先入観でのらりくらり避けてきたわけですが、いざ触れてみたら思いのほか好奇心を掻き立てられてしまいました。
そんでもって、のらりくらりしてきた間の歴史や最近のトレンドを追ううちに、多分みなさんも一度は躓いたであろう問題に漏れなく僕もぶち当たり、無事洗礼を受けたというワケです。
今回は歴史の復習と解決した課題についてつらつらとメモっていきます。
Alpineとlibc
とりあえずベースに軽いAlpineを使おうと思ったらアプリケーションが動かなくてゲッソリしたこと、あると思います。
その原因はC言語の共通ライブラリにあります。
一般的なLinuxディストリビューションの多くは glibc (GNU C Library) を採用しています。
glibcは歴史が古く採用実績も多いですが、故に中身も肥大化してしまったため、モダンでスリムなglibcの移行先として musl が開発されました。
muslは後発なだけあって軽量高速ですがglibcとの互換性がありません。
流石にそれはあんまりとのことで互換レイヤも用意されましたが、あくまでオプション扱いで部分互換に留まります。
そして、軽量がウリのAlpineが採用したのはmuslでした。
ここで運命が分かたれてしまったのです。
muslの歴史は浅く、巷のアプリケーションはほとんどがglibcに依存した状態のため、幾多ものDockerユーザーに頭痛を引き起こしてきたという話に繋がるわけです。
C言語のアプリケーションではない、例えばNode.jsやRustでもこの問題にぶち当たるのは、それらの実行エンジンやコンパイラが内部でglibcを使用しているためです。
Distroless
そんなこんなでAlpineって互換性イマイチじゃね?という風潮が出た辺りでGoogleが Distroless というAlpineに代わる便利な超軽量イメージをリリースしました。
Distrolessは名前からも想像できる通り、パッケージマネージャはおろかシェルすら削ぎ落としたストイックなイメージで、最低限の依存関係だけ入れておくから後はよろしくやってね、という感じです。
そしてDistrolessには幾つかバリエーションがあり、それは以下の通りです。
- 単品
- glibcを同梱
- glibcとlibsslを同梱
- glibcとlibsslとlibgccを同梱
そう、最初からglibcが同梱されているのです。
これを使えば、ノープロブレムでglibcに依存したアプリケーションを実行できます、やったね!
...
......
.........
でもやっぱりAlpineを使いたい
しかし人生、シェルが入ってる親切なディストリビューションに色々と積み上げたい時だってあります。
最初はglibcをソースからビルドしたり、サードパーティのコンパイル済バイナリも試しましたが、どれもせっかくクリーンなベースイメージを複雑にしてしまい、腑に落ちませんでした。
そして頭を悩ませているうちに、天才的発想が舞い降りてきました😏
それがこちら。
FROM gcr.io/distroless/cc-debian12:latest AS cc
FROM alpine:latest
ENV LD_LIBRARY_PATH="/usr/local/lib"
COPY --from=cc --chown=root:root --chmod=755 /lib/*-linux-gnu/* /usr/local/lib/
RUN mkdir /lib64 && ln -s /usr/local/lib/ld-linux-*.so.2 /lib64/
聡明な諸先輩方なら何をしているかは一目瞭然かと思いますが、ざっと解説します。
- glibc同梱版のDistrolessイメージを用意
- Alpineイメージを用意
- Distroless内のglibc関連をAlpineへコピーする
- マルチステージビルドで出来あがり
これなら、クリーンなオフィシャルイメージのみを使用しつつglibcも簡単に入手できるので「怪レいイメージ」に頼らず済みます。
Deno で試しましたが、無事に動作確認できました。
試したアーキテクチャはx86_64だけですが、意図的にDockerfile上でアーキテクチャが明示されてるパス表記はワイルドカードで読み飛ばしてるので、AlpineとDistrolessが両方とも対応していれば動くと思います。
おわりに
「Debian-slimを使え」とかそーいうのナシな!
個人的にAlpineが好きなの!