- Published on
Ingress の高度なプロトコル — gRPC、WebSocket、HTTP/2、HTTP/3
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- プロトコルごとの要件の概観
- gRPC ルーティング
- WebSocket の処理
- HTTP/2 バックエンド
- HTTP/3 と QUIC
- TLS ALPN ネゴシエーション
- コントローラーごとの総合対応表
- Gateway API での処理
- おわりに
- 参考資料
はじめに
Kubernetes に最初にサービスをデプロイするとき、その多くはありふれた HTTP/1.1 の REST API です。しかしサービスが成長するにつれて状況は急速に複雑になります。マイクロサービス間の通信を gRPC に切り替え、リアルタイム通知のために WebSocket を開き、フロントエンドの性能のために HTTP/2 の多重化を有効にし、モバイル環境の遅延を減らすために HTTP/3(QUIC)を検討するようになります。そしてこれらのトラフィックはすべて、最終的にクラスターの入口である Ingress コントローラーを通過しなければなりません。
問題は、Ingress リソース自体が本質的に HTTP/1.1 時代の抽象だという点です。Ingress API はホストとパスをバックエンドの Service にマッピングする単純なモデルで、gRPC ストリーミングや WebSocket のアップグレード、QUIC といった概念を直接表現するフィールドを持ちません。そのため、これらの高度なプロトコルはほぼすべて、コントローラー固有のアノテーションや CRD を通じて間接的に有効化されます。同じ機能でも、ingress-nginx、Traefik、HAProxy、Contour、Kong で設定方法がまったく異なるということです。
本記事では 4 つのプロトコルを一つずつ取り上げ、それぞれが Ingress 層に何を要求するのか、コントローラーごとにどう設定するのか、そしてどんな落とし穴にはまりやすいのかを、実践的な YAML とともに整理します。2026 年現在、Ingress API は凍結(frozen)状態で新機能は追加されず、これらの高度なプロトコルは後継標準である Gateway API でより一級の存在として扱われます。そこで最後に、同じ問題を Gateway API でどう解くのかも見ていきます。
プロトコルごとの要件の概観
各プロトコルが Ingress 層に正確に何を要求するのかを整理すると、設定がぐっと明確になります。
| プロトコル | 中核となる要件 | Ingress 層がすべきこと |
|---|---|---|
| gRPC | HTTP/2 上で動作、双方向ストリーミング、トレーラー | バックエンドまで HTTP/2(h2 または h2c)を維持、バッファリング無効化 |
| WebSocket | HTTP/1.1 の Upgrade ハンドシェイク、長時間接続 | Connection/Upgrade ヘッダーの転送、長い idle タイムアウト |
| HTTP/2 | 多重化、ヘッダー圧縮、TLS ALPN | クライアント側 h2 ネゴシエーション、任意でバックエンド h2 |
| HTTP/3 | QUIC(UDP 443)、TLS 1.3 必須、Alt-Svc の広告 | UDP 443 リスナー、Alt-Svc ヘッダー、フォールバック保証 |
要点は二つです。第一に gRPC と HTTP/2 は「接続そのもの」が HTTP/2 でなければならない点、第二に WebSocket と HTTP/3 は通常のリクエスト・レスポンスモデルを外れた長時間接続や別の転送層を使う点です。Ingress コントローラーはこの差を吸収しなければなりません。
[ Client ]
|
TLS 1.3 + ALPN ネゴシエーション (h2 / h3 / http/1.1)
|
[ Ingress Controller ]
+-----------+-----------+-----------+
| | | |
gRPC WebSocket HTTP/2 HTTP/3
(h2c?) (Upgrade) (backend) (QUIC->TCP fallback)
| | | |
[ Backend Services ]
gRPC ルーティング
gRPC が Ingress に要求するもの
gRPC は HTTP/2 のフレーミング上で動作するため、Ingress が単に HTTP/1.1 でバックエンドにプロキシすると動作しません。コントローラーはバックエンドへの接続を HTTP/2 で維持し、レスポンスのバッファリングを無効にし、トレーラー(gRPC は grpc-status をトレーラーで送ります)をそのまま転送する必要があります。バックエンドが TLS なしの平文 HTTP/2 を使う場合、これを h2c(HTTP/2 cleartext)と呼びます。
ingress-nginx での gRPC
ingress-nginx は nginx.ingress.kubernetes.io/backend-protocol アノテーションでバックエンドプロトコルを指定します。gRPC なら値を GRPC、TLS バックエンドの場合は GRPCS にします。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grpc-ingress
namespace: demo
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
spec:
ingressClassName: nginx
tls:
- hosts:
- grpc.example.com
secretName: grpc-tls
rules:
- host: grpc.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grpc-server
port:
number: 50051
注意したいのは、gRPC over HTTP/2 は実質的に TLS を強く推奨するという点です。クライアントが h2 をネゴシエートするには ALPN が必要で、ALPN は TLS ハンドシェイクで行われるからです。したがって上の例のように tls ブロックを必ず一緒に置きます。
パスベースの gRPC メソッドルーティング
gRPC のメソッドは HTTP/2 のパスにマッピングされます。たとえば helloworld.Greeter/SayHello メソッドはパス /helloworld.Greeter/SayHello で届きます。したがってサービス単位でルーティングするには、パッケージ・サービス名をパスの prefix として使えます。
- host: grpc.example.com
http:
paths:
- path: /helloworld.Greeter
pathType: Prefix
backend:
service:
name: greeter-svc
port:
number: 50051
- path: /inventory.Stock
pathType: Prefix
backend:
service:
name: stock-svc
port:
number: 50051
Traefik での gRPC
Traefik は h2c バックエンドのためにサービス単位で scheme を指定します。Kubernetes では Service や IngressRoute にアノテーションまたは ServersTransport で対応します。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: grpc-route
namespace: demo
spec:
entryPoints:
- websecure
routes:
- match: Host(`grpc.example.com`)
kind: Rule
services:
- name: grpc-server
port: 50051
scheme: h2c
tls:
secretName: grpc-tls
gRPC のトラブルシューティング — 504 と UNAVAILABLE
gRPC を初めて Ingress に載せると、よく二つの症状に出会います。
- 504 Gateway Timeout: バックエンドプロトコルが HTTP/1.1 のままで、コントローラーが HTTP/2 フレームを理解できない場合です。
backend-protocolがGRPCか、平文バックエンドが h2c に指定されているかを確認します。 - ストリーミング応答が途切れる: プロキシのバッファリングが有効で、サーバーストリームがチャンク単位で流れない場合です。ingress-nginx では
nginx.ingress.kubernetes.io/proxy-buffering: "off"で無効にします。 - grpc-status が消える: トレーラーの転送が遮られた場合です。最新のコントローラーは既定で対応しますが、古いプロキシが前段にあるとトレーラーを落とすことがあります。
WebSocket の処理
Upgrade ハンドシェイク
WebSocket は HTTP/1.1 接続で始まり、Upgrade: websocket と Connection: Upgrade ヘッダーでプロトコルを切り替えます。Ingress コントローラーはこれらのヘッダーをバックエンドにそのまま転送する必要があり、切り替え後の接続はリクエスト・レスポンスではなく、長時間維持される双方向ストリームになります。
ほとんどのコントローラーは WebSocket のアップグレードを自動で検出します。ingress-nginx は Upgrade ヘッダーを見て、特別なアノテーションなしで自動的に処理します。本当の問題はハンドシェイクではなくタイムアウトです。
idle タイムアウトを延ばす
既定のプロキシタイムアウトはたいてい 60 秒前後です。WebSocket 接続はメッセージがしばらく行き来しないとこのタイムアウトに引っかかって切断されます。したがって idle タイムアウトを十分に延ばす必要があります。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ws-ingress
namespace: demo
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
rules:
- host: ws.example.com
http:
paths:
- path: /ws
pathType: Prefix
backend:
service:
name: ws-server
port:
number: 8080
Traefik では EntryPoint やミドルウェアでタイムアウトを調整します。WebSocket 自体は Traefik が透過的に処理するため、別途の有効化は不要です。
WebSocket のトラブルシューティング
- 1 分ごとに接続が切れる: 上で見た read/send タイムアウトを延ばしていない典型的な症状です。
- 426 Upgrade Required またはハンドシェイク失敗: 前段に HTTP/2 を強制するプロキシがあり、Upgrade ヘッダーが無視される場合です。WebSocket は HTTP/1.1 の経路を流れる必要があります。
- セッションがバックエンド間を行き来して壊れる: 複数レプリカにスティッキーセッションがないと、ハンドシェイクと後続フレームが別の Pod に行くことがあります。
nginx.ingress.kubernetes.io/affinity: "cookie"でセッションアフィニティを有効にします。
HTTP/2 バックエンド
クライアントと Ingress の間の HTTP/2 は、TLS を有効にすれば ALPN で自動的にネゴシエートされることが多いです。別途気にすべきは Ingress とバックエンドの間の HTTP/2 です。gRPC でなくても、バックエンドが HTTP/2 に対応し多重化の利点を得たい場合は、バックエンドプロトコルを HTTP/2 に指定します。
ingress-nginx では同じ backend-protocol を使います。
metadata:
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
# クライアント側 HTTP/2 はグローバル ConfigMap の use-http2 などで制御
クライアント側 HTTP/2 は ingress-nginx のグローバル ConfigMap で制御します。たいてい既定で有効になっており、TLS が有効なホストで ALPN により h2 をネゴシエートします。
HTTP/3 と QUIC
HTTP/3 が異なる理由
HTTP/3 は TCP ではなく UDP 上で動作する QUIC を転送層として使います。つまり Ingress コントローラーは UDP 443 のリスナーを開く必要があり、TLS 1.3 が必須で、既存の TCP ベースの HTTP/2 と共存するために Alt-Svc ヘッダーで「このサービスは HTTP/3 にも対応」と広告します。クライアントは最初は HTTP/2 で接続し、Alt-Svc を見たあと、次の接続から HTTP/3 を試みる流れです。
コントローラーごとの HTTP/3 対応状況
2026 年時点で、HTTP/3 の成熟度はコントローラーごとに大きく異なります。
| コントローラー | HTTP/3 対応 | 備考 |
|---|---|---|
| ingress-nginx | 限定的/実験的 | 基盤 nginx の QUIC モジュール依存、メンテナンスモードの影響 |
| Traefik | 対応 | 実験的フラグで有効化、EntryPoint に http3 を設定 |
| HAProxy | 対応 | QUIC ビルドで UDP bind に対応 |
| Contour/Envoy | データプレーン対応 | Envoy は HTTP/3 対応、公開経路の設定が必要 |
| クラウド LB ベース | おおむね対応 | クラウド L7 LB で HTTP/3 をトグル |
Traefik で HTTP/3 を有効化する例
Traefik は静的設定の EntryPoint で HTTP/3 を有効にします。下記は値の構造を示す例です。
entryPoints:
websecure:
address: ":443"
http3:
advertisedPort: 443
UDP 443 が LoadBalancer Service とノードのファイアウォールで開いている必要があるのが要点です。これが塞がれていると、クライアントは静かに HTTP/2 にフォールバックするため、「HTTP/3 を有効にしたのに使われない」というよくある混乱が生じます。
HTTP/3 のトラブルシューティング
- 常に HTTP/2 でしか接続しない: UDP 443 が LB やファイアウォールで塞がれているか、Alt-Svc ヘッダーが広告されていない場合です。
- 断続的な接続失敗: 経路の途中に UDP を遮断するミドルボックスがあると QUIC が失敗します。このとき正常動作のために TCP フォールバックが必ず保証されている必要があります。
TLS ALPN ネゴシエーション
ここまで見たプロトコルの多くは ALPN(Application-Layer Protocol Negotiation)に依存します。ALPN は TLS ハンドシェイクの途中で、クライアントとサーバーが「どのアプリケーションプロトコルを使うか」を合意する拡張です。h2(HTTP/2)、http/1.1、h3(HTTP/3)などがネゴシエーション対象です。
ClientHello (ALPN: h3, h2, http/1.1)
-> ServerHello (ALPN 選択: h2)
-> 以後の接続は HTTP/2 で進行
したがって gRPC、HTTP/2、HTTP/3 を適切に使うには TLS が前提条件です。平文(h2c)でバックエンドを置くことはできても、クライアント・Ingress 区間ではほぼ常に TLS を有効にし、証明書を cert-manager などで管理するのが標準です。
コントローラーごとの総合対応表
| 機能 | ingress-nginx | Traefik | HAProxy | Contour |
|---|---|---|---|---|
| gRPC | backend-protocol GRPC | scheme h2c | 対応 | 対応(HTTPProxy) |
| WebSocket | 自動 | 自動 | 自動 | 自動 |
| バックエンド HTTP/2 | backend-protocol | ServersTransport | 対応 | 対応 |
| HTTP/3 | 実験的 | 対応 | 対応 | Envoy 経由 |
| TLS ALPN | 対応 | 対応 | 対応 | 対応 |
Gateway API での処理
Ingress API が凍結された今、上記のアノテーション乱立の問題を根本的に解決するのが Gateway API です。Gateway API はプロトコルを一級の概念として扱います。たとえば gRPC は GRPCRoute という専用リソースで表現され、アノテーションの回避策なしにサービス・メソッド単位のルーティングを宣言できます。
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: greeter
namespace: demo
spec:
parentRefs:
- name: prod-gateway
hostnames:
- grpc.example.com
rules:
- matches:
- method:
service: helloworld.Greeter
method: SayHello
backendRefs:
- name: greeter-svc
port: 50051
HTTP/2 と WebSocket は HTTPRoute で自然に流れ、Listener の protocol と TLS 設定が ALPN ネゴシエーションを決めます。HTTP/3 は実装(Envoy、Traefik など)の Gateway 実装の水準に応じて対応します。要点は、コントローラーごとのアノテーションの代わりに標準リソースで表現され、移植性が大きく高まる点です。新規クラスターであれば、高度なプロトコルが必要になった時点で Gateway API を優先的に検討することをおすすめします。
おわりに
Ingress で gRPC、WebSocket、HTTP/2、HTTP/3 を扱うことは、結局「HTTP/1.1 を前提に作られた抽象に、非 HTTP/1.1 のトラフィックを流す」作業です。gRPC はバックエンドまで HTTP/2 を維持しバッファリングを無効にすること、WebSocket は Upgrade の転送と長いタイムアウト、HTTP/2 は ALPN とバックエンドプロトコルの指定、HTTP/3 は UDP 443 と Alt-Svc、そしてフォールバック保証が要点でした。
同じ機能でもコントローラーごとに設定方法が異なり運用負担は大きいですが、Gateway API がこの断片化を標準リソースに吸収しつつあります。2026 年現在 Ingress で運用中のサービスであっても、高度なプロトコルの要求が増えるなら、GRPCRoute と HTTPRoute をベースとした Gateway API への段階的な移行のロードマップを描いておくとよいでしょう。
参考資料
- Kubernetes Ingress 公式ドキュメント: https://kubernetes.io/docs/concepts/services-networking/ingress/
- ingress-nginx gRPC 例: https://kubernetes.github.io/ingress-nginx/examples/grpc/
- ingress-nginx アノテーションリファレンス: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
- Traefik Kubernetes IngressRoute: https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/
- HAProxy Kubernetes Ingress Controller: https://www.haproxy.com/documentation/kubernetes-ingress/
- Contour HTTPProxy ドキュメント: https://projectcontour.io/docs/
- Gateway API GRPCRoute: https://gateway-api.sigs.k8s.io/api-types/grpcroute/
- Gateway API HTTPRoute: https://gateway-api.sigs.k8s.io/api-types/httproute/
- cert-manager ドキュメント: https://cert-manager.io/docs/