Skip to content

필사 모드: 閉域網 Kubernetes の Day 2 運用 — 証明書の期限切れで死ぬクラスターを防ぐ方法

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

はじめに — 閉域網クラスターはたいてい証明書で死にます

閉域網クラスターの障害対応を何度かやっているとパターンが見えてきます。派手な障害はむしろ稀です。圧倒的に多いのはこういう形です。

去年構築してうまく回っていたクラスターで、ある日 kubectl が通らなくなります。エラーは証明書関連のメッセージ 1 行です。検索しようとしてもインターネットがありません。社内 Wiki には構築手順しかなく、更新手順はありません。構築した人は退職しています。API サーバーが上がらないのでログもパッドログでは見られません。

このシナリオの原因はいつも同じです。証明書が 1 年満期で、その 1 年のあいだ誰もクラスターを再起動しなかったのです。インターネットが使える環境ならこの問題も結局は何度か検索すれば解けますが、閉域網では復旧まで数日かかります。

この記事はインストールの翌日からの話です。検証の基準は以下のとおりです。

項目確認した値確認時点出典
kubeadm クライアント証明書デフォルト 1 年2026-07-31kubeadm certs
kubeadm CA 証明書デフォルト 10 年2026-07-31kubeadm certs
k3s クライアント・サーバー証明書365 日、再起動時に期限まで 120 日以内なら自動更新2026-07-31k3s certificate
k3s 自動更新のしきい値変更2025 年 5 月のリリース以前は 90 日2026-07-31k3s certificate
k3s スナップショット既定保持数52026-07-31k3s etcd-snapshot

証明書 — 検知が先で、更新はそのあとです

更新コマンドを覚えることより重要なのは、期限が近づいているという事実を前もって知ることです。期限当日に更新コマンドを知っていてもたいして助けになりません。そのときにはすでにサービスが止まったあとです。

検知

kubeadm クラスターには専用コマンドがあります。

# コントロールプレーンノードで
sudo kubeadm certs check-expiration

出力には証明書ごとの期限日と残り期間、署名した CA、そして外部で管理されている証明書かどうかが表示されます。重要なのは、kubelet クライアント証明書がこの出力に出てこないという点です。公式ドキュメントは、kubelet クライアント証明書が自動ローテーションで管理され、kubelet ディレクトリの下に別途あると説明しています。別に見る必要があります。

# kubelet 証明書は別途確認します
sudo ls -la /var/lib/kubelet/pki/
sudo openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -enddate

k3s には専用のサブコマンドがあります。出力形式を指定できるのでスクリプトに入れやすいです。

sudo k3s certificate check --output table

そして k3s は期限が近づくと Kubernetes のイベントを残します。公式ドキュメント基準で理由(reason)は CertificateExpirationWarning で、該当ノードに紐づきます。このイベントを通知として拾っておけば検知が自動化されます。

# 期限警告イベントの照会
sudo k3s kubectl get events -A --field-selector reason=CertificateExpirationWarning

ディストリビューションに依存せず動く検知スクリプトを一つ置いて cron に仕掛けておくことをおすすめします。閉域網では外部の監視 SaaS がないので、このスクリプトが事実上唯一の早期警報です。

#!/usr/bin/env bash
# cert-watch.sh — 毎日実行。期限まで 60 日以内なら異常終了で知らせます。
set -uo pipefail
THRESHOLD_DAYS=60
NOW=$(date +%s)
WARN=0

check_pem() {
  local f="$1"
  [ -f "${f}" ] || return 0
  local end epoch left
  end=$(openssl x509 -in "${f}" -noout -enddate 2>/dev/null | cut -d= -f2) || return 0
  epoch=$(date -d "${end}" +%s 2>/dev/null) || return 0
  left=$(( (epoch - NOW) / 86400 ))
  if [ "${left}" -lt "${THRESHOLD_DAYS}" ]; then
    echo "WARN  残り ${left}${f}  (${end})"
    WARN=$((WARN+1))
  else
    echo "ok    残り ${left}${f}"
  fi
}

