※本記事は OpenShift Virtualization アドベントカレンダーの 11 日目の記事です。
皆さんこんにちは、OpenShift Virtualization とストレージを生業にしている Red Hat のうつぼ(宇都宮)です。
実は私けん玉を趣味にしておりまして、休日には息子と一緒に公園でずっとけん玉してたり、イベントに行ったりしています。先月開催された KWC(Kendama World Cup)も、現地には行けなかったんですがずっと YouTube Live で観てました。
今年の KWC は昨年に続いて同郷の川本凌雅選手の二連覇になりました。別のけん玉イベントで無理矢理川本選手にサインしてもらった1本のけん玉は私の宝物です。
大事に保管しておきたい気持ちもありますが、やっぱけん玉は振ってなんぼです。だから宝物でもバリバリ使ってます。落として傷も入ってますし、汚れも剥がれもしてます。
ですがけん玉の傷はそれだけ振ったという勲章なので、ボロッボロまで使い込んだけん玉を川本選手に持っていって、また新しいけん玉にサインしてもらおうと思います。

さて、使っていれば壊れていくのはけん玉もマシンも一緒。2つあれば1つ壊れても安心ですが、1つしかないと心もとない。"Two is one and one is none." ほんとに金言ですね。
ということで今日のテーマは High Availability です。
OpenShift Virtualization(以下 Virt)に限らず、全ての仮想化基盤において「ホストに障害が起きても、VM が自動で健全な別のホストで再起動する」仕組み、つまり High Availability (HA) の実装は超クリティカルなポイントですね。
本稿では、OpenShift 環境での HA の基本的な考え方から、Virt で VM の HA をどう実現するか、その具体的なコンポーネントである Node Health Check (NHC) Operator と Remediation Operator について解説します。
OpenShift 上の Pod と VM の HA の違い
OpenShift は、コンテナのアプリケーション(Pod)の HA は Kubernetes の自己修復機能を適用します。
つまり、Compute ノードがダウンしても、OpenShift のコントローラーが自動的に別の健全なノードに Pod を作り直してサービスを再開する仕組みを標準で持っています。
ところが、Virt の VM はこの標準の HA の仕組みではカバーできません。
Virt では、実行中の VM は VirtualMachineInstance (VMI) というカスタムリソースで宣言され、この VMI が virt-launcher Pod を呼び出して稼働します。
参考 : OpenShift Virtualization ( Kubevirt ) でVM管理もCloud Nativeに (1) - 赤帽エンジニアブログ
「じゃあ Compute ノードが落ちたらこの virt-launcher Pod が別のノードに移って、それでいいんじゃないの?」と思うかも知れませんが、残念。そうはなりません。実際は、落ちたノードでもう一度 Pod を上げようと試みます。
その理由は virt-launcher Pod は VMI で管理されているからです。VMI が Pod が稼働するノードの情報を持っており、同じノードで再び上げようとするのです。
ゆえに、VM を別のノードで起動するには、VMI 自身を削除して再作成することが必要になります。しかし OpenShift は標準では自動で VMI を再作成してくれません。
だから、Compute ノードが落ちても VM が自動で別のノードに上がってくるという機能を実現するには、OpenShift の標準機能とは別に専用の仕組みが必要になるわけです。

VM の HA を実現するコンポーネント
Virt で VM の HA を実現するためは以下の2つがキーとなります。
- Node Health Check (NHC) Operator
- Remediation Operator
この2つの Operator が連携することで、ノード障害の検知から VM の自動再起動までの一連の流れが完成します。
1. Node Health Check (NHC) Operator
NHC Operator は、名前の通り Compute ノードが元気かどうかをずーっと監視するものです。
ノードが事前に決めた健全性の基準(ヘルスチェック)を満たしているかをしげしげとチェックし、例えばノードが一定時間「NotReady」のステータスを続けたら障害(Unhealthy)だと判定する、のような動きをします。
そして、NHC が Unhealthy なノードを見つけると、そのノードを修復するために Remediation Operator を呼び出します。
2. Remediation Operator
Remediation Operator は、NHC から障害の通知を受け取ったノードに対して、修復措置(Remediation)を実行するものです。
具体的には障害ノードを再起動することでクラスタから一時的に切り離し、そのノードで動いていた VM が健全なノードで再起動するように VMI を再作成します。
Remediation Operator には、よく使われる2種類があります。
| Operator 名 | 特徴 |
|---|---|
| Self Node Remediation (SNR) Operator | 障害ノード自身に自己修復(再起動など)を試みさせる。設定はシンプルだけど、深刻な障害だと対処できないことも。 |
| Fence Agents Remediation (FAR) Operator | 別のノードから外部の電源管理機能(IPMI / Redfish など)を使って、障害ノードを強制的にシャットダウンする。確実に切り離せるが外部設定が必要。 |

