ランタイム・クリーンアップ

2024/05/01

コンテナイメージのクリーンアップを終えつつある。
ソフトウェア開発は60年以上の歴史を持つが、いまだに不要なコードやモジュールを削除する一般的な手法がない。

「ぜったいに使われない」ことを保証するロジックがないため、イメージ削除は手作業にならざるを得ない。

コンテナイメージは目的別にパックした専用OSと言える。従来の多くの利用環境では、1台のハード・1つのOS上に多目的のソフトウェアを混載するセットアップが主流だ。アプリケーションの追加やバージョンアップは必要な一方、削除手法が手薄であるがために、もう不要なのに消せないモジュールが累積しがちだった。

コンテナは各アプリケーションに必要なモジュールセットのみをパックするため、実行時には不要なソフトウェアを排除しやすくなる。古いソフトウェアは脆弱性と強く関連するため、特にセキュリティ確保に必要な機能だ。

そして最後に産業廃棄物のような過去のコンテナイメージが残る。いずれはクリーンアップ用ツールが登場するだろうと考えていたが、何年待っても進展がない。
削除操作には、未来において失っていることのリスクをいま評価する必要があり、自動化できない宿痾になっている。

不要になった主なジャンル

クリーンアップの過程で不要なものを積極的に廃止したのだが、とくにバリエーションが多かったもののうちJavascriptが1種類のみになり、Rubyは実質0種類になった。

Javascriptは優勝劣敗が進んだ

Javascript開発の変遷を書いた時点では、Javascriptの新興開発ツールが無数に登場して見通しが悪かった。3年ほど経過したいま、新興ツール群は総崩れの情勢であり、何も変化は起きなかったとも言える。

フロントエンド開発は「長い目で見るとアウトプットが何もない」という珍騒動がよく起きる。

JavascriptはWebブラウザ向けの開発で必要になる。過去を振り返って大局的に見ると npmパッケージレポジトリが最重要であり、npmを置き換えるレポジトリが確立しなかったことで Node.jsが防衛王者になった。

Denoはnpmとの互換性を向上する方針に舵を切ったため、実質的な守備範囲がビルドスクリプトのインタープリターに縮小した。その結果、コンテナイメージに同梱しておき、対応できるプロジェクトはDenoを用いて構築する位置づけに落ちついた。
npmクライアントがDeno上で安定動作する状況になれば、Node.jsの廃止も可能だろう。Denoの方が合理的に設計されているため、選べる状況ならDenoを優先したい。

Node.jsやDenoの競争がトーンダウンしたのは、ブラウザ向けコンパイラの主機能を esbuildswcが担っていることが大きい。これらはgoやRustで作られており、ブラウザ向けJavascript開発の実行環境がもはやJavascriptではなくなっている。

そのためコンテナイメージについては、単にDebian標準のnodejsをインストールするだけで実用上の不足がなく、非常に簡素になった。

Rubyコンテナは不要化した

かつてサーバーサイド技術はRuby on Rails主体でスタートしたのだが、 Web開発の変遷で書いたとおりRubyやPython, PHP, Javaといった言語はコンテナに適していないため廃止した。
今回のイメージクリーンアップ作業で使用状況を確認していった結果、無数にあったRubyのコンテナイメージはゼロになった。

削除したイメージの多さから分かるのは、前提ライブラリのセットアップに多大な手間をとられていたということだ。
Ruby言語が年1回バージョンアップするほか、Ruby on Railsは積極的に互換性のない新機能を採用し、他のライブラリもそれぞれのタイミングで更新される。

Rubyなどのスクリプト言語の難点は、主要なライブラリがC言語で書かれており、セットアップの再現性が落ちる点にもある。コンテナイメージのビルドは頻繁に失敗していた。
またC言語が メモリ安全ではないことから、Cライブラリを同梱するRubyもまた断続的にセキュリティ脆弱性が発見され、コンテナイメージの更新が欠かせない。

