【Docker】Alpine は今後の開発には採用を避けたいと思った話

linux システム開発

Alpine Linux という OS をご存知でしょうか。
Debian系 とも RedHat系とも違うディストリビューションで、とても軽量なのが特徴です。

Alpine Linux の公式サイトはこちら。
https://www.alpinelinux.org/

その軽量さ故、Docker コンテナのベースイメージとして使われることもあります。

Docker Hub にて管理されている Alpine Linux はこちら。
https://hub.docker.com/_/alpine

が、プライベートおよび実務でもがっつり使ってみたところ、デメリットが非常に大きく、実プロダクトの開発にて採用するのは極めて慎重にした方がよいのでは? という印象です。

というか、メリットを享受できる場面は極めて局所的で、多くのプロジェクトは Alpine を使って得られるメリットよりも、デメリットの方が目立つようになるのではないかと思われます。

以下、Alpine の採用を見送った方がよいのではないかと考えた理由です。

apk というマイナーなパッケージマネージャーが採用されている

Linux のパッケージマネージャーには yum や apt、PHPを使うのであれば Composer や pecl といったパッケージマネージャーを使用する事が多いと思う。

新しく環境を作る場合、ストレートに全てが上手くいく事は滅多になく、プロジェクトで使いたいライブラリのバージョンを指定するためにパッケージの参照元を色々こねくり回したり、時には remi、EPEL、pickle といった用語に翻弄されしつつ、何度も何度もトライアンドエラーを繰り返し、苦労の果てにようやく完了する事が多いと思う。

必然的に色々なパッケージマネージャーを触ったり、パッケージマネージャーの設定を色々変えて実験したりする事が多くなるが、そんな時「遭遇した問題が、解決しやすいか」という事は、非常に重要になってくる。

具体的には、「ネットで検索した時、その情報がヒットしやすいか」という事が重要となる。

yum や apt と比較した時、apk はパッケージマネージャーとしてはマイナーで、必然的にネットに落ちている情報が少なく、解決策を見つけたり解決の糸口を見つける事が難しくなっている。

yum を使っても解決が難しいケースにはいくらでも遭遇するが、apk だとその苦労は、さらに拍車がかかる。

環境構築の難度なんてプロダクトの良し悪しに一切関係ないので、シンプルに済ませる事ができるのであれば是非そうしたいところだ。

また、apk は Android アプリの実行ファイルの名前でもあるので、ググった時にはそっちの情報の方がはるかにヒットしやすく、場合によっては情報のフィルタリングに苦労をさせられます。

パッケージマネージャーで管理しているライブラリのバージョンが古い事がある

apk でライブラリをインストールする事ができたとしても、新しいバージョンはインストールできない(パッケージマネージャーで管理されていない)ケースがある。

そんな場合どうするかというと、ソースを持ってきてコンパイルする必要がある。(ケースによりますが)

はっきり言って超絶やりたくない。

他のパッケージマネージャーを使えばコマンド一発で解決できる問題を、何故こんな面倒な事をしなければならないのか。

ash というマイナーなシェルが採用されている

ash というマイナーなシェルを採用しており、これがまた環境構築の難易度を上げている。

ちなみに自分は OSのデフォルトのシェルを、ほぼノンカスタマイズで使用しています。(Alias の設定ぐらいはするけど)

理由は、シェルに余計なカスタマイズを加えると環境構築時におかしな現象に遭遇する可能性が上がり、ただでさえも苦労する事が多い環境構築の難易度が無駄に上がってしまうのが嫌だからです。

一時期 fish というシェルが流行った事がありましたが、その情報は全無視してデフォルトのシェルを使い続けていました。
多くの場面で「シェルを弄る事によって得られるデメリット > シェルを弄る事によって得られるメリット」となっていると感じていたのが理由です。(※個人の感想です)

もちろん、シェルをカスタマイズする事で様々なメリットがある事は理解しているが、それでも環境構築時に妙なハマりポイントができてしまうのは十分あり得る事だと思う。
これが自分でカスタマイズし、どんな設定が入っていて、どんな動きをするかを完全に把握している状態なら問題はないが、他の人がカスタマイズしたシェルを使う場合、余計な苦痛を抱え込む事になってしまう。

