Node Disruption Policy について
OpenShift Advent Calendar 2025 の 14 日目です。だいぶ過ぎてますが、気にせず書いていきます。
概要
OpenShift 4.17 から導入された Node Disruption Policy は MahchineConfig で適用する設定ファイルなどの変更に対し「ノードを再起動する」「Daemon を再起動する」「何もしない」などのアクションをユーザーが定義できます。
これまでも OCP 4.7 以降、SSH キーや registries.conf など一部の変更は再起動なしで適用される仕組みがありましたが、これらはハードコードされており、ユーザーが挙動をカスタマイズすることはできませんでした。
MachineConfig の変更には Machine Config Operator (MCO) が管理しており、MachineConfig を新規に作成や変更するとノードのドレインと再起動を伴いますが、Node Disruption Policy により、特定の変更で再起動や影響を最小化できます。
Node Disruption Policy は MachineConfiguration API を使用します。
デフォルトで適用されているポリシーは status.nodeDisruptionPolicyStatus フィールド配下にあるので、まずは実際にざっと眺めてみます。
現時点で詳細はわからなくても、コンテナランタイム関連のファイルは crio.service のリロード (type: Reload)、ファイルによっては何もしない (None) などのアクションが実行されそうとなんとなくわかると思います。
$ oc get MachineConfiguration cluster -n openshift-machine-config-operator -oyaml
# ...
status:
# ...
nodeDisruptionPolicyStatus:
clusterPolicies:
files:
- actions:
- type: None
path: /var/lib/kubelet/config.json
- actions:
- reload:
serviceName: crio.service
type: Reload
path: /etc/machine-config-daemon/no-reboot/containers-gpg.pub
- actions:
- reload:
serviceName: crio.service
type: Reload
path: /etc/containers/policy.json
- actions:
- type: Special
path: /etc/containers/registries.conf
- actions:
- reload:
serviceName: crio.service
type: Reload
path: /etc/containers/registries.d
- actions:
- reload:
serviceName: crio.service
type: Reload
path: /etc/crio/policies
- actions:
- type: None
path: /etc/nmstate/openshift
- actions:
- restart:
serviceName: coreos-update-ca-trust.service
type: Restart
- restart:
serviceName: crio.service
type: Restart
path: /etc/pki/ca-trust/source/anchors/openshift-config-user-ca-bundle.crt
sshkey:
actions:
- type: None
# ...
設定方法と制約
まず管理できるファイルなどの対象と実行できるアクションを見てみます。
詳しくは Chapter 3. Using node disruption policies to minimize disruption from machine config changes を参照してください。
- 設定ファイル:
/etcや/varディレクトリ配下のファイル、または/etc/containers/registries.dのようにディレクトリ単位でも指定できる - Systemd Unit:
auditd.serviceなど Systemd Service に対する変更 - ユーザとグループ: SSH 鍵の変更
- イメージミラー関連: ImageContentSourcePolicy (ICSP), ImageTagMirrorSet (ITMS), ImageDigestMirrorSet (IDMS) リソースの変更
基本的に MCO が変更をサポートする対象は/etcと/varディレクトリであることと同じ考えで、これ以外に/optや/usr/localディレクトリ配下などすべての設定ファイルに対して適用できるわけではありません。
次に実行できる主要なアクションです。None, Reload や Restart の利用が主流になると思います。
- Reboot: ノードの再起動 (デフォルト)
- None: 何もしない (MachineConfig をノードに適用するだけ)
- Drain: ノードのドレイン
- Reload: サービス (デーモン) のリロード
- Restart: サービス (デーモン) の再起動
- DaemonReload: Systemd Manager の再起動
- Special: 内部的に MCO が実行できるアクションでユーザは指定できない
設定は MachineConfiguration リソースの spec.nodeDisruptionPolicy フィールドに定義します。
以下は NTP サーバを管理する Chrony の例で、/etc/chrony.conf の変更に対し chronyd.service を再起動します。
spec:
nodeDisruptionPolicy:
files:
- actions:
- restart:
serviceName: chronyd.service
type: Restart
path: /etc/chrony.conf
あるサービスやデーモンの設定ファイルの変更を有効にするためには、ノードの再起動は不要でデーモンのリロードや再起動で十分です。
当然と言えば当然ですが、このポリシーがない場合は chrony.conf の変更に対しノードの再起動を伴うので、本来はそれは避けたいと思うはずです。
主な制約や注意点です。
- デフォルトは再起動: Node Disruption Policy に定義していないファイルや Systemd Unit の変更は従来通りノードの再起動 (
Type: Reboot)がデフォルトのアクション - About the Machine Config Operator に記載している カーネル引数やオプションの追加 などノード再起動が必須なアクションはあり、あらゆる MachineConfig への変更を対象としない
- バリデーションなし: MCO はユーザが追加したポリシーをバリデーションするわけでないので、ユーザ自身で正常性を検証
- Image Mode (On-cluster image mode) を利用する場合はサポートしない
簡易的な内容ですが、Node Disurpution Policy の適用には MCO の一部である Machine Config Daemon (MCD) が関連します。
- 検知: MCD が現在の設定と、適用したい新しい設定(Desired Config)の差分を検知
- ポリシー確認: MCD は従来のハードコードされたリストではなく、MachineConfiguration オブジェクトの .status.nodeDisruptionPolicyStatus を参照
- アクションの実行: ポリシーに基づき、必要なアクション(サービスの再起動など)をキューに追加し、順次実行
では次のステップでは実際に検証します。
検証
chrony.conf の変更に対し chrony.service の再起動だけが実行されることを検証します。
まず chrony.conf の設定を変更する MachineConfig を作成し、ノードの NTP サーバを変更します。
この時点では該当するポリシーを適用していないので、ノードの再起動が実行されます。
ここでは MachineConfig を直接書かず Buane コンフィグ (.bu) を作成します。
butane コマンドを使用することで、Buane コンフィグから MachineConfig を生成することができ、必要最低限な設定を書けば済むので、MachineConfig を直接書くよりも便利です。
インストール方法は Installing butane を参照してください。
cat << EOF > 99-worker-chrony.bu
variant: openshift
version: 4.20.0
metadata:
name: 99-worker-chrony
labels:
machineconfiguration.openshift.io/role: worker
storage:
files:
- path: /etc/chrony.conf
mode: 0644
overwrite: true
contents:
inline: |
pool 0.rhel.pool.ntp.org iburst
EOF
butane コマンドで MachineConfig を生成し、この MachineConfig を適用します。
butane 99-worker-chrony.bu | oc apply -f -
oc get machineconfignodes コマンドは MachineConfig の適用が完了したかを表示します。-o wide フラグを付けることで MachineConfig を適用するフェーズを表示します。-o wide をつけないと、UPDATED のフェーズのみが表示されます。
大まかには UPDATEPREPARED が False -> True になると Machine Config Operator が MachineConfig の適用を準備し始め、REBOOTEDNODE が False -> True になると実際にノードが再起動され、最終的に UNCORDONEDNODE が False -> True -> False になるとノードの Uncordon (スケジュール対象外のノードをスケジュール対象としてマーク) が完了します。
このコマンドで表示されるフェーズの詳細は About node status during updates に記載しています。
$ oc get machineconfignodes -o wide -w
NAME POOLNAME DESIREDCONFIG CURRENTCONFIG UPDATED AGE UPDATEPREPARED UPDATEEXECUTED UPDATEPOSTACTIONCOMPLETE UPDATECOMPLETE RESUMED UPDATEDFILESANDOS CORDONEDNODE DRAINEDNODE REBOOTEDNODE UNCORDONEDNODE
ip-10-0-112-112.us-west-1.compute.internal worker rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 True 85m False False False False False False False False False False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 False 85m True Unknown False False False False True Unknown False False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 False 85m True Unknown False False False Unknown True True False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 False 85m True True False False False True True True False False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 False 85m True True False False False True True True Unknown False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-a01a8e200e324b5c88929c0df0911a54 True 87m False False False False False False False False False False
oc get machineconfignodes コマンドはフェーズの推移をステップバイステップでみるにはいいですが、本当にノードが再起動したのか少し分かりづらいかもしれません。
Web Console を使うと Computes > Nodes の Events タブからそれぞれのノードが実際に再起動したかイベントのメッセージを確認することもできます。

