はじめに
本番用Dockerfileには軽いからAlpine Linuxを使おう!という話はよく聞きますが、リスクがあるので何も考えずに使うのは避けましょう、というお話です。
Alpine Linuxを使う意味
イメージサイズの減少
「軽量」LinuxなのでこれがAlpine Linuxを使う最大かつ唯一の理由だと思います。
ただし現実的にイメージサイズに差があるか?というと、大きな差はないです。
debian-slim | alpine | |
---|---|---|
ベース | 28.77MB | 3.09MB |
node:18 | 65.82MB | 48.75MB |
python:3.12 | 46.7MB | 19.59MB |
確かにサイズ差はあるものの、その差は約20MB程度。
イメージサイズが影響するのは主にデプロイ速度ですが、この程度で現実的に影響が出るとは考えづらいです。
アプリケーションを載せるとそちらの方が大きなサイズを占めるため、より比率は低くなります。
Alpine Linuxの欠点
glibcがない
ほとんどのLinuxディストリビューションは標準Cライブラリとしてglibcを採用していますが、Alpine Linuxはglibcを採用しておらず、代わりにmusl-libcを利用しています。
このため、以下のような問題を引き起こします。
- バイナリが動作しない
- glibcに依存するバイナリは動作しない
- ex) 開発環境の豊富なDebianイメージでビルドしてAlpineイメージにコピーすると動作しない場合がある
- バイナリのビルドに失敗する
- glibcに依存するソースのビルドに失敗する
- PythonやNode.jsパッケージなどの内部でビルドするバイナリでも発生しえる
- パフォーマンス劣化の可能性がある
- glibcを使う場合より有意に低速になることがある
- 参考(少し古いですが…): https://superuser.com/questions/1219609/why-is-the-alpine-docker-image-over-50-slower-than-the-ubuntu-image
平たくいうと互換性・パフォーマンスの担保ができないということです。
これらの問題は個別に対応が必要で、例えばNext.jsのDockerサンプルでは互換レイヤーとしてlibc6-compatを別途導入しています。
https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
またパッケージやランタイムをアップデートした後に新しく問題が発生することもあるため、アップデート時のリスクを増やすことにつながります。
apkパッケージを固定できない
DockerイメージにOS提供パッケージを追加する場合、動作の再現性のためにパッケージバージョンは固定したいですよね。
しかしAlpine Linuxでは実質的にパッケージバージョンの固定が不可能です。
Alpine Linuxの採用するapk(alpine package keeper)ではパッケージバージョンを指定することができるので、これを使えば良さそうですが、、、
1 2 3 |
RUN apk --no-cache add git=2.43.0-r0 jq=1.7.1-r0 |
しかしAlpineのパッケージは最新版しか維持されません。
リポジトリを見ると、同一パッケージは基本的に1バージョンしかないことがわかります。
http://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/
このため、バージョンを指定してしまうとある日突然イメージビルドに失敗するようになります。
つまり固定は実質不可能です。
GitHub Actionsで使用していたサードパーティアクションがこの問題を踏んでいて、突然動かなくなることがありました。
開発環境として使いづらい
Alpine LinuxはDebian等と比べるとOS提供パッケージが少ないです。
このため開発環境としてはUbuntu/Debianベースイメージが使われることが多く、本番用イメージとの差分が大きくなってしまいます。これにより本番イメージでのみ発生する問題が多くなってしまう危険があります。
どうするのが良いか
debian-slimを使う
上記の通りalpineとdebian-slimベースイメージの差はわずかなので、基本的にdebian-slimを使うようにすると良いかと思います。
開発環境ではdebian(full)ベースイメージを使うことで利便性との両立も可能です。パッケージバージョンを固定する場合の問題も起こりません。
distrolessを使う
よりアグレッシブにイメージサイズを削りたい場合は、distrolessベースイメージを使用します。
これはalpineよりイメージサイズが小さく、かつglibcを採用しているので互換性問題が起こりづらいです。
ただし
- パッケージマネージャが存在しない
- シェルすらない
- コンテナ内へのログインが不可能
という割り切りがされているので、マルチステージビルドしたアプリをコピーしてくるのが前提のイメージになります。
scratchを使う
Go限定ですが、scratchを使う方法もあります。
GoでCGO_ENABLED=0
でビルドしたバイナリは単独で動作可能なので、何も入っていないscratchイメージに追加すればそれだけで動作します。
こちらもシェルが入っておらず、コンテナ内に入れないので注意が必要です。
まとめ
Alpine Linuxについて、イメージサイズの減少幅の小ささ、それに対する種々のリスク・課題について解説しました。
debian-slimイメージが十分サイズが小さいため、長期的な運用において不確定要素を抱えるAlpineを採用する採用するメリットは薄くなっているのではないかと考えています。
特に開発用コンテナですでにdebianを使っているような環境では、debian-slimに乗り換えてみるのも考えてみてください。