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という新しいディストリをデザインするところもさすがといえる。

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

自作Linuxコンテナの時代

最近、Docker以外のコンテナ型仮想化技術の流れとして、自作コンテナエンジンの時代が来るのではないかと感じている。

自作コンテナエンジンとは、コンテナ型仮想化技術を構成する個々の要素技術を組み合わせ、自分の用途にあわせて最適化したコンテナエンジンのことだ。 他のOSのコンテナ仮想化技術について疎いため、以下ではLinuxに限定して話を進める。

続きを読む

Linuxサーバにログインしたらいつもやっているオペレーション

主にアプリケーション開発者向けに、Linuxサーバ上の問題を調査するために、ウェブオペレーションエンジニアとして日常的にやっていることを紹介します。 とりあえず調べたことを羅列しているのではなく、本当に自分が現場で使っているものだけに情報を絞っています。 普段使っているけれども、アプリケーション開発者向きではないものはあえて省いています。

MySQLやNginxなど、個別のミドルウェアに限定したノウハウについては書いていません。

ログインしたらまず確認すること

他にログインしている人がいるか確認(w)

wコマンドにより現在サーバにログインしてるユーザ情報をみれます。

$ w
 11:49:57 up 72 days, 14:22,  1 user,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
y_uuki   pts/0    x.x.x.x 11:49    1.00s  0.02s  0.00s w

再起動などシステム影響がありうるオペレーションをする場合、同僚が他の作業をしていないかどうかをチェックします。

サーバの稼働時間の確認 (uptime)

サーバが前回再起動してから現在までの稼働している時間をみれます。下記の例の場合、72日間稼働しています。

$ uptime
 11:55:39 up 72 days, 14:28,  1 user,  load average: 0.00, 0.02, 0.05

DOWNアラートがきたときに、このコマンドをよく打ちます。DOWNアラートが来ていても、実際にOSが再起動していないということはよくあります。 これは監視システムと対象ホスト間のネットワーク不調が原因で、DOWNアラートが来ることがあるためです。 AWS EC2のようなVM環境の場合、ハイパーバイザ側のネットワークスタック不調などにより、監視システムと疎通がなくなるということがありえます。

uptimeコマンドにより、前回のシャットダウンから現在までの経過時間を知れます。上記の例の場合、72日間ですね。 OSが再起動している場合、MySQLサーバのようなデータベースのデータが壊れている可能性があるので、チェックする必要があります。

プロセスツリーをみる (ps)

サーバにログインしたときに必ずと言っていいほどプロセスツリーを確認します。 そのサーバで何が動いているのかをひと目で把握できます。

ps auxf

ps コマンドのオプションに何を選ぶかはいくつか流派があるようです。自分はずっと auxf を使っています。 aux でみてる人もみたりしますが、 f をつけるとプロセスの親子関係を把握しやすいのでおすすめです。 親子関係がわからないと、実際には同じプロセスグループなのに、同じプロセスが多重起動していておかしいと勘違いしやすいです。

...
root     25805  0.0  0.1  64264  4072 ?        Ss    2015   0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
nginx    25806  2.6  0.7  70196 15224 ?        S     2015 2691:21  \_ nginx: worker process
nginx    25807  2.7  0.8  71700 16416 ?        S     2015 2725:39  \_ nginx: worker process
nginx    25808  2.7  0.7  70672 15336 ?        S     2015 2725:30  \_ nginx: worker process
nginx    25809  2.7  0.7  71236 15976 ?        S     2015 2709:11  \_ nginx: worker process
nginx    25810  2.6  0.9  74084 18888 ?        S     2015 2646:32  \_ nginx: worker process
nginx    25811  2.6  0.6  69296 14040 ?        S     2015 2672:49  \_ nginx: worker process
nginx    25812  2.6  0.8  72932 17564 ?        S     2015 2682:30  \_ nginx: worker process
nginx    25813  2.6  0.7  70752 15468 ?        S     2015 2677:45  \_ nginx: worker process
...

pstree でも可。ただし、プロセスごとのCPU利用率やメモリ使用量をみることも多いので、ps をつかっています。

NICやIPアドレスの確認 (ip)

ifconfig コマンドでもよいですが、ifconfig は今では非推奨なので、 ip コマンドを使っています。 ipコマンドはタイプ数が少なくて楽ですね。

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:16:3e:7d:0d:f9 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.10/22 brd z.z.z.z scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.0.20/22 scope global secondary eth0
       valid_lft forever preferred_lft forever