# kubeadm 系
for f in /etc/kubernetes/pki/*.crt /etc/kubernetes/pki/etcd/*.crt; do
  check_pem "${f}"
done
# kubelet
check_pem /var/lib/kubelet/pki/kubelet-client-current.pem
# k3s 系
for f in /var/lib/rancher/k3s/server/tls/*.crt; do
  check_pem "${f}"
done

echo "警告 ${WARN} 件"
exit "${WARN}"

kubeadm クラスターの更新

kubeadm には実務上とても重要な性質が一つあります。kubeadm upgrade を実行するとすべての証明書が自動的に更新されます。つまり定期的にアップグレードを行うクラスターは証明書の問題をほとんど経験しません。閉域網クラスターが証明書で死ぬ理由もここにあります。持ち込み審査のせいでアップグレードをしないので、更新の機会もないというわけです。

手動更新は次のとおりです。

# 全体更新
sudo kubeadm certs renew all

# 個別更新も可能です
sudo kubeadm certs renew apiserver
sudo kubeadm certs renew admin.conf

公式ドキュメントが明記している重要な注意事項があります。手動更新のあとはコントロールプレーンのコンポーネントを自分で再起動しなければなりません。kubeadm が勝手に再起動してくれるわけではありません。更新だけして再起動を忘れると、コマンドは成功したのにサービスは依然として古い証明書を握ったままという状態になります。

# スタティックポッドを再起動させる最も単純な方法 — マニフェストを一時的に移してから戻します
sudo mkdir -p /tmp/kube-manifests
sudo mv /etc/kubernetes/manifests/*.yaml /tmp/kube-manifests/
sleep 20
sudo mv /tmp/kube-manifests/*.yaml /etc/kubernetes/manifests/

# 新しい admin.conf を反映
sudo cp /etc/kubernetes/admin.conf "${HOME}/.kube/config"
sudo chown "$(id -u):$(id -g)" "${HOME}/.kube/config"

# 確認
sudo kubeadm certs check-expiration
kubectl get nodes

有効期間そのものを延ばしたいなら、クラスター設定で指定します。閉域網のように再起動の機会が少ない環境では検討する価値があります。

apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
certificateValidityPeriod: 8760h # デフォルト 365 日
caCertificateValidityPeriod: 87600h # デフォルト 3650 日

値は Go の duration 形式で、サポートされる最大の単位は時間です。ただし有効期間を延ばすことはローテーション周期を遅らせることなので、セキュリティポリシーと衝突しないか先に確認してください。そして外部 CA を使う場合は kubeadm が証明書を管理できません。この場合は kubeadm certs generate-csr で CSR を作り外部で署名を受ける必要があり、更新は全面的に手動です。

k3s の更新

k3s はサービス再起動時に、期限まで 120 日以内の証明書を自動的に更新します(2025 年 5 月のリリース以前は 90 日)。この動作は既存の鍵を再利用しながら有効期間だけを延ばす方式です。

# 自動更新を誘発する最も単純な方法 — 再起動
sudo systemctl restart k3s

# 明示的なローテーション
sudo systemctl stop k3s
sudo k3s certificate rotate
sudo systemctl start k3s

# 特定のサービスだけローテーション
sudo k3s certificate rotate --service api-server,etcd,kubelet

ローテーション可能なサービス名は公式ドキュメントに admin、api-server、controller-manager、scheduler、k3s-controller、k3s-server、cloud-controller、etcd、auth-proxy、kubelet、kube-proxy と列挙されています。

CA は別物です。k3s が最初の起動時に生成する自己署名 CA は 10 年有効で、自動更新されません。ローテーションには専用のコマンドがあります。

# 自己署名 CA のローテーション — クロス署名で信頼チェーンを維持します
# (スクリプトは持ち込み時に一緒に入れておく必要があります。閉域網では curl で取得できません。)
sudo bash /opt/staging/rotate-default-ca-certs.sh
sudo k3s certificate rotate-ca --path=/var/lib/rancher/k3s/server/rotate-ca
# 社内 CA に差し替える場合 — 別ディレクトリに用意してから適用
sudo k3s certificate rotate-ca --path=/opt/k3s/server
# 適用後、すべてのノードで k3s を再起動します。
# ルート CA そのものを変える場合は --force が必要で、参加済みノードのトークン再設定が先行しなければなりません。

rotate-default-ca-certs.sh は k3s リポジトリにあるスクリプトです。閉域網ではインターネットから取得できないので、最初の持ち込みリストに入れておく必要があります。必要になった時点でないと気づく代表的な項目です。

時間と名前 — NTP、内部 DNS、OS パッケージミラー

証明書の次に閉域網クラスターを静かに崩すのが時刻です。そして時刻問題の症状は、いつも時刻問題のようには見えません。

TLS 検証は証明書の有効開始・終了時刻を現在時刻と比較します。ノードの時計が数分ずれただけでも「証明書がまだ有効ではない」あるいは「すでに期限切れだ」というエラーが出ます。etcd はもっと敏感です。リースの期限切れとリーダー選出が時間に基づくので、ノード間の偏差が大きくなるとリーダーが繰り返し変わって書き込み遅延が跳ね、最悪の場合は定足数の判断が揺らぎます。

閉域網でこの問題が特によく起きる理由は明確です。デフォルトの NTP 設定はパブリック NTP プールを指しており、それは閉域網からは到達できません。インストール当時はハードウェアクロックが合っているので誰も気づかず、数か月かけて徐々に開いていきます。

# 現在の同期状態を確認 — ここが閉域網点検の 1 番目の項目です
timedatectl status
chronyc sources -v 2>/dev/null || ntpq -p 2>/dev/null
chronyc tracking 2>/dev/null
# 社内 NTP サーバーに固定
sudo tee /etc/chrony.d/internal.conf > /dev/null <<'EOF'
server 10.10.10.11 iburst prefer
server 10.10.10.12 iburst
makestep 1.0 3
EOF

# パブリックプールを指すデフォルト設定を削除する必要があります(ディストリビューションによってパスが違います)
sudo grep -rn "pool\|ntp.org" /etc/chrony.conf /etc/chrony.d/ 2>/dev/null

sudo systemctl restart chronyd
sleep 5
chronyc sources -v
# クラスター全ノードの時刻偏差を一度に見る
for n in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
  printf "%-20s " "${n}"
  ssh "${n}" "chronyc tracking 2>/dev/null | awk '/System time/{print \$4, \$5, \$6}'" || echo "unreachable"
done

内部 DNS も同じ性格の問題です。CoreDNS はクラスタードメイン外の問い合わせをノードのリゾルバに転送しますが、そのリゾルバが到達できないパブリック DNS を指していると問い合わせのたびにタイムアウトを待つことになります。症状は「遅い」として現れ、原因が DNS だと突き止めるのに時間がかかります。

# CoreDNS が何を見ているか確認
kubectl -n kube-system get configmap coredns -o yaml

# ノードのリゾルバ
cat /etc/resolv.conf

# 社内 DNS の応答確認
dig @10.10.10.53 registry.internal.example +short

OS パッケージミラーは証明書や NTP ほど急ぎではありませんが、ないとある日突然なんの作業もできなくなります。カーネルのセキュリティパッチ、コンテナランタイムの依存パッケージ、デバッグツール(tcpdump、strace、zstd)のインストールが全部塞がれます。閉域網の構築時に社内パッケージミラーも一緒に立て、クラスターノードがそれだけを見るように設定してください。ミラー自体の同期が定期的な持ち込み対象に入っていなければなりません。

etcd バックアップとリハーサル済みの復旧

バックアップはたいていのチームがやっています。復旧はたいていのチームがやったことがありません。閉域網ではこの差が致命的です。復旧手順を初めて実行する時点が障害の真っ只中なら、ドキュメントを検索することも外部に聞くこともできません。

k3s にはスナップショット機能が内蔵されています。公式ドキュメント基準でデフォルトは 12 時間間隔のスケジュール、保持 5 個、保存場所はデータディレクトリ配下の db/snapshots です。

# /etc/rancher/k3s/config.yaml — 閉域網の推奨設定
etcd-snapshot-schedule-cron: '0 */6 * * *'
etcd-snapshot-retention: 12
etcd-snapshot-compress: true
etcd-snapshot-dir: /var/backups/k3s-snapshots
# オンデマンドスナップショット — アップグレードや危険な変更の直前には必ず
sudo k3s etcd-snapshot save

