NGINX Ingress Controller と MetalLB を使った Ingress の構築に失敗していた話です。
背景
自宅ラボの Kubernetes クラスタでは、NGINX Ingress Controller と MetalLB をデプロイしています。
これらを使って、Ingress リソースに NGINX Ingress Controller 向け LoadBalancer Service の External-IP を割り当てて、各種サービスを Ingress 経由で公開しています。
一方、Kubernetes クラスタとは別に自宅ラボ用の DNS サーバを構築しており、夏季休暇中に CoreDNS から PowerDNS に入れ替える作業をしました(この話は別途投稿したいと思います)。
するとどうでしょう、DNS サーバの入れ替え作業後に Ingress で公開していたサービスに接続できなくなってしまいました。
状況
Ingress リソースを確認したところ、設定されている IP アドレスが想定していたアドレスと異なっていました。 想定アドレスは上述の通り「NGINX Ingress Controller 向け LoadBalancer Service の External-IP」です。
しかし、実際には「Kubernetes クラスタの Worker Node の IP アドレス」が設定されていました。
$ kubectl get ingress -A NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE argocd argocd-server-ingress <none> argocd-ingress.k8s.nnstt1.work 192.168.2.29 80, 443 22h monitoring grafana <none> grafana.k8s.nnstt1.work 192.168.2.29 80 20h monitoring k8s <none> prometheus.k8s.nnstt1.work 192.168.2.29 80 20h sandbox sample-app <none> sample-app.k8s.nnstt1.work 192.168.2.29 80, 443 20h # 192.168.2.29 は Worker Node の IP アドレス
Node Port のように Worker Node の IP アドレスでアクセスしても NGINX Ingress Controller のサービスには到達できないため、Ingress で指定したサービスにも接続できていませんでした。
原因
根本的な原因は、NGINX Ingress Controller の引数に --publish-service を設定できていなかったことでした。
調査のため Ingress や NGINX Ingress Controller を消しては作り直しを繰り返したのですが、最終的には GitHub の こちらの Issue を見つけたことで原因を特定できました。
自宅ラボの Kubernetes クラスタでは NGINX Ingress Controller 公式ドキュメントの ベアメタル向け Installation Guide を参照し、ベアメタル向けのマニフェスト を使って Ingress Controller をデプロイしていました。
しかし、ベアメタル向けのマニフェストは「MetalLB を使ってベアメタルでも LoadBalancer Service を使えるようにしている環境」向けではないため、--publish-service の引数が設定されていませんでした。
MetalLB を使っている場合は --publish-service が記載されている Cloud 向けのマニフェスト を使うほうがよさそうです。
NGINX Ingress Controller の引数に --publish-service をつけることで、無事に LoadBalancer の External-IP が割り当てられるようになりました。
# 説明箇所のみ抜粋 apiVersion: apps/v1 kind: Deployment metadata: name: ingress-nginx-controller spec: template: spec: containers: - name: controller image: k8s.gcr.io/ingress-nginx/controller:v0.48.1@sha256:e9fb216ace49dfa4a5983b183067e97496e7a8b307d2093f4278cd550c303899 args: - /nginx-ingress-controller - --election-id=ingress-controller-leader - --ingress-class=nginx - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller - --validating-webhook=:8443 - --validating-webhook-certificate=/usr/local/certificates/cert - --validating-webhook-key=/usr/local/certificates/key - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller # この行を追加
--publish-service とは
NGINX Ingress Controller 公式ドキュメントの Command line arguments ページ に記載があります。
Service fronting the Ingress controller. Takes the form "namespace/name". When used together with update-status, the controller mirrors the address of this service's endpoints to the load-balancer status of all Ingress objects it satisfies.
(機械翻訳) Ingressコントローラの前にあるサービス。 "namespace/name"という形式をとる。 update-statusと一緒に使用すると、コントローラは、このサービスのエンドポイントのアドレスを、満足するすべてのIngressオブジェクトのロードバランサステータスにミラーリングします。
NGINX Ingress Controller に --publish-service を付けなかった場合、今回のように Worker Node の IP アドレスが Ingress リソースに割り当てられるようですね。
このページをしっかり読んでいれば、今回の事象は防げていたはずです。
そもそも何で今までアクセスできていたの?
なんと、DNS サーバの入れ替え前(CoreDNS を使っていたとき)は、DNS サーバの /etc/hosts に直接 Ingress 用の DNS レコードを記述していたのです。
$ cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.2.244 argocd-ingress.k8s.nnstt1.work 192.168.2.244 grafana.k8s.nnstt1.work 192.168.2.244 prometheus.k8s.nnstt1.work 192.168.2.244 sample-app.k8s.nnstt1.work
元々 Ingress Controller を正しく設定できていなかったので、ワークアラウンド的に無理やり名前解決させていたようですね(他人事)。
それが PowerDNS への入れ替え時に /etc/hosts を綺麗に整理したことで名前解決できなくなり、Ingress 経由のサービスにアクセスできない事象が発生しました(正確には元々繋がらない設定だった)。
教訓
趣味でワークアラウンドな対応は(極力)しない。
ドキュメントはちゃんと読もう(N 回目の登場)。