mcpField
D
debug-node

debug-node

github.com/mysticaltechクラウド・DevOps3.9k73 回インストール7 週間前に更新
リポジトリ

debug-node について

SSHで接続できない、またはプロビジョニングが停止したHetzner Cloudノードを調査するスキルです。レスキューモードでディスクを確認し、Leap Microのトランザクション型ファイルシステムも考慮して診断・修復します。

主な機能

  1. 01レスキューモードでのファイルシステム検査
  2. 02cloud-initとSSHの診断
  3. 03KubernetesとSELinuxのログ確認
  4. 04アクティブスナップショットの修復案内
  5. 05Hetzner CLIによる復旧手順

活用シーン

  1. 01到達不能なHetznerノードの調査
  2. 02cloud-initのプロビジョニング失敗の診断
  3. 03k3sまたはRKE2の起動障害の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mysticaltech/terraform-hcloud-kube-hetzner --skill debug-node

インストール後、Claude Code で/debug-nodeを入力して読み込まれたか確認します。

詳細

Rescue Console を介した Hetzner ノードのデバッグ

概要

Hetzner Cloud サーバーに接続できない場合(SSH が応答しない、プロビジョニングが停止している、cloud-init が失敗しているなど)、このスキルでは Hetzner のレスキューモードを使用してノードのファイルシステムをマウントし、外部からすべてを調査します。SSH が機能している必要はありません。

使用方法

/debug-node

実行時に、次の情報を尋ねます。

  1. サーバー名または IP(hcloud server list で確認できます)
  2. 発生している症状(SSH のタイムアウト、プロビジョニングの停止など)

前提条件

  • hcloud CLI がインストールされ、有効なトークンで設定されていること
  • サーバーが Hetzner Cloud に存在すること
hcloud server list

Leap Micro のファイルシステムモデル

Leap Micro は、btrfs 上でトランザクショナルアップデートシステムを使用します。以下のすべてを理解するための基本モデルです。

レイヤー書き込み可能?再起動後も保持?Hetzner のスナップショット後も保持?
/usr(スナップショット)いいえ(読み取り専用)はいはい
transactional-update shell 経由の /etcはい(新しいスナップショット)はい(再起動後)はい
実行中のシステムで直接編集した /etcはい(現在のスナップショットのオーバーレイ)はい。ただし、保留中のスナップショットが同じパスを変更していない場合現在のスナップショットの内容のみ
/var(独立したサブボリューム)はいはいはい

ルール: 通常の /etc の編集内容は、通常の再起動後も保持されます。ただし、transactional-update が保留中のスナップショットを作成した後は、そのスナップショットが同じパスを変更している場合、現在のスナップショットの /etc に対する同時編集内容はマージされません。イメージのビルドやパッケージに関連する /etc の変更は、transactional-update --continue shell の中で行うか、編集前に保留中のスナップショットで再起動してください。

Packer のビルドフェーズ:

  1. レスキューモード: qcow2 をディスクに書き込み、再起動します
  2. install_packages: transactional-update 内で実行 — 変更は保持されます
  3. clean_up: 揮発性オーバーレイ — /etc の変更は Hetzner のスナップショットで失われます

ステップ 1:サーバーを特定する

hcloud server list -o columns=id,name,status,ipv4 | grep <pattern>

ステップ 2:レスキューモードを有効にして再起動する

hcloud server enable-rescue <SERVER_ID> --type linux64
hcloud server reboot <SERVER_ID>
sleep 30

出力に表示されるレスキュー用 root パスワードを保存してください(通常は鍵認証で接続できますが、念のため保存しておきます)。

ステップ 3:レスキュー環境に SSH 接続する

ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@<SERVER_IP>

ステップ 4:ファイルシステムをマウントする

Leap Micro / MicroOS(btrfs)
# Mount btrfs top-level
mount -o subvolid=5 /dev/sda3 /mnt

# List snapshots — highest number is active
ls /mnt/@/.snapshots/

レイアウト:

/mnt/@/.snapshots/N/snapshot/    latest active snapshot — /etc lives here
/mnt/@/root/                     /root home (/root/.ssh/authorized_keys)
/mnt/@/var/                      /var (logs, cloud-init state, journal)

ポイント: /etc はスナップショット内にあります。/var と /root は、それぞれ @/var と @/root にある独立したサブボリュームです。

サポート対象外/カスタムの ext4 イメージ
mount /dev/sda1 /mnt

ステップ 5:診断チェックリスト

ここで一度設定し、以降はそのまま使用します。

SNAP=/mnt/@/.snapshots/N/snapshot   # replace N with highest snapshot number
5a. Cloud-Init の状態