# 一覧と整理
sudo k3s etcd-snapshot ls
sudo k3s etcd-snapshot prune --snapshot-retention 12
sudo k3s etcd-snapshot delete on-demand-node1-1753900000

閉域網で必ず一緒にバックアップしなければならないファイルがもう一つあります。公式ドキュメントが明示的に警告している項目です。サーバートークンです。トークンが機密データの暗号化に使われるので、復旧時に同じトークンの値がないとスナップショットを使えません。

# スナップショットとトークンを一組で保管します
sudo install -m 0600 /var/lib/rancher/k3s/server/token \
  /var/backups/k3s-snapshots/token-$(date +%Y%m%d)

# バックアップ一式をクラスター外のストレージへ(閉域網内部の別 NAS など)
sudo rsync -a --delete /var/backups/k3s-snapshots/ backup-nas:/k3s/prod/

復旧手順です。この手順は四半期ごとにステージングで実際に実行してみてください。読んだことと、やったことは違います。

# 単一サーバー
sudo systemctl stop k3s
sudo k3s server \
  --cluster-reset \
  --cluster-reset-restore-path=/var/backups/k3s-snapshots/etcd-snapshot-node1-1753900000
# 上のコマンドが完了メッセージを出したら Ctrl-C で抜けたあと
sudo systemctl start k3s
# 複数サーバー — 順序を守る必要があります
# 1) すべてのサーバーで k3s を停止
sudo systemctl stop k3s

