kubectl drainについて

村田俊哉氏:メインのノードのアップグレードですね。ノードは、実際にサービスを稼働させているPodが動いているので、無停止でアップグレードするには、このPodをグレースフルシャットダウンさせてから、ノードを停止していく必要があります。ノードをグレースフルにシャットダウンする方法として、Kubernetesが提供しているコマンドkubectl drainがあります。

このコマンドを実行すると、そのノードでPodがスケジュールされない状態になって、そのノードに動作しているすべてのPodをeviction、つまり退去する動作をします。この退去がされる際に一時的にPodの数が減ることも。それを許容できない場合は、PodDisruptionBudgetで停止許容Pod数が定義できるので、こちらを設定するといいと思います。

それ以外にも、グレースフルにPodを停止させるにはいくつか注意事項があります。興味があれば別の発表資料とかQiitaがあるので、そこのリンクや記事を見てください。

このkubectl drainでは、安全なノードの停止が可能ですが、そのあとノードの更新をしていく必要があります。この更新の方法はいくつか戦略があるので、その説明から。

ちなみにノードを削除せずにコンテナイメージのパッケージングを更新するインプレース方式もあります。冪等性とオペレーションの難易度から、弊社ではイミュータブルインフラストラクチャ系の方式でノードを削除して作る、という方式で更新するようにしています。今日の話は基本的にそれ前提の話になるので、ご了承ください。

ノードのアップグレード

ではアップグレード戦略について見ていきたいと思います。その前に軽くアップグレードの要件から。まず、サービス全体でダウンタイムが発生しない方法が用意されていること。これは一応クラスタが複数あれば、1つのクラスタが止まってもよいことを意味しています。

次に稼働中の全体リソースが減らないこと。これはアップグレード中にランニングノード数がアップグレード前よりも下がらないことを意味しています。あとはマスターコンポーネントのダウンタイムは極力発生しない。これはコントローラマネージャーやスケジューラといったコンポーネントがHA化されていても、リーダー交代中は止まってしまうので、その間は許容するということです。

あとはローカルディスクの内容は保持しなくてもいいという要件ですね。他社の例とかを見ると、このローカルディスクの内容が必要だったりすることがあるみたいです。弊社は保持してほしいデータは、PVかクラスタ内に保存するのがポリシーなので、ローカルディスクの内容は保持しなくてもいいという要件でアップグレードします。

ではこれらの要件を満たしたよくある戦略を見てみると、先ほども話があったように2つの戦略が多いようです。ライブアップグレードは、先ほど説明したインプレースで、クラスタマイグレーションというのが、今回ではブルーグリーンアップグレードと呼ばれるものにあたります。

ますはライブアップグレードから。

これはサービス稼働中のクラスタを、サービスインしたままアップグレードする方式です。全体のリソースが減らさないようにするので、基本的には削除してから追加するのではなく、追加してから削除するようにしています。この方式のメリットは、稼働したままアップグレードできるので、運用コストを低くしやすいこと。そして追加のハードウェアリソースが最小で1台ですので、追加のリソースが少なくて済むという点ですね。

ただ、一度に追加するノード数をSurgeと呼んでいますが、このSurgeが少なくノード数が多いと、アップグレードに掛かる時間が、ノード数分比例して増えていくので、多く必要になります。ただ、このSurgeというのを大きくすれば、アップグレードの速度は上がりますが、その分リソースが必要となるのでその場合は要件に合わせて調整することが必要となってきます。

具体的に、このライブアップグレードがどんな感じに行われるかを順を追って説明します。この例では1.17から18にアップグレードしていく例です。今回はノードだけの話にしているので図はノードだけになっています。左側が古いバージョンのノードグループで、右側が新しいバージョンのノードグループ。最初はアップグレード前なので、古いバージョンのノードだけになっています。

ではアップグレードを開始すると、まず新しいバージョンにノードを追加する。この例だとSurgeを1にしているので1台だけ追加します。追加したノードが稼働中になったことを確認できたら、次は旧バージョンのノードを1台削除します。この次に先ほど言ったkubectl drainが走っていて、Podの退去が行われて、新しいノードが空いているのでそっちにスケジュールされるという感じです。

同様にSurgeの数だけ追加して、稼働を確認できたら古いノードを削除する、ということを繰り返します。最後の古いノードの削除が終わったら、アップグレード完了です。このように、Surgeの数によってアップグレードの速度だったり必要なハードウェアのリソース数というのが変わってくるので、環境に合った調整が必要になります。

