Kubespray と Terraform でクラスターを構築する
1 台から複数台へ — クォーラム、ロードバランサー、ノードの追加と削除の順序
一言でいうと
ノードを複数台に広げるとき、kubesprayで変わるのは、インベントリの名前の数行とhost_varsのファイルの数個ですが、その裏には、etcdのクォーラム・APIサーバーのロードバランシング・ノードの追加と削除の順序という、設計上の判断があります。
なぜ必要なのか
このコースのラボは、VM 1台でコントロールノードと対象ノードを兼ねていました。実際のクラスターは違います。コントロールプレーンが1台止まるとAPIが止まり、etcdが1台止まるとデータが止まります。Kubernetesのドキュメントの高可用性トポロジーの節は、2つの形を説明しています。etcdをコントロールプレーンのノードに一緒に置くstackedと、etcdを別に置くexternalです。stackedはノードが少なくて済みますが、ノードを1つ失うと、コントロールプレーンとetcdのメンバーを一緒に失い、externalはそのリスクを分ける代わりに、ノードが2倍必要です。kubesprayでこの選択は、[etcd:children] kube_control_planeにするか、[etcd]に別の名前を書くかの違いです。
どう動くのか
etcdは奇数です。etcdは、書き込みのたびに過半数(クォーラム、n//2+1)の同意を得ます。そのため、耐えられる障害の数は、n − 過半数です。
멤버 과반 견디는 장애
1 1 0
2 2 0 ← 한 대보다 나을 것이 없다
3 2 1
5 3 2
7 4 3 ← 쓰기마다 넷을 기다린다
このコードブロックの韓国語の部分は、列の見出しがメンバー、過半数、耐えられる障害であること、2台の行が1台よりよいところがないこと、7台の行が書き込みのたびに4台を待つことを述べています。
2台は、1台と耐えられる数が同じなのに、過半数だけが大きくなります。kubesprayはこれをvalidate_inventoryの「Stop if even number of etcd hosts」で止め、ドキュメントも、障害に備えるには3台以上を置くよう書いています。etcdのFAQは、本番のクラスターに3台か5台を勧め、メンバーを増やすほど書き込みのレイテンシが増えると説明しています。
ワーカーはlocalhostでAPIサーバーに接続します。コントロールプレーンが複数あると、kubeletとkube-proxyがどのAPIサーバーに接続するかを決める必要があります。kubesprayのデフォルトは、外部ロードバランサー(loadbalancer_apiserver)を定義しないとloadbalancer_apiserver_localhostが真になり、コントロールプレーンではないノードごとにnginxプロキシ(loadbalancer_apiserver_type: nginx)を起動して、localhostのkube_apiserver_port(6443)で受け取り、すべてのAPIサーバーに振り分ける方式です。HAのドキュメントは、この方式が専用のLBよりAPIサーバーにヘルスチェックを多く送るので効率は劣りますが、VIPの管理が面倒な場所では実用的だと書いています。外部のクライアント(運用者のkubectlなど)のためには、別にLBやkube-vipを置きます。
ノードの追加と削除は専用のプレイブックです。始め方のドキュメントの順序は、こうです。ワーカーを追加するときは、インベントリのグループに名前を加えてscale.ymlを実行します。cluster.ymlを再実行してもよいですが、scale.ymlは、新しいワーカーにkubeletを載せるのに必要なことだけを行います。削除するときは、remove-node.yml -e node=<이름>がdrain → サービスの停止 → 証明書の整理 → ノードの削除を行います(プレースホルダーはノード名です)。アップグレードのドキュメントは、--limitでノードを選んで実行する前に、playbooks/facts.ymlを制限なしで1回実行して、factsのキャッシュを新しくするよう求めています。リポジトリのansible.cfgがfactsを/tmpにjsonfileで1日(86400秒)キャッシュしていて、--limitでの実行は、他のノードのfactsを集めずにキャッシュを使うからです。
インベントリの検査は、ノードなしでできます。boilerplate.ymlは、factsを集めず、インベントリと変数だけを見ます。-e ansible_connection=localで接続を上書きすれば、実際のノードがなくても動きます。実測では、5ノードのインベントリを2.4秒で検査しました。--list-hostsは、何も変更せずに、各playが誰を対象にするかだけを表示するので、scale.ymlが新しいノードだけを対象にするか、remove-node.ymlが削除するノードだけを確認するかを、実行する前に見られます。
現場での姿
よく見る失敗は3つです。etcdを偶数に増やすこと(4台になると過半数が3なので、3台のときより耐えられる数はそのままです)、コントロールプレーンを増やしながら、ワーカーのAPIサーバーへのアクセス方式を決めないこと、そして、新しいノードだけを--limitで実行して、factsのキャッシュが空のために、新しいノードの/etc/hostsやetcdの設定が、見当違いのアドレスを受け取ることです。
このモジュールは、設計と検査までです。1つのセッションでVMを複数台提供する機能が用意されたら、このインベントリをそのまま使って、ノードのjoin(scale.yml)、コントロールプレーン3台とetcdのクォーラム、ノードの障害と復旧(remove-node.ymlとrecover-control-plane.yml)、1台ずつ上げるローリングアップグレード(serial=1)を実際に行うラボが続きます。1台構成のラボで、接続方式をhost_varsに出しておいたことが、そのときのための準備でした。
次のラボですること
サンプルをコピーして、コントロールプレーン3台・ワーカー2台のインベントリと、ノードごとのhost_varsを作り、sshなしでkubesprayのインベントリの検査を通過させます。etcdを2台に変えたインベントリがどこで止まるかを見て、クォーラムの表を計算します。ワーカーがAPIサーバーに接続する方式をデフォルト値から読み取り、ワーカーを1つ追加して削除するコマンドが誰を対象にするかを--list-hostsで確認したうえで、ランブックに書きます。