Googleが数千台もある10年前のLinuxディストリをライブアップグレードした話

Googleが、太古のディストリビューションであるRed Hat 7.1から、10年新しいDebianベースのディストリビューションへ、ライブアップグレードした話を紹介する。 そのあと、自分の身の回りの環境と比較し、参考にすべきポイントを考察する。

原文は USENIX LISA の投稿論文だ。しかし、中身は論文体というよりは、事例の紹介といった適切かもしれない。

MERLIN, M. Live Upgrading Thousands of Servers from an Ancient Red Hat Distribution to 10 Year Newer Debian Based One. In Proceedings of the 27th conference on Large Installation System Administration (LISA) (2013), pp. 105–114

ペーパーのPDFが Live Upgrading Thousands of Servers from an Ancient Red Hat Distribution to 10 Year Newer Debian Based One | USENIX にて公開されている。 LinuxCon Japan 2013で、同テーマの発表があり、発表資料は http://marc.merlins.org/linux/talks/ProdNG-LC2013-JP/ProdNG.pdf にて公開されている。

この文章は、原文の邦訳では決してなく、自分が理解しやすいように組み立てなおした文章であるため、誤りないし誤解を生む可能性がある。正しい記述を知りたいのであれば、原文を参照してほしい。

概要

Googleでは太古のディストリであるRed Hat 7.1を独自パッチを重ねて10年間運用してきた。

Googleのサーバでは、アプリケーションはルートパーティションの外側のchroot jail環境で動作するため、ルートパーティションの変更がアプリケーションに影響しない。 これまで、Red Hat 7.1のゴールデンイメージのファイルをルートパーティションへ同期することにより、パッケージを更新してきた。

この方法は長い間驚くほどうまくいっていた。しかし、永久にアップグレードを延期するわけにはいかない。実際、モダンなシステム上でパッケージをビルドできないなどの問題があった。

そこで、DebianベースのProdNGという新しいディストリビューションへ移行することにした。 ProdNGは以下のような特徴をもつ。

  • セルフホスティング
  • ソースから完全にリビルドする
  • すべてのパッケージは不要な依存を取り除かれる (xml2, seLinux library, libacl2など)
  • upstart、dbus、plymouthなど複雑なものを取り入れない
  • 小さければそれだけ同期や再インストール、fsckが速くなる
  • 新しいパッケージが常に優れているわけではない。ときには古いもののほうがよいこともある
  • 隔離されている。新しいパッケージをビルドするたびに、ProdNGのchroot jail環境を作り、ビルドツールをインストールする

ProdNGへの移行の手順は以下のようなものだ。これらはOSの再起動なしに実施されている。

  1. ProdNG上でビルドしたDebianパッケージをRPMへ変換する
  2. 現行のRed HatディストリからX Serverやfonts、localesやman pageのような不要なパッケージの削除
  3. 現行ディストリのlibc 2.2.2から、ProdNGのlibc 2.3.6へのアップグレード
  4. chroot jail環境でスクラッチからビルドされた約150パッケージのアップグレード ProdNGと現行Red Hatイメージの両方に配置
  5. 現行Red HatイメージにしかないRPMをdebパッケージに変換 alien(1)と独自のchangelogコンバータの組み合わせ
  6. ここまでで、ProdNGが現行Red Hatイメージと同様に動作するようになったため、リグレッションテストをパスして、手動レビューしたのちにProdNGをデプロイする

これらの手順は、一言でいうと、本番環境で2つのディストリを並行して運用することなく、常に均一な状態を保って、新しいディストリに移行している。各ステップで数千台のホストを均一な状態に保つことは、非常に手間がかかり、全工程を数年かけてやり遂げた。

トピック

前節でペーパーの概要をまとめた。ここでは、概要から省いた、いくつかの興味深いトピックを紹介する。 これらのトピックに加えて、実際の移行時のトラブルや泥臭いlibcアップグレード方法など、興味深いトピックはいくつかある。

前提環境

Googleの本番Linuxはカーネル・デバイスドライバ、ユーザスペース、アプリケーションの3レイヤで管理されている。

カーネル・デバイスドライバはディストリビューションからは切り離されていて、頻繁に更新される。 このレイヤは別のチームがメンテナンスしており、今回の話には関係がない。

各アプリケーションはchroot jail環境で動作する。このjailはアプリケーションの動作に必要なすべてのプログラム、共有ライブラリ、データファイルを含む。

残りのユーザスペース部分は、initスクリプト、/usr/binバイナリなどだ。 OSのパッケージシステムはこの部分にのみ使われる。ペーパーでフォーカスしているのはこの部分になる。 1~2人でメンテナンスされているらしい。

(※ペーパーのタイトルに数千台のサーバとあるが、Googleのサーバはもっと多いはず。どのセクションのサーバの話なのかは書かれていなかった。)

従来のパッケージアップデート

最初期には、sshループでインストール・アップデートコマンドを走らせていた。 sshループは実行に時間がかかり、失敗するホストもあり、スケールしない。 一般的に、プッシュベースの手法は失敗するように運命付けられている。

次に、cronでapt-get/yumを実行した。だいたい期待した更新はできた。 しかし、数千台のサーバでapt-get/dpkg/rpm/yumを走らせていると、ランダムで失敗することがある。 更新中に再起動やクラッシュすると、パッケージデータベースが壊れた。 パッケージデータベースが壊れなくても、設定ファイルがコンフリクトしたり、パッケージアップデートによりホストを予期しない状態になったりなどの多くの問題があった。

