- 技術 💻
Kubernetes Ingressの書き方|パス・ホスト・TLS設定例
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
/foobar:Prefix はPathPrefix("/foo")(文字列の前方一致)になっていました。Traefik のドキュメントには、providers.kubernetesIngress.strictPrefixMatching(既定false)をtrueにすると仕様どおりの要素ごとの一致になる、とあります。trueにすると、ルールは(Path("/foo") || PathPrefix("/foo/"))に変わり、/foobarは 404 になりました。/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期連続の黒字決算。