Nodelocal DNS Cache のアップデート戦略
概要
Kubernetes 環境で Nodelocal DNS Cache (以下、NDC) を運用していると、DaemonSet のアップデート戦略によって DNS 解決に影響が出るケースがあります。特に RollingUpdate を利用している場合、Pod のリスタート時に i/o timeout が必ず発生してしまうという問題に直面しました。
本記事では、その原因と安全にアップデートを行うための戦略について解説します。
前提
Karpenter で Node のプロビジョニングを行っている前提になっています。
ただし、Karpenter でなくても Shellscript などの他の手段で代替することは可能です。
RollingUpdate で発生する問題
なぜ i/o timeout が起きるのか
NDC Pod は終了時に以下の処理を行います。
- 自身が作成した仮想インターフェースを削除
- iptables ルールを削除
削除後は、通常の kube-dns (CoreDNS) に自動的にフォールバックする仕組みになっています。
しかし 「削除が完全に終わってから新しい Pod が仮想インターフェースを作成する」というシーケンスを保証できないため、一時的に存在しないインターフェースに DNS トラフィックが送られ続けてしまいます。その結果、応答がなく i/o timeout が発生してしまいます。
実運用での影響
検証において RollingUpdate の際、1分30秒程度の間に各マイクロサービスで最大 3.5% 程度の i/o timeout が発生することが確認できました。
アップデート頻度はコントロールできるため、そこまで多くないものの最大で 3.5%程度のエラーが発生することがわかっているのであれば、深夜のリクエスト数が少なく、影響が小さい時間にアップデートするなどの対応が必要になります。
解決策: OnDelete 戦略を使う
updateStrategy の OnDelete とは
私は今回始めて採用したため、あまり馴染みがないかもしれないですが、updateStrategy に OnDelete という選択肢があります。
updateStrategy.type を OnDelete に設定することで、DaemonSet の Pod は自動的には入れ替わらなくなります。変更が反映されるのは以下のタイミングです。
- Pod が削除され、再作成されたとき
- 新規に作成された Pod がスケジュールされたとき
つまり「明示的に Pod を消すまで勝手に更新しない」ため、アップデート時に RollingUpdate のような強制的なリスタートは発生しません。
アップデート時の運用フロー
実際の手順は以下の通りです。
-
変更 PR をマージ
- NDC のコンテナイメージをアップデート
-
EC2NodeClass を変更
- Karpenter の drift 機能により、自動的にノードが総入れ替えされる
-
Drift 機能(ノードの drain) による NDC の削除・再作成
- 先にマイクロサービスが evict され、その後 DaemonSet(NDC)が evict される
- このため、ノードに NDC が存在しなかったり、リスタートによる影響をマイクロサービスは受けません
メリットと注意点
- エラーがほぼ発生せず、日中にアップデートすることが可能
- 大量の
i/o timeoutを避けられる - 多少時間はかかるが、年数回程度の作業なので許容可能
結果として、「深夜メンテ対応必須」から「日中対応可能」に変わるため、運用コスト削減にもつながります。
まとめ
Nodelocal DNS Cache を RollingUpdate で運用すると、Pod リスタート時に必ず i/o timeout が発生するという課題がありました。
この問題は updateStrategy を OnDelete に切り替え、ノードの総入れ替え(Karpenter の drift)と組み合わせることで解消可能です。
安定性を優先し、安心して日中に対応できる仕組みを取り入れるのが現実的な選択肢と言えるでしょう。
Discussion