頻繁にアップデートが必要なKubernetes

ここでちょっと話がぜんぜん変わるんですけど、なぜKubernetesをアップグレードするのかという話をしたいと思います。1つはバグの脆弱性とかを修正することですね。もう1つは新しい機能を使いたい。3つ目が一番多いかなと思うんですけど、Kubernetesの公式がサポートしているバージョンを使いたい。やっぱり問題が起きたときにちゃんと修正されるバージョンを使いたいというところで、アップグレードしていく人が多いんじゃないかなと思います。

これは公式の日本ページの引用なんですけど、何が書いてあるかというと最新リリースから3バージョンまではサポートしますと。

今は、今日(発表時)1.18が最新になったので、1.18と1.17と1.16がサポートされることになります。なので1.15は、もうサポートされなくなっています。

3か月ごとにマイナーバージョンも新しくリリースされるので、新しく出たバージョンでも9ヶ月経てばサポートされなくなります。なのでKubernetesの場合は頻繁にアップグレードしていかないといけません。サポートされているバージョンを使うんだったら、頻繁にアップデートが必要になってくるということですね。

《図:https://speakerdeck.com/bgpat/kubernetes-cluster-migration?slide=28》

頻繁にアップデートが必要になってくるということは、互換性のないインプレースアップグレードだと、ダウンタイムが発生してしまうような変更が入ったときには、クラスタマイグレーションをやるしかないことになります。

準備が必要ということで、まずどうやるのかということを今から知ってほしいということと、クラスタマイグレーションはけっこう難しい作業になるんですけど、これをやりやすくするためにはどうするのかをここから説明したいと思います。

簡単にざっくり説明すると、また新しいクラスタを立てて、古いクラスタから新しいクラスタにリソースを移行していくという作業があるんですけど、これはバックアップからリストアをする手順でWantedlyでは行っています。あとは新しいクラスタにリソースが全部移ったら、新しいクラスタにトラフィックを流して、新しいクラスタが動いているということを確認できたら、最後に古いクラスタは消してしまうような感じです。

ただ、この4つの手順をやるためには、かなり裏側でかなりがんばらないといけないことがあります。

これを守って運用すればクラスタマイグレーションは怖くない

それをまとめたのがこのk8s、Kubernetes 8 factorsというものになります。これは僕の同期がCloudNative Days Tokyo 2019で発表した資料になるんですけど、ここでがんばってWantedlyのノウハウみたいなものをまとめたものになります。これ実は、今日(発表時)ブログで公開したのでぜひ調べてみてください。

この中に、8つの要素があります。これをちゃんと守って運用すれば、クラスタマイグレーションは怖くないです。

実はクラスタをポータブルに持ち運びやすくするという意味で書かれているんですけど、クラスタマイグレーションだけに絞ると、5つぐらいに分類できるかなということで、今日はこれを説明したいと思います。

まずクラスタをコード管理するところなんですけど、クラスタマイグレーションというのはどうしても検証に工数が掛かってしまいます。互換性のない変更が入ったということは、かなり大きな変更だったりとか、あとはクラウドとかツールの恩恵が受けられなくなってしまうので、検証にはけっこう時間を掛けることが多いです。なのでクラスタを作り直す負担をできるだけ抑えておくことが必要になります。

このために、クラスタの設定というのをできるだけコード化しておく。クラスタを作る手順みたいなものもコード化して、自動でできるだけ作れるように負担を減らします。あとは、これは当たり前の話かもしれないんですけど、差分の比較というのは簡単にできたほうがレビューもしやすいです。また、検証時に設定していたものを本番に入れ忘れるようなことを防げるので、環境ごとの差分は明示的に書きましょうというのがあります。

次に動作確認の省力化です。これはさっきのクラスタを作り直すことが多くなるということと同じなんですけど、理由としては、恩恵が受けられないとか大きな変更が含まれているというところで、慎重に確認が必要だということです。結局何をやるかというと、検証環境では起きなかったのに本番だけで起きる事故を防ぐために、できるだけ検証環境との差異は減らして運用しましょうという教訓になっています。

もう1つは、クラスタへの反映はコードを基にすることで、まずコードを変更してからクラスタにアプライをしましょうという話です。これは意図しないリソースができてしまったときに、それがクラスタのアップデートによるものだった、新しい変更によるものだったのかを検知するために行っています。

複数クラスタの運用を考慮した命名にする

次に非名前管理ということなんですけど、これは聞いたことがないと思います。複数クラスタを立てるというところで、これの運用を考慮しないといけなくなります。具体的に何が起きるかというと、クラスタ名が同じだと識別できないので、別のクラスタの名前で新しくクラスタを作ることになります。なので、監視設定とかアラート設定みたいなものを古いクラスタのままではなくて、新しいクラスタ用に用意しないといけなくなります。

また、クラスタの切り替えのときにロールバックしないといけなくなるパターンも考えておかないといけないので、これもやりやすいようにするには名前をちゃんと考えましょうということになっています。具体的に何をやるかというと、同一用途のリソースが複数作られることはちゃんと想定しておいて、命名をちゃんと確認しましょうというようなことになります。

どういうことかと言うと、例えばproductionというクラスタを作りました。ただこれは本番環境に複数クラスが作られることを想定できていないですよね。

じゃあどうするかというと、例えばproductionの後ろに日付を付けることによって衝突を回避できるのではないかと思います。これなら同じ日に複数のクラスタを作ったら壊れるんじゃないか、衝突するんじゃないかみたいな話はあるんですけど、運用上はそういうことはないかなと思うので、これでWantedlyでは運用しています。

