Certified Kubernetes Administrator (CKA) #19 Networking 2: Ingress、IngressClass、TLS

#18 Networking 1 では Service で Pod のまとまりに安定した進入口を付けました。ところが外部に公開するサービスが数十個あったらどうなるでしょうか。NodePort はポート番号でしか分けられず、LoadBalancer はサービスごとにクラウドロードバランサーを 1 つずつ立ち上げてコストが膨らみます。ドメインやパス別にトラフィックを分ける要求も、Service 一層だけでは解けません。

この記事のテーマである Ingress がその負担を一箇所にまとめます。L7 (HTTP/HTTPS) レベルで host と path を見て、トラフィックを適切な Service へ分けてくれるオブジェクトです。ただし Ingress は ルールを書いたマニフェストにすぎず、そのルールに従って実際のトラフィックを処理するのは Ingress Controller です。この二層モデルが試験と運用の両方で最もよくつまずく地点なので、そこに重点を置きます。

二層モデル: Ingress と Ingress Controller #

まず最初に掴むべきは、Ingress オブジェクトと Ingress Controller が互いに別物である という点です。この 2 つを混同すると、試験会場で「Ingress を作ったのになぜ応答がないのか」という罠にはまります。

区分正体役割
Ingress (オブジェクト)API リソース (マニフェスト)「この host のこの path はあの Service へ」のような ルール宣言
Ingress Controller実際に動く Pod (通常 Deployment + Service)そのルールを読んで トラフィックを実際にルーティングする リバースプロキシ

Ingress マニフェストはそれ自体では何もしません。クラスターに Ingress Controller が立ち上がっていてこそ、コントローラーが Ingress オブジェクトを監視し、ルールどおりにプロキシ設定を作って外部トラフィックを受けます。コントローラーは種類が複数あります。

  • ingress-nginx (Kubernetes プロジェクトが管理する nginx ベース。試験と学習での標準)
  • Traefik
  • HAProxy
  • クラウドマネージド (GKE Ingress、AWS Load Balancer Controller など)

CKA 試験クラスターには通常 ingress-nginx が事前にインストールされているか、インストール用マニフェストが提供されるケースが多いです。運用クラスターを自分で構成するなら、Helm や公式マニフェストでコントローラーを先に入れる必要があります。コントローラーなしで Ingress だけ作ると、ADDRESS は空のままでトラフィックはどこにも届きません。

Ingress オブジェクトの構造 #

Ingress の核となるフィールドは spec.rules です。ルール 1 つは host (任意) と path のリストから成り、各 path はどの Service のどの port へ送るかを指します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

上のマニフェストは、shop.example.com へ入ってきたリクエストのうち /api で始まるものを api-service:8080 へ、それ以外の /web-service:80 へ送ります。1 つのドメインの下でパスによってバックエンドを分ける path ベースのルーティング です。

host ベースのルーティング #

host を複数のルールに分けると、ドメインごとに別の Service へ送れます。1 つの進入口から複数のサイトをホストする構造です。

spec:
  ingressClassName: nginx
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80
  - host: blog.example.com   # ドメインごとにルールを追加
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: blog-service
            port:
              number: 80

host を省略すると、すべてのホスト名にルールが適用されます。

pathType: Prefix と Exact #

pathType は v1 Ingress で 必須フィールド です。抜かすとマニフェストが拒否されるため、試験でよく減点される地点です。

pathTypeマッチング方式
Prefixパス要素 (/ 単位) の接頭辞一致/api/api/api/v1 にマッチ
Exactパスが正確に同一/api/api のみにマッチ、/api/v1 は不一致
ImplementationSpecificコントローラーの実装に委任コントローラーごとに解釈が異なる

ほとんどの場合は Prefix を使い、正確に 1 つのパスだけ受ける必要があるときだけ Exact を使います。ImplementationSpecific はコントローラーの動作に依存するため、移植性が必要なら避けるほうが安全です。

defaultBackend #

どのルールにもマッチしないリクエストを受ける既定のバックエンドを置けます。設定しなければ、マッチしないリクエストはコントローラーの既定 404 に落ちます。spec.rules と同じレベルに置きます。

spec:
  ingressClassName: nginx
  defaultBackend:        # rules にマッチしないリクエストの行き先
    service:
      name: fallback-service
      port:
        number: 80
  rules:
  - host: shop.example.com
    # ... (上と同じルール)

IngressClass: 複数のコントローラーを分ける #

1 つのクラスターに Ingress Controller が複数立ち上がっていることがあります。たとえば内部用 nginx と外部用クラウドコントローラーを一緒に運用するケースです。このとき 各 Ingress オブジェクトがどのコントローラーの管轄か を決める必要があり、その役割を IngressClass が担います。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingress オブジェクトは spec.ingressClassName で自分が従う IngressClass を指定します。上のマニフェストの ingressClassName: nginx がまさにこの IngressClass を指します。両者が名前で結ばれてこそ、ingress-nginx コントローラーがその Ingress を自分のものと認識して処理します。