ファイルレベルのファイルシステム同期ならば、どの状態からでもリカバーできて、パッケージマネージャーに依存しなくなる。 各サーバを均一に保ち、サーバ固有のパッケージと設定ファイルは同期するエリアの外に置く必要がある。 各サーバはサーバ固有ファイルのリスト(ネットワーク設定やresolve.conf、syslogなど)を持ち、リスト内のファイルは同期からは除外される。 ホスト全体にマスタイメージからrsyncするのはサーバ側(※おそらくマスタイメージを保有する中央サーバ)がスケールしない。 さらに、特定のファイルが変更されたときに、デーモンが再起動するためのトリガーが必要だ。

そのため、独自のrsyncライクなソフトウェアを書いた。基本的に、マスタイメージから全ホストへファイルレベル同期を行う。適切にシェルトリガーを設定できる。I/Oはスロットリングされるため、各ホストの負荷に影響はない。 (※ rsyncライクなソフトウェアとは、おそらく、多数のサーバへ同期をかけられるように、クライアント側を効率化したようなものではないか。各ホストでサーバソフトウェアが起動していて、マスタイメージを保有するホスト上のクライアントプロセスが各サーバへ接続し、ファイル同期を行う。これをrsyncでやると、1つのサーバに対して1つのクライアントプロセスを毎回起動することになり効率が悪い。)

ただし、この手法は各ホストが均一な状態である必要がある。前述のようにルートパーティション外のchroot jailにアプリケーションをデプロイしているからこそできるといえる。

このファイルレベルの同期はProdNGディストリの更新にも用いらている(はず)。ProdNGの場合は、マスターイメージがパッチを重ねたものではなく、毎回クリーンな環境でスクラッチでビルドされたイメージとなる。

どのディストリビューションがよいか

Debianは標準のRed Hatに比べて、多くのパッケージをもつ。(昔のRed Hat 9が1500パッケージで、当時のDebianが15000パッケージ。今日のFedore Core 18が、13500パッケージに対してDebian Testingが40000パッケージ。) Ubuntuは、upstartやplymouthのようなオプションでもなく、信頼もできないいくつかの複雑さを強制される。

ディストリのInitシステムについても、以下のような議論がなされている。

  • Sysv: /etc/rcX.d/Sxxdaemonのようなシーケンシャルなブートがわかりやすい。しかし、遅い。
  • Upstart: シンタックスが完全に異なる(シェルよりはよい)。ブート順の保証がない。何かがおかしかったらデッドロックすることがある。場合によっては、upstartは再起動を要求する状態に陥ることがある。デバッグが難しい。
  • Systemd: 大きな混乱をもたらす。Linuxシステムのブートの大きな再設計。Linuxの低レベルな多くのコア部分を置き換える。自動で依存関係を計算してくれるという理想に反して、手動な依存関係の解決が要求される。Upstart同様にブート順を指定できない。
  • Insserv: 再起動前に、insservは指定された依存を解析して、initスクリプトをS10やS20のようにリネームする。S10以下のすべてのスクリプトは同時に起動し、S20はS10xxのすべてが起動してから起動する。依存関係の可視化とレビューが簡単。ProdNGではこれが採用された。

はてなとの比較と考察

はてなの本番環境のOSはほぼすべてLinuxで、ディストリはCentOS5とDebianが混在している。 数年前からDebianに移行し始めたため、最近のサービスはすべてDebian上で動作している。

とはいえ、まだまだCentOS5のホストが数多く残っており、来年3月のEOLに向けて、今まさに撤退中というステータスだ。

レガシーOSを数多く抱えているという状況は、ペーパーに書かれていたGoogleの状況に近い。 ホスト数のオーダーも同じだ。

大きく異なるのは、レイヤごとに分離していないことだ。(というより一般的には、分離していない環境が多いと思う) はてなの環境では、カーネル、ユーザスペース、アプリケーションが密結合している。 OSをアップグレードしようと思うと、これらすべての変更を意識しないといけない。 特にApache + mod_perl と古いCPANモジュールを新しいOSで動作させるのが課題になっている。

レガシーOSの撤退と一口にいっても、環境により課題は異なり、Googleの事例は特殊にみえる。 どちらかというと、アップグレードの手法よりも、レイヤ別にOSを管理する手法を参考にしたい。 毎回EOLが迫るたびに、すべてをアップグレードするのは現実的とはいえない。

以前に、chrootベースのアプリケーションデプロイを提案 した。 アプリケーションを隔離するという方向性は間違ってないと思う。

マスターイメージのファイル同期による管理は、ChefやAnsibleのようなサーバ構成管理ツールで管理する手法とは大きく異なる。 サーバ構成管理ツールにより、プログラマブルに柔軟な構成ができる一方で、複雑化しやすく、10年もつ仕組みとは言い難い。

そこで、ルートパーティションのみ、ファイル同期で更新して、アプリケーションはchroot jailのようなコンテナで動かす。この手法が、自分の中で長期の運用に耐えやすい手法なのではないかと思えてきた。

あとがき

Googleでさえ、太古のディストリビューションのメンテナンスに苦しんでいる。そして、解決するためにかなり泥臭い方法を選択しているというのは、サーバの構成管理の本質的な難しさが現れているのかもしれない。

手法自体は泥臭い一方で、できるかどうかわからなかったライブアップグレードする技術力があるのはさすがで、既存のディストリにそのまま依存すべきではなくProdNGという新しいディストリをデザインするところもさすがといえる。

やりたいことを実現できる技術力と、長期運用するためにディストリはどうあるべきなのかというビジョンと、レガシーな環境を技術で解決するぞというやっていく気持ちを感じられた。