ここから確認してください。プロビジョニングの失敗の多くは cloud-init に起因します。

cat /mnt/@/var/lib/cloud/data/result.json
cat /mnt/@/var/lib/cloud/data/status.json
cat /mnt/@/var/lib/cloud/instance/datasource

# What Terraform actually sent
cat /mnt/@/var/lib/cloud/instance/user-data.txt
zcat /mnt/@/var/lib/cloud/instance/user-data.txt.i 2>/dev/null

# Logs
tail -100 /mnt/@/var/log/cloud-init.log
tail -100 /mnt/@/var/log/cloud-init-output.log

期待される状態: DataSourceHetzner で、エラーがないこと。 確認する内容: Skipping modules — cloud-init がこの instance-id に対してすでに実行されたことを意味します。

Hetzner と Leap Micro における cloud-init の情報:

  • データソース:DataSourceHetzner(メタデータ API)
  • Terraform の cloudinit_config → gzip+base64 の multipart MIME → user_data
  • disable_root: false は cloud-init による root の無効化を防ぎますが、ロックされたアカウントのロックは解除しません
  • ssh_authorized_keys は鍵を /root/.ssh/authorized_keys に書き込みます
5b. SSH 鍵
cat /mnt/@/root/.ssh/authorized_keys

ローカルの公開鍵と比較してください。見つからない場合は、cloud-init による鍵の注入に失敗しています。5a のログを確認してください。

5c. SSHD の設定

設定の読み込み順(最初に一致した設定が適用されます):

1. /etc/ssh/sshd_config.d/40-kube-hetzner-authorized-keys-command.conf
2. /etc/ssh/sshd_config.d/50-cloud-init.conf
3. /etc/ssh/sshd_config.d/kube-hetzner.conf           (MaxAuthTries 2)
4. /usr/etc/ssh/sshd_config.d/40-suse-crypto-policies.conf
5. /usr/etc/ssh/sshd_config                            (UsePAM yes)
ls $SNAP/etc/ssh/sshd_config.d/
cat $SNAP/etc/ssh/sshd_config.d/*.conf
cat $SNAP/usr/etc/ssh/sshd_config
ls -la $SNAP/etc/ssh/ssh_host_*
5d. アカウントの状態
grep '^root:' $SNAP/etc/shadow
パターン意味SSH 公開鍵は機能する?
root:*:...ロック解除済み、パスワードなしはい
root:!*:... または root:!:...ロック中いいえ(UsePAM yes では PAM に拒否されます)

これは packer で修正されています(transactional-update 内で usermod -p '*' root を実行)。cloud-init の bootcmd はセーフティネットとして機能します。新しいノードでアカウントがロックされている場合は、packer のスナップショットを再ビルドする必要があります。

5e. Journal ログ
journalctl -D /mnt/@/var/log/journal/ -u sshd --no-pager | tail -50
journalctl -D /mnt/@/var/log/journal/ -u k3s --no-pager | tail -30
journalctl -D /mnt/@/var/log/journal/ -u rke2-server --no-pager | tail -30
journalctl -D /mnt/@/var/log/journal/ -u rke2-agent --no-pager | tail -30
5f. ネットワーク
ls $SNAP/etc/NetworkManager/system-connections/
cat $SNAP/etc/NetworkManager/system-connections/*.nmconnection 2>/dev/null
5g. Kubernetes
cat $SNAP/etc/rancher/k3s/config.yaml 2>/dev/null
cat $SNAP/etc/rancher/rke2/config.yaml 2>/dev/null
cat /mnt/@/var/lib/rancher/k3s/server/token 2>/dev/null
5h. SELinux
cat $SNAP/etc/selinux/config
chroot $SNAP rpm -qa | grep -iE 'selinux|k3s|rke2'
grep -i 'avc:.*denied' /mnt/@/var/log/audit/audit.log | tail -50
journalctl -D /mnt/@/var/log/journal/ --no-pager | grep -i 'avc:.*denied' | tail -50

ワークロードが拒否された場合は、docs/selinux.md に従ってください。上流のポリシー変更を提案する前に、AVC の行、ワークロード名/バージョン、k3s/RKE2 のディストリビューション、OS イメージ、udica の結果を収集してください。最初の対応として SELinux をグローバルに無効化しないでください。ポリシーの下で実行できないワークロードまたはノードプールに限り、最後の手段としてプールごとの selinux = false を使用してください。

ステップ 6:修正を適用する

@/ のベースではなく、アクティブなスナップショット($SNAP)内のファイルを編集してください。

# Unlock root account (if locked)
sed -i 's/^root:!*/root:*/' $SNAP/etc/shadow