is-default-class: "true" アノテーションが付いた IngressClass が 1 つあれば、ingressClassName を省略した Ingress は自動的にその既定クラスに紐づきます。コントローラーが複数あったり既定クラスがない環境では、必ず ingressClassName を明示 しなければ意図したコントローラーへ行きません。

かつては kubernetes.io/ingress.class アノテーションでクラスを指定していました。この方式は deprecated であり、現在は spec.ingressClassName フィールドを使うのが標準です。試験でもフィールド方式で書きます。

TLS 終端 #

Ingress は HTTPS トラフィックを受けて TLS を終端 (terminate) できます。証明書とキーはクラスターに Secret として保存し、Ingress の spec.tls セクションでその Secret を参照します。

まず kubernetes.io/tls タイプの Secret を作ります。

# 証明書 (tls.crt) とキー (tls.key) で TLS Secret を生成
kubectl create secret tls shop-tls \
  --cert=tls.crt \
  --key=tls.key

その後 Ingress に tls セクションを追加し、該当する host を紐づけます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  namespace: default
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - shop.example.com
    secretName: shop-tls
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

tls[].hosts に書いた host へ入ってくる HTTPS リクエストは、secretName が指す Secret の証明書で終端されます。ここでよくある誤りが 2 つあります。1 つ目は、tls[].hosts のドメインと rules[].host のドメインが 一致しないと 証明書が正しく適用されません。2 つ目は、Secret を Ingress と 別のネームスペース に作ると参照されません。Secret は Ingress と同じネームスペースになければなりません。

検証とトラブルシューティング #

作った後はすぐに状態を確認します。Ingress の ADDRESS が埋まったかが 1 次シグナルです。

# Ingress の一覧と ADDRESS を確認
kubectl get ingress

# ルール・TLS・イベントを詳しく見る
kubectl describe ingress shop-ingress

運用と試験で詰まる典型的な原因は次のとおりです。

  • ADDRESS が空: Ingress Controller がインストールされていないか、立ち上がっていません。kubectl get pods -n ingress-nginx でコントローラー Pod の状態を先に見ます。
  • 404 に落ちる: pathType や path がずれているか、backend が指す Service・port が間違っています。kubectl get svc で対象の Service と port を照合します。
  • TLS が適用されない: Secret の名前・タイプ (kubernetes.io/tls)・ネームスペース、そして tls.hostsrules.host の一致を確認します。
  • 見当違いのコントローラーが処理する: ingressClassName が空か、間違ったクラスを指しています。

試験ポイント #

CKA の Services and Networking ドメイン (20%) で Ingress は定番の出題テーマです。次を手に覚えておくと時間を節約できます。

  • Ingress は 宣言 (オブジェクト) であり、ルーティングは コントローラー が行うという二層モデル。コントローラーがなければ動作しない点を常に最初に思い出します。
  • v1 Ingress で pathType必須 です。抜かして減点されないようにします。
  • コントローラーの指定はアノテーションではなく spec.ingressClassName フィールドで行います。
  • TLS は kubernetes.io/tls Secret を作って spec.tls で参照し、host が rules と一致してネームスペースが同じである必要があります。
  • apiVersionnetworking.k8s.io/v1 です。旧バージョンの表記を使わないよう注意します。

kubectl create ingress コマンドで骨組みを素早く作ってから手で編集する方法も有効です。

# コマンドで Ingress の骨組みを生成し YAML を出力
kubectl create ingress shop-ingress \
  --class=nginx \
  --rule="shop.example.com/api*=api-service:8080" \
  --rule="shop.example.com/*=web-service:80" \
  --dry-run=client -o yaml

Ingress の概念をより広い文脈で復習するなら、K8s 中級 #3 Ingress と Ingress Controller を併せて見るとよいです。

まとめ #

この記事で押さえたこと:

  • Ingress は host・path ベースの L7 ルーティングルールを宣言するオブジェクト であり、実際のトラフィック処理は Ingress Controller が行います。コントローラーがなければ動作しません。
  • ルールは spec.rules の host と path で構成し、各 path は pathType (Prefix/Exact) と backend (Service + port) を持ちます。defaultBackend で未マッチのリクエストの既定の行き先を置きます。
  • IngressClass が複数のコントローラーを分け、Ingress は spec.ingressClassName で自分のクラスを指定します。
  • TLSkubernetes.io/tls Secret を作って spec.tls で参照し、host の一致と同一ネームスペースが条件です。
  • 検証は kubectl get ingress の ADDRESS と describe から始めます。

次へ: Networking 3 #

Ingress で外部からの進入を整理しました。次はクラスター内部の名前解決とトラフィック制御です。

#20 Networking 3: CoreDNS、NetworkPolicy では、Pod と Service が互いを名前で見つける CoreDNS の動作と設定、そしてどの Pod がどの Pod と通信できるかを制御する NetworkPolicy の ingress/egress ルールを運用の観点から整理します。

X