Ingress(イングレス)は、クラスターの外から来た HTTP・HTTPS のリクエストを、ホスト名とパスで Service に振り分ける設定です。Ingress を書くだけでは動かず、Traefik や NGINX などの Ingress コントローラーが、その内容を読んで実際に振り分けます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: traefik        # どのコントローラーに任せるか
  tls:
  - hosts: [app.example.com]
    secretName: app-tls             # 証明書の Secret
  rules:
  - host: app.example.com           # ホスト名
    http:
      paths:
      - path: /api                  # パス
        pathType: Prefix            # 一致のさせ方
        backend:
          service:
            name: api               # 振り分け先の Service
            port:
              number: 80
書く場所 決めること
ingressClassName どの Ingress コントローラーが処理するか
rules[].host どのホスト名のリクエストか(*.example.com も可)
paths[].path + pathType どのパスか、どう一致させるか(Prefix・Exact・ImplementationSpecific)
backend.service 送り先の Service 名とポート
tls[] HTTPS にするホストと、証明書の Secret

確認環境:k3s v1.36.5+k3s1(Kubernetes 1.36.5)を Ubuntu 24.04 に1ノードで立て、k3s に入っている Traefik 3.7.13(Helm チャート 40.1.5)を Ingress コントローラーにして確かめました。裏のアプリは、受け取ったパスと Host を返す BusyBox 1.37.0 の小さなサーバーです。仕様の部分は Kubernetes のドキュメント(Ingress・Ingress NGINX の終了告知)と Gateway API のドキュメントで確かめています(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kubernetes Ingressの書き方|パス・ホスト・TLS設定例」

まず知っておくこと:Ingress API は「凍結」、Ingress NGINX は保守終了

Kubernetes のドキュメントの Ingress のページには、次の注意があります。

  • Kubernetes プロジェクトは、Ingress ではなく Gateway API を勧めている
  • Ingress API は凍結(これ以上機能を足さない)。ただし一般提供(GA)の API として残り、削除の予定はない

また、よく使われてきた Ingress NGINX(kubernetes/ingress-nginx)は、2026年3月でベストエフォートの保守を終えました。 Kubernetes ブログの告知では、その後はリリース・バグ修正・脆弱性の修正が出ないこと、既存の環境はそのまま動き続けること、移行先として Gateway API や他の Ingress コントローラーが挙げられています。

これから 選び方の目安
既存の Ingress を使い続ける Traefik・HAProxy・Kong・クラウドの LB など、保守されているコントローラーに載せ替える
新しく作る Gateway API(HTTPRoute)も候補にする(後半で比べます)

Ingress コントローラーを用意する

Ingress は「設定」で、動かすのはコントローラーです。クラスターにどのコントローラーがあるかは、IngressClass で確かめます。

$ kubectl get ingressclass
NAME                CONTROLLER                      PARAMETERS   AGE
traefik (default)   traefik.io/ingress-controller   <none>       61s

k3s では Traefik が最初から入り、(default) の IngressClass になっていました。マネージドの Kubernetes では、例えば EKS は AWS Load Balancer Controller(ALB を作る)、GKE は GKE Ingress など、それぞれのドキュメントに沿って入れます。

振り分け先のアプリと Service

Ingress の backend は Service を指します。Pod を直接は指せません。

apiVersion: v1
kind: Service
metadata:
  name: web-a
spec:
  selector: {app: web-a}
  ports:
  - name: http
    port: 80          # Ingress の port.number はこちら
    targetPort: 8080  # コンテナのポート

Ingress の port は、Service の port(80)か、ポート名(name: http)で書きます。コンテナのポート(8080)を書くと、後で出てくる service port not found になりました。

pathType の違い:Prefix・Exact・ImplementationSpecific

pathType 一致のさせ方(Kubernetes の仕様)
Exact パスが完全に同じとき。大文字小文字も区別
Prefix / で区切った要素ごとの前方一致。/foo は /foo/bar に当たるが /foobar には当たらない
ImplementationSpecific コントローラーに任せる

ドキュメントの例を、よく迷うものに絞った表です。

pathType path リクエスト 当たるか
Exact /foo /foo 当たる
Exact /foo /foo/ 当たらない
Prefix /foo /foo、/foo/ 当たる
Prefix /aaa/bbb /aaa/bbb/ccc 当たる
Prefix /aaa/bbb /aaa/bbbxyz 当たらない
Prefix / すべて 当たる
Prefix と Exact が同じ /foo /foo Exact が優先

複数のパスに当たるときは、仕様では「いちばん長いパスが優先、同じ長さなら Exact が優先」です。

実測:Traefik では /foobar も当たった

/foo(Prefix → web-a)と /foo/bar(Exact → web-b)の Ingress を作り、k3s の Traefik(既定の設定)に送りました。

      paths:
      - path: /foo
        pathType: Prefix
        backend: {service: {name: web-a, port: {number: 80}}}
      - path: /foo/bar
        pathType: Exact
        backend: {service: {name: web-b, port: {name: http}}}
リクエスト Kubernetes の仕様 Traefik(既定) strictPrefixMatching=true
/foo web-a web-a web-a
/foo/ web-a web-a web-a
/foo/baz web-a web-a web-a
/foobar 当たらない(404) web-a に当たった 404
/foo/bar web-b(Exact) web-a web-a
/FOO 当たらない 404 404

2つの違いが出ました。 Traefik の API で、Ingress から作られたルールを見ると理由が分かります。

Host("path.example.test") && Path("/foo/bar")       | priority 45 | web-b
Host("path.example.test") && PathPrefix("/foo")     | priority 47 | web-a
  1. /foobar:Prefix は PathPrefix("/foo")(文字列の前方一致)になっていました。Traefik のドキュメントには、providers.kubernetesIngress.strictPrefixMatching(既定 false)を true にすると仕様どおりの要素ごとの一致になる、とあります。true にすると、ルールは (Path("/foo") || PathPrefix("/foo/")) に変わり、/foobar は 404 になりました。
  2. /foo/bar:Traefik のドキュメントでは、ルーターの優先度は「ルールの文字列の長さ」です。PathPrefix("/foo") を含むルール全体(47文字)が、Path("/foo/bar") を含むルール全体(45文字)より長いため、Prefix 側が勝っていました。strictPrefixMatching を true にしても、ルールがさらに長くなり、結果は同じでした。

Exact 側を別の Ingress に分けて、Traefik の注釈で優先度を上げると、仕様どおり web-b に振り分けられました。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-exact
  annotations:
    traefik.ingress.kubernetes.io/router.priority: "100"
spec:
  ingressClassName: traefik
  rules:
  - host: path.example.test
    http:
      paths:
      - path: /foo/bar
        pathType: Exact
        backend: {service: {name: web-b, port: {number: 80}}}
リクエスト 結果
/foo/bar web-b
/foo/bar/ web-a(Exact には当たらない)
/foo/baz web-a

同じ Ingress でも、コントローラーによって振り分けが変わることがあります。 コントローラーを載せ替えるときは、パスごとに curl で送って、行き先を確かめておくと安全です。

ImplementationSpecific は、strictPrefixMatching=true の Traefik では (Path("/impl") || PathPrefix("/impl/")) になり、/impl・/impl/x は当たり、/implx は 404 でした。

k3s の Traefik の設定は、HelmChartConfig で変えました。

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    additionalArguments:
      - "--providers.kubernetesingress.strictPrefixMatching=true"

ホスト名で振り分ける(ワイルドカードも)

spec:
  ingressClassName: traefik
  rules:
  - host: a.example.test
    http:
      paths:
      - path: /
        pathType: Prefix
        backend: {service: {name: web-a, port: {number: 80}}}
  - host: "*.example.test"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend: {service: {name: web-b, port: {number: 80}}}
Host ヘッダー 結果
a.example.test web-a(完全一致が優先)
b.example.test web-b(ワイルドカード)
x.y.example.test 404(ワイルドカードは1階層だけ)
example.test 404

Kubernetes のドキュメントでも、*.foo.com は bar.foo.com に当たり、baz.bar.foo.com と foo.com には当たらない、とされています。* で始まるホストは YAML ではクォートで囲みます。

ingressClassName の書き方と、書き忘れ・間違い

ingressClassName 結果
traefik(ある名前) 振り分けられた
書かない 既定の IngressClass(traefik)が自動で入り、振り分けられた
nginx(クラスターに無い名前) 作成は成功するが 404。どのコントローラーも処理しない

書かずに作った Ingress を見ると、spec.ingressClassName: traefik が入っていました。既定の IngressClass は、ingressclass.kubernetes.io/is-default-class: "true" の注釈で決まります。ドキュメントでは、既定が2つ以上あると、ingressClassName を書かない Ingress は作れなくなるとされています。

無い名前を書いても kubectl apply はエラーになりません。 404 が返るときは、kubectl get ingress の CLASS 列と kubectl get ingressclass を見比べます。

以前の書き方の注釈 kubernetes.io/ingress.class: nginx は、ingressClassName に置き換えられたものです。

TLS(HTTPS)の設定

Secret を作る

TLS の Secret は、tls.crt と tls.key の2つのキーを持つ kubernetes.io/tls 型です。

# 確認用の自己署名証明書
openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=secure.example.test" -addext "subjectAltName=DNS:secure.example.test"

kubectl create secret tls secure-tls --cert=tls.crt --key=tls.key
$ kubectl get secret secure-tls -o jsonpath='{.type}'
kubernetes.io/tls

Secret は Ingress と同じ名前空間に作ります。 本番の証明書は、cert-manager などで Let's Encrypt から取って Secret に入れる方法がよく使われます。

Ingress に tls を書く

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-demo
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - secure.example.test
    secretName: secure-tls
  rules:
  - host: secure.example.test
    http:
      paths:
      - path: /
        pathType: Prefix
        backend: {service: {name: web-a, port: {number: 80}}}

curl で確かめた結果です。

$ curl -v --cacert tls.crt https://secure.example.test:30842/x   # 30842 は Traefik の NodePort
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519 / RSASSA-PSS
*  subject: CN=secure.example.test
*  issuer: CN=secure.example.test
web-a path=/x Host: secure.example.test:30842 X-Forwarded-Proto: https

kubectl describe ingress では TLS: secure-tls terminates secure.example.test と表示されました。TLS は Ingress コントローラーで終わり(終端)、裏の Pod には HTTP で届きます。アプリには X-Forwarded-Proto: https が渡っていました。

HTTP から HTTPS へのリダイレクト

tls を書いても、HTTP から HTTPS へのリダイレクトは、Ingress の仕様にはありません。 コントローラーごとの設定です。Traefik では Middleware を作り、注釈で付けます。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: redirect-https
spec:
  redirectScheme:
    scheme: https
    permanent: true

注釈の値は <名前空間>-<Middleware名>@kubernetescrd です。HTTP で送ると 301 Moved Permanently と Location: https://secure.example.test/x が返りました。

NodePort など 443 以外のポートで受けている確認環境では、HTTPS 側のリクエストも 301 になり、ループしました。redirectScheme に port: "30842"(受けているポート)を足すと止まりました。

Secret が無いとき・tls を書かないとき

状況 結果
Secret を消した Traefik のログに Error configuring TLS error="secret default/secure-tls does not exist"。HTTPS は Traefik の既定の証明書(CN=TRAEFIK DEFAULT CERT)になり、curl は self-signed certificate で失敗
tls を書かない Ingress に HTTPS で送った 200 が返った。証明書は TRAEFIK DEFAULT CERT

k3s の Traefik は、起動引数 --entryPoints.websecure.http.tls=true で 443 側でも全ルーターを受けていました。tls を書き忘れても HTTPS で「つながってしまう」ので、証明書の CN まで確かめると見落としにくくなります。

よくある書き方の早見表

やりたいこと 書き方
1つのホストで、パスごとに分ける rules[0].http.paths に複数書く
ホストごとに分ける rules を複数書く
どれにも当たらないとき spec.defaultBackend
HTTPS tls[].hosts + secretName
HTTP → HTTPS コントローラーの設定(Traefik は Middleware)
パスの書き換え(/api を外す) コントローラーの設定(Traefik は stripPrefix の Middleware)
優先度を変える コントローラーの設定(Traefik は router.priority)

kubectl create ingress で雛形を作る

kubectl create ingress quick --class=traefik \
  --rule="quick.test/api*=web-a:80" \
  --rule="quick.test/=web-b:80" \
  --rule="secure.quick.test/*=web-a:80,tls=quick-tls" \
  --dry-run=client -o yaml

パスの末尾に * を付けると Prefix、付けないと Exact になりました。 quick.test/=web-b:80 は path: /・pathType: Exact になるので、トップページだけに当たります。

Gateway API との違い

項目 Ingress Gateway API
リソース Ingress(+IngressClass) GatewayClass・Gateway・HTTPRoute など
担当の分け方 1つのリソースに全部 Gateway はインフラ担当、HTTPRoute はアプリ担当
ヘッダーで振り分け 仕様に無い(注釈) matches.headers
重み付きの振り分け 仕様に無い(注釈) backendRefs[].weight
リダイレクト・書き換え 仕様に無い(注釈) filters(RequestRedirect・URLRewrite)
TLS の設定場所 Ingress の tls Gateway の listeners
どれにも当たらないとき defaultBackend / の PathPrefix のルールを書く
TCP・UDP・gRPC 扱わない TCPRoute・UDPRoute・GRPCRoute
今後 凍結(削除予定なし) Kubernetes が推奨

実測:同じ Traefik で HTTPRoute を動かした

k3s の Traefik で Gateway API を有効にし(HelmChartConfig の providers.kubernetesGateway.enabled: true)、Gateway と HTTPRoute を作りました。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
spec:
  gatewayClassName: traefik
  listeners:
  - name: web
    protocol: HTTP
    port: 8000          # Traefik の web エントリーポイント
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: route-demo
spec:
  parentRefs:
  - name: my-gateway
  hostnames: [gw.route.test]
  rules:
  - matches: [{path: {type: PathPrefix, value: /foo}}]
    backendRefs: [{name: web-a, port: 80}]
  - matches: [{path: {type: Exact, value: /foo/bar}}]
    backendRefs: [{name: web-b, port: 80}]
  - matches: [{headers: [{name: X-Canary, value: "true"}]}]
    backendRefs: [{name: web-b, port: 80}]
  - backendRefs:
    - {name: web-a, port: 80, weight: 80}
    - {name: web-b, port: 80, weight: 20}
リクエスト 結果
/foo・/foo/ web-a
/foo/bar web-b(Exact が優先された)
X-Canary: true を付けて /other を20回 20回とも web-b
ヘッダーなしで200回 web-a 160回・web-b 40回(80:20)

Gateway API では、ヘッダーでの振り分けと重み付けが仕様の中で書け、Exact の優先も仕様どおりでした。Traefik が作ったルールの優先度は、Exact が 100016、PathPrefix が 10417 でした。

最初、k3s が作る kube-system の traefik-gateway を parentRefs に指定すると、HTTPRoute の状態が Accepted=False NotAllowedByListeners になりました。その Gateway は allowedRoutes.namespaces.from: Same(同じ名前空間だけ)だったためで、アプリと同じ名前空間に Gateway を作ると Accepted=True になりました。Ingress から HTTPRoute への変換には、Gateway API の ingress2gateway というツールが案内されています。

よくあるエラーと直し方

エラー・状況 原因 直し方
spec.rules[0].http.paths[0].path: Invalid value: "foo": must be an absolute path パスが / で始まっていない /foo にする
spec.rules[0].http.paths[0].pathType: Required value: pathType must be specified pathType の書き忘れ Prefix などを書く
pathType: Unsupported value: "Regex": supported values: "Exact", "ImplementationSpecific", "Prefix" 無い値 3つから選ぶ(正規表現はコントローラーの機能で)
404、Traefik のログに Cannot create service error="service not found" Service 名の間違い・別の名前空間 kubectl get svc で名前を確かめる
404、ログに error="service port not found" コンテナのポートを書いた Service の port かポート名を書く
503 no available server Service はあるが、Ready な Pod が0 kubectl get endpointslices・Pod の状態を見る
404 で、Ingress の CLASS が想定外 ingressClassName の間違い kubectl get ingressclass の名前にする
ADDRESS が空のまま コントローラーの Service に外部 IP が無い LoadBalancer の用意(今回は k3s の ServiceLB を止めていたため空)

上の文面は、k3s 1.36.5 の kubectl apply と Traefik 3.7.13 のログで確かめたものです。

よくある質問

Q. Ingress と Service の type: LoadBalancer はどう違いますか。 A. LoadBalancer の Service は、1つの Service に1つの外部 IP(ロードバランサー)を割り当て、TCP・UDP を通します。Ingress は1つの入口で、ホスト名とパスを見て複数の Service に HTTP を振り分けます。Service の種類は、関連記事「Kubernetes入門|主要リソースとYAMLの書き方、kubectl早見表」で扱っています。

Q. Ingress コントローラーを Helm で入れるときは? A. 多くのコントローラーは Helm チャートで配られています。k3s の Traefik も Helm チャート(HelmChart リソース)で入っていました。Helm の使い方は、関連記事「Helm入門|チャートの作り方とコマンド一覧」で扱っています。

Q. Ingress NGINX の注釈(nginx.ingress.kubernetes.io/rewrite-target など)は他のコントローラーでも使えますか。 A. 注釈はコントローラーごとの拡張なので、そのままでは効きません。Traefik では、パスの書き換えは Middleware(stripPrefix・replacePathRegex)、リダイレクトは redirectScheme で書きます。載せ替えるときは、注釈ごとに置き換え先を決めます。

まとめ

  • Ingress はホスト名とパスで Service に振り分ける設定。動かすのはコントローラー
  • Prefix は要素ごとの一致が仕様。ただし Traefik の既定では /foobar も当たり、Exact より Prefix が勝った
  • ingressClassName が無い名前でも apply は成功して 404。書かなければ既定の IngressClass が入る
  • TLS は kubernetes.io/tls の Secret を tls[] で指す。リダイレクトはコントローラーの設定
  • Ingress API は凍結、Ingress NGINX は保守終了。新規なら Gateway API(HTTPRoute)も候補

参考・出典

確認日はいずれも 2026年10月13日です。

  • Kubernetes ドキュメント「Ingress」:https://kubernetes.io/docs/concepts/services-networking/ingress/
  • Kubernetes ブログ「Ingress NGINX Retirement: What You Need to Know」:https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/
  • Gateway API「Migrating from Ingress」:https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/
  • K3s ドキュメント「Networking Services」(Traefik Ingress Controller):https://docs.k3s.io/networking/networking-services
  • Traefik ドキュメント「Kubernetes Ingress Provider」(strictPrefixMatching):https://doc.traefik.io/traefik/reference/install-configuration/providers/kubernetes/kubernetes-ingress/
  • Traefik ドキュメント「Rules & Priority」:https://doc.traefik.io/traefik/reference/routing-configuration/http/routing/rules-and-priority/
  • Traefik ドキュメント「Kubernetes Ingress」(注釈):https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/ingress/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kubernetes Ingressの書き方|パス・ホスト・TLS設定例」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?