このようにサーバーサイドではメンテナンスコストが非常に大きいため使わなくなったが、 LucaSuiteのようなCLIツールではRubyは実用的と言える。

コアのRuby機能に限定したスクリプトの場合、前提ライブラリのバージョンを問わなくなるため、単にDebian標準のrubyをインストールするだけで動作する。
ここまで簡素化できれば、Rubyの最新動向を追跡する必要はなくなる。

実行環境の課題

みずから書いたコードとは別に、動作に必要なランタイムやライブラリの維持・管理には無視できないコストがかかる。 依存するソフトウェアを減らすことが有効な対策となる。

ブラウザ向けJavascriptの開発は旧世代のツールを置き換えることで改善する。一方、サーバーサイドの開発は、広く普及している言語に選択肢がない。

組み合わせ再現性の複雑さとメモリ安全性の欠如は最終的に許容できないと考えられ、長い目で見ると各種言語の実行環境は大きく変化するはずだ。
おそらく WebAssemblyに集約されるのではないか。調達しやすいCPUの選択肢にARMが加わったこともWebAssemblyのような中間言語を後押しする要因となる。

ただしガベージコレクション(GC)は引き続きボトルネックとして残り続けるように思う。各言語が独自実装しているGCがWebAssemblyの実装に置き換わる。導入初期には、GCの違いによるトラブルを見ることになるだろう。

簡素なセットアップやメモリ安全は得られるだろう。WebAssembly導入で保守の課題の多くは解決するが、最終的にオブジェクト指向そのもののネックはおそらく残る。

オブジェクト指向言語は、オブジェクト初期化時からRAM消費が大きくGCまでオブジェクトを保持し続ける性質上、メモリのフットプリントが大きい。ハードの進化によりRAM容量と転送帯域に劇的な改善が起きるのであれば解決するが、これまでの経緯を考えると期待しづらい。

また、これらの進化にはまだまだ時間がかかる。
エンジニアの適応と引きかえにできるのであれば、Rustはこれらの課題を既に解決している。要するにランタイムは無いに越したことはない。

かつての選択は誤りだったのか?

いま、捨て去ることを論じている。

Rails上の機能の一部をブラウザ上に移し、Rustに換骨奪胎したうえで古いコードを撤去した。
新旧2つの実装の経験から、Railsには複雑なコンテナイメージ管理が必要だったことを理解できた。

RailsだけでなくPythonなども同様の状況であることを確認しており、今後これらを採用するつもりはない。

イメージ管理が更新のたびに便利になっていく性質の変更であったなら、Railsを捨て去ることに損失があるのだが、実際にはそうではない。
一部のライブラリの変更が別のパーツの動作を破壊するようなものであったり、下層にメモリ安全ではなかった部分が見つかってセキュリティ攻撃への防衛を迫られるような作業が多い。

RubyやPythonのWebフレームワークはいまだに人気がありベーシックなツールではあるのだが、振り返るとまったく健康的に見えない。
とくにメモリ安全性については、むこう5年ほどでルーズなスタンスが厳しく敵視される状況が来るように見ている。

要するに、Rustがコンパイラで一定のメモリ安全を保証するパラダイムを導入したことで、ネットワークアクセスするアプリケーションの最低ラインが引き上がったのではないか?
ネットワークアクセスの代表例はサーバーアプリケーションだが、程度の違いはあっても大多数のアプリケーションを包含する。

おそらく今、プログラミング言語は過渡期にあり、Rustがひと足速く未来を提示している。
より広い用途にはWebAssemblyが有望で、WebAssemblyにフィットする新たな言語が今後登場するのではないかと思う。

既存のポピュラーな言語はWebAssembly向けのサブセットが普及するのであれば生き残るだろうし、さもなければレガシーへと朽ちていくように見ている。

⁋ 2024/05/01↻ 2024/05/02