はじめに
この記事では、Knative ServingをEnvoy Gatewayと組み合わせて動かす方法と機能拡張する方法を紹介します。
前提条件
以下のツールがインストールされている必要があります:
- kind
- kubectl
- helm
- ko
- Go
cloud-provider-kindのインストール
cloud-provider-kindは、kindクラスタでLoadBalancer Serviceを使えるようにするツールです。以下のコマンドでインストールできます。
go install sigs.k8s.io/cloud-provider-kind@latest
koのインストール
koは、Go言語で書かれたアプリケーションをコンテナイメージとしてビルド・デプロイするツールです。以下のコマンドでインストールできます。
go install github.com/google/ko@latest
セットアップ手順
1. kindクラスタの作成
まず、kindでローカルKubernetesクラスタを作成します。
kind create cluster
2. cloud-provider-kindの起動
LoadBalancer Serviceを利用できるようにするため、別のターミナルを開いてcloud-provider-kindを起動します。このプロセスは起動したままにしておく必要があります。
# 別のターミナルで実行
sudo cloud-provider-kind
このターミナルはそのままにして、元のターミナルに戻って以下の手順を続けます。
3. Knative Servingのインストール
Knative ServingのCRDとコアコンポーネントをインストールします。
kubectl apply -f https://github.com/knative/serving/releases/latest/download/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/latest/download/serving-core.yaml
4. Knative ServingをGateway API用に設定
Knative Servingのnetwork設定を変更し、Gateway APIを使用するように構成します。
kubectl patch configmap/config-network \
-n knative-serving \
--type merge \
-p '{"data":{"ingress.class":"gateway-api.ingress.networking.knative.dev"}}'
5. Envoy Gateway用のvaluesファイル作成
Envoy Gatewayの設定ファイルを作成します。ここでは、GatewayがデプロイされるNamespaceにEnvoy Proxyもデプロイする設定にしています。
cat <<'YAML' > values-eg.yaml
config:
envoyGateway:
provider:
type: Kubernetes
kubernetes:
deploy:
type: GatewayNamespace
YAML
6. Envoy Gatewayのインストール
Helmを使ってEnvoy Gatewayをインストールします。
export ENVOY_GATEWAY_VERSION=v1.5.4
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version $ENVOY_GATEWAY_VERSION \
-n envoy-gateway-system --create-namespace \
-f ./values-eg.yaml
7. net-gateway-apiリポジトリのクローン
Knative ServingとGateway APIを統合するためのリソースが含まれているリポジトリをクローンします。
git clone https://github.com/knative-extensions/net-gateway-api.git
cd net-gateway-api
8. Gateway API統合コンポーネントのデプロイ
koを使って、Knative ServingとGateway APIを統合するコンポーネントをデプロイします。
export KO_DOCKER_REPO=kind.local
ko apply -f config/
9. Gateway APIリソースのデプロイ
Knative Serving用のGatewayリソースをデプロイします。
kubectl apply -f ./third_party/envoy-gateway
10. Gateway設定のConfigMapを適用
Knative ServingがEnvoy Gatewayを認識するための設定を追加します。
cat <<'YAML' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: config-gateway
namespace: knative-serving
labels:
app.kubernetes.io/component: net-gateway-api
app.kubernetes.io/name: knative-serving
serving.knative.dev/release: devel
data:
external-gateways: |
- class: eg-external
gateway: eg-external/eg-external
service: eg-external/knative-external
supported-features:
- HTTPRouteRequestTimeout
# local-gateways defines the Gateway to be used for cluster local traffic
local-gateways: |
- class: eg-internal
gateway: eg-internal/eg-internal
service: eg-internal/knative-internal
supported-features:
- HTTPRouteRequestTimeout
YAML
11. ドメイン設定
テスト用のドメイン設定を行います。
kubectl patch configmap config-domain -n knative-serving --type merge -p '{"data":{"example.com":""}}'
12. サンプルアプリケーションのデプロイ
動作確認用のサンプルアプリケーションをデプロイします。
cat <<'YAML' | kubectl apply -f -
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-go
spec:
template:
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go
env:
- name: TARGET
value: Go Sample v1
YAML
動作確認
1. LoadBalancer IPの取得と/etc/hostsの設定
LoadBalancerのIPアドレスを取得します。
export LB_IP=$(kubectl -n eg-external get svc knative-external -o jsonpath='{.status.loadBalancer.ingress[*].ip}{"\n"}')
echo $LB_IP
取得したIPアドレスを/etc/hostsに追加します。
echo "$LB_IP helloworld-go.default.example.com" | sudo tee -a /etc/hosts
2. アクセステスト
curl http://helloworld-go.default.example.com
正常にデプロイされていれば、以下のようなレスポンスが返ってきます。
Hello Go Sample v1!
応用: Envoy GatewayでKnative Serviceを機能拡張
Envoy Gatewayを使うメリットの一つは、Knative Service自体を変更することなく、SecurityPolicyなどのリソースを使って機能を追加できることです。ここでは、Basic認証を追加する例を紹介します。
Basic認証の追加
1. 認証情報の作成
まず、htpasswdコマンドで認証ファイルを作成します(ユーザー名: foo, パスワード: bar)。
htpasswd -cbs .htpasswd foo bar
2. Secretの作成
作成した認証ファイルをKubernetesのSecretとして登録します。
kubectl -n default create secret generic basic-auth --from-file=.htpasswd
3. SecurityPolicyの適用
Envoy GatewayのSecurityPolicyリソースを使って、HTTPRouteにBasic認証を追加します。
cat <<'YAML' | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
name: helloworld-go-basic-auth
namespace: default
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: helloworld-go.default.example.com
basicAuth:
users:
name: basic-auth
YAML
4. 動作確認
認証なしでアクセスすると、401エラーが返されます。
curl http://helloworld-go.default.example.com
# 401 Unauthorized
認証情報を付けてアクセスすると、正常にレスポンスが返ってきます。
curl -u foo:bar http://helloworld-go.default.example.com
# Hello Go Sample v1!
このように、Knative ServiceやHTTPRouteのマニフェストを一切変更することなく、Envoy GatewayのSecurityPolicyを使ってBasic認証を簡単に追加できました。同様に、Rate Limiting、CORS設定、JWT検証、etc...など、様々な機能をGatewayレイヤーで追加できますので、一度 Security | Envoy Gateway を参照してみてください。
まとめ
この記事では、Knative ServingをEnvoy Gatewayと組み合わせて動かす方法を紹介しました。
Envoy Gatewayは高性能なEnvoy公式のゲートウェイ実装であり、Knative Servingとの組み合わせにより、Serverlessアプリケーションのルーティングを柔軟に制御・拡張できます。
参考リンク
- Knative Serving
- Basic Authentication | Envoy Gateway
- knative-extensions/net-gateway-api
- Kubernetes Gateway API
備考
Traffic Splittingを有効化した際に出力されているHTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
annotations:
networking.internal.knative.dev/rollout: '{"configurations":[{"configurationName":"helloworld-go","percent":50,"revisions":[{"revisionName":"helloworld-go-00005","percent":50}],"stepParams":{}},{"configurationName":"helloworld-go","tag":"qa","percent":100,"revisions":[{"revisionName":"helloworld-go-00005","percent":100}],"stepParams":{}}]}'
networking.knative.dev/ingress.class: gateway-api.ingress.networking.knative.dev
serving.knative.dev/creator: kubernetes-admin
serving.knative.dev/lastModifier: kubernetes-admin
creationTimestamp: "2025-11-09T05:41:51Z"
generation: 33
labels:
networking.knative.dev/visibility: ""
serving.knative.dev/route: helloworld-go
serving.knative.dev/routeNamespace: default
serving.knative.dev/service: helloworld-go
name: helloworld-go.default.example.com
namespace: default
ownerReferences:
- apiVersion: networking.internal.knative.dev/v1alpha1
blockOwnerDeletion: true
controller: true
kind: Ingress
name: helloworld-go
uid: f15718d7-91ba-482f-8322-27968a218697
resourceVersion: "220984"
uid: 07fca6bb-1e7a-4a53-8e09-107df56fcb25
spec:
hostnames:
- helloworld-go.default.example.com
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: eg-external
namespace: eg-external
rules:
- backendRefs:
- filters:
- requestHeaderModifier:
set:
- name: Knative-Serving-Namespace
value: default
- name: Knative-Serving-Revision
value: helloworld-go-00003
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go-00003
port: 80
weight: 50
- filters:
- requestHeaderModifier:
set:
- name: Knative-Serving-Namespace
value: default
- name: Knative-Serving-Revision
value: helloworld-go-00005
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go-00005
port: 80
weight: 50
filters:
- requestHeaderModifier:
set:
- name: K-Network-Hash
value: f3149388225bc6465417757efc25ea3b739741e2037cf7a5fc49772260fc882f
type: RequestHeaderModifier
matches:
- headers:
- name: K-Network-Hash
type: Exact
value: override
path:
type: PathPrefix
value: /
timeouts:
request: 0s
- backendRefs:
- filters:
- requestHeaderModifier:
set:
- name: Knative-Serving-Namespace
value: default
- name: Knative-Serving-Revision
value: helloworld-go-00003
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go-00003
port: 80
weight: 50
- filters:
- requestHeaderModifier:
set:
- name: Knative-Serving-Namespace
value: default
- name: Knative-Serving-Revision
value: helloworld-go-00005
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go-00005
port: 80
weight: 50
matches:
- path:
type: PathPrefix
value: /
timeouts:
request: 0s
status:
parents:
- conditions:
- lastTransitionTime: "2025-11-10T04:00:42Z"
message: Route is accepted
observedGeneration: 33
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: "2025-11-10T04:00:42Z"
message: Resolved all the Object references for the Route
observedGeneration: 33
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg-external
namespace: eg-external
DomainMappingを利用した場合に出力されるHTTPRoute
以下を適用します。
apiVersion: networking.internal.knative.dev/v1alpha1
kind: ClusterDomainClaim
metadata:
name: test.com
spec:
namespace: default
---
apiVersion: serving.knative.dev/v1beta1
kind: DomainMapping
metadata:
name: test.com
namespace: default
spec:
ref:
name: helloworld-go
kind: Service
apiVersion: serving.knative.dev/v1
以下が出力されます.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
annotations:
networking.knative.dev/ingress.class: gateway-api.ingress.networking.knative.dev
serving.knative.dev/creator: kubernetes-admin
serving.knative.dev/lastModifier: kubernetes-admin
creationTimestamp: "2025-11-10T04:48:55Z"
generation: 1
labels:
networking.knative.dev/visibility: ""
serving.knative.dev/domainMappingNamespace: default
serving.knative.dev/domainMappingUID: 88954e71-03c4-4004-81e1-2916c6ba976c
name: test.com
namespace: default
ownerReferences:
- apiVersion: networking.internal.knative.dev/v1alpha1
blockOwnerDeletion: true
controller: true
kind: Ingress
name: test.com
uid: d7351ea1-9e67-4ebc-80cb-4227195362c1
resourceVersion: "231722"
uid: 45f70e96-0f30-4cda-b1a2-f6f1a25dad0b
spec:
hostnames:
- test.com
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: eg-external
namespace: eg-external
rules:
- backendRefs:
- filters:
- requestHeaderModifier:
set:
- name: K-Original-Host
value: test.com
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go
port: 80
weight: 100
filters:
- requestHeaderModifier:
set:
- name: K-Network-Hash
value: 244b84148c16d15938fd7f3ab2d377b0b40f6418229edee9af3fe0d961fd152a
type: RequestHeaderModifier
- type: URLRewrite
urlRewrite:
hostname: helloworld-go.default.svc.cluster.local
matches:
- headers:
- name: K-Network-Hash
type: Exact
value: override
path:
type: PathPrefix
value: /
timeouts:
request: 0s
- backendRefs:
- filters:
- requestHeaderModifier:
set:
- name: K-Original-Host
value: test.com
type: RequestHeaderModifier
group: ""
kind: Service
name: helloworld-go
port: 80
weight: 100
filters:
- type: URLRewrite
urlRewrite:
hostname: helloworld-go.default.svc.cluster.local
matches:
- path:
type: PathPrefix
value: /
timeouts:
request: 0s
status:
parents:
- conditions:
- lastTransitionTime: "2025-11-10T04:48:55Z"
message: Route is accepted
observedGeneration: 1
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: "2025-11-10T04:48:55Z"
message: Resolved all the Object references for the Route
observedGeneration: 1
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg-external
namespace: eg-external
この helloworld-go.default.svc.cluster.local は Internal用のEnvoy GatewayのEnvoy Proxyに向かっています。
apiVersion: v1
kind: Service
metadata:
annotations:
serving.knative.dev/creator: kubernetes-admin
serving.knative.dev/lastModifier: kubernetes-admin
creationTimestamp: "2025-11-09T05:41:51Z"
labels:
serving.knative.dev/route: helloworld-go
serving.knative.dev/service: helloworld-go
name: helloworld-go
namespace: default
ownerReferences:
- apiVersion: serving.knative.dev/v1
blockOwnerDeletion: true
controller: true
kind: Route
name: helloworld-go
uid: 24cc3f82-f7b3-40d9-89ce-ea516e46382f
resourceVersion: "56240"
uid: c6ea6306-abf9-49c0-8a28-9a71c9a3a99a
spec:
externalName: knative-internal.eg-internal.svc.cluster.local
ports:
- appProtocol: kubernetes.io/h2c
name: http2
port: 80
protocol: TCP
targetPort: 80
sessionAffinity: None
type: ExternalName
status:
loadBalancer: {}
そのため、例えばALBで公開した場合の外部から入ってくる時の経路としては以下の様になり、Internal Envoy Proxyを経由する分、通常のEnvoy Externalに公開されている経路と比べると1 hop多くなります.