ピーターズのWebサイトをはじめ、一緒に稼働させている複数のウェブサイトが2週間にわたりダウンしてしまいました。楽しみにご訪問頂いた皆様には、大変ご迷惑をおかけしました。
ピーターズのWebサイトは、Kubernetesを利用したローカルサーバーで稼働しています。Kubernetesは、いわゆるAWS(アマゾン・ウェブ・サービス)やGCP(グーグル・クラウド・プラットフォーム)でも使用されているサーバーを構成するツールで、オープンソースで開発されているため、自宅でも同様の環境を構築する事が可能です。
コンテンツ
Kubernetesとは
Kubernetesの最大の特徴は、サーバーを使い捨てにするコンセプトです。そもそもは、高価なサーバーを使用せず、安価なパソコンを大量に並列処理させ、故障しても簡単に交換してサーバー機能は維持するという、ハードウェアの使い捨てというコンセプトから派生しています。今では、ハードウェアの使い捨てというよりかは、Kubernetes内で稼働する仮想サーバーが使い捨て対象です。応答が無くなった仮想サーバーは不良サーバーと判断され、自動で再起動されます。この再起動は、直前のデータが残っている状態での再起動ではなく、新しいサーバーを1から作る処理になります。このリフレッシュされる再起動処理は、ウイルスやハッキングへの対策として非常に効果的だと思います。もちろん今でも、ハードウェアの切り離しや追加は容易に可能です。
障害の原因
さて、前置きが長くなりましたが、今回のKubernetes障害の発生原因と経緯を簡単に説明したいと思います。
昨今、メモリに寄生型のウイルスが流行っているという情報を知り、仮想サーバーのみではなく、物理サーバーの定期的な再起動の必要性を感じ、Cron Jobによる自動再起動の処理を入れた直後の障害でした。合計7台の端末で稼働させていたため、毎日1台ずつ再起動させる予定でしたが、誤って1組だけ2台を同日の同じタイミングに再起動させてしまいました。
Kubernetesでは、2種類のサーバーを組み合わせて稼働させます。Control Planeという窓口役の端末とWorker Nodeというアプリケーションを動作させる端末です。この2つをそれぞれ複数台で構成する事によって、たとえ1台が障害などでダウンしても、他の端末がサービスを引き継いで継続できるのが特徴です。但し、Kubernetesの頭脳とも言えるControl Planeに関しては、あまり知られていない要件が存在します。まず、Control Planeの稼働台数については、奇数台での稼働が推奨されています。更に重要な事は、Control Planeのダウン数は過半数を超えない事!
今回の場合、Control Planeは3台、Worker Nodeを4台で、ロードバランサーにKube-VIPとファイルサーバーに搭載したNFSで構成していました。直前に6台構成から7台構成に増強し、Control PlaneとWorker Nodeの入れ替えを行っていました。そして自動再起動も毎日順調に稼働している状態でしたが。。。なんとControl Planeの2台を同日に再起動する設定にしてしまっていました。
Control Planeは過半数ダウンで即死
Control Planeでは、ETCDと呼ばれるデータベースを各サーバーで同期して、サーバーやアプリケーションの管理を行っています。Kubernetesの頭脳部分といえる機能です。このデータベースであるETCDは、同期しているデータベースの過半数が応答しなくなると、災害が発生したと自認して、一切の応答を受け付けなくなります。例え直ぐに復旧したとしても、データベースは復旧しません。
この状況下において、Kubernetesがどうなるかというと、既存のアプリケーションは何も無かったかのように動作し続けます。しかし、新しいアプリケーションの作成や、動作サーバーの変更などの、変更が一切反映されない状態になります。私のケースの場合、Kube-VIPの稼働サーバーを変更したかったのですが、変わらなかった事で発覚しました。当初、Kube-VIPの不具合と疑っていましたが、今回のKubernetes障害のそもそもの原因はETCDでした。
実はKubernetesでWebサーバーを構築して以降、この1年間で今までに2回ほど同様の事象に陥ったことがあります。従って、決して珍しい訳ではなく、Kubernetesを使っていて、かつ端末の再起動を行うケースがあれば、誰にでも起こりうる事象です。本番環境であっても、アップデート時などにはサーバーの再起動を求められるケースも多々あります。
ETCDの復旧には、バックアップが必須
ETCDの復旧には、正常稼働時に取得していたバックアップが必要になります。ETCDのバックアップ機能は、Kubernetesには付属していませんので、別途etcdctlの導入が必要です。
sudo apt update && sudo apt install etcd-client
クラスター内のControl Planeにしている端末にETCDの管理ツールをインストールします。作業を行う1台だけで十分ですが、Worker Nodeを含めたクラスター内の全端末にインストールしておいても問題ありません。
スナップショット(バックアップ)の取得は、下記のコマンドを使用します。
ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db
$ENDPOINT変数の部分は、Kubernetesの初期設定時に–control-plane-endpointで指定したIPアドレス(ホスト名)です。Kube-VIPを導入している場合は、Kube-VIPのアドレスになります。
私のケースでは、残念ながらETCDのバックアップを事前に取得していなかったため、この方法は使えませんでした。教訓として今後は、構成変更時などに定期的にバックアップを取っていくのと、練習環境を構築して、バックアップからのリカバリの練習をしていきたいと思います。
なお、バックアップからのリカバリ方法は、下記サイトをご参照ください。
https://etcd.io/docs/v3.5/op-guide/recovery
Kubernetesの再構築
リカバリの方法を模索している間に1週間弱が経過してしまいました。重要なデータ自体はファイルサーバーに格納されているのと、アプリのデプロイはHelmのみで行っており、Helmコマンドが生きていたため、チャートの設定値を出力する事に成功したため、復旧させるより新しく構築した方が早いと判断し、再構築を試みる事にしました。
なお、デプロイ済みのHelmチャートの設定値を出力(バックアップ)するコマンドは以下の通りです
helm get all 「デプロイ時に指定したリリース名」 -n 「ネームスペース名」 > 「出力したいファイル名」
私の場合、チャートのデフォルト設定から変更している部分であるvalue値のみが必要だったため、上記「all」の代わりに「values」を使用しました。allを指定すると、valuesの値も含めて出力されます。
これでリカバリする準備が整い、順調に再構築作業が進んだのですが、データベースの復旧で再びつまづいてしまいました。以前と同じデータベースをHelmでデプロイし、生成されたデータディレクトリ内に以前のデータをつっ込んだのですが、テーブル一覧までは表示されても、その中身を確認しようとすると、「Table doesn’t exist in engine」というエラーが表示されてしまいます。
MySQLはデータファイルからの復元はできない
調べたところ、MySQLでディスク上に保存された生データファイルでは、単純にファイルの再配置のみではデータベースを復活出来ない事が分かりました。まず、データベースが起動状態の生ファイルだとロック状態にあるファイルであり、他のサーバーから参照しても中身の読み込みが出来ない仕様になっているそう。先ほどのETCDと同様に、事前のバックアップの重要性を改めて実感しました。
生データからの復旧には、全く同じ構造のデータベースを作業端末で再構築し、「IMPORT TABLESPACE」としてibdファイルをテーブルごとに読み込む必要があるそうです。こうして復活させたデータをエクスポートし、本番環境にインポートさせるのだそうですが。。。
https://qiita.com/utamakura/items/12004d0d09f6749ae44b
また、作業用に用意するデータベースは全く同じ構造でなければいけない為、構造が分からない場合は、frmファイルからテーブル構造のsql文を生成させるツール「mysqlfrm」も存在していた様ですが、最新バージョンのMySQLでは利用できなくなっています。
Hyper-Vで作業端末を用意し、インポートを何度か試してみましたが、私の環境ではフリーズしてしまい、成功しませんでした。色々と試していて、障害発生から間もなく2週間が迫っている時期でした。よく考えると、MySQLのバージョンが違っているのが原因だったのかもしれません。
Kubernetesは使い捨て環境である!
ふと、この事を思い出しました。そう、私が最初にKubernetesを紹介した言葉「Kubernetesは使い捨て」という事。
Kubernetes上で動作する仮想環境(Podと呼ばれています)は、不調になると即座に破棄され、直ぐに真新しい環境が1から構築されます。バージョンのアップグレード時も同じく、新しい環境が1から構築されます。しかし、保存データはNFS上に格納された生データを利用します。
もしや、Helmのデプロイ時に生データが揃っていれば、バージョンアップと勘違いして、データを綺麗にして、何も無かったかのように復活できるのではないかと。
1回目のデプロイ時はデータフォルダが無かったので、失敗しましたが、PVCが残っている状態で、生データをつっ込み、デプロイを試したところ、何も無かったかのように、全てのデータが復活してくれました。
究極の使い捨て環境とも言える「Kubernetes」、内部的には日常的に自動でリストアを行っているのでしょうね。改めて感心しました。