もう1つ、直接名前で参照せずにクッションレイヤーを挟もうということなんですけど、まずユーザーに見える場所というのは、CNAMEを使って直接ロードバランサーを参照させないようにしています。これは、ロードバックしやすくするためにWantedlyでは運用しています。また、さっきの監視とかアラートの設定はラベルとかタグでリソースを指定するようにしています。

例えばDatadogによる監視をしているんですけど、昔はメトリクスとかアラートの設定にクラスタ名をそのまま使っていました。ただ、これだとクラスタのマイグレーションを行うときに新しくもう1個のクラスタができてしまうので、ここを控える必要があった。

ということで、このときは全部の設定を置き換える必要がありましたが、「次からは同じようなことはしたくないよね」ということでクラスタ名を使うのではなくて、env:productionみたいなタグを付けたりラベルを付けることで回避しています。なので、2回目にクラスタマイグレーションをしたときにはDatadogはまったく触っていないうまくいった例になっています。

次にバックアップです。これはリストアによってクラスタの中身を全部移行していくことになるので、クラスタを維持するために必要なリソースが全部入ったバックアップを取っておく必要があります。

障害対策みたいな話にもなるので、常にバックアップは取っておいたほうがいいと思うんですが、基本的にどの方法でバックアップを取ってもらっても大丈夫です。すべてのリソースが入っていればどんな方法を取ってもらっても大丈夫です。Wantedlyでは、Veleroというツールを使用しています。

重複と欠損、両方許容できない場合はKubernetesに乗せないことを徹底

最後に重複欠損管理。まずこの重複というのは1つしかできてほしくない状態だったリソースが複数できてしまっている状態のことを指します。欠損というのは簡単に言えばダウンタイムが起きたというようなことを指しています。ダウンタイムが発生したことで、特定のリソースとか特定のアプリケーションが欠損したことを指します。

これも非名前管理と同じく複数クラスタでの運用の副作用というものを減らすために導入しています。Kubernetesは、基本的には重複を許容するリソースしか想定されていないものになります。なので重複を許容できない場合は、そのリソース専用に手順を用意する必要があります。

Wantedlyの場合は、欠損を許容すれば一応重複を許容できない場合でもまとめて対応できるんじゃないかということで、重複と欠損のどちらかは許容できるかたちで構成しましょう、構成した上でKubernetesに乗せましょうということを進めています。重複を許さないものについては専用の手順が必要なので、これについては専用のラベルを付けるという運用をしています。

重複と欠損の両方を許容できない場合は、そもそもKubernetesに乗せないということを徹底しています。

まず重複を許容しないリソースの例なんですけど、Wantedlyでは定期的にバッチ処理を実行するためにCronJobをかなり多用しています。本当はよくないんですが、重複実行が許容されないジョブがいくつか存在しています。

ただ、今日見た範囲では800ぐらいのジョブが存在していて、全部をちゃんと重複しても大丈夫かなと見るのはかなり厳しいものがあるので、重複を許す状態で重複しないような運用をしています。これは欠損を許容してクラスタマイグレーションを行っています。具体的にこの場合は、欠損というとジョブが実行されていないというケースを許容することになります。

具体的にどうやったかと言うと、まず旧クラスタから消して新しいクラスタにリストアするようなことをやっています。

これはWantedlyでは運用していない原因があるんですけど、重複と欠損の両方を許容しないリソースの例を挙げると、RDBとかがあるかなと思います。

ステートフルセットとかを使ってPersistentVolumeをマウントして、そこの中にデータベースのファイルを置いたりされている方はいるかなと思うんですけど、まず複数の状態が存在していると一貫性が保たれない。

例えば旧クラスタと新しいクラスタで入っている中身が違うよとかは、RDBとしてはかなりやばい状態なのかなと思うんですけど、あとはダウンタイムがあるとそもそもサービスに影響が出ちゃうということで、そもそもKubernetesでは管理しないという運用になっています。WantedlyではKubernetesと外部のリソース、AWSのRDSというものを用意しています。

実際のクラスタマイグレーションの手順

これらを考慮すると、最終的にこの下線を引いた部分が最初の手順から増えた部分になっています。クラスタを作るところは一緒で、新しいクラスタにリストアする部分というのは重複を許容するリソースだけをまずはリストアしています。これは重複を許容するリソースの中にかなり重要なコンポーネントが入っていて先にリストアしておかないといけないので、最初にリストアしています。

このあとで、CronJobのような重複を許容しないリソースを、まず古いクラスタから削除して動かないようにして、それから新しいクラスタにリストアします。この方法を取ることによって、欠損は生じてしまうんですけど、同時に複数のクラスタで動くということはなくなります。あとは新しいクラスタにトラフィックを切り替えてうまく動いてくれれば、古いクラスタを削除する、という運用をしています。

実際にどういう手順でやったのか。スプレッドシートで管理をしていて、こんな感じで一つひとつコマンドを書いたりして対応しました。めちゃくちゃ大変なことだなというのがわかるかと思います。これでもかなり手順が減って、1回目より2回目のほうがかなり削減されてスムーズに行った例になっています。

クラスタマイグレーションはとても大変な作業になってくるんですけど、工夫することで改善するものもあると思うので、みなさんもぜひ挑戦してもらえたらなと思います。

複数クラスタ運用の副作用を想定しておく

まとめです。クラスタマイグレーションの必要性を説明しました。基本はこのあとで説明があると思うんですけど、インプレースアップグレードを試してみてそれでだめだったらクラスタマイグレーションを実施する方法を取ります。

次にクラスタマイグレーションをどうやるかということなんですけど、バックアップからリストアすることで新しいクラスタにリソースを移しています。同じ設定で複数クラスタを運用するときは副作用があるということを想定しておかないということを説明しました。

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

(会場拍手)