SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor

Alpine Linuxを使うべき時と避けるべき時:musl・Docker・sshdの注意点

Dockerのalpineタグは今も避けるべきか。Pythonのwheel、DNS、glibc専用バイナリ、日本語ロケールとTZを一次情報で確認し直し、musl+PAMなしsshdの脆弱性から学べることも整理します。

Alpine Linux とは何か

Alpine Linux(アルパイン・リナックス)は、musl libc と BusyBox を土台にした小さな Linux ディストリビューションです。Docker の -alpine タグを今使ってよいかは、ほぼ一つの点で決まります。アプリが glibc に依存しているかどうかです。依存していなければ、Alpine は今でも小さく扱いやすいベースイメージです。依存しているなら、debian の slim イメージか Ubuntu を選ぶほうが手間がかかりません。

Alpine の公式説明によると、構成要素は四つあります。C ライブラリ(プログラムが OS の機能を呼び出すための土台)は glibc ではなく musl libc です。基本コマンドは BusyBox が一つのバイナリにまとめて提供します。パッケージ管理には独自の apk を使います。init システム(起動時にサービスを立ち上げる仕組み)は systemd ではなく OpenRC です。

Ubuntu や Debian との違いで一番影響が大きいのは musl です。glibc 向けにビルドされたバイナリは、musl の上ではそのまま動かないことがあります。この記事では、2021年ごろによく読まれた「Alpine は使うな」という記事の論点を、2026年11月7日に一次情報で確認し直します。最後に、11月5日に公表された sshd の問題から、長く役に立つ教訓を取り出します。

SSD Nodes の VPS で Alpine に出会う場所

SSD Nodes の製品ページに載っている OS イメージは、Ubuntu、Debian、CentOS、Fedora、AlmaLinux、Rocky Linux の最新安定版です。この一覧に Alpine は入っていません。この記事では、Ubuntu や Debian の上で動く Docker コンテナの中で Alpine を使います。ホスト OS の選び方は VPS に入れる OS の選び方 にまとめています。

この記事のコマンドは二種類あります。docker で始まるものはホスト(Ubuntu や Debian)で実行します。apk や sshd などは Alpine コンテナの中で実行します。

今の公式 alpine イメージ

Docker Hub の公式 alpine イメージには、2026年11月7日時点で 3.24.4、3.24、3、latest のタグが付いています。ページの説明ではサイズは約 5 MB です。対応アーキテクチャは amd64、arm64v8、arm32v6、arm32v7、i386、ppc64le、riscv64、s390x です。タグが指す中身は更新で変わるので、使う前に Docker Hub のページを確認してください。

Alpine のリリース表では、最新の安定ブランチは v3.24 です。v3.24 は 2026-06-09 に分岐し、main リポジトリのサポートは 2028-06-01 までです。community リポジトリのサポートは次の安定版が出るまでです。v3.21 のサポートは 2026-11-01 で終わりました。latest を使うと、ある日ブランチが変わって中身が入れ替わります。本番では alpine:3.24 のようにブランチまで固定するのが安全です。

今どのリリースを使っているかは、コンテナの中で確認できます。

docker run --rm alpine:3.24 cat /etc/alpine-release

表示された番号が、使うつもりのブランチと合っているか確認してください。

Alpine Linux を使うべき時と避けるべき時

先に結論をまとめます。自分のアプリがどちらに当てはまるかは、この後の各節で確認してください。

Alpine が向いている場合です。

  • 自分でビルドした静的リンクのバイナリを一つ入れるだけのイメージ。C ライブラリの違いがほとんど問題になりません。
  • 使う Python パッケージに musllinux 向けのホイールがあり、ビルドが起きない場合。
  • 必要なツールがすべて apk で揃い、イメージを小さく保ちたい場合。
  • arm64 や riscv64 など、複数のアーキテクチャ向けに同じ Dockerfile を使いたい場合。

debian の slim イメージや Ubuntu が向いている場合です。

  • 配布元が glibc 向けのバイナリしか出していないソフトを使う場合。
  • 多数のスレッドを使い、メモリ割り当ての性能が重要なサーバーで、Alpine 版をまだ測定していない場合。
  • 日本語のロケール(ja_JP.UTF-8 など)を前提に動くアプリで、musl での動作を確認していない場合。
  • チームのデバッグ手順が apt と glibc を前提にしていて、調査の時間を減らしたい場合。