どっちを使うとよいのよ?
SNR と FAR はどっちを使うかは環境や要件次第で選べますし、両方組み合わせて使うことも可能です。
といっても、どっちを使うとよいのよ?と思われるかたもいると思います。
| Self Node Remediation (SNR) Operator | Fence Agents Remediation (FAR) Operator | |
|---|---|---|
| 動作原理 | 障害ノード自身が再起動などの自己修復を試みる。 | クラスタ内の別のノードから外部の電源管理を使って強制的に電源を切る/入れ直す。 |
| メリット | 外部の機器設定が不要で、構成がシンプル。 | ノードが完全にフリーズしても、外部から確実に電源を落として切り離せる(データ破損防止)。 |
| デメリット | 完全なハングアップなど、深刻な障害には対応できない可能性がある。 | 物理的な電源管理インターフェースの設定とアクセス設定が必要で、構成が少し複雑。 |
| Remediation 速度 | 比較的遅い。ノードOS内での処理が必要なため、ノードの状態によって処理開始に時間がかかる。 | 比較的速い。外部インターフェース経由で直接電源操作を行うため、確実かつ迅速にノードを切り離せる。 |
どれだけ VM のダウンタイムを短くできるか?という点に興味が寄せられることが多いですが、一般的に FAR (Fence Agents Remediation) の方が SNR (Self Node Remediation) よりも迅速に Remediation を完了させられる 傾向があります。
FAR は、健全なノードから外部の電源管理インターフェースに対して直接コマンドを送り、障害ノードの電源を強制的に切断します。これは OS がどんな状態であろうともマシンをハードリセットするので少々危険ですが、非常に速くかつ確実にノードをクラスタから切り離せます。
一方で SNR は、障害ノード自身が watchdog を使って OS レベルで再起動などの処理を行うため、ノードの状態によっては処理開始までに時間がかかったり、深刻な障害ではそもそも処理が実行できなかったりする可能性があります。ただ、ソフトリセットなので graceful で安全です。
なるべく復旧時間を短くしたい場合は FAR 、時間より OS のデータインテグリティを重視する場合は SNR という方針がよいと思います。
NHC と Remediation Operator の連携フロー
VM の HA が実現するまでの流れは、こんな感じです。
- ノード障害が発生: Compute ノード A がダウンして「NotReady」になる。
- NHC が障害を検出: NHC Operator が、ノード A がヘルスチェックの基準を満たさないことを検知し、"Unhealthy" とみなす。
- Remediation Operator を呼び出し: NHC Operator が、設定されている Remediation Operator(SNR または FAR)を起動する。
- ノードを修復(切り離し):
- SNR の場合: ノード A が自己再起動を試みる。
- FAR の場合: 外部インターフェース経由でノード A を再起動。
- VM が自動再起動: ノード A がクラスタから完全に切り離されると、そのノードで動いていた VMI は「ホストを失った」状態とみなされます。Virt はこれを検知して、健全な Compute ノード B で自動的に VMI を作り直して、VM を起動する。
この連携のおかげで、ノード障害から VM の自動復旧までが実現し、VM の HA が確保されるというわけです。
インストールとデプロイ
NHC Operator、SNR Operator、FAR Operator は、いずれも OperatorHub から簡単にインストールできます。
デフォルトでは、これらの Operator は openshift-workload-availability という専用の Namespace に展開されます。
インストールしたら、あとはノードのヘルスチェックの条件や、どの Remediation Operator を使うかといった詳細設定をカスタムリソースとして適用すれば、HA 機能が使えるようになります。
一例として、NHC と FAR を使う場合は次のような YAML を作成します。
apiVersion: remediation.medik8s.io/v1alpha1
kind: NodeHealthCheck
metadata:
name: nhc-worker-vms
namespace: openshift-workload-availability
spec:
selector:
matchLabels:
node-role.kubernetes.io/worker: "" # 監視対象のノードラベル
unhealthyConditions: # Unhealthy とみなす条件。Ready 状態が "Unknown" or "False" = NotReady が 30s 続くと Unhealthy
- type: Ready
status: "Unknown"
timeout: 30s
- type: Ready
status: "False"
timeout: 30s
minHealthy: 51% # クラスタノードの過半数が Healthy でなければ remediation は実行しない
maxUnhealthy: 1 # 一時点で Unhealthy とみなすノード数は1つまで
remediationTemplate: # 呼び出す FAR のテンプレート
apiVersion: fence-agents-remediation.medik8s.io/v1alpha1
kind: FenceAgentsRemediationTemplate
name: fence-ipmilan-template
apiVersion: fence-agents-remediation.medik8s.io/v1alpha1
kind: FenceAgentsRemediationTemplate
metadata:
name: fence-ipmilan-template
namespace: openshift-workload-availability
spec:
template:
spec:
agent: fence_ipmilan
remediationStrategy: OutOfServiceTaint
nodeparameters: # 対象ノードごとの別々のパラメータ
--ip:
worker-01: '192.168.10.11'
worker-02: '192.168.10.12'
worker-03: '192.168.10.13'
sharedParameters: # 対象ノードで共有のパラメータ
retryCount: 2
retryInterval: 10
timeout: 15
login: "<bmc-user>"
passwd: "<bmc-password>"
NHC の spec.unhealthyConditions で timeout を短くすればするほど、VM は速く再起動します。
ただしあまりに極端に短くすると、ちょっとしたネットワークの遅延でノードが再起動されてしまうリスクもあるため、注意して設定しましょう。
まとめ
Virt で Compute ノード障害時に VM の HA を確保するには、NHC と SNR / FAR のような Remediation Operator を導入して連携させることで実現できます。
OpenShift は Kubernetes だから放っておけば復活するんでしょ、と思っている人はいると思います。私もはじめはそうでした。
これから機能検証や PoC を始められるかたは、ぜひこれらの Operator をちゃんと設定すれば、VMware vSphere HA と同じくらい信頼できる VM の可用性を Virt で実現できると思っていただけると幸いです。
ということで今日はここまで。