次にブルーグリーンアップグレードですね。これは既存のクラスタをアップグレードするのではなくて、新規に新しいクラスタを作ってその新しいクラスタに切り替えるという方式です。この方式のメリットはアップグレード後にもし問題があってもロールバックが容易という点。デメリットとしてクラスタ2台分のリソースが必要になります。

次にブルーグリーンアップグレードの流れを簡単に紹介します。図のようにロードバランサーからクラスタのPodへリクエストを流したとします。それでアップグレードするときは、新規のクラスタを作って新しいバージョンへ構築する。そのあとにロードバランサーの行き先を旧バージョンから新しいバージョンに切り替えます。

しばらく並行稼働をさせておいて、もし問題があれば旧バージョンへロールバックすることも、ロードバランサーの行き先を変えるだけでできます。新バージョンのクラスタで問題ないことを確認できたら、最後に旧バージョンのクラスタを削除してアップグレードは完了です。

アップグレードの問題点

戦略のまとめなんですけど、まとめるとライブアップグレードは、運用コストが低くできるけども、安全性は少し低くなってしまう。ブルーグリーンアップグレードは、安全性は高くなるけども、ロードバランサーの切り替えなどの運用が発生するのでハードウェアリソースが多く必要になる可能性が発生します。このように、各戦略にはメリット・デメリットがあるので、運用の要件に合わせて柔軟に判断してください。

実際によく聞くマネージドサービスを見てみると、ドキュメントを見た限りでは、今回紹介した戦略が利用できるようでした。実際に自分が試したわけではないので詳しくは各製品のドキュメントを見てもらえばと思います。

弊社でも同様の戦略ができるようにしていますが、ほとんどのクラスタでこのライブアップグレードを採用して運用していました。これは運用の手間が大きな理由だと思っていますけど、詳しくは調査中です。

また、ライブアップグレードでもKubernetes自体のアップグレードで事故になった事案は今のところ弊社ではありません。強いて言えば、最近はメトリクス名の変更が多くあって、それによってアラートが上がらなくなったというのは聞いたことがある程度です。

このノードのローリングアップデートが発生することによってノードが削除される関係で、サービスに影響が出てしまうことが多々発生しています。原因として多いのは、Podのグレースフルシャットダウンに完全に対応できてない点です。これについては、ドキュメントやノウハウの蓄積だったり、テストの手間の軽減の施策も実施して軽減しようとしています。

正直なところ、クラスタのバージョンアップよりもアドオンのバージョンアップのほうが事故になっているケースが多いです。とくにこのIngress controllerは、リクエストが直接通るので、けっこう事故になっています。いろんなHTTPのリクエストが通るので、本番の特定要件でしか発生しないため、そこが今の悩みの種です。

ただ、事故が発生していないと言っても、問題がなかったかというとそうでもありません。弊社だと、今は2点ほど問題があります。まずは、Podの無駄な移動が発生する点ですね。ライブアップグレードだと、Podの移動時に削除予定が古いバージョンのノードに再配置されてしまうことがあるので、この場合だとPodの移動を2回しないといけなくなってしまうんですね。

そうすると、このPodの起動や終了が速いPodではそんなに気にならないですが、起動や終了に時間が掛かるPodだと、それによってアップグレードが遅くなってしまう問題があります。一応削除予定をノードにTaintsで付与して、そいつにスケジュールされにくくすることは可能です。しかし、リソースをギリギリまで使っているようなクラスタだと、あまり効果がないので課題にはなっています。

あとはアップグレードに時間が掛かるという点ですね。弊社の場合はクラスタの数も多いので、すべてのクラスタが一斉にアップグレードみたいなことをすると、アップグレードがスタックするという問題がけっこう発生します。この問題が解消されないと、ノードのオートヒールなどやその辺の機能が使えなくなってしまって、サービスに影響が出てしまう可能性もあるので、対処の方法を考えている状態です。

原因としては、基本的にはIaaSがボトルネックになっているというのはわかっています。IaaSのボトルネックを解消するというのはなかなか時間が掛かるので、そのパフォーマンスの向上に取り組みつつも、例えばアップグレードのノードの作成の優先順位を付けるなどの検討はしています。

アップグレード以外の管理や更新

最後にアップグレードの自動化について紹介で、これは主に弊社でやっている内容です。