論点1:Python のホイールは今もビルドが必要か

2021年ごろに一番多かった不満は、Alpine だけ pip install がソースからのビルドになることでした。ビルドには時間がかかり、失敗もします。原因は、PyPI のビルド済みホイールが glibc 向け(manylinux)だけで、musl 向けがなかったことです。

その後、PEP 656 が musllinux というプラットフォームタグを定義しました。形式は musllinux_${MUSLMAJOR}_${MUSLMINOR}_${ARCH} で、PEP の状態は Final です。このタグのおかげで、パッケージ作者は musl 向けのホイールを PyPI に上げられるようになりました。

2026年11月7日に、PyPI の JSON API で人気パッケージ三つの最新版を確認しました。

グラフPyPI 最新版に含まれる musllinux ホイールの数(2026年11月7日時点)
このグラフのデータ
[
  {
    "label": "numpy 2.5.4",
    "musllinux_wheels": 12,
    "all_files": 66
  },
  {
    "label": "pandas 3.1.1",
    "musllinux_wheels": 14,
    "all_files": 58
  },
  {
    "label": "grpcio 1.85.1",
    "musllinux_wheels": 18,
    "all_files": 61
  }
]

三つとも musllinux_1_2 のホイールを出しています。numpy 2.5.4 は 12 個で、aarch64 と x86_64 向けです。対象は cp312、cp313、cp314、cp314t、cp315、cp315t です。cp311 はこの一覧にありません。このリリースは Python 3.11 向けのホイールをまったく出していません。pandas 3.1.1 は 14 個で、cp311 から cp315t までの aarch64 と x86_64 向けです。i686 向けの musllinux ホイールはありません。grpcio 1.85.1 は 18 個で、cp310 から cp315 までの aarch64、i686、x86_64 向けです。

つまり「Alpine だと numpy はビルドになる」という話は、少なくともこの三つの最新版と主要なアーキテクチャには当てはまりません。ただし、使っている Python のバージョンとアーキテクチャに合うホイールがなく、互換性のあるプラットフォーム非依存のホイールもなければ、pip は利用可能なソース配布物からのビルドに戻ります。指定したパッケージのホイールを取得できるかは、対象の Alpine 環境でビルドを禁止した状態で取得してみると分かります。

pip download numpy pandas grpcio --only-binary=:all: --no-deps -d /tmp/wheels

--only-binary=:all: はホイール以外を受け付けません。そのため、合うホイールがないパッケージはここでエラーになります。知らないうちにビルドが始まることはありません。--no-deps を付けているので、依存パッケージはこの確認の対象外です。依存パッケージも同じ方法で確認してください。人気パッケージが対応していても、小さな依存パッケージが互換性のあるホイールを出していないことがあります。PyPI の各パッケージの「Download files」タブでも、自分の Python とアーキテクチャに合うファイルがあるか確認できます。

論点2:DNS の TCP フォールバック

古い記事のもう一つの論点は DNS でした。昔の musl のスタブリゾルバ(アプリの名前解決を DNS サーバーに問い合わせる部分)は UDP しか使いませんでした。そのため、応答が大きくて UDP に収まらない DNS レコードを引けませんでした。

musl の公式リリース一覧によると、2023年5月1日の musl 1.2.4 でスタブリゾルバに TCP フォールバックが追加され、大きな DNS レコードを引けない問題が直りました。この musl 1.2.4 を最初に載せた Alpine は、2023-05-09 にリリースされた Alpine 3.18.0 です。Alpine 3.17 は musl 1.2.3 のままでした。パッケージ一覧では、v3.24 の musl は 1.2.6-r3 です。

3.18 より古いイメージを使っているなら、この点だけでも更新する理由になります。なお、3.18 もリリース表では通常のサポート期間を過ぎています。コンテナの musl のバージョンは、次のコマンドで確認できます。

docker run --rm alpine:3.24 ldd --version

表示された musl のバージョンが 1.2.4 以上か確認してください。

違いはもう一つ残っています。musl の wiki によると、musl のリゾルバは設定されたネームサーバーすべてに同時に問い合わせ、最初に届いた応答を使います。このため、/etc/resolv.conf に社内専用の DNS と公開 DNS を並べると、どちらの答えが使われるかは応答の速さで決まります。社内の名前が引けたり引けなかったりする場合は、まずこの設定を疑ってください。

論点3:スレッドのスタックとメモリアロケータ