# Fix authorized_keys
mkdir -p /mnt/@/root/.ssh
echo "ssh-ed25519 AAAA..." > /mnt/@/root/.ssh/authorized_keys
chmod 700 /mnt/@/root/.ssh && chmod 600 /mnt/@/root/.ssh/authorized_keys

# Regenerate host keys
mount --bind /proc $SNAP/proc && mount --bind /sys $SNAP/sys && mount --bind /dev $SNAP/dev
chroot $SNAP ssh-keygen -A
umount $SNAP/proc $SNAP/sys $SNAP/dev

注: レスキューモードでの編集は、その場で適用される修正です。長期的な適切な修正は、packer テンプレートまたは cloud-init に加えてください。

ステップ 7:通常モードで再起動する

umount /mnt 2>/dev/null
exit
hcloud server disable-rescue <SERVER_ID>
hcloud server reboot <SERVER_ID>
sleep 60
ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@<SERVER_IP> 'echo ok'

よくある診断

症状考えられる原因確認方法修正方法
SSH がタイムアウトするファイアウォールまたはネットワークHetzner のファイアウォールルールポート 22 を開く
SSH「Connection refused」sshd が実行されていないジャーナルログsshd の設定構文を修正する
SSH キーが拒否されるキーが注入されていないauthorized_keys が空cloud-init のログを確認する
SSH「Too many auth failures」エージェントが多すぎるキーを提示しているMaxAuthTries 2-o IdentitiesOnly=yes を使用する
SSH「unable to authenticate」root がロックされている、またはキーが一致しない/etc/shadow、authorized_keyspacker スナップショットを再ビルドする
プロビジョナーが「Still creating」で停止するSSH で接続できない上記すべて根本的な SSH の問題を修正する
cloud-init がモジュールをスキップするインスタンス ID に対してすでに実行済みcloud-init.log/var/lib/cloud/instance をクリーンアップする
k3s/rke2 が起動しない設定または SELinuxジャーナルと audit.log設定またはポリシーを修正する
ワークロードが SELinux に拒否されるワークロードポリシーが不足しているaudit/journal 内の AVC の行docs/selinux.md に従い、プールを無効化する前に udica を試す
ネットワーク/サブネットの削除が停止するAutoscaler が作成したサーバーが Terraform の状態外にあるクラスター名または残存する kh-ci-* について hcloud server list を実行するコントロールプレーンが停止してから削除するか、先に autoscaler の min_nodes = 0 に設定する
トランザクショナル再起動後に /etc の変更が消える現在のスナップショットと保留中のスナップショットで同じパスが変更されたtransactional-update status と /var/lib/overlay を調べるアクティブなスナップショットで再適用するか、イメージビルド時の変更をトランザクション内に移す

SSH を手動でデバッグする

# Verbose with specific key (avoids agent key spray hitting MaxAuthTries 2)
ssh -vvv -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@<SERVER_IP>

# In -vvv output:
# "Offering public key: ..."              → key was offered
# "Server accepts key: ..."               → success path
# "Authentications that can continue: ..."  → key was REJECTED
# "Too many authentication failures"       → agent sent too many keys

プロのヒント

  1. subvolid=5 を指定してマウントする — 実際の btrfs ルートにアクセスし、@/.snapshots/N/snapshot/ に移動できます
  2. 最も番号の大きいスナップショットがアクティブ — /etc があるのはそこです
  3. /var は別です — ログと cloud-init はスナップショット内ではなく、/mnt/@/var/ にあります
  4. システムが稼働していなくてもジャーナルを参照できる — journalctl -D /path/to/journal/
  5. -o IdentitiesOnly=yes を使用する — kube-hetzner は MaxAuthTries 2 を設定します
  6. 揮発性オーバーレイの落とし穴 — レスキューモードで表示される内容が稼働中のシステムと異なる場合、コミットされない揮発性オーバーレイ上で実行されていました
  7. packer を修正したらスナップショットを再ビルドする — ビルドログの transactional-update 出力内に変更が表示されていることを確認します
  8. レスキューモードは非破壊的 — ディスク上のファイルを読み書きするだけです
  9. ラッパーを使って破棄する — クラスター全体を削除するには、Terraform のルートから scripts/destroy.sh を実行します。既知の ingress-LB 切り離し競合のみを再試行し、読み取り専用の孤立リソースレポートを表示します。scripts/cleanup.sh は強制的な最終手段としてのみ使用してください。これはトークンの HCloud プロジェクト全体をクラスター専用として扱うため、まずドライランを確認してください。

コメント