# 2) 最初のサーバーでのみ cluster-reset 復旧を実施(上のコマンド)

# 3) 最初のサーバーを起動
sudo systemctl start k3s

# 4) 残りのサーバーでデータディレクトリを削除 — この段階を飛ばすと合流できません
sudo rm -rf /var/lib/rancher/k3s/server/db/

# 5) 残りのサーバーを起動
sudo systemctl start k3s

kubeadm クラスターなら etcdctl を直接使います。

# スナップショット
sudo ETCDCTL_API=3 etcdctl snapshot save /var/backups/etcd-$(date +%Y%m%d%H%M).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# スナップショットの完全性確認 — 保存するだけで確認しないチームが本当に多いです
sudo ETCDCTL_API=3 etcdctl snapshot status \
  /var/backups/etcd-*.db --write-out=table

復旧リハーサルを四半期の予定に入れ、リハーサルの結果をドキュメントに記録してください。記録すべきなのは成否ではなくかかった時間と詰まった地点です。それが実際の障害時の復旧目標時間になります。

上流なしのアップグレード — ステージングバンドルとスキュー規則

閉域網のアップグレードは「新しいアーティファクトを持ち込んで差し替える」ことです。手順自体は難しくありませんが、難しいのは計画です。

まず最初に確認すべきはバージョンスキューポリシーです。コントロールプレーンと kubelet、kubectl のあいだで許容されるバージョン差はリリースごとに更新されるので、持ち込み計画を立てる前に公式のバージョンスキューポリシー文書で対象バージョンの許容範囲を確認し、その値を持ち込み計画書に明示的に書いておいてください。この値を記憶に頼って書くと必ず間違えます。

そしてコントロールプレーンはマイナーバージョンを飛ばして上げることができません。持ち込み審査が四半期に一度なのにマイナーバージョンがそのあいだに 2 回上がったなら、一度の持ち込みに 2 段階アップグレードのためのアーティファクトをすべて詰め込まなければなりません。この計算を先にしておかないと、持ち込み媒体を受け取っておきながら半分しか上げられないという状況になります。