マイナーなシェルを使う事は、時としてその苦痛を背負いこむ事と酷似する。

また、ash も apk(Alpine のパッケージマネージャー) 同様、ググラビリティが非常に低いので、問題が発生した時の解決には、余計な苦労が発生する事が多い。

マイナーディストリは色々つらい

そもそも、マイナーなディストリを使うと、上手くいかないケースも多い上、問題が発生した時に解決に非常に苦労をさせられる事も多い。

以下、いくつかの Linux をディストリを使ってみた時の記録です。

Linux:マイナーディストリをいくつか試した感想

色々触ってみたところ、結局 Ubuntu に落ち着きました。
ディストリはメジャーである事が正義。

Dockerfile作成が職人芸になりがち

上記の理由により、Dockerfile が非常に難解になりがちになる。

パッケージマネージャーもマイナー、シェルもマイナー、時としては妙なバッドノウハウが必要になる、など Debian系やRedHat系と比較した場合、無駄に技術負債が上がる印象です。

他のディストリと比較して、パフォーマンスは出ない(ことがあるらしい)

こんなのが見つかりました。

軽量Dockerイメージに安易にAlpineを使うのはやめたほうがいいという話

libcに一般的な互換性が不足しているからです。Ruby、Python、Node.jsなどでNativeモジュールをバンドルしているアプリケーションの場合、パフォーマンスの劣化や互換性の問題にぶち当たる場合があります。

実際、自分も Alpine をベースイメージに React の開発環境を作ってみたところ、npm run watch や watch-poll が重過ぎて、開発には使いづらかった印象です。
(作り方の問題だったかもしれませんが。幸い、趣味で触っている程度だったので、お蔵入りになりました。)

別案件にて、node-slims(node:16-slims) をベースにした環境を触ってみたところ、特にビルドの速度に不満はありませんでした。

正確に計測するには、全く同じ環境を Alpine と node-slims で作成し、双方の速度を測る、という方法になるかと思いますが、さすがにそこまでやる気力は無いですし、どっちかというと Alpine 推進側が「Alpine と node-slims で比較しても、ほとんど速度性能の差はないぜ!」という計測結果を出してくれるのを期待したいと思います。

リリース後、調査タスクが辛くなる

Fargate や ECS といった AWS のサービスを使えば、EC2 に色々なものをインストールせずとも、コンテナそのものをデプロイして使用できます。

ですが、「ローカルでは発生しなかったけど、本番環境やステージング環境では発生する問題」に直面する場面は数多く存在するので、そんな場合、コンテナにログインして調査したくなります。
(時々、「コンテナを使えば、ローカル環境と本番環境の差異は無くなり、環境の差異による苦労は消滅する!」といった意見を見る事もありますが、実際は、ローカル環境では AWSのリソースを使わず、本番環境では RDS・ElastiCache・SQS・SES といった AWSのサービスを使うので、その疎通部分や使用しているサービスそのものに設定不備があると、当然、アプリは上手く動きません。)

ですが、EC2 と違い、Fargate にデプロイしていると、ssh ログインのように気軽にログインする事ができません。
(Fargate は内部的には AWS管轄の EC2 にて管理されており、一般ユーザがそうやすやすとログインできないように制御されているらしい。)

「ECS Exec」というサービスを使えば Fargate にデプロイされたコンテナにログインできるようですが、これを使うためにはコンテナに SSMエージェントというアプリケーションをインストールする必要がある。
が、Alpine は SSM エージェントのサポート予定はなく、公式外の方法で運用するしか方法がない。

一応、こんなのが見つかりました。
Alpine Linux に amazon-ssm-agent をインストールする

amazon-ssm-agent は Alpine Linux をサポートする予定はないとのこと。

一応、インストールはできるみたいですが、正規の方法ではなく、迂回路を辿っています。

ちなみに私は上記の方法で上手く行きませんでした。
検索してヒットした内容が自分の環境と同一でない事は当然なので、エラーメッセージを手掛かりに解決策を探す必要があるのですが、Alpine は予測不能な事態が起こった時に、頼りにできる情報が、かなり限られてしまう印象があります。

AWS環境での検証が開始した時、コンテナにログインして調査ができないと、「ログを吐くプログラムを仕込んでデプロイを繰り返す」ぐらいしか取れる方法がなかったりするので、運用を考える上では極めて重要な点となる。

