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 回目の登場)。