60 ライフサイクルアクション #
このセクションでは、SUSE Telco Cloud経由でデプロイされたクラスターのライフサイクル管理について説明します。
60.1 ロードバランサーの除外 #
ノードのドレインを必要とするライフサイクルアクションは多数あります。ドレインプロセス中、すべてのポッドはクラスター内の他のノードに移動されます。ドレインプロセスが完了すると、ノードはサービスをホストしなくなるため、トラフィックをルーティングすべきではありません。MetalLBなどのロードバランサーは、ノードにラベルを適用することで、この状態を認識させることができます。
node.kubernetes.io/exclude-from-external-load-balancers: "true"詳細については、 Kubernetesドキュメントを参照してください。
クラスター内のすべてのノードのラベルを確認するには、次のコマンドを実行します。
kubectl get nodes -o json | jq -r '.items[].metadata | .name, .labels'ダウンストリームのクラスターのアップグレードの場合、管理クラスター上のRKE2ControlPlaneにアノテーションを付けることで、これを自動化できます。
rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion="true"これにより、そのRKE2ControlPlaneの管理クラスター上のすべてのマシンオブジェクトにアノテーションが即座に作成されます。
pre-drain.delete.hook.machine.cluster.x-k8s.io/rke2-lb-exclusion: ""マシンオブジェクトにこのアノテーションが付与されると、ダウンストリームのクラスターでドレインがスケジュールされているノードには、ドレインプロセスの開始前に上記のノードラベルが付けられます。ノードが再び利用可能になり準備が整うと、ラベルはノードから削除されます。
60.2 管理クラスターのアップグレード #
管理クラスターのアップグレードについては、Day 2 管理クラスター (第59章 「管理クラスタ」)のドキュメントで説明されています。
60.3 ダウンストリームクラスターのアップグレード #
ダウンストリームのクラスターのアップグレードには、いくつかのコンポーネントの更新が含まれます。以下のセクションでは、各コンポーネントのアップグレードプロセスについて説明します。
オペレーティングシステムのアップグレード
このプロセスについては、新しいオペレーティングシステムバージョンで新しいイメージをビルドするために、以下のリファレンス (第50章 「接続されたシナリオ向けのダウンストリームクラスターイメージの準備」)を確認してください。 `EIB`によって生成されたこの新しいイメージを使用すると、次のプロビジョニングフェーズで提供された新しいオペレーティングバージョンが使用されます。 次のステップでは、新しいイメージを使用してノードをアップグレードします。
RKE2クラスターのアップグレード
自動ワークフローを使用して`RKE2`クラスターをアップグレードするために必要な変更は以下の通りです。
以下のセクション (第52章 「ダイレクトネットワークプロビジョニング(シングルノード)によるダウンストリームクラスターのプロビジョニング」)に示す`RKE2ControlPlane`のブロック`capi-provisioning-example.yaml`を変更します。
目的の`rolloutStrategy`を指定してください。
少なくとも3つのコントロールプレーンノードを持つマルチノード`RKE2`クラスターにおいて、`RKE2ControlPlane.spec.rolloutStrategy.type`フィールドを`"RollingUpdate"`に、`rolloutStrategy.rollingUpdate.maxSurge`を`0`に設定することで、関連する`Metal3MachineTemplate.spec.nodeReuse`も`True`に設定されている場合、アップグレードする際に余分なノードが作成されたり、追加が必要になったりすることがない点にご注意ください。
RKE2`クラスターのバージョンを、${RKE2_NEW_VERSION}`を置き換える新しいバージョンに変更します。
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: RKE2ControlPlane
metadata:
name: multinode-cluster
namespace: default
spec:
infrastructureRef:
apiGroup: infrastructure.cluster.x-k8s.io
kind: Metal3MachineTemplate
name: multinode-cluster-controlplane
version: ${RKE2_NEW_VERSION}
replicas: 3
rolloutStrategy:
type: "RollingUpdate"
rollingUpdate:
maxSurge: 0
serverConfig:
cni: cilium
tlsSan:
- ${EDGE_VIP_ADDRESS_IPV4}
- ${EDGE_VIP_ADDRESS_IPV6}
- https://${EDGE_VIP_ADDRESS_IPV4}.sslip.io
- https://${EDGE_VIP_ADDRESS_IPV6}.sslip.io
rolloutStrategy:
rollingUpdate:
maxSurge: 0
registrationMethod: "control-plane-endpoint"
registrationAddress: ${EDGE_VIP_ADDRESS}
agentConfig:
format: ignition
additionalUserData:
config: |
variant: fcos
version: 1.4.0
systemd:
units:
- name: rke2-preinstall.service
enabled: true
contents: |
[Unit]
Description=rke2-preinstall
Wants=network-online.target
Before=rke2-install.service
ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
[Service]
Type=oneshot
User=root
ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
# Note we use _ as a delimiter for sed here because the providerid includes / characters
ExecStart=/bin/sh -c "sed -i \"s_PROVIDERID_$(jq -r .providerid /mnt/openstack/latest/meta_data.json)_\" /etc/rancher/rke2/config.yaml"
ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
ExecStart=/bin/sh -c "echo \"node-label:\" >> /etc/rancher/rke2/config.yaml"
ExecStart=/bin/sh -c "echo \" - metal3.io/uuid=$(jq -r .uuid /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
ExecStartPost=/bin/sh -c "umount /mnt"
[Install]
WantedBy=multi-user.target
storage:
directories:
- path: /var/lib/rancher/rke2/server/manifests
overwrite: true
files:
- path: /var/lib/rancher/rke2/server/manifests/endpoint-copier-operator.yaml
overwrite: true
contents:
inline: |
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: endpoint-copier-operator
namespace: kube-system
spec:
chart: oci://registry.suse.com/edge/charts/endpoint-copier-operator
targetNamespace: endpoint-copier-operator
version: {version-endpoint-copier-operator-chart}
createNamespace: true
- path: /var/lib/rancher/rke2/server/manifests/metallb.yaml
overwrite: true
contents:
inline: |
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: metallb
namespace: kube-system
spec:
chart: oci://registry.suse.com/edge/charts/metallb
targetNamespace: metallb-system
version: {version-metallb-chart}
createNamespace: true
- path: /var/lib/rancher/rke2/server/manifests/metallb-cr.yaml
overwrite: true
contents:
inline: |
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: kubernetes-vip-ip-pool
namespace: metallb-system
spec:
addresses:
- ${EDGE_VIP_ADDRESS_IPV4}/32
- ${EDGE_VIP_ADDRESS_IPV6}/128
serviceAllocation:
priority: 100
namespaces:
- default
serviceSelectors:
- matchExpressions:
- {key: "serviceType", operator: In, values: [kubernetes-vip]}
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: ip-pool-l2-adv
namespace: metallb-system
spec:
ipAddressPools:
- kubernetes-vip-ip-pool
- path: /var/lib/rancher/rke2/server/manifests/endpoint-svc.yaml
overwrite: true
contents:
inline: |
apiVersion: v1
kind: Service
metadata:
name: kubernetes-vip
namespace: default
labels:
serviceType: kubernetes-vip
spec:
ipFamilyPolicy: PreferDualStack
ports:
- name: rke2-api
port: 9345
protocol: TCP
targetPort: 9345
- name: k8s-api
port: 6443
protocol: TCP
targetPort: 6443
type: LoadBalancer
###############################################################################
# In case of deploying the RKE2 ingress controller, the following 2 files must
# be added.
# NOTE: The .providers stanza can be removed from the Helm parameters settings
# here below in case of not deploying/not planning to deploy Ingress
# objects including ingress-nginx annotations in its `.metadata` stanza.
###############################################################################
- path: /var/lib/rancher/rke2/server/manifests/rke2-traefik-config.yaml
overwrite: true
contents:
inline: |
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-traefik
namespace: kube-system
spec:
valuesContent: |-
ingressClass:
isDefaultClass: false
ports:
web:
hostPort: null # disallow hostPort
exposedPort: 80
websecure:
hostPort: null # disallow hostPort
exposedPort: 443
service:
enabled: true
spec:
type: LoadBalancer
ipFamilyPolicy: PreferDualStack
externalTrafficPolicy: Local
allocateLoadBalancerNodePorts: false
providers:
kubernetesIngressNGINX: # this provider allows Traefik to "understand" most of the Ingress-NGINX annotations
enabled: true
ingressClass: "rke2-ingress-nginx-migration"
controllerClass: "rke2.cattle.io/ingress-nginx-migration"
mode: 0644
user:
name: root
group:
name: root
- path: /var/lib/rancher/rke2/server/manifests/metallb-traefik-cr.yaml
overwrite: true
contents:
inline: |
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: ingress-ippool
namespace: metallb-system
spec:
addresses:
- ${INGRESS_IPV4}/32
- ${INGRESS_IPV6}/128
serviceAllocation:
priority: 100
serviceSelectors:
- matchExpressions:
- {key: app.kubernetes.io/name, operator: In, values: [rke2-traefik]} # assign to rke2-traefik LB svc
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: ingress-l2-adv
namespace: metallb-system
spec:
ipAddressPools:
- ingress-ippool
mode: 0644
user:
name: root
group:
name: root
kubelet:
extraArgs:
- provider-id=metal3://PROVIDERID
nodeName: "localhost.localdomain"以下のセクション (第52章 「ダイレクトネットワークプロビジョニング(シングルノード)によるダウンストリームクラスターのプロビジョニング」)に示す`Metal3MachineTemplate`のブロック`capi-provisioning-example.yaml`を変更します。
前のステップで生成された新しいバージョンに合わせて、イメージ名とチェックサムを変更します。
新しいノードの作成を回避するために、ディレクティブ`nodeReuse`を`true`に追加します。
ノードの自動クリーンアップを有効にするには、
automatedCleaningModeをmetadataに追加します。
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: Metal3MachineTemplate
metadata:
name: multinode-cluster-controlplane
namespace: default
spec:
nodeReuse: True
template:
spec:
automatedCleaningMode: metadata
dataTemplate:
name: multinode-cluster-controlplane-template
hostSelector:
matchLabels:
cluster-role: control-plane
image:
checksum: http://imagecache.local:8080/${NEW_IMAGE_GENERATED}.sha256
checksumType: sha256
diskFormat: raw
url: http://imagecache.local:8080/${NEW_IMAGE_GENERATED}.rawcapi-provisioning-example.yaml ファイルを適用する前に、外部ロードバランサー(MetalLBなど)にノードがドレイン中であることを通知し、その状態のノードにトラフィックがルーティングされないようにすることをお勧めします。前述の 60.1項 「ロードバランサーの除外」 セクションで述べたように、管理クラスター上の RKE2ControlPlane にアノテーションを付けることで、これを自動化できます。この例では、multinode-cluster という名前の RKE2ControlPlane オブジェクトにアノテーションが付けられています。
kubectl annotate RKE2ControlPlane/multinode-cluster rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion="true"マシンオブジェクトにアノテーションが付けられていることを確認します。
pre-drain.delete.hook.machine.cluster.x-k8s.io/rke2-lb-exclusion: ""すべてのマシンオブジェクトのアノテーションを取得します。
kubectl get machines -o json | jq -r '.items[].metadata | .name, .annotations'これらのアノテーションがないと、ロードバランサーがドレインされたノードを認識できないため、サービスの応答時間が長くなる可能性があります。
これらの変更を行った後、次のコマンドを使用して capi-provisioning-example.yaml ファイルをクラスターに適用できます。
kubectl apply -f capi-provisioning-example.yaml60.4 Longhorn Engineイメージのクリーンアップ #
本セクションでは、クラスターをアップグレードする際に蓄積される不要なLonghorn Engineイメージの自動クリーンアップを実装する方法について説明します。
60.4.1 問題の定義 #
Longhornは、アップグレード後に古いエンジンイメージを自動的に削除しません。これは、偶発的なデータ損失を防ぐための意図的な設計上の決定です。Longhornは、ロールバックが必要になった場合に備えて、安全対策として以前のエンジンイメージバージョンを保持します。
しかし、頻繁にアップグレードが行われる本番環境では、これらの未使用のエンジンイメージが時間の経過とともに蓄積され、以下の問題が発生します。
ストレージ消費:各エンジンイメージがクラスターノード上のディスク容量を消費する
リソースオーバーヘッド:Kubernetesが不要なイメージリソースを管理してしまう
運用上の複雑さ:不要になったイメージの手動特定とクリーンアップが必要になる
60.4.2 クリーンアップを実施すべきタイミング #
エンジンイメージのクリーンアップは、以下のシナリオで推奨されます。
定期的なクラスターアップグレード:Longhornのバージョンアップグレードを頻繁に行う環境
ストレージ容量が限られた環境:ノードのストレージ容量が限られているクラスター
運用上の衛生管理:日常的なDay 2メンテナンス手順の一環として
このクリーンアップ手順は、アクティブな参照がゼロのエンジンイメージのみを削除するため、低リスクに分類されます (refCount == 0)。現在ボリュームで使用されているイメージは保護されており、削除されません。
60.4.3 ソリューションアーキテクチャ #
クリーンアップソリューションは、定期的なメンテナンス作業を実行するKubernetes CronJobを使用します:
検出:クラスターにデプロイされている最新のLonghorn Engineイメージバージョンを特定します
候補の選択:アクティブなボリューム参照がない古いエンジンイメージバージョンをすべて特定します
クリーンアップの実行:安全基準を満たす不要なエンジンイメージを削除します
監査ログ:運用の可視化のためにクリーンアップ操作を記録します
実装には以下が必要です:
ServiceAccount:Kubernetes APIへの認証用
RBAC権限:`engineimages.longhorn.io`リソースへの読み取りおよび削除アクセス権
CronJob:クリーンアップロジックを含むスケジュール実行コンテナ
60.4.4 実装 #
完全なクリーンアップソリューションは、`longhorn-system`ネームスペースにデプロイされる4つのKubernetesマニフェストで構成されています。
ServiceAccountを作成
クリーンアップCronJob専用のServiceAccountを作成します。
apiVersion: v1 kind: ServiceAccount metadata: name: longhorn-engineimage-cleanup namespace: longhorn-system labels: app: longhorn-engineimage-cleanupRBAC権限を構成
最小限の必要な権限を持つRoleを定義します。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: longhorn-engineimage-cleanup namespace: longhorn-system labels: app: longhorn-engineimage-cleanup rules: - apiGroups: [longhorn.io] resources: [engineimages] verbs: [get, list, delete]RoleをServiceAccountにバインドします。
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: longhorn-engineimage-cleanup namespace: longhorn-system labels: app: longhorn-engineimage-cleanup subjects: - kind: ServiceAccount name: longhorn-engineimage-cleanup namespace: longhorn-system roleRef: kind: Role name: longhorn-engineimage-cleanup apiGroup: rbac.authorization.k8s.io
クリーンアップCronJobをデプロイ
自動クリーンアップCronJobをデプロイします。
apiVersion: batch/v1 kind: CronJob metadata: name: longhorn-engineimage-cleanup namespace: longhorn-system labels: app: longhorn-engineimage-cleanup annotations: description: "Cleanup old Longhorn engine images left over after upgrades" source: "SUSE Edge Telco Cloud - Day 2 Operations" spec: # Execute weekly on Sunday at 2:00 AM UTC schedule: "0 2 * * 0" successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 concurrencyPolicy: Forbid jobTemplate: spec: backoffLimit: 2 activeDeadlineSeconds: 600 template: metadata: labels: app: longhorn-engineimage-cleanup spec: serviceAccountName: longhorn-engineimage-cleanup restartPolicy: Never automountServiceAccountToken: true containers: - name: cleanup image: registry.suse.com/bci/bci-base:15.6 command: - sh - -c - | set -e # Install required tools zypper --non-interactive install curl jq # Install kubectl from official Kubernetes curl -LO "https://dl.k8s.io/release/v1.36.3/bin/linux/amd64/kubectl" chmod +x kubectl mv kubectl /usr/local/bin/ echo "=== Longhorn Engine Image Cleanup ===" echo "Automated maintenance - Day 2 Operations" echo "" echo "Step 1: Discovering latest engine image version..." ENGINE_IMAGES=$(kubectl get engineimages.longhorn.io -n longhorn-system -o json) LATEST_VERSION=$(echo "$ENGINE_IMAGES" | jq -r ' .items[] | select(.status.version != null) | .status.version' | sed 's/^v//' | sort -V | tail -1) if [ -z "$LATEST_VERSION" ]; then echo "No engine images found. Nothing to cleanup." exit 0 fi echo "Latest version detected: v${LATEST_VERSION}" echo "" echo "Step 2: Identifying obsolete engine images..." TO_DELETE=$(echo "$ENGINE_IMAGES" | jq -r --arg latest "v${LATEST_VERSION}" ' .items[] | select(.status.version != $latest) | select(.status.refCount == 0) | .metadata.name') if [ -z "$TO_DELETE" ]; then echo "No obsolete engine images found" echo "All engine images are either:" echo " - Latest version (v${LATEST_VERSION})" echo " - Still in use (refCount > 0)" exit 0 fi DELETE_COUNT=$(echo "$TO_DELETE" | wc -l | tr -d ' ') echo "Found $DELETE_COUNT obsolete engine image(s) for deletion:" echo "$TO_DELETE" echo "" COUNT=0 FAILED=0 for IMAGE_NAME in $TO_DELETE; do VERSION=$(echo "$ENGINE_IMAGES" | jq -r --arg name "$IMAGE_NAME" \ '.items[] | select(.metadata.name == $name) | .status.version') REFCOUNT=$(echo "$ENGINE_IMAGES" | jq -r --arg name "$IMAGE_NAME" \ '.items[] | select(.metadata.name == $name) | .status.refCount') echo "Deleting: $IMAGE_NAME (version: $VERSION, refCount: $REFCOUNT)" if kubectl delete engineimages.longhorn.io "$IMAGE_NAME" -n longhorn-system; then COUNT=$((COUNT + 1)) echo " ✓ Deleted successfully" else FAILED=$((FAILED + 1)) echo " ✗ Failed to delete" fi done echo "" echo "=== Cleanup Summary ===" echo "Latest version: v${LATEST_VERSION}" echo "Successfully deleted: $COUNT obsolete engine images" echo "Failed deletions: $FAILED engine images" if [ $FAILED -gt 0 ]; then exit 1 fi echo "" echo "Cleanup completed successfully"設定を適用
4つのマニフェストすべてを1つのファイル(例:
longhorn-engine-cleanup.yaml)に保存し、クラスターにデプロイします。kubectl apply -f longhorn-engine-cleanup.yamlCronJobが作成されたことを確認します。
kubectl get cronjob -n longhorn-system longhorn-engineimage-cleanup予想される出力:
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE longhorn-engineimage-cleanup 0 2 * * 0 False 0 <none> 5s
60.4.5 運用手順 #
このセクションでは、手動実行、スケジュールのカスタマイズ、監視、検証など、LonghornエンジンイメージクリーンアップCronJobを管理するための日常的な運用タスクについて説明します。
60.4.5.1 手動実行 #
スケジュールされた時間を待たずにすぐにクリーンアップを実行するには、次のコマンドを実行します。
kubectl create job --from=cronjob/longhorn-engineimage-cleanup \
longhorn-engineimage-cleanup-manual-$(date +%Y%m%d-%H%M%S) \
-n longhorn-system手動実行の監視:
ジョブステータスの監視:
kubectl get jobs -n longhorn-system -wジョブログの表示:
kubectl logs -n longhorn-system -l app=longhorn-engineimage-cleanup --tail=100
60.4.5.2 スケジュール設定 #
デフォルトのスケジュールでは、毎週日曜日の午前2:00(UTC)にクリーンアップが実行されます。
schedule: "0 2 * * 0"一般的なスケジュールの代替案:
毎日午前3:00:
"0 3 * * *"毎月1日の午前4:00:
"0 4 1 * *"6時間ごと:
"0 */6 * * *"
スケジュールを変更します:
kubectl patch cronjob longhorn-engineimage-cleanup \
-n longhorn-system \
--type='merge' \
-p '{"spec":{"schedule":"0 3 * * *"}}'60.4.5.3 監視とログ #
クリーンアップ履歴を表示します:
完了したジョブを一覧表示します:
kubectl get jobs -n longhorn-system -l app=longhorn-engineimage-cleanup前回の正常実行のログを表示します:
kubectl logs -n longhorn-system \ -l app=longhorn-engineimage-cleanup \ --tail=200 \ | grep -A 50 "Cleanup Summary"
特定のジョブ実行を検査します:
# Get job name
JOB_NAME=$(kubectl get jobs -n longhorn-system \
-l app=longhorn-engineimage-cleanup \
--sort-by=.metadata.creationTimestamp \
-o jsonpath='{.items[-1].metadata.name}')
# View job details
kubectl describe job $JOB_NAME -n longhorn-system
# View job logs
kubectl logs job/$JOB_NAME -n longhorn-system60.4.5.4 検証 #
クリーンアップ実行後、現在のエンジンイメージのみが残っていることを確認します:
すべてのエンジンイメージを一覧表示します:
kubectl get engineimages.longhorn.io -n longhorn-system参照カウントを確認します:
kubectl get engineimages.longhorn.io -n longhorn-system \ -o custom-columns=NAME:.metadata.name,VERSION:.status.version,REFCOUNT:.status.refCount
アクティブな参照を持つ最新バージョンのみが表示される予期される出力です:
NAME VERSION REFCOUNT
ei-acba2c08 v1.12.1 3