musl の wiki によると、musl の既定のスレッドスタックサイズは 128 KiB です(musl 1.1.21 より前は 80 KiB)。深い再帰や大きなローカル配列を使うスレッドがあるなら、実際に割り当てられるスタックサイズにその処理が収まるか確認が必要です。128 KiB は固定の上限ではありません。pthread_attr_setstacksize でスレッドのスタックサイズを指定でき、musl 1.1.21 以降は PT_GNU_STACK プログラムヘッダーで既定値を大きくすることもできます。

メモリアロケータ(malloc の実装)は、musl 1.2.1(2020年8月4日)で「mallocng」という新しい実装に置き換わりました。

ですから「Alpine は遅い」とも「もう速い」とも、一般論としては言えません。答えを出す方法は一つです。自分のアプリに同じ負荷をかけ、debian の slim イメージと Alpine イメージの両方で測ってください。スタックが原因なら、言語やランタイムの設定でスレッドのスタックサイズを指定できるか確認してください。

論点4:glibc 専用のバイナリと gcompat

配布元が glibc 向けにしかビルドしていないバイナリは、Alpine ではそのまま動かないことがあります。ELF バイナリ(Linux の実行ファイル形式)は、起動時に使う動的リンカのパスをファイルの中に持っています。そのパスが glibc のもので、Alpine に存在しなければ起動できません。どのリンカを要求しているかは、次のように確認できます。

apk add --no-cache binutils
readelf -l ./your-binary | grep interpreter

ld-linux を含むパスが表示されれば glibc 向けのバイナリです。ld-musl を含むパスなら musl 向けです。何も表示されなければ、静的リンクの可能性があります。

2026年11月7日の確認では、Alpine の wiki は、単純なバイナリなら gcompat を入れる方法を勧めていました。gcompat は、musl の上で glibc 互換の API(プログラムから呼び出す関数の集まり)を提供するライブラリです。v3.24 の main リポジトリには gcompat 1.1.0-r4 があります。

apk add --no-cache gcompat

gcompat を入れれば必ず動くという保証はありません。2026年11月7日の確認では、wiki 自体も gcompat を「単純なバイナリ向け」としていました。ほかの方法としては、chroot、distrobox や Podman のコンテナ、Flatpak や AppImage を挙げていました。gcompat を入れたら、実際の処理を一通り動かして確認してください。glibc 専用バイナリが中心のイメージなら、最初から debian の slim イメージを使うほうが、互換層を調べる時間を節約できます。

論点5:日本語のロケールと TZ=Asia/Tokyo

日本の開発者が気にするのは、日本語のロケールとタイムゾーンです。

まずロケールです。musl の wiki は、musl の既定のロケールを C.UTF-8 としています。一方、2026年11月7日に確認した Alpine の wiki によると、musl-locales パッケージが入れるのは、musl が出すコンソールメッセージの翻訳の限られたセットでした。これは glibc の ja_JP.UTF-8 のような完全なロケールデータとは別物です。v3.24 の main には musl-locales 0.1.0-r1 があります。2026年11月7日に確認した Alpine のパッケージのファイル一覧(v3.24、x86_64)では、このパッケージが /usr/share/i18n/locales/musl/ に入れるロケールファイルは cs_CZ、de_CH、de_DE、en_GB、en_US、es_ES、fi_FI、fr_FR、it_IT、nb_NO、nl_NL、pt_BR、pt_PT、ru_RU、sr_RS、sv_SE の16個(すべて .UTF-8)でした。ja_JP はこの一覧に入っていませんでした。

文字コード変換については、musl のリリースノートに JIS 系の文字コード変換の記録があります。たとえば 1.1.19 で、状態を持つ ISO-2022-JP の変換が入りました。

日本語ロケールを前提にしたアプリは、自分のコンテナで確認してください。

apk add --no-cache musl-locales
locale -a

Alpine の musl-locales 0.1.0-r1 は、実際のロケールファイルとは別に、ja_JP を含む固定の一覧を locale -a で表示します。名前が出るだけでは、対応するロケールデータがあることの確認にはなりません。日付の書式と文字列の並び順を実際のアプリで試してください。ロケールに依存する書式や並び順が必要なら、glibc のロケールで ja_JP.UTF-8 を生成できる debian の slim イメージを検討してください。