上記の出力例ではよくみているとセカンダリIPがついています。セカンダリIPがついているということは、なんらかの特殊な役割をもつホストである可能性が高いですね。 特にVIPを用いた冗長構成をとっている場合、VIPがついているならばマスタであると判断できます。 社内では、eth0に対するセカンダリIPや、eth1にプライマリIPをVIPとして利用することが多いです。

このような事情もあり、サーバにログインするとIPアドレスのチェックを癖でやってしまっています。

ファイルシステムの確認(df)

ファイルシステムの容量確認は df コマンドを使います。-T をつけると、ファイルシステムの種別を確認できます。-h をつけると、サイズ表記がヒューマンリーダブルになります。

$ df -Th
Filesystem                                             Type      Size  Used Avail Use% Mounted on
rootfs                                                 rootfs     20G  2.4G   17G  13% /
udev                                                   devtmpfs   10M     0   10M   0% /dev
tmpfs                                                  tmpfs     3.0G  176K  3.0G   1% /run
/dev/disk/by-uuid/2dbe52e8-a50b-45d9-a2ee-2c240ab21adb ext4       20G  2.4G   17G  13% /
tmpfs                                                  tmpfs     5.0M     0  5.0M   0% /run/lock
tmpfs                                                  tmpfs     6.0G     0  6.0G   0% /run/shm
/dev/xvdf                                              ext4     100G   31G    69G   4% /data/redis

dfコマンドにより、ディスク容量以外にも、いくつかわかることがあります。

例えば、MySQLのようなデータをもつストレージサーバは、本体とは別の専用のディスクデバイスをマウントして使う(EC2のEBSボリュームやFusion-IOの ioDriveなど)ことがあります。

上記の出力例の場合、/data/redis に着目します。前述の「プロセスツリーをみる」により、Redisが動いていることもわかるので、RedisのRDBやAOFのファイルが配置されていると想像できます。

負荷状況確認

次は、Linuxサーバの負荷を確認する方法です。そもそも負荷とは何かということについて詳しく知りたい場合は、「サーバ/インフラを支える技術」の第4章を読むとよいです。

Mackerelのような負荷状況を時系列で可視化してくれるツールを導入していても、以下に紹介するコマンドが役に立つことは多いと思います。 秒単位での負荷の変化やCPUコアごと、プロセスごとの負荷状況など、可視化ツールで取得していない詳細な情報がほしいことがあるからです。

Mackerelである程度あたりをつけて、サーバにログインしてみて様子をみるというフローが一番多いです。

top

top -c をよく打ってます。 -c をつけると、プロセスリスト欄に表示されるプロセス名が引数の情報も入ります。(psコマンドでみれるのでそっちでも十分ですが)

さらに重要なのが、top 画面に遷移してから、 キーの 1 をタイプすることです。1 をタイプすると、各CPUコアの利用率を個別にみることができます。学生時代のころから当たり前に使ってたので、たまにご存知ない人をみつけて、意外に思ったことがあります。(mpstat -P ALL でもみれますが)

かくいう自分も これを書きながら、top の man をみてると知らない機能が結構あることに気づきました。

top - 16:00:24 up 22:11,  1 user,  load average: 1.58, 1.43, 1.38
Tasks: 131 total,   2 running, 129 sleeping,   0 stopped,   0 zombie
%Cpu0  : 39.7 us,  4.1 sy,  0.0 ni, 48.6 id,  0.3 wa,  0.0 hi,  6.9 si,  0.3 st
%Cpu1  : 24.4 us,  1.7 sy,  0.0 ni, 73.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  : 14.7 us,  1.7 sy,  0.0 ni, 83.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  2.0 sy, 98.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu4  :  3.3 us,  0.7 sy,  0.0 ni, 96.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  :  2.0 us,  0.3 sy,  0.0 ni, 97.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu6  :  2.3 us,  0.3 sy,  0.0 ni, 97.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu7  :  0.7 us,  0.3 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   7199160 total,  5764884 used,  1434276 free,   172496 buffers
KiB Swap:        0 total,        0 used,        0 free,  5161520 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
16659 root      39  19  8612  632  440 R 100.3  0.0   0:12.94 /bin/gzip
 2343 nginx     20   0 60976  14m 1952 S  23.2  0.2 112:48.51 nginx: worker process
 2328 nginx     20   0 61940  15m 1952 S  19.9  0.2 111:49.12 nginx: worker process
 2322 nginx     20   0 61888  15m 1952 S  19.3  0.2 113:44.95 nginx: worker process
 2324 nginx     20   0 61384  14m 1952 S  16.6  0.2 113:30.52 nginx: worker process
 2340 nginx     20   0 61528  14m 1952 S  11.0  0.2 114:02.36 nginx: worker process
 ...