# 持ち込み計画の算出 — 現在バージョンと目標バージョンのあいだの段階を確認
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.kubeletVersion}{"\n"}{end}'
kubectl version -o json | jq -r '.serverVersion.gitVersion'

ステージングバンドルという概念を導入してください。持ち込み媒体一つに一度のアップグレード回に必要なものすべてを入れ、その中でバージョンがずれないようマニフェストで固定する方式です。

/opt/staging/upgrade-2026Q3/
├── VERSION                       # 目標バージョン 1 行
├── MANIFEST.sha256
├── step1-v1.35.7/
│   ├── k3s
│   └── k3s-airgap-images-amd64.tar.zst
├── step2-v1.36.2/
│   ├── k3s
│   └── k3s-airgap-images-amd64.tar.zst
├── addons/                       # この回に一緒に上げる付加コンポーネントのイメージ
└── ROLLBACK.md                   # 各段階のロールバック判断基準とコマンド

アップグレード実行前のチェックリストは、短くないと実際には守られません。

#!/usr/bin/env bash
# pre-upgrade.sh
set -uo pipefail
ERR=0

echo "[1] スナップショット"
sudo k3s etcd-snapshot save || ERR=$((ERR+1))

echo "[2] トークンのバックアップ"
sudo cp /var/lib/rancher/k3s/server/token "/var/backups/token-$(date +%Y%m%d%H%M)" || ERR=$((ERR+1))

echo "[3] バンドルの完全性"
(cd /opt/staging/upgrade-2026Q3 && sha256sum -c MANIFEST.sha256) || ERR=$((ERR+1))

echo "[4] ノード状態"
kubectl get nodes --no-headers | grep -v " Ready " && ERR=$((ERR+1))

echo "[5] PodDisruptionBudget"
kubectl get pdb -A

echo "[6] 証明書の残り期間"
sudo k3s certificate check --output table

echo "エラー ${ERR} 件"
exit "${ERR}"

アップグレードそのものはアーカイブの差し替えとバイナリの差し替えです。このとき古いアーカイブは必ず消さなければなりません。残しておくとイメージディレクトリに 2 つのバージョンが共存し、予測しづらい状態になります。

sudo systemctl stop k3s
sudo rm -f /var/lib/rancher/k3s/agent/images/k3s-airgap-images-amd64.tar.zst
sudo rm -f /var/lib/rancher/k3s/agent/images/.cache.json
sudo cp /opt/staging/upgrade-2026Q3/step2-v1.36.2/k3s-airgap-images-amd64.tar.zst \
  /var/lib/rancher/k3s/agent/images/
sudo install -m 0755 /opt/staging/upgrade-2026Q3/step2-v1.36.2/k3s /usr/local/bin/k3s
sudo systemctl start k3s
sudo k3s kubectl get nodes -o wide

SaaS なしの可観測性と持ち出し可能なサポートバンドル

閉域網では可観測性スタックも全部内側になければなりません。リモートライトの宛先も、ダッシュボードのマーケットプレイスも、通知の宛先もありません。設計するときに決めなければならないことは 3 つです。

一つ目、ダッシュボードとルールは持ち込み物です。Grafana のダッシュボードを UI からインポートで取得する経路は閉域網では動きません。収集段階で JSON をダウンロードして持ち込み、プロビジョニングでデプロイしてください。

# DMZ 収集段階 — ダッシュボードの JSON をファイルとして確保
mkdir -p dashboards
curl -fL -o dashboards/node-exporter.json \
  "https://grafana.com/api/dashboards/1860/revisions/latest/download"
