[{"data":1,"prerenderedAt":414},["ShallowReactive",2],{"blog-ja-k3s-spegel-embedded-registry-mirror-airgap-node-join":3,"blog-related-ja-k3s-spegel-embedded-registry-mirror-airgap-node-join":363,"blog-ja-k3s-spegel-embedded-registry-mirror-airgap-node-join-alt":351},{"id":4,"title":5,"author":6,"body":7,"date":345,"description":346,"extension":347,"image":348,"locale":349,"meta":350,"navigation":351,"path":352,"seo":353,"stem":354,"tags":355,"__hash__":362},"blog\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join.md","K3sの組み込みレジストリミラーが効いているはずなのに、新しいエッジノードだけimage pullに失敗した","Kubo Team",{"type":8,"value":9,"toc":334},"minimark",[10,15,33,40,46,57,61,72,78,95,108,117,121,138,144,150,157,161,181,187,207,211,238,244,253,257,266,272,275,303,312,315,321],[11,12,14],"h2",{"id":13},"_1-このノードだけimage-pullが失敗した日","1. 「このノードだけ」image pullが失敗した日",[16,17,18,19,23,24,28,29,32],"p",{},"K3sには、クラスタ全体で自動的にイメージを共有し合う",[20,21,22],"strong",{},"組み込みレジストリミラー","という機能がある。エッジ拠点が増えて、新しい工場にもう1台K3sエージェントノードを追加することになったとき、この機能のおかげで設定は不要だと思い込んでいた。手順は既存ノードと同じ、",[25,26,27],"code",{},"k3s agent","コマンドでクラスタに参加させるだけ。ところが数分後、そのノードにスケジュールされたPodだけが",[25,30,31],{},"ImagePullBackOff","で止まった。",[16,34,35],{},[36,37],"img",{"alt":38,"src":39},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection01.webp",[16,41,42,43,45],{},"既存ノードでは同じイメージが問題なくpullできている。おかしいのは新規ノードだけ。最初に疑ったのはネットワーク疎通だったが、pingもDNSも正常。原因はもっと地味な場所、K3sの",[20,44,22],{},"の仕様そのものにあった。",[16,47,48,49,56],{},"K3sはv1.27以降、",[50,51,55],"a",{"href":52,"rel":53},"https:\u002F\u002Fgithub.com\u002Fspegel-org\u002Fspegel",[54],"nofollow","Spegel","というステートレスなP2P型OCIレジストリミラーを標準で同梱している。「組み込み」だから新しいノードを足せば自動的に恩恵を受けられる、と思い込んでいたのが最初のつまずきだった。",[11,58,60],{"id":59},"_2-k3sの組み込みレジストリミラーspegelはそもそも何をしているか","2. K3sの組み込みレジストリミラー（Spegel）はそもそも何をしているか",[16,62,63,64,67,68,71],{},"Spegelは、クラスタ内のどのノードが既に持っているイメージでも、他のノードからP2Pで直接取得できるようにする仕組みだ。有効化はserverノードで",[25,65,66],{},"--embedded-registry","フラグを指定するか、設定ファイルに",[25,69,70],{},"embedded-registry: true","を書くだけで済む。",[16,73,74],{},[36,75],{"alt":76,"src":77},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection02.webp",[16,79,80,81,86,87,90,91,94],{},"ただし、この手軽さの裏にはネットワーク要件がある。",[50,82,85],{"href":83,"rel":84},"https:\u002F\u002Fdocs.k3s.io\u002Finstallation\u002Fregistry-mirror",[54],"K3s公式ドキュメント","によれば、全ノードは内部IPアドレス経由でTCPポート",[20,88,89],{},"5001","（P2P配布）と",[20,92,93],{},"6443","（ノードごとのローカルOCIレジストリ）に相互到達できなければならない。到達できない場合は外部レジストリへフォールバックするが、レイテンシが増える。",[16,96,97,98,103,104,107],{},"レジストリAPIへのアクセスにはクラスタのCAが署名したクライアント証明書が必須で、組み込みレジストリ自体は読み取り専用だ。",[50,99,102],{"href":100,"rel":101},"https:\u002F\u002Fdocumentation.suse.com\u002Fcloudnative\u002Fk3s\u002Flatest\u002Fen\u002Finstallation\u002Fregistry-mirror.html",[54],"SUSEの公式ドキュメント","も、この分散レジストリが「クラスタメンバー間で対等な信頼」を前提にしていると明記している。つまり、ミラー対象として",[25,105,106],{},"registries.yaml","に登録したレジストリの認証済みイメージは、検証なしで全ノードからアクセスできる設計だ。",[16,109,110,111,116],{},"K3sの運用をフルマネージドで任せられる",[50,112,115],{"href":113,"rel":114},"https:\u002F\u002Fkubo.hexabase.io\u002F",[54],"Kubo","のようなサービスであれば、こうしたネットワーク要件やTLS設定の初期検証はプラットフォーム側で担保されるが、自前運用ではここを手動で確認する必要がある。",[11,118,120],{"id":119},"_3-原因-registriesyamlは全ノード必須という仕様の見落とし","3. 原因①: registries.yamlは「全ノード必須」という仕様の見落とし",[16,122,123,124,127,128,133,134,137],{},"調査を進めると、最初の原因が見えてきた。K3sはノードの起動時に",[25,125,126],{},"\u002Fetc\u002Francher\u002Fk3s\u002Fregistries.yaml","の有無を確認し、存在すればcontainerdの設定に反映する。",[50,129,132],{"href":130,"rel":131},"https:\u002F\u002Fdocs.k3s.io\u002Finstallation\u002Fprivate-registry",[54],"K3s公式のプライベートレジストリ設定ドキュメント","には、この設定ファイルが",[20,135,136],{},"プライベートレジストリから画像をpullする全ノード（serverとagentの両方）に配置されている必要がある","と明記されている。",[16,139,140],{},[36,141],{"alt":142,"src":143},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection03.webp",[16,145,146,147,149],{},"新しいエッジノードのプロビジョニング手順書には、OSセットアップとK3sエージェントのインストールは書かれていたが、",[25,148,106],{},"をコピーするステップが抜けていた。既存ノードはクラスタ構築時に手動でファイルを配置していたため問題が表面化せず、ノード追加作業のたびに「暗黙の前提」として忘れられていた。",[16,151,152,153,156],{},"さらに厄介なのは、設定ファイルを後から配置しても",[20,154,155],{},"各ノードでK3sを再起動しない限り反映されない","という点だ。ドリフトに気づかないまま再起動せずに放置すれば、永遠に直らない。",[11,158,160],{"id":159},"_4-原因-ワイルドカードミラーとcontainerd-20の退行バグ","4. 原因②: ワイルドカードミラーとcontainerd 2.0の退行バグ",[16,162,163,164,166,167,170,171,176,177,180],{},"原因①を直してもまだ不安定だった理由は、",[25,165,106],{},"のミラー設定にワイルドカード",[25,168,169],{},"\"*\":","を使っていたことにある。これは全レジストリを分散ミラーの対象にする便利な書き方だが、",[50,172,175],{"href":173,"rel":174},"https:\u002F\u002Fgithub.com\u002Fk3s-io\u002Fk3s\u002Fissues\u002F11857",[54],"k3s-io\u002Fk3sのIssue #11857","で報告されている通り、containerd 2.0へ移行したK3s v1.32.2+k3s1以降で、ミラーにイメージが無い場合のフォールバックが正しく働かず",[25,178,179],{},"not found","エラーでpullが失敗する退行バグが存在した。",[16,182,183],{},[36,184],{"alt":185,"src":186},"section04","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection04.webp",[16,188,189,190,192,193,198,199,202,203,206],{},"このバグはv1.31.7-rc1・v1.32.3-rc2で修正済みだが、ワイルドカード設定と",[25,191,106],{},"未配布という2つの問題が重なったことで、原因の切り分けが一気に難しくなっていた。回避策は、ワイルドカードを使わず対象レジストリを明示的に列挙すること。もう一つ、",[50,194,197],{"href":195,"rel":196},"https:\u002F\u002Fgithub.com\u002Fk3s-io\u002Fk3s\u002Fissues\u002F10246",[54],"Issue #10246","では",[25,200,201],{},"--bind-address","をデフォルトの",[25,204,205],{},"127.0.0.1","以外に変更している環境で組み込みレジストリへのpullが失敗する既知の不具合も報告されており、カスタムネットワーク構成を取っている場合は合わせて確認する価値がある。",[11,208,210],{"id":209},"_5-原因-ノード間の認証情報ドリフトはサイレントに失敗する","5. 原因③: ノード間の認証情報ドリフトはサイレントに失敗する",[16,212,213,215,216,218,219,222,223,226,227,226,230,233,234,237],{},[25,214,106],{},"を配布し、ワイルドカードも整理したあとに最後まで残ったのが、プライベートレジストリの認証情報そのものの食い違いだった。",[25,217,106],{},"の",[25,220,221],{},"configs","セクションにはbasic認証やTLS証明書（",[25,224,225],{},"cert_file","・",[25,228,229],{},"key_file",[25,231,232],{},"ca_file","）を設定できるが、ノードごとにこの値がわずかに異なると、エラーメッセージが曖昧なまま",[20,235,236],{},"サイレントに失敗する","。",[16,239,240],{},[36,241],{"alt":242,"src":243},"section05","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection05.webp",[16,245,246,247,252],{},"実際に",[50,248,251],{"href":249,"rel":250},"https:\u002F\u002Foneuptime.com\u002Fblog\u002Fpost\u002F2026-02-09-k3s-registry-mirror-air-gapped\u002Fview",[54],"OneUptimeの運用レポート","でも、ノードが異なるタイミングでプロビジョニングされるとイメージバンドルや認証情報が同期されず「設定ドリフト」が起きると指摘されている。認証失敗はネットワーク疎通の失敗ほど分かりやすいログを残さないため、トラブルシューティングの優先順位としては後回しにされがちだが、エアギャップ環境の運用では最初に疑うべきポイントの一つだ。",[11,254,256],{"id":255},"_6-バニラkubernetesとの違いそしてエアギャップ運用の正しい配布手順","6. バニラKubernetesとの違い、そしてエアギャップ運用の正しい配布手順",[16,258,259,260,265],{},"ここで改めて確認しておきたいのは、Spegelのような組み込みP2Pレジストリミラーはバニラの標準Kubernetesには存在しないという点だ。標準Kubernetes自体はレジストリ機能を持たず、kubeletが設定されたレジストリから直接pullするだけなので、同等の仕組みを作るにはHarborのようなプルスルーキャッシュを別途構築し、自前で運用する必要がある。",[50,261,264],{"href":262,"rel":263},"https:\u002F\u002Fdocs.rke2.io\u002Finstall\u002Fregistry_mirror",[54],"RKE2の公式ドキュメント","でも同じSpegelベースの仕組みが採用されており、K3s系ディストリビューション共通の設計思想であることが分かる。",[16,267,268],{},[36,269],{"alt":270,"src":271},"section06","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Fsection06.webp",[16,273,274],{},"今回の教訓を踏まえると、エアギャップ\u002Fエッジ環境でノードを追加する際の正しい配布手順は次のようになる。",[276,277,278,288,297],"ul",{},[279,280,281,284,285,287],"li",{},[20,282,283],{},"配布を自動化する",": Ansibleやcloud-initの構成テンプレートに",[25,286,106],{},"を含め、手動コピーの手順を無くす",[279,289,290,293,294,296],{},[20,291,292],{},"ワイルドカードを避ける",": ",[25,295,169],{},"ではなく対象レジストリを明示的に列挙し、containerdのバージョン依存の挙動変化を避ける",[279,298,299,302],{},[20,300,301],{},"事前ヘルスチェックを入れる",": ノード追加のプレイブックに、ポート5001・6443への到達性と認証情報の一致を確認するステップを組み込む",[16,304,305,306,311],{},"こうした配布・検証の自動化は、エッジ拠点が増えるほど運用負荷が跳ね上がる部分でもある。",[50,307,310],{"href":308,"rel":309},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fkubo\u002Fon-premise",[54],"Kubo On-Premise","なら、Air-Gapped環境での完全自社管理を前提に、こうしたレジストリ配布や証明書管理の運用をフルマネージドで引き受けられる設計になっている。",[11,313,314],{"id":314},"まとめ",[16,316,317,318,320],{},"「組み込み機能だから自動的に全ノードで効くはず」という思い込みが、今回のインシデントの出発点だった。実際には、K3sの組み込みレジストリミラー（Spegel）は便利な一方で、",[25,319,106],{},"の全ノード配布・ワイルドカード設定の既知バグ・ノード間の認証情報ドリフトという3つの落とし穴を併せ持つ。",[16,322,323,324,327,328,333],{},"エッジ拠点を増やすたびに同じ調査を繰り返すのは現実的ではない。データ主権やAir-Gapped要件を抱えながらエッジ×Kubernetesの運用を広げていくなら、レジストリ配布や証明書管理をプラットフォーム側に任せられる",[50,325,310],{"href":308,"rel":326},[54],"の構成を検討してみてほしい。導入の相談は",[50,329,332],{"href":330,"rel":331},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[54],"お問い合わせ","から受け付けている。",{"title":335,"searchDepth":336,"depth":336,"links":337},"",2,[338,339,340,341,342,343,344],{"id":13,"depth":336,"text":14},{"id":59,"depth":336,"text":60},{"id":119,"depth":336,"text":120},{"id":159,"depth":336,"text":160},{"id":209,"depth":336,"text":210},{"id":255,"depth":336,"text":256},{"id":314,"depth":336,"text":314},"2026-10-30","K3sの組み込みレジストリミラー（Spegel）はクラスタ全体で有効なはずが、新規追加したエッジノードだけimage pullに失敗した。原因はregistries.yamlの未配布と認証ドリフト、containerd 2.0の既知バグ。エアギャップ運用の正しい配布手順を解説する。","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join\u002Ftitle.webp","ja",{},true,"\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join",{"title":5,"description":346},"blog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join",[356,357,358,359,360,361],"k3s","kubernetes","spegel","registry-mirror","airgap","edge-computing","b6Lr3pmLXhvEZojZP8B606rENXSRMjkalyHKNY4ELnE",[364,372,380,388,397,405],{"path":365,"title":366,"description":367,"date":368,"tags":369},"\u002Fblog\u002Fja\u002Fk3s-prometheus-agent-mode-stale-marker-edge-monitoring","K3sエッジのPrometheusをAgent modeにしたら、ターゲットが消えた瞬間にメトリクスも消えていた","K3sエッジクラスタでPrometheus Agent modeを使うと、ターゲット消失時のstale marker処理次第で中央監視に古い値が残り続けることがある。K3s edge環境の監視設計とつまずきポイントを解説する。","2026-11-03",[356,357,370,361,371],"prometheus","observability",{"path":373,"title":374,"description":375,"date":376,"tags":377},"\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain","Planは消えずに残っていた。K3sのsystem-upgrade-controllerが止まり続けた理由","K3sのsystem-upgrade-controllerで自動アップグレードを組んだら、特定ノードだけPlanが進まなくなった。原因はPodDisruptionBudgetとtaintの盲点。仕組みと復旧手順、Kuboでの解決策を解説する。","2026-10-31",[356,357,378,379,361],"system-upgrade-controller","rolling-upgrade",{"path":381,"title":382,"description":383,"date":384,"tags":385},"\u002Fblog\u002Fja\u002Fk3s-microshift-etcd-ostree-edge-comparison","エッジでetcdを固定されるのは罠か前提か。K3sとMicroShift、OSTree自動ロールバックの代償を比較する","K3sとMicroShiftはどちらも「軽量Kubernetes」と語られるが、データストアとOS更新モデルの前提が根本的に違う。etcd固定とOSTree自動ロールバックの実態を比較し、エッジ本番運用でどちらを選ぶべきかを整理する。","2026-10-29",[356,386,357,361,387],"microshift","redhat",{"path":389,"title":390,"description":391,"date":392,"tags":393},"\u002Fblog\u002Fja\u002Fk3s-vs-microk8s-ha-etcd-dqlite-edge-production","K3sとMicroK8s、同じ\"軽量\"でも本番で勝つのはどちらか。dqliteとetcdが分けるHA構成の分岐点","K3sとMicroK8s、同じ軽量Kubernetesでも本番運用の明暗を分けるのはHA構成だ。K3sのembedded etcdとMicroK8sのdqlite、それぞれの最小RAM要件・アーキテクチャの違いを公式ドキュメントのデータで比較し、選定基準を解説する。","2026-10-25",[356,394,357,395,361,396],"microk8s","high-availability","comparison",{"path":398,"title":399,"description":400,"date":401,"tags":402},"\u002Fblog\u002Fja\u002Fedge-k3s-hardware-selection-beyond-appliance","家庭用ルーターを自作PCに置き換える動きが加速。エッジKubernetesも「専用アプライアンス」依存を見直す時期だ","ISP支給ルーターを自作PC+OSSに置き換える動きが個人の間で広がっている。工場や店舗のエッジKubernetesも同じ理由で専用アプライアンス依存を見直すべき理由と、ハードウェア選定の具体的なチェックリスト、GitOpsによる拠点運用の標準化までを解説する。","2026-08-29",[356,357,361,403,404],"hardware-selection","network-appliance",{"path":406,"title":407,"description":408,"date":409,"tags":410},"\u002Fblog\u002Fja\u002Fk3s-baremetal-loadbalancer-bgp-metallb-cilium","ベアメタルK3sのLoadBalancer、ARPだけでは限界が来る。BGPで外部ルーティングを設計する","オンプレ・エッジのK3sクラスタでクラウドLBに頼れないとき、Layer2(ARP)方式のロードバランシングはなぜ限界に達するのか。MetalLB BGPモードとCilium BGP Control Planeの違い、移行の判断基準を解説する。","2026-08-26",[356,357,411,412,413,361],"metallb","bgp","load-balancer",1793798696426]