上記の出力例は結構おもしろいタイミングを示していて、下のプロセスリストをみると、 /bin/gzip がCPU 100%使いきっています。 これはlogrotateがアクセスログを圧縮している様子を表しています。 上段のCPU利用率欄をみると、Cpu3 が 0.0 idとなっています。 id は idle の略であり、 id が 0% ということはCpu3を使いきっているということです。これらの状況から gzip プロセスが Cpu3 を使いきっているということが推測できます。

CPU利用率欄には他にも、us, sy、ni、wa、hi、si、stがあります。(カーネルバージョンにより多少異なります) これらは、CPU利用率の内訳を示しています。よくみるのは、us、sy、waの3つですね。

  • us(user): OSのユーザランドにおいて消費されたCPU利用の割合。userが高いということは、アプリケーション(上記の場合nginx)の通常の処理にCPU処理時間を要していることです。
  • sy(system): OSのカーネルランドにおいて消費されたCPU利用の割合。systemが高い場合は、OSのリソース(ファイルディスクリプタやポートなど)を使いきっている可能性があります。カーネルのパラメータチューニングにより、負荷を下げることができるかもしれません。fork 回数が多いなど、負荷の高いシステムコールをアプリケーションが高頻度で発行している可能性があります。straceでより詳細に調査できます。
  • wa(iowait): ディスクI/Oに消費されたCPU利用の割合。iowaitが高い場合は、次の iostat でディスクI/O状況をみましょう。

基本は各CPUコアのidleをざっと眺めて、idle が 0 に近いコアがないかを確認し、次に iowait をみてディスクI/Oが支配的でないかを確認し、user や system をみます。

ちなみに、自分は si (softirq) についてこだわりが強くて、 Linuxでロードバランサやキャッシュサーバをマルチコアスケールさせるためのカーネルチューニング - ゆううきブログ のような記事を以前書いています。

iostat

ディスクI/O状況を確認できます。-d でインターバルを指定できます。だいたい5秒にしています。ファイルシステムのバッファフラッシュによるバーストがあり、ゆらぎが大きいので、小さくしすぎないことが重要かもしれません。 -x で表示する情報を拡張できます。

$ iostat -dx 5
Linux 3.10.23 (blogdb17.host.h)     02/18/16    _x86_64_    (16 CPU)

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     3.09    0.01    3.25     0.13    36.27    22.37     0.00    1.45    1.59    1.45   0.52   0.17
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     7.99     0.00    0.07    0.07    0.00   0.07   0.00
xvdf              0.01     7.34   49.36   33.05   841.71   676.03    36.83     0.09    8.08    2.68   16.13   0.58   4.80

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     3.20    0.40    2.80     2.40    30.40    20.50     0.00    0.25    0.00    0.29   0.25   0.08
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
xvdf              0.00     5.00  519.80    4.00  8316.80    96.00    32.12     0.32    0.61    0.60    1.40   0.52  27.36

iostat コマンドは癖があり、1度目の出力はディスクデバイスが有効になってから現在まのの累積値になります。 現在の状況を知る場合は、2度目以降の出力をみます。

自分の場合、IOPS (r/s、w/s)と%utilに着目することが多いです。 上記の場合、r/s が519と高めで、%util が 27%なので、そこそこディスクの読み出し負荷が高いことがわかります。

netstat / ss

netstat はネットワークに関するさまざまな情報をみれます。 TCPの通信状況をみるのによく使っています。

-t でTCPの接続情報を表示し、 -n で名前解決せずIPアドレスで表示します。-n がないと連続して名前解決が走る可能性があり、接続が大量な状況だとつまって表示が遅いということがありえます。(-n なしでも問題ないことも多いので難しい)

-l でLISTENしているポートの一覧をみれます。下記の場合、LISTENしているのは 2812, 5666, 3306, 53549, 111, 49394, 22, 25 ですね。

$ netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:2812            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:5666            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:53549           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:49394           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN
tcp6       0      0 :::54282                :::*                    LISTEN
tcp6       0      0 :::111                  :::*                    LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::53302                :::*                    LISTEN
tcp6       0      0 :::25                   :::*                    LISTEN

TCPの全部のステートをみるには -a を指定します。 -o はTCP接続のタイマー情報、 -pはプロセス名の表示 (-p には root権限が必要) ができます。

$ sudo netstat -tanop
...
tcp        0      0 10.0.0.10:3306         10.0.0.11:54321        ESTABLISHED 38830/mysqld keepalive (7190.69/0/0)
tcp        0      0 10.0.0.10:3306         10.0.0.12:39150       ESTABLISHED 38830/mysqld keepalive (7157.92/0/0)
tcp        0      0 10.0.0.10:3306         10.0.0.13:49036         TIME_WAIT  38830/mysqld timewait (46.03/0/0)
tcp        0      0 10.0.0.10:3306         10.0.0.14:41064         ESTABLISHED 38830/mysqld keepalive (7223.46/0/0)
tcp        0      0 10.0.0.10:3306         10.0.0.15:34839        ESTABLISHED 38830/mysqld keepalive (7157.92/0/0)
...

一番、よくみるのは、このホストはどのホストから接続されているかです。 なぜか本番サーバなのに、ステージングサーバから接続されているというようなことがわかったりすることもあります。

他にも、単にやたらと接続が多いなとかざっくりした見方もします。そのときに、TCPのステートでESTABLISHED以外がやたらと多くないかなどをみたりします。

netstat は非推奨なので、ss コマンドをつかったほうがよいようです。 ただし、自分の場合、ss コマンドの出力の余白の取り方があまり好きではないので、netstat をつかっています。

ログ調査

いうまでもなくログ調査は重要です。 ログをみるためには、OSや、各種ミドルウェア、アプリケーションが吐くログがどこに履かれているのかを知る必要があります。

基本的には /var/log 以下を眺めてそれっぽいものをみつけて tail してみます。

/var/log/messages or /var/log/syslog

まずはここを見ましょう。カーネルやOSの標準的なプロセスのログをみることができます。他にもcron実行するコマンドの標準出力や標準エラー出力を明示的に logger にパイプしている場合などはここにログが流れます。

/var/log/secure

ssh 接続の情報がみれます。他の人がssh接続しているのに接続できない場合、ここに吐かれているログをみると原因がわかることがあります。

/var/log/cron

cronが実行されたかどうかがわかります。ただし、cronが実行したコマンドの標準出力または標準エラー出力が /var/log/cron に出力されるわけではなく、あくまでcronのスケジューラが動いたかどうかがわかるだけです。cronが実行したコマンドの標準出力または標準エラー出力はどこに出力されるか決まっているわけではなく、crontab内でloggerコマンドにパイプしたり、任意のログファイルにリダイレクトしたりすることになります。

/var/log/nginx, /var/log/httpd, /var/log/mysql

ミドルウェアのログは /var/log/{ミドルウェア名} 以下にあることが多いです。 特によくみるのはリバースプロキシのアクセスログやDBのスロークエリログですね。

自分が開発しているシステムのログの位置は確認しておいたほうがよいです。

/etc

/var/log 以下にログを吐くというのは強制力があるものではないので、ログがどこにあるのか全くわからんということがあります。

ログファイルのパスは設定ファイルに書かれていることもあります。 設定ファイルは /etc 以下にあることが多いので、 /etc/{ミドルウェア名} あたりをみて、設定ファイルの中身を cat してログファイルのファイルパスがないかみてみましょう。

lsof

/etcをみてもわからんというときは最終手段で、lsofを使います。 ps や top でログをみたいプロセスのプロセスIDを調べて、lsof -p <pid> を打ちます。 そのプロセスが開いたファイルディスクリプタ情報がみえるので、ログを書き込むためにファイルを開いていれば、出力からログのファイルパスがわかります。

他には例えば、daemontools を使っていると、 /service 、もしくは /etc/service 以下に multilog が吐かれているなど、使用しているスーパーバイザによっては、特殊なディレクトリを使っている可能性があります。

関連

あとがき

ここに紹介した内容はあくまでy_uukiが普段やっていることであり、どんな状況でも通用するようなベストプラクティスではありません。 むしろ、自分はこうやってるとか、こっちのほうが便利だということがあれば教えてほしいです。

