第560回の「microk8sでお手軽Kubernetes環境構築」では、「シングルノードのみに対応したKubernetes環境構築ツール」としてmicrok8sを紹介しました。その後、このmicrok8sは大幅な進化を遂げて「特定のプロダクション用途でも使える」までになっています。今回はそのmicrok8sに最近追加された、高可用性クラスター機能について紹介しましょう。
密に開発されクラスターにも対応したmicrok8s
第560回の記事が公開されたのはmicrok8sのv1.13がリリースされ、Canonicalとしてもmicrok8sの利用をアピールしだした時期でした。当時はシングルノードにしか対応していないことに加えて、ARM64のサポートを拡充していったことからもわかるように、開発者によるKubernetesの学習用や組み込み用のシンプルなアプリケーションの実行用を主なユースケースとして想定していたようです。
しかしながら世の中の「簡単に導入できるKubernetes環境」への渇望は凄まじく、すでに同等のMinikubeというツールがあるにも関わらず、microk8sの開発者も機能もどんどん増えていくことになりました。第560回ではmicrok8sの「ウィークポイント」として紹介していたマルチノード対応も半年後の2019年9月のv1.16で正式に対応していますし、1年後のv1.18ではWindowsやmacOSへのインストールもサポートされるようになりました[1]。今年の8月にリリースされたv1.19に至っては、高可用性クラスタリング機能にも対応したのです[2]。
Canonicalによるアナウンスでも、組み込み機器用・学習用としてだけでなく、日常的なメンテナンスが難しい環境での利用も提案しており、Canonicalによるエンタープライズ向けのサポートを行うことも明言しています[3]。
他にもKubeflowやMultusといった人気のアドオンもサポートしている上に、「ぐぐったらそれなりに使っている人がいる」ようになったため、「Kubernetesを試せる環境を、ちゃちゃーっと社内に用意しておいてくれない?」とかいう上司の非情な要求にも対応できるソリューションに成長したのです。
今回はこのmicrok8sの、クラスター機能と高可用性機能(High Availability機能)について紹介します。とは言え、一般のご家庭にはクラスターを組める台数のマシンは存在しないでしょう。そこでLXDと組み合わせて、「ちょっと強い1台のマシン」の上でクラスター化を試みます。
仮想マシン版LXDインスタンスの準備
microk8sそのものはただのKubernetesのインストーラーでしかありませんので、コンテナ版のLXDインスタンスの上でも動きます。しかしながらコンテナ版のLXDでKubernetesを動かすには、名前空間のネストやその他各種権限の調整が必要です。また物理マシンのクラスターをエミュレーションする観点に立つと、仮想マシンを利用したほうがより実態に近くなります。よって今回は仮想マシン版のインスタンスで構築します[4]。
おおよそmicrok8sが動くマシンに必要なリソースは次のとおりです。
- CPU:「2コア x ノードの数」だけCPUコアが必要です。ただしKVMの場合はインスタンスがCPUを占用する必要はありませんし、昨今のそれなりのマシンはCPUのコアが十分に多いと思いますので、そこまで気にする必要はないでしょう。
- メモリー:最低でも「2GiB x ノードの数」はほしいところです。これもノートPCでもない限り、困ることはないでしょう。
- ストレージ:microk8sの上で何を動かすかに強く依存します。今回の手順のように本当に小さなコンテナを動かすだけであれば10GiBでも十分です。
ちなみにVirtualBox等の上にインストールしたUbuntuの上で構築することも可能です。VirtualBoxの上で仮想マシン版のLXDを動かす場合は、Nested KVM機能を有効化しておいてください。LXDで仮想マシンインスタンスを作っている部分をVirtualBox上のUbuntuインスタンスに置き換えてもらっても問題ありません[5]。もちろんVirtualBoxのインスタンスそのものに、上記に準じたスペックが必要になります。
では、実際にUbuntu 20.04 LTS上のLXD 4.0を利用して仮想マシンを作成しましょう。LXDの仮想マシン機能に関する詳細は第609回の「LXDからコンテナではなく仮想マシンを起動する」を参照してください。ちなみに当時のLXD 3.19では、cloud-initを利用してアカウントを作成する必要がありましたが、LXD 4.0までに仮想マシン機能が大幅にブラッシュアップされた結果、その手順は不要になりました。LXDそのものについては第521回から始まるLXD関連の記事で解説しています。
HA機能を利用するためには最低でも3台のマシンが必要です。よってまずは以下の手順で3台の仮想マシンを作成してください。
「--vm
」で仮想マシンの作成を指定しています。
LXDの仮想マシンインスタンスは何も指定しないと「CPU 1個、メモリ1GiB、ストレージ10GiB」で作成してしまいます。そこで上記ではCPUの数とメモリのサイズを明示的にしています。インスタンス作成後に「lxc config set インスタンス名 limits.memory 4GiB
」と指定してもかまいません。他にもインスタンスタイプで指定する方法もあります。これはAWSのように「-t t2.micro
」のような名前で一括してリソースを設定する方法です。名前とリソースの対応表はinstance-typeリポジトリからリンクしているURLを参照してください。
ストレージのサイズも変えたい場合は次のように実行してください。
仮想マシンインスタンスの場合、ストレージサイズはシャットダウン状態でのみ変更できます。そこで一旦lxc stop
などでシャットダウンした上で実行してください。「lxc config device override
」はルートファイルシステムやeth0のように、プロファイルから継承・生成されたデバイスに対して特定の設定だけを変更したい場合に有効なコマンドです。
必要ならミラーサーバーを設定し、パッケージを最新に更新して、再起動しておきましょう。
この3台の仮想マシンインスタンスがKubernetesクラスターの「ノード」になります。ちなみに仮想マシンの場合、lxc restart
に時間がかかってタイムアウトエラーが発生するかもしれません。その場合は、lxc status
ですべてシャットダウンされたかを確認した上で、手動でlxc start
を実行してください。
microk8sをインストールしてクラスター化する
次に各ノードにmicrok8sをインストールします。手順自体は第560回と同じです。
「--channel=1.19
」と指定することで、v1.19の最新安定版をインストールしています。microk8sの場合は、バージョンがそのままKubernetsのバージョンに紐付いているため、上記のように明示的にチャンネルを指定したほうが良いでしょう。そうしないと、たとえばv1.20がリリースされたときに自動的にv1.20に(つまりKubernetes 1.20に)アップグレードされてしまいます。
ここではk8s0ノードにのみインストールしていますが、他のノードにも同じようにインストールしておいてください。
インストールが完了したら次のコマンドを実行し、Kubernetesのセットアップができるまでしばらく待ちましょう。
上記のようにステータスが表示されたら準備完了です。これも他のノードにも実行しておきます。このタイミングではすべてのノードが独立しているため、「high-availability: no」となり、マスターノードには自分自身のみが存在する状態となっています。ちなみにv1.19から、ha-clusterアドオンが自動的に有効化されるようになりました。
Kubernetesとしては動作している状態になったので、たとえば次のような方法でkubectl
を実行できます。
「microk8s kubectl
」となっていることに注意してください。もし毎回「microk8s
」と入力したくないのであれば、「alias kubectl='microk8s kubectl'
」を「~/.bash_aliases
」に書いておくと良いでしょう。ただし「lxc exec
」経由で実行する場合は、エイリアス設定が効かないので注意してください。第560回で紹介したように「snap alias microk8s.kubectl kubectl
」を実行する方法も引き続き利用可能です[6]。
クラスター化は、特定のノードでノード連結させるためのクレデンシャルを生成・表示し、それ以外のノードからそのクレデンシャルを用いて登録要求を出すという流れになります。今回は3台のノードがすべてマスターノードになるため、どのノードでクレデンシャルを作ってもかまいません。そこでk8s0ノードで生成し、k8s1とk8s2から登録することにしましょう。
クレデンシャルの生成は「add-node
」サブコマンドを利用します。
「IPアドレス:ポート番号/クレデンシャル」の形式で表示されるので、それを登録する側のノード上で「join
」サブコマンドに渡す形になります。一度「join
」するとそのクレデンシャルは使えなくなるので、必要な数だけ「add-node
」を実行してください。複数のネットワークインターフェースを持つマシンだと、別のノードから到達できるインターフェースが限られているかもしれません。最後の2行のように、インターフェースごとにコマンド例を表示しているので、環境に合わせて選んでください。
登録する側のノード(今回だとk8s1とk8s2)でjoin
サブコマンドを実行します。
登録までにはしばらく時間がかかります。おとなしく待ちましょう。無事に登録できると、次のように「kubectl get nodes
」で登録されたノードがすべて表示されるようになります。
また、「microk8s status
」の先頭が次のような表記になります。
ポイントは「high-availability: yes
」になっていること、そして「master nodes
」にクラスター化した3台のノードのIPアドレスが表示されていることです。
ちなみに「クラスター化」だけであれば、2台でもかまいません。しかしながらHA機能に対応するためには「最低3台」のノードが必要になります。
マスターノードにログインする
3台のノードがクラスター化されました。このため、マスターノードであればどのノードからでもkubectl
で操作可能です[7]。そこで「lxc exec
」の入力を省くために、ここからはk8s0ノードにログインして、そこから操作することにします。まずはk8s0ノードに最初から作られているubuntuアカウントで、SSHログインできるようにしておきましょう。
上記のように実行することで、GitHubに登録されているSSHの公開鍵をk8s0ノードのubuntuユーザーのauthorized_keys
にインポートしてくれます。あとはk8s0のIPアドレス(たとえばlxc list
で確認可能)にログインします。
試しにmicrok8s kubectl
を実行すると次のようなメッセージが表示されます。
microk8sはいくつか管理者権限が必要な操作があります。microk8sグループに入っておくと、これらの処理を実行できるようになるわけです。chown
しているのは、microk8sインストール直後に自動的に作られた「~/.kube
」の一部が管理者権限であるためです。前述の手順だと、最初は「lxc exec
」で実行している都合上、「/root/.kube
」になっているので、chown
は実施しなくてもかまいません。
usermod
とchown
を実行し、ログインし直したら、きちんと動くか確認しておきましょう。
microbotのデプロイ
HA機能の動作確認を行う前に、テスト用にmicrobotをデプロイしておきます。これは単なるホスト名をウェブページとして表示するだけのHTTPサーバーです。
kubectl
コマンドで実際にデプロイされていることを確認しましょう。
ノードの外からアクセスできるように、NodePortタイプでexposeしておきます。
今回はExternal-IPは割り当てていません。各ノードのポート300085で受け付けていることがわかるため、試しにLXDが動いているホスト上からcurlで取得してみましょう。k8s0ノードの中からではないことに注意してください。
「Container hostname
」として表示されているホスト名が、前述の「kubectl get all
」で表示されていたPodのインスタンス名と一致していることがわかります。
本来ならここでウェブブラウザーを使って表示することも試してみたいところではありますが、少し手間がかかるので省略します。コンテナ版のLXDの場合、「proxyデバイス」を作成することで、簡単にホストの外からコンテナへアクセスできるように設定できます。しかしながらこの設定は、現時点ではコンテナのみ利用可能で、仮想マシンでは使えません。よってもし設定するとなると、自分でiptables/nftablesの設定を記述するか、Nginxのようなリバースプロキシーをセットアップする必要があります。
ちなみに上記の環境だとPodはk8s2ノード上で動いているようです。
スタンバイノードの作成とフェイルオーバーのテスト
最後に実際にmicrobotが動いているノードを落として、Podが他のノードにフェイルオーバーするかを確認してみましょう。
まず、スタンバイノードを作成します。microk8sのHAクラスターでは最低3台のマスターノード(voter)が必要です。マスターノード上の特定のノードが状態変更を受けると、マスターノード間で調停を行い、実際に適用するかどうかを判断し、データベースをレプリケーションします。それに対して「スタンバイノード」は、レプリケーションは行われるものの調停には参加しません。何らかの理由でマスターノードの特定のノードが応答しなくなったときに、スタンバイノードのいずれかが、マスターノードに昇格します。
HAクラスターに参加するノードが3台未満になると、HA機能は無効化されます。つまり障害発生時もHA機能が維持されるためには、3台のマスターノードに加えて、最低1台のスタンバイノードが必要です。スタンバイノードそのものは、他のノードと同じように作成します。ここでは新規にk8s3ノードをスタンバイノードとして作りましょう。
さらにk8s0ノード上で、「microk8s add-node
」した結果をもとに、k8s3ノードを「microk8s join
」しておいてください。これでk8s3ノードがHAクラスターに追加されたことになります。追加後、少し待ってからmicrok8s status
を実行するとスタンバイノードが追加されていることがわかりますね。
ここでmicrobotが動いているk8s2ノードを落としてしまいましょう。まずmicrobotのノードの位置は次のように確認できます。
障害発生を装うならk8s2の仮想マシンインスタンスを落としてしまうという手もあります。今回はもう少しおとなしく「正式な手順でHAクラスターから離脱する」という方法をとります。特定のノードのメンテナンスのために、クラスターから取り外したい場合に使われる方法です。これは離脱したノード上で、「microks leave
」コマンドを実行することで実現できます。
しばらくしてからk8s0側から見ると、k8s2ノードがNotReadyになったことがわかります。
取り外されたノードを完全にクラスターから除外するには「microk8s remove-node
」を実行します。
ステータスを見ると、スタンバイノードだったk8s3(10.203.13.186)がマスターノードに昇格していることがわかります。
ここから数秒から数十秒の間に、フェイルオーバーが実施されます。つまりk8s2で動いていたmicrobotインスタンスが、生きているノードのいずれかに移動するわけです。kubectl describe pod
で確認してみましょう。
どうやらk8s3ノードに移行したようです。
もちろん、ノードの外からアクセスする方法はフェイルオーバー前後で変わりません。前回と同様にcurlを用いて同じURLにアクセスできます。
ポイントは「Container hostname
」の値が前回(microbot-5f5499d479-bjjxw
)から末尾だけ変わっていることです。これは同じイメージを利用しているものの、コンテナのインスタンスが変わったことを示しています。
このようにLXDとmicrok8sを組み合わせると、簡単にKubernetesのHAクラスターを構築できます。本番環境ほどのマシン台数は用意できないけれども、ちょっと動作を確認したい場合に使えるのではないでしょうか。