0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Knative ServiceをEnvoy Gatewayと連携し、機能拡張する

Last updated at Posted at 2025-11-07

はじめに

この記事では、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アプリケーションのルーティングを柔軟に制御・拡張できます。

参考リンク

備考

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多くなります.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?