途中で ctrl+c などで oc get -w コマンドを中断し、MachineConfigPool を確認するとノードの再起動が実行されている台数を確認することができます。
ここでは MACHINECOUNT (ノードの総数) が 3 に対し、READYMACHINECOUNT (アップデートが完了したノードの数) が 2 であることから、全体で 3 台のうち 2 台のノードのアップデートが完了したことがわかります。
ノード数は 3 台で順次再起動を伴うため、全体の完了まで数分かかり、体感的にもそれなりに時間がかかる印象を受けるはずです。
$ oc get mcp worker
NAME CONFIG UPDATED UPDATING DEGRADED MACHINECOUNT READYMACHINECOUNT UPDATEDMACHINECOUNT DEGRADEDMACHINECOUNT AGE
worker rendered-worker-0b504ab68d3c5957b38fbd2a5152b263 False True False 3 2 2 0 98m
ここからは Node Disruption Policy の適用を検証します。
MachineConfig を作成して chrony.conf を修正するとノードの再起動は実行されずに chronyd.service が再起動されることを確認します。
Node Disruption Policy を設定するため MachineConfiguration を編集し、spec フィールド配下に次の内容を追加します。
この設定により、chrony.conf への変更に対し、ノード再起動でなく chronyd.service を再起動します。
$ oc edit MachineConfiguration cluster -n openshift-machine-config-operator
spec:
nodeDisruptionPolicy:
files:
- actions:
- restart:
serviceName: chronyd.service
type: Restart
path: /etc/chrony.conf
vi コマンドで 99-worker-chrony.bu を編集し、pool 0.rhel.pool.ntp.org iburst を pool 2.rhel.pool.ntp.org iburst に変更します。
$ vi 99-worker-chrony.bu
先ほどと同様に butane コマンドで MachineConfig を生成し、oc apply コマンドでこの MachineConfig を適用します。
butane 99-worker-chrony.bu | oc apply -f -
oc get machineconfignodes -o wide -w コマンドで MachineConfigNodes リソースを参照すると先程より短い時間、恐らく数十秒程度の体感で完了したと感じるはずです。
今回はノードの再起動が実行されないので、REBOOTEDNODE が False -> True になることはなく、UPDATEPREPARED から UPDATEDFILESANDOS までのステータスが False -> True -> False に推移します。
なおノードの再起動や cordon/Uncordon は実行されないため、 CORDONEDNODE から UNCORDONEDNODE までのステータスは False のままで、これらが False -> True に推移することはありません。
$ oc get machineconfignodes -o wide -w
NAME POOLNAME DESIREDCONFIG CURRENTCONFIG UPDATED AGE UPDATEPREPARED UPDATEEXECUTED UPDATEPOSTACTIONCOMPLETE UPDATECOMPLETE RESUMED UPDATEDFILESANDOS CORDONEDNODE DRAINEDNODE REBOOTEDNODE UNCORDONEDNODE
ip-10-0-112-112.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-a01a8e200e324b5c88929c0df0911a54 True 121m False False False False False False False False False False
ip-10-0-23-203.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-a01a8e200e324b5c88929c0df0911a54 True 121m False False False False False False False False False False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-a01a8e200e324b5c88929c0df0911a54 rendered-worker-a01a8e200e324b5c88929c0df0911a54 True 121m False False False False False False False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 True 121m False False False False False False False False False False
<...>
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 121m False False False False False False False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 121m True False False False False False False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 121m True False False False False False False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 121m True Unknown False False False False False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 121m True Unknown False False False Unknown False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 122m True True False False False True False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 122m True True False False True True False False False False
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-a01a8e200e324b5c88929c0df0911a54 False 122m True True False True True True False False False True
ip-10-0-63-132.us-west-1.compute.internal worker rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd rendered-worker-aa69f0c470bb7c949edd54f0442e7ddd True 122m False False False False False False False False False False
<...>
Web Console の Computes > Nodes の Events タブをみると再起動は要求されず、chrony.service の再起動だけが実行されることがわかります。

ここまでの流れで Node Disruption Policy でポリシーを作成すると MachineConfig で chrony.conf で指定する NTP サーバを変更させると、ノード再起動を伴わずに chronyd.service の再起動で済むことが理解できたと思います。
ユーザ自身でどのファイルに対しどのようなアクションを適用するか検討する必要があり、動的にノード再起動の可否を判定して自動でポリシーを組み込むような機能や処理は含まれないので、運用面や設計面での課題や検討事項はあると思います。
一方で MachineConfig を作成して設定を適用する際になるべく必要最低限のサービスやデーモンの再起動で済ませるといった基本的なできるので、ノード再起動を最小限にとどめたいケースでは便利だと思います。
いくつか文中で紹介したリンクもありますが、興味があるかたは以下のリファレンスも参照してみてください。
Discussion