次にタイムゾーンです。Alpine の公式ドキュメントによると、タイムゾーンのデータは tzdata パッケージに入っています。小さなベースイメージには、このデータが入っていないことがあります。データがなければ、TZ=Asia/Tokyo を設定しても日本時間になりません。Dockerfile では次のように入れます。

FROM alpine:3.24
RUN apk add --no-cache tzdata
ENV TZ=Asia/Tokyo

ビルドしたイメージの中で、次のコマンドを実行してください。

ls /usr/share/zoneinfo/Asia/Tokyo
date

最初のコマンドでファイルが見つかるか、date の表示が JST になっているかを確認します。UTC のままなら、tzdata が入っていないか、TZ が設定されていません。

sshole から学ぶこと:musl と PAM なしの sshd は独立したリスク

2026年11月5日、Alpine は OpenSSH のパスワード認証回避について告知を出しました。研究者 oz9un の公開リポジトリ名は sshole です。同じ日に Portable OpenSSH 10.6p2 が公開されました。リリースノートは、musl libc を使い PAM を無効にした Linux システムに影響する、重大なセキュリティ対策だと説明しています。OpenSSH によると、根本原因は sshd の外にあります。ただし sshd 経由で悪用できるので、sshd 側で回避しています。

ニュースの経緯より大事なのは、問題の形です。PAM(Pluggable Authentication Modules、ログイン認証を外部モジュールに任せる仕組み)を使わない sshd では、パスワードの照合を sshd とその下の C ライブラリが行います。そのため、C ライブラリが glibc か musl かによって、認証の動作が変わる可能性があります。glibc の上で長く使われてきた組み合わせと、musl の上で PAM を使わない組み合わせは、別のリスクとして扱う必要があります。

Alpine の告知によると、影響を受けるのは次の条件がすべてそろった場合だけです。

  • Alpine の openssh-server(PAM なしでビルド)を使っている。または openssh-server-pam を UsePAM no で使っている。
  • PasswordAuthentication yes になっている。
  • パスワード欄が * のアカウントがある。たとえば root です。ただし root へのパスワードログインには PermitRootLogin yes も必要です。
  • $5$ または $6$ のハッシュを持つローカルアカウントが一つ以上ある。

openssh-server-pam を UsePAM yes で使っている場合は影響を受けません。Alpine の告知によると、root はおよそ Alpine 3.16 以降、ベースイメージで root:* として出荷されています。Alpine が確認した範囲は 3.21 から 3.24 と edge、それより古い 3.16 から 3.20 です。

研究者の README は、musl のシステムとして alpine、openwrt、buildroot、yocto、postmarketos、void-musl、chimera を挙げています。影響を受けないものとしては、glibc、PAM、PasswordAuthentication no、openbsd、freebsd、macos を挙げています。研究者の解説ページは、debian、ubuntu、fedora のような glibc のディストリビューションは問題ないとしています。dropbear も対象外としています。それでも、Ubuntu や Debian のホストで確実を期すなら、自分の sshd 設定の UsePAM の値と、使っている C ライブラリを確認してください。

悪用の状況について、Alpine の告知、OpenSSH のリリースノート、研究者の README のどれも、実際の攻撃で悪用されたとは書いていません。Alpine は告知の中で、CVE 番号はまだないと書いています。ただし、現在は CVE-2026-123158 が付与されています。2026年11月9日に更新された CVE レコードの CISA による追記は、悪用状況を「active」としています。

一次情報は次のとおりです。

自分のコンテナで sshd を確認する

まず前提として、普通のアプリ用コンテナでは sshd は動いていません。確認が必要なのは、コンテナの中で sshd を動かしている場合、Alpine を VM や実機で使っている場合、README が musl 系として挙げる機器で OpenSSH の sshd を使っている場合です。ここのコマンドは、sshd が動いている Alpine 環境の中で root として実行します。結果は環境によって違うので、出力を見て自分で判断してください。

C ライブラリと sshd のパッケージを確認します。

ldd --version
apk list --installed | grep openssh

sshd が実際に使っている設定を確認します。

sshd -T | grep -Ei '^(usepam|passwordauthentication)'

パスワード欄が * のアカウントと、$5$ か $6$ のハッシュを持つアカウントを一覧にします。

awk -F: '$2 == "*" {print $1}' /etc/shadow
awk -F: '$2 ~ /^\$[56]\$/ {print $1}' /etc/shadow

両方のコマンドでアカウント名が出て、passwordauthentication yes で、しかも PAM なしの構成なら、告知の条件に当てはまります。