弊社の場合、クラスタの数も多く、手動でノードのローリングアップデートを実施するのは現実的ではないのかなと思っています。同じようなことを考えているところが他にもあるようで、例えば前回のKubeCon NA(KubeCon + CloudNativeCon North America 2019)の発表でも、ノードのアップグレードを自動化したみたいな話がありました。弊社でもKubernetes Cluster Operatorを作って、自動化したりしています。

弊社の場合だと、ノードのアップデート以外にもアドオンのアップデートやストレージバージョンの更新やアップグレード作業については、ほとんど自動化しています。まずそれぞれの紹介を簡単に。

最初はKubernetes Cluster Operatorですね。これは過去にも何回か発表したので知っている方もいると思います。簡単に言うと、Kubernetesクラスタを管理するオペレーターになっていて、クラスタの作成、削除、更新を自動化するものです。これは主にアップグレードコストの最小化を狙って開発したものです。細かい動きは過去の発表で紹介しているので、よければ見てください。

次にアドオンの管理ですね。これは他社の事例を聞いたことがないので、よかったらどんな管理をしているのか、ツイートしてくれるとうれしいです。弊社だとaddon-managerという公式が提供しているツールを使って管理しています。addon-managerは、簡単に言うと指定されたディレクトリに配置されたマニフェストをapplyし続けるというシェルスクリプトです。

これは特定のラベルを付けるようになっていて、prune付きのアプライをするので、ディレクトリからマニフェストを消せばリソースも削除されます。弊社もこれを使っていますが、これだけだと参照するディレクトリのマニフェストを変えないと更新されません。このディレクトリのマニフェストを更新するための仕組みを用意して自動更新します。

ざっくり言うと、マスターのバージョンからマイナーバージョンごとに用意されているマニフェストを含むコンテナを引っ張り出す。そこのコンテナからマニフェストを生成してaddon-managerが参照するディレクトリに配置する、という動きをしていきます。

次にストレージバージョンの更新です。これも更新するためのスクリプトが公式で提供されていました。過去形なのは、なぜか削除されてしまったからです。弊社ではそれを参考にして、いくつかのパッチを充てて利用するようにしています。公開しているので、よければ見てみてください。しかしこれだけだと、ただのスクリプトなので、実行も自動化を進めていて、アドオンの更新と同様に、自動で実行するコントローラーを用意して動かします。

自動化を導入して効果があったと思われるものを挙げてみました。ただ、Kubernetesの本番運用は、自動化前から実施していたというわけではありません。この内容はけっこう感覚的な内容も含んでいるので、ご了承ください。

まずは今運用しているすべてのクラスタがサポート期間内という、健全な状態を保ち続けられているという点ですね。弊社のクラスタは今300以上あると言いましたが、そのすべてのクラスタが年内に1.16以上になっていて、この健全な状態を維持し続けられています。

次はコストの削減ですね。大雑把な見積もりにはなると思いますが、300以上のオンプレでのクラスタのサポート、教育の内容を今は20名から30名程度でできているのは、この自動化のおかげかなと思っています。

次は副次的な効果。Kubernetesのバージョンアップ以外にもクラスタを更新したいことが多々あります。OSの移行だったりIaaSのアップグレード、クラスタのスケールアップなどといったクラスタを、利用している側に大きな影響を与えることもなく実施できるようになったのも、自動化の副次的な効果として大きいかなと思っています。

あとは細かいことですけど、オペレーションミス自体もかなり減っていると思っています。実際に、このオペレーションミスによって障害が起きた事案は、ほとんど発生していません。

最後にまとめたいと思います。Kubernetesはリリースサイクルとサポート期間から、定期的なアップグレードは必須です。なのでKubernetesを導入するときは、定期的なアップグレードコストや、今回は話はしていませんがセキュリティとか可用性の面もあります。それらを踏まえた上で全体コストを考え、オンプレミスにして構築するメリットがあるかを考えて、検討するのがいいかなと思います。

あとはKubernetesに限った話ではないと思いますが、Kubernetes以外のコンポーネントの更新もあるので、導入する際には定期的にチェックする体制を作って、継続的な更新ができる体制を作るといいです。アップグレード戦略についてはいろんな方法があって一長一短なので、基本的には環境や要件に合わせて選んでみてください。

自動化については、大規模になってくるとほぼ必須だなと思っています。オペレーションミスを防ぐためにも、コストに見合うのだったら実施してください。実現方法については何でもいいとは思いますが、オペレーターまわりに慣れてれば、Kubernetesの仕組みを利用すると容易に安定した自動化みたいなものを実現できるので、個人的にはオススメです。

以上で発表を終わります。ありがとうございました。

(会場拍手)