オペレーションエンジニアは息を吸うように当たり前にやっているのに、意外とアプリケーションエンジニアはそれを知らない、ということはよくあることだと思います。オペレーションエンジニア同士でも、基本的過ぎてノウハウを共有していなかったということもあるのではでしょうか。

知ってる人は今さら話題にするのもなとかみんな知ってるでしょとか思いがちです。しかし、これだけ毎日のように新しい話題がでてくると、基本ではあっても、意外と見落としているノウハウは結構あると思います。見落としていた話題にキャッチアップできるように、新卒研修の時期にあわせて定期的に話題になるとよいですね。

昨今は、No SSHと呼ばれるように、サーバにSSHしないという前提で、インフラを構築する発想があります。 少なくとも現段階では、No SSHは一切サーバにSSH(ログイン)しないという意味ではなく、あくまでサーバ投入や退役など、通常のオペレーションをするときにSSHしないという意味だと捉えています。 もしくは、Herokuのユーザになり、サーバ側の問題はなるべくPaaS事業者に任せるということかもしれません。

したがって、サーバにログインして問題調査するという形はまだしばらくは続くでしょう。

異常を発見した場合、より深く問題に踏み込むために、straceやgdbなどを駆使するという先のオペレーションの話もあります。そのうち書きたいと思います。

Linuxでロードバランサやキャッシュサーバをマルチコアスケールさせるためのカーネルチューニング

本記事の公開後の2016年7月にはてなにおけるチューニング事例を紹介した。 はてなにおけるLinuxネットワークスタックパフォーマンス改善 / Linux network performance improvement at hatena - Speaker Deck

HAProxy や nginx などのソフトウェアロードバランサやリバースプロキシ、memcached などの KVS のような高パケットレートになりやすいネットワークアプリケーションにおいて、単一の CPU コアに負荷が偏り、マルチコアスケールしないことがあります。 今回は、このようなネットワークアプリケーションにおいて CPU 負荷がマルチコアスケールしない理由と、マルチコアスケールさせるための Linux カーネルのネットワークスタックのチューニング手法として RFS (Receive Flow Steering) を紹介します。

Redis や Nodejs のような1プロセス1スレッドで動作するアプリケーションをマルチコアスケールさせるような話ではありませんのでご注意ください。

続きを読む

Linuxのブロックデバイスレベルで実現するrsyncより高速な差分バックアップについて

社内で論文輪読会みたいなことやってて、そこで紹介した論文の内容についてです。

最近、Graphite に保存しているデータのバックアップ(データ同期)に rsync 使ってて、かなり遅いので困ってた。 LISA っていう 大規模システム、sysadmin 系のカンファレンスがあって、ここから論文探してたら、ちょうど巨大データの高速バックアップの実装の話があったので読んでみた。

論文概要

dsync: Efficient Block-wise Synchronization of Multi-Gigabyte Binary Data - https://www.usenix.org/conference/lisa13/technical-sessions/presentation/knauth - Thomas Knauth and Christof Fetzer, Technische Universität Dresden - In Proceedings of the 27th Large Installation System Administration Conference (LISA ’13)

GB単位のデータだと、rsyncがとにかく遅い。 rsync はブロック分割されたデータのハッシュ値の比較により差分検知して、ネットワーク転送するデータ量をとにかく減らそうとしている。 その反面、同期させたいデータサイズに対してハッシュ値計算のCPU時間は線形に増加する。 さらに、ハッシュ値計算のために全データブロックを読み出す必要があり、I/Oコストも高い。 バックアップ時に、差分を事後計算しようとすると、どうしてもこうなる。 そこで発想を変えて、カーネルのブロックデバイスレベルで、更新のあったブロックを常に記録(トラッキング)しておき、バックアップ時にフラグのあったブロックだけを転送することにする。

発想自体はシンプルだけど、既存の device mapper の Snapshot 機能だけでは実現できなくて、パッチをあてる必要があったのがやや難点。 カーネルのメインラインに取り込まれてほしい。

実装はこちら。 https://bitbucket.org/tknauth/devicemapper/ Linuxカーネル 3.2 の device mapper モジュールにパッチを当てるような感じになってそう。

追記 2014/05/26 16:10

それ ZFS でできるよと DRBD でいいのでは系のコメントをいくつかいただきました。 ちなみに論文の本文には両者に関して言及があります。