更新と再起動、そしてバージョン番号の落とし穴

Alpine の告知が示す手順は、パッケージを更新してから sshd を再起動することです。

apk upgrade -U -a
rc-service sshd restart

告知によると、パッケージを更新しても、動いている sshd は再起動されません。そのため、更新のあとに自分で再起動する必要があります。コンテナの場合は、Dockerfile の中で更新したイメージをビルドし直し、コンテナを作り直すのが基本です。

注意が必要なのは、修正済みパッケージのバージョン番号です。Alpine の修正は、各ブランチにある既存の OpenSSH にパッチを当てたものです。安定ブランチは 10.6p2 に上げたわけではありません。告知の表にある修正済みバージョンは次のとおりです。

  • edge:10.6_p2-r0
  • 3.24:10.3_p1-r2
  • 3.23:10.2_p1-r1
  • 3.22:10.0_p1-r11
  • 3.21:9.9_p2-r1
  • 3.20:9.7_p1-r6
  • 3.19:9.6_p1-r3
  • 3.18:9.3_p2-r4
  • 3.17:9.1_p1-r7
  • 3.16:9.0_p1-r6

「OpenSSH 10.6p2 より古ければ脆弱」と判定すると、修正済みである 3.24 の 10.3_p1-r2 も脆弱と判定されてしまいます。判定するときは、自分のブランチの修正済みバージョンと比べてください。パッケージのリビジョン(-r のあとの数字)まで比べる必要があります。

すぐに更新できない場合の回避策は、PasswordAuthentication no にして sshd を再起動することです。必ず先に、鍵でログインできることを確認してください。確認せずにパスワード認証を切ると、自分が締め出されます。告知は、パスワードのないアカウントについて Accepted password for という記録が /var/log/messages にないか確認するよう勧めています。

grep 'Accepted password for' /var/log/messages

パスワード認証を切る判断と鍵の運用は、VPS の SSH を安全にする設定 で詳しく説明しています。この問題から得られる長く役に立つ教訓は一つです。musl の上で PAM なしの sshd を公開するなら、パスワード認証は最初から無効にしておいてください。条件が一つ欠けるだけで、この種の問題の影響は受けません。

FAQ

Docker の alpine イメージは今でも使わないほうがいいですか?

一律に避ける理由は、もうありません。DNS の TCP フォールバックは musl 1.2.4 で入り、Alpine 3.18.0 以降に含まれています。numpy、pandas、grpcio の最新版は、musllinux のホイールを PyPI に出しています。ただし、glibc 専用のバイナリ、日本語ロケールへの依存、多数スレッドでの性能、apt 前提のデバッグ手順は、今も自分の環境で確認が必要です。どれかに当てはまるなら、debian の slim イメージを選んでください。

Alpine で pip install が遅いのはなぜですか?

使っている Python のバージョンとアーキテクチャに合うホイールがなく、互換性のあるプラットフォーム非依存のホイールもなければ、pip が利用可能なソース配布物からビルドするためです。対象の Alpine 環境で pip download <パッケージ名> --only-binary=:all: --no-deps -d /tmp/wheels を実行すると、ホイールのないパッケージはエラーになります。これで確認できるのは指定したパッケージだけです。--no-deps は依存パッケージを対象外にするので、ビルドの原因を調べるには依存パッケージも確認してください。たとえば numpy 2.5.4 は Python 3.11 向けのホイールを出していません。

Alpine のコンテナで TZ=Asia/Tokyo が効かないのはなぜですか?

イメージにタイムゾーンのデータが入っていない可能性があります。Alpine ではこのデータは tzdata パッケージに入っているので、Dockerfile に RUN apk add --no-cache tzdata を追加してください。そのあと、ls /usr/share/zoneinfo/Asia/Tokyo でファイルがあるか、date が JST を表示するかを確認します。

Ubuntu の VPS は今回の OpenSSH の問題の影響を受けますか?

研究者の README は、glibc の環境と PAM を使う環境を対象外としています。解説ページも ubuntu、debian、fedora は問題ないと名指ししています。Alpine の告知では、影響を受ける条件は次の五つがすべてそろうことです。musl、PAM なしの sshd、有効なパスワード認証、パスワード欄が * のアカウント、$5$ か $6$ のハッシュを持つアカウントです。ただし、VPS の上の Alpine コンテナが sshd を動かしているなら、そのコンテナは別に確認してください。

この記事は役に立ちましたか?