NGINX Ingress Controller と MetalLB を使った Ingress の構築に失敗していた話です。

背景

自宅ラボの Kubernetes クラスタでは、NGINX Ingress ControllerMetalLB をデプロイしています。 これらを使って、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 回目の登場)。