そして、デプロイ時間が 20分ぐらいかかったりすると、担当者の健康に確実に悪影響が出ます。

AWS Fargate を使う事を前提とするなら、コンテナログインの仕組みが正式サポートされていない Alpine は、それだけでもプロダクト採用を見送っていいレベルだと思う。

Docker公式の『Dockerfile 記述のベスト・プラクティス』にて、Debian イメージをお勧めしている

以下、公式サイトより引用。
http://docs.docker.jp/engine/userguide/eng-image/dockerfile_best-practices.html

イメージのベースは、できるだけ現時点での公式リポジトリを利用してください。

Debian イメージ がお勧めです。 このイメージはしっかりと管理されていて、充実したディストリビューションであるにもかかわらず、非常にコンパクトなものになっています(現在 150 MB 以下)。

公式で Debian をお勧めしている以上、「どうしても Alpine でなければいけない理由」がない限り、ベストプラクティスに沿った方がいいんじゃないかと思います。

そこまでデプロイの時間が短縮されない

Alpine を使って軽量化しても、実はデプロイ時間はそこまで短縮されない。

「軽量化しているので、短時間で素早くデプロイできるのでは?」と思いきや、デプロイが完了するまでには数多くのステップがあり、軽量化の恩恵を受けられる「コンテナのアップロード時間」は、そのうちのほんの1ステップでしかない。

しかも、コンテナをアップロードする時は、1つ1つアップするのではなく、ブロードキャスト配信のように展開されるようで、コンテナの配布は並列で処理され、実行時間にそれほど差が出ない。
具体的には、「100個のコンテナをデプロイする時は、1個デプロイする時の100倍時間がかかる」という計算にはならず、ほぼ同じ実行時間となる。
そのため、コンテナの軽量化によって得られる恩恵は、純粋にコンテナ 1個分で比較した時の量のみとなるようです。
(この辺、AWSに詳しい方に聞きました)

むしろ、「軽量化の裏では、実行速度を犠牲にしているのでは?」と思われる節もあり、そっちによるデメリットの方が目につきやすい。

コンテナの起動時間は早くなっているのかもしれないが、
「コンテナの起動時間は1分だけど、npm run watch が完了するまで 1分かかるコンテナ」と
「コンテナの起動にはは5分かかるけど、npm run watch は 1秒で終わるコンテナ」があった場合、
ほとんどの開発者は後者を選ぶと思う。

デメリットを振り返ってみて

気が付けば、かなりの量のデメリットを書くことになってしまいました。

「Alpine を使わなければ、簡単に回避できる」というのも多いのではないかと思います。

どんな状況下で Alpine を使った方がよいか

Alpine を採用するだけで、かなり開発コストおよび運用コストが上がる印象があります。
社内にインフラを専属で扱う部隊がいて、コンテナやデプロイ作業のスペシャリストがいる状態じゃないと、結構キツい気がします。

技術というよりバッドノウハウも多いので、バックエンドエンジニアが兼任で作業するには、かなり重い作業になります。

「インフラの構築は、外部のスペシャリストに依頼している」という状況だったとしても、Alpine の採用は慎重にした方がいいんじゃないかと思います。
多くの場合、こちらが気が付いて情報を引き出さない限りバッドノウハウは引き継がれないですし、抱え込んだ技術負債と向き合い続けるにも、解決が非常に困難なので、とんでもなく健康に悪いです。

最後に

お仕事で、あるエンジニアと Docker および環境構築の話をしている時、互いに Alpine には苦しめられた経験があって意気投合したので、せっかくだから文書化してみようと思い、エントリにしてみました。

もし、今後技術選定のフェーズで、「コンテナのベースイメージは、Alpine を採用しようと思います」という話があった場合、「やめろおおお!!! Debian(もしくは Debian Slim)を使ってくれええええー--!!」と提案するために、このエントリを書きました。

色々と反論もあるかと思いますが、それでもどうしても Alpine を使いたいなら、上記で挙げたデメリットを上回るメリットがある場合のみ、採用に踏み切った方がよいのではないかといのが率直な意見です。

コメント

タイトルとURLをコピーしました