sha256sum dashboards/*.json >> MANIFEST.sha256
# 閉域網の内部 — プロビジョニングでデプロイ(ConfigMap マウント方式)
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-dashboard-provider
  namespace: monitoring
data:
  provider.yaml: |
    apiVersion: 1
    providers:
      - name: airgap
        orgId: 1
        folder: 'Airgap'
        type: file
        disableDeletion: true
        options:
          path: /var/lib/grafana/dashboards

二つ目、通知の宛先は内部システムに決めてください。外部メッセンジャーやページング SaaS へは出られません。実務的に残る選択肢は社内 SMTP、社内メッセンジャーの Webhook エンドポイント、社内チケットシステムの API 程度です。3 つともないなら最後の手段はノードでローカルログを残して人が定期的に確認することですが、それでは通知ではなく点検です。通知経路のない可観測性スタックはダッシュボードであってモニタリングではありません。

# Alertmanager — 社内 SMTP だけに出る構成
route:
  receiver: internal-mail
  group_wait: 30s
  repeat_interval: 4h
receivers:
  - name: internal-mail
    email_configs:
      - to: 'k8s-ops@internal.example'
        from: 'alertmanager@internal.example'
        smarthost: 'smtp.internal.example:25'
        require_tls: false

三つ目、サポートバンドルを作れるようになっていなければなりません。閉域網でベンダーや外部の専門家の助けを借りる唯一の方法は、診断に必要な情報をアーカイブにまとめ、持ち出し審査を経て外へ持って出ることです。ところが何の準備もなく作ると 2 つの問題が起きます。必要な情報が抜けているか、持ち出してはいけない情報が入っていて審査で差し戻されるかです。

#!/usr/bin/env bash
# support-bundle.sh — 持ち出し審査を考慮した診断バンドル
set -uo pipefail
TS="$(date +%Y%m%d-%H%M)"
DIR="/var/tmp/support-${TS}"
mkdir -p "${DIR}"/{cluster,nodes,logs}
K="sudo k3s kubectl"

echo "== クラスターリソース(シークレット除く)"
${K} get nodes -o wide            > "${DIR}/cluster/nodes.txt" 2>&1
${K} get pods -A -o wide          > "${DIR}/cluster/pods.txt" 2>&1
${K} get events -A --sort-by=.lastTimestamp > "${DIR}/cluster/events.txt" 2>&1
${K} describe nodes               > "${DIR}/cluster/nodes-describe.txt" 2>&1
${K} api-resources                > "${DIR}/cluster/api-resources.txt" 2>&1
${K} version -o json              > "${DIR}/cluster/version.json" 2>&1
# シークレットは名前だけ — 値は絶対に入れません
${K} get secrets -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,TYPE:.type \
                                  > "${DIR}/cluster/secrets-list.txt" 2>&1

echo "== 失敗中のポッドのログだけ"
${K} get pods -A --field-selector=status.phase!=Running --no-headers 2>/dev/null \
| while read -r ns name _; do
    ${K} -n "${ns}" logs "${name}" --all-containers --tail=500 \
      > "${DIR}/logs/${ns}_${name}.log" 2>&1 || true
    ${K} -n "${ns}" describe pod "${name}" \
      > "${DIR}/logs/${ns}_${name}.describe" 2>&1 || true
  done

echo "== ノード情報"
uname -a                          > "${DIR}/nodes/uname.txt" 2>&1
free -h                           > "${DIR}/nodes/mem.txt" 2>&1
df -h                             > "${DIR}/nodes/disk.txt" 2>&1
timedatectl status                > "${DIR}/nodes/time.txt" 2>&1
chronyc tracking                  > "${DIR}/nodes/chrony.txt" 2>&1
sudo journalctl -u k3s -n 5000 --no-pager   > "${DIR}/logs/k3s.log" 2>&1
sudo tail -n 5000 /var/lib/rancher/k3s/agent/containerd/containerd.log \
                                  > "${DIR}/logs/containerd.log" 2>&1
sudo k3s certificate check --output table   > "${DIR}/cluster/certs.txt" 2>&1

echo "== 持ち出し前の点検 — トークンと鍵のパターン検出"
grep -rIlE 'BEGIN (RSA |EC )?PRIVATE KEY|K10[0-9a-f]{16,}|password:\s*\S+' "${DIR}" \
  > "${DIR}/REDACTION-REVIEW.txt" 2>/dev/null || true
echo "次のファイルを持ち出し前に必ず確認してください:"
cat "${DIR}/REDACTION-REVIEW.txt"

tar -czf "/var/tmp/support-${TS}.tar.gz" -C /var/tmp "support-${TS}"
sha256sum "/var/tmp/support-${TS}.tar.gz"
echo "生成完了: /var/tmp/support-${TS}.tar.gz"

最後の段階の機微情報検出は必ず入れてください。サポートバンドルにサーバートークンや秘密鍵が混ざって出ていくと、持ち出し審査で差し戻されて終わるならまだ幸運で、最悪の場合はセキュリティ事故として扱われます。検出結果が空であることを人が目で確認したうえで持ち出し申請を上げてください。

日次・週次・四半期の点検表

閉域網運用で自動化できないものは、結局のところ人が定期的に見るしかありません。周期を分ける基準は「これが放置されるとどれくらいでサービスが止まるか」です。

周期点検項目確認コマンドまたは方法放置したとき
日次ノードの Ready 状態kubectl get nodesワークロードが静かに片方へ寄る
日次異常なポッドkubectl get pods -A の状態フィルタリトライループがノードのリソースを侵食
日次ノードのディスク使用率df -h とイメージ GC のしきい値イメージ pull 不可 — 閉域網では復旧が遅い
日次時刻同期の状態chronyc trackingTLS 検証の失敗と etcd の不安定化
週次証明書の残り期間cert-watch.sh または certificate check期限当日にクラスター停止
週次etcd スナップショットの生成有無とサイズetcd-snapshot ls とファイルサイズの推移バックアップがあると信じていたのになかった
週次脆弱性 DB の鮮度持ち込み台帳の最終 Trivy DB 持ち込み日スキャン結果が無意味になる
週次イメージ持ち込み台帳と実レジストリの突き合わせ持ち込みログとレジストリのタグ一覧を比較出所不明のイメージがクラスターに存在
月次OS セキュリティパッチの適用社内パッケージミラー基準の更新リストカーネル脆弱性が蓄積
月次base イメージの再持ち込み持ち込みパイプラインの実行派生イメージ全体が同じ脆弱性を共有
四半期etcd 復旧リハーサルステージングで cluster-reset 復旧を実行実際の障害時に初めてやることになる
四半期バージョンアップグレードの持ち込みと適用ステージングバンドルを持ち込んで段階別に適用スキューが開いて一度に上げられない
四半期サポートバンドル生成の訓練support-bundle.sh の実行と持ち出し審査手順の確認障害時にバンドル形式で差し戻されて時間を損失
半期CA の期限確認とローテーション計画の点検CA 証明書の enddate 確認10 年後に一度に全クラスター停止
半期持ち込みリストと実使用リストの突き合わせリストファイルとクラスターのイメージ一覧を diff使わないものを運び、使うものを落とす

この表で最もよく飛ばされる項目が四半期の復旧リハーサルです。そしてそれが実際の障害時に最も高くつきます。リハーサルはクラスターを一つ新しく作ってやってもよく、スナップショットを別ノードで復元してみるだけでもかなりの部分が確認できます。

おわりに — 閉域網の運用はカレンダーの上で行われます

閉域網クラスターの Day 2 運用に特別な技術はあまりありません。証明書の更新コマンドも、スナップショット復旧の手順も、アップグレードの順序も、全部公式ドキュメントに書かれています。それでもクラスターが死ぬ理由は、そのコマンドが必要な時点で実行されないからです。

インターネットが使える環境ではこの問題はあまり表に出ません。問題が起きても検索して、ツールを取ってきて、数日あれば復旧します。閉域網では同じ問題が数週間になります。だから閉域網運用の実力は、事故対応の能力ではなくカレンダーを守る能力として現れます。

点検表をカレンダーに入れ、担当者を指定し、リハーサルの結果を記録してください。その 3 つが、証明書の期限切れで死ぬクラスターと 5 年間静かに回り続けるクラスターを分けます。

参考資料

현재 단락 (1/281)

閉域網クラスターの障害対応を何度かやっているとパターンが見えてきます。派手な障害はむしろ稀です。圧倒的に多いのはこういう形です。

작성 글자: 0원문 글자: 16,292작성 단락: 0/281