"どうせブロックデバイス使うなら実績のあるDRBDでいいんじゃないかと思う。非同期モードで。" http://b.hatena.ne.jp/n314/20140526#bookmark-196769866

ZFS について

ブロックデバイスレベルで実現できてうれしい点は、ZFS など特殊なファイルシステムに依存しないことだと思います。 ext3 のような壊れにくいLinux環境で実績と運用ノウハウがある ファイルシステムを使いつつ、差分バックアップできるのは実運用上でうれしいことが結構あると思います。 あとは、ZFS で定期的に差分バックアップとるときに、スナップショット分のディスクサイズオーバヘッドが気になります。(これは論文にも比較データはないのでどれほどかわかりませんが) ちなみに dsync のオーバヘッドは、10 TiB データに対して、320 MiB 程度のようです。(ただしディスクではなくメモリオーバヘッド)

DRBD について

そもそも非同期モードだろうが同期モードだろうが、DRBD はオンラインなレプリケーションには使えても、定期バックアップに使うものではない気がします。 例えば、プライマリノードで、間違えて重要なファイルを rm してしまったときに、即座にセカンダリノードにその rm の結果が伝播してしまうので、データが壊れた時に復旧するためのバックアップ用途には向いてない気がします。

何か勘違いがありましたらご指摘いただけると幸いです。

スライド

Keynote のテンプレートは、Azusa にお世話になっています。

LISA の他の論文

LISA はオペレーションエンジニアにとって興味深い論文が結構ある。 一例をあげてみる。 今回のように実装が公開されてたいたりするので、あんまりアカデミック感がなくてよい。

所感

たまにサーバ管理ツールとか作ってて、サーバ負荷の未来予測とかサービス間のトラッフィク依存関係(AサービスがBサービスのDB引いてるとか)を可視化できたらいいねとか言ってたりしてた。 今回、LISAの論文眺めてたらちょうどそういうのあって驚きがあった。 研究の世界はできないことができるようになる系統の技術において先を行っていて、ブログとかウォッチしているだけでは追いつけないので、たまに論文もよみたい。

カーネルのI/Oシステム周りの知識に乏しいので、詳解Linuxカーネルを読みつつ知識を補填していた。

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版

  • 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
  • 出版社/メーカー: オライリー・ジャパン
  • 発売日: 2007/02/26
  • メディア: 大型本
  • 購入: 9人 クリック: 269回
  • この商品を含むブログ (71件) を見る

Docker 使ってたらサーバがゴミ捨て場みたいになってた話 #immutableinfra

Immutable Infrastructure Conference #1 : ATND

でLTしてきた。

内容はきれいにゴミを捨てましょうという話以上のものは特にない。

背景の説明が少し雑だったので補足すると、Jenkins のジョブスクリプトで、git push する度に docker run していたら ゴミがどんどんたまっていったという感じ。 1 push あたり、アプリコンテナ、DBコンテナとか合わせて3コンテナぐらい起動してるから開発が活発だと、どんどんゴミがたまる。 さらに補足すると、Device mapper がらみのゴミは、aufs 使うとかなり解決できそうな感じはしてる。 (Device mapper だとブロックデバイスレベルでイメージ差分を表現するので、デバイス毎(差分)毎に mount が走るみたいな実装になってるけど、aufs だとファイルシステム単位で複数のディレクトリを束ねてマウントするみたいなことができるので、無駄なマウントが走りにくそう。多分。)

Disposable Infrastructureを特にコンテナ型仮想化技術を使って実現しようとする場合は、ゴミがどれくらいできるのかは結構重要な指標なんじゃないかと思う。 (ゴミがひどいから枯れてるハイパーバイザ型仮想化でやるという選択もあるかもしれない)

バッドノウハウがたまったり、Docker 自体が安定してきたので、最近は rpm/deb パッケージのビルドを Docker on Jenkins でやらせるようにして開発効率をあげたりしてる。

ともあれ、今現在ゴミ掃除にしか興味がなくなってる。

効率よくゴミ掃除するために、Docker の内部実装の勉強してて、そういう話をコンテナ型仮想化勉強会とかいうやつでしゃべるかもしれない。

第3回 コンテナ型仮想化の情報交換会@大阪 (コンテナ型VMや関連するカーネル等の技術が話題の勉強会) : ATND