本記事は「Kong Advent Calendar 2025」の6日目のエントリとして、Kong Operatorの使い方について解説する。
今までKong GatewayのエンティティのKubernetesサポートという観点では2つの方法が提供されてきた。
1つはKong Gateway Operatorで、もう1つはKong Ingress Controller(KIC)である。
これがKong Gateway Operator 2.0を機に統合され、Kong Operatorとして提供されるようになった。
(公開時のアナウンスはこちら)
現時点で操作できるものは以下となっている。
| 機能 | サポート状況 |
|---|---|
| Gateway Control Planes | サポート済み |
| Gateway Configuration | サポート済み |
| Dedicated Cloud Gateways | 部分サポート |
| Kong Mesh | 計画中 |
| Developer Portal | 計画中 |
| Catalog | 計画中 |
| Analytics | 計画中 |
| Team Management | 計画中 |
| Organization Configuration | 計画中 |
Kong Gateway OperatorではKongのエンティティを独自のカスタムリソースで定義し、KongServiceやKongRouteといったリソースを作成することでKong Gatewayの設定を行う。
一方で、Kong Ingress ControllerではKubernetesの標準リソースであるkind: Serviceやkind: Gatewayを使ってKong Gatewayの設定を行う。
Kong Operatorはそれら2つの方法をサポートしており、両方の方法でKong Gatewayの設定を行うことが出来る。
今回、検証ではそれら2つの方法が適切に動作するかを確認する。
検証環境
今回はk3dで検証した。
構築手順はこちらを参照。
またKong Konnectを利用するため、Databaseは用意せず、Control Planeについてはカスタムリソースとして定義してKonnect内に作成する。
準備 - Kong Operatorのインストール
公式のインストール手順に従って構築する。
最初にGateway APIリソースを使うためのCRDをインストールする。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
cert-managerとの連携が出来るみたいなので、こちらの手順に従いcert-managerもインストールする。
helm install \
cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.19.1 \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
Helmのリポジトリを追加する。
helm repo add kong https://charts.konghq.com
helm repo update
cert-managerとの連携を有効にしてKong Operatorをインストールする。
helm upgrade --install kong-operator kong/kong-operator -n kong-system \
--create-namespace \
--set image.tag=2.0.5 \
--set env.ENABLE_CONTROLLER_KONNECT=true \
--set global.webhooks.options.certManager.enabled=true
インストール後のkong-system Namespaceの様子は以下の通り。
$ kubectl get all -n kong-system
NAME READY STATUS RESTARTS AGE
pod/kong-operator-kong-operator-controller-manager-db5fb9f6c-bjqhj 1/1 Running 0 116s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kong-operator-kong-operator ClusterIP 10.43.201.235 <none> 8443/TCP 117s
service/kong-operator-kong-operator-metrics-service ClusterIP 10.43.225.128 <none> 8080/TCP 117s
service/kong-operator-kong-operator-webhook ClusterIP 10.43.2.168 <none> 443/TCP,5443/TCP 117s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kong-operator-kong-operator-controller-manager 1/1 1 1 117s
NAME DESIRED CURRENT READY AGE
replicaset.apps/kong-operator-kong-operator-controller-manager-db5fb9f6c 1 1 1 117s
検証 - KICを使わない構成
Data Planeの作成
こちらの手順に従ってData Planeを作成する。
最初にKonnectにアクセスするためのトークンを環境変数に設定する。
今回はKonnectのOrganizationのSystem Accountsから作成できるシステムトークンを使う。(Organization Admin権限を付与)
以下のように取得したトークンをKONNECT_TOKENという環境変数に設定する。
export KONNECT_TOKEN="spat_hC..."
次にKonnectへのAPI認証に使うカスタムリソースを作成する。
最初にkongというNamespaceを作成する。
kubectl create namespace kong
次にAPI認証用のカスタムリソースを作成する。
echo '
kind: KonnectAPIAuthConfiguration
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: konnect-api-auth
namespace: kong
spec:
type: token
token: "'$KONNECT_TOKEN'"
serverURL: us.api.konghq.com
' | kubectl apply -f -
YAMLを見てもらえれば分かるが、接続先リージョンはUSとなっているので、もしリージョンを変更したい場合はserverURLの値を変更する。
次にControl Planeを構築する。これもカスタムリソースとして定義できる。
echo '
kind: KonnectGatewayControlPlane
apiVersion: konnect.konghq.com/v1alpha2
metadata:
name: gateway-control-plane
namespace: kong
spec:
createControlPlaneRequest:
name: my-operator-demo
konnect:
authRef:
name: konnect-api-auth
' | kubectl apply -f -
spec.konnect.authRef.nameには先程作成したAPI認証用のカスタムリソース名を指定する。
あと、spec.createControlPlaneRequest.nameにはKonnect上で作成されるControl Planeの名前を指定する。
ここでは作成するControl Planeの名前をmy-operator-demoとして、動作を理解するために敢えてmetadata.nameとは異なる名前にしている。
次にKonnectExtensionというカスタムリソースを作成する。
echo '
kind: KonnectExtension
apiVersion: konnect.konghq.com/v1alpha2
metadata:
name: my-konnect-config
namespace: kong
spec:
clientAuth:
certificateSecret:
provisioning: Automatic
konnect:
controlPlane:
ref:
type: konnectNamespacedRef
konnectNamespacedRef:
name: gateway-control-plane' | kubectl apply -f -
これはData PlaneがControl Planeに接続するための設定を定義するもので、ここでは自動的に証明書をプロビジョニングするように設定している。
spec.konnect.controlPlane.ref.konnectNamespacedRef.nameには先程作成したControl Planeのカスタムリソース名を指定する。
以上でData Planeをデプロイするための前提が整う。
次にData Planeをデプロイする。
echo '
apiVersion: gateway-operator.konghq.com/v1beta1
kind: DataPlane
metadata:
name: dataplane-example
namespace: kong
spec:
extensions:
- kind: KonnectExtension
name: my-konnect-config
group: konnect.konghq.com
deployment:
podTemplateSpec:
spec:
containers:
- name: proxy
image: kong/kong-gateway:3.12
' | kubectl apply -f -
先程作成したKonnectExtensionをspec.extensionsで指定することで、Control Planeとの紐づけを行う。
デプロイが完了するとUI上からも確認できる。

起動後のkong Namespaceの様子は以下の通り。
$ kubectl get all -n kong
NAME READY STATUS RESTARTS AGE
pod/dataplane-dataplane-example-m8w7t-5965cc84bc-5p6fx 1/1 Running 0 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dataplane-admin-dataplane-example-2pzp5 ClusterIP None <none> 8444/TCP 69s
service/dataplane-ingress-dataplane-example-vvzfl LoadBalancer 10.43.220.173 172.18.0.3 80:30360/TCP,443:30289/TCP 68s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/dataplane-dataplane-example-m8w7t 1/1 1 1 68s
NAME DESIRED CURRENT READY AGE
replicaset.apps/dataplane-dataplane-example-m8w7t-5965cc84bc 1 1 1 68s
Data Planeへのアクセスはtype: LoadBalancerでport80と443に対して提供されていることが分かる。
Secretを確認すると動的に証明書が作成されていることも分かる。
$ kubectl describe secret dataplane-dataplane-example-4bzlx -n kong
Name: dataplane-dataplane-example-4bzlx
Namespace: kong
Labels: gateway-operator.konghq.com/managed-by=dataplane
gateway-operator.konghq.com/service-secret=dataplane-admin-dataplane-example-2pzp5
konghq.com/secret=true
Annotations: <none>
Type: kubernetes.io/tls
Data
====
ca.crt: 648 bytes
tls.crt: 867 bytes
tls.key: 227 bytes
適当なPodから動作確認として443ポートにアクセスした結果は以下となる。
$ curl -k https://172.18.0.3:443
{
"message":"no Route matched with those values",
"request_id":"378cfa111c30f20c5a0cda7179a70a45"
}
問題なさそうだ。
Service, Route, Pluginの作成
Service, Route, Pluginもカスタムリソースとして定義できる。
最初にServiceを作成する。
echo '
kind: KongService
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: service
namespace: kong
spec:
name: service
host: httpbin.konghq.com
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: gateway-control-plane
' | kubectl apply -f -
ここではKong社がホスティングしているhttpbinをServiceとして定義している。
spec.controlPlaneRefには先程作成したControl Planeのカスタムリソース名を指定する。
次にRouteを作成する。
echo '
kind: KongRoute
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: route-with-service
namespace: kong
spec:
name: route-with-service
protocols:
- http
paths:
- "/"
serviceRef:
type: namespacedRef
namespacedRef:
name: service
' | kubectl apply -f -
spec.serviceRefには先程作成したServiceのカスタムリソース名を指定する。
作成後は以下のような感じになる。
$ kubectl get kongservice,kongroute -n kong
NAME HOST PROTOCOL PROGRAMMED
kongservice.configuration.konghq.com/service httpbin.konghq.com True
NAME PROGRAMMED
kongroute.configuration.konghq.com/route-with-service True
ちなみにServiceとRouteはKong Gatewayのエンティティとしては親子関係的な関係になるが、Kong Operatorではそれぞれ独立したリソースとして定義され、ownerReferencesも設定されない。
アクセスすると以下のようになる。
$ curl localhost:80/ip
{
"origin": "10.42.0.1"
}
Pluginも同様にカスタムリソースとして定義できる。
echo '
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-5-per-min
namespace: kong
plugin: rate-limiting
config:
minute: 5
limit_by: ip
policy: local
' | kubectl apply -f -
Pluginは単体では特定のエンティティとは紐づかない。
エンティティとの紐づけにはKongPluginBindingというカスタムリソースで紐づける。
echo '
kind: KongPluginBinding
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: binding-route-example-rate-limiting
namespace: kong
spec:
pluginRef:
kind: KongPlugin
name: rate-limit-5-per-min
targets:
routeRef:
group: configuration.konghq.com
kind: KongRoute
name: route-with-service
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: gateway-control-plane
' | kubectl apply -f -
spec.pluginRefには先程作成したPluginのカスタムリソース名を指定し、spec.targets.routeRefにはPluginを適用するRouteのカスタムリソース名を指定する。
KongPluginBindingを作成した後にアクセスすると、ヘッダからRate Limiting Pluginが動作していることが分かる。
$ curl -s localhost:80/ip -i |grep Rate
X-RateLimit-Limit-Minute: 5
X-RateLimit-Remaining-Minute: 3
RateLimit-Reset: 38
RateLimit-Remaining: 3
RateLimit-Limit: 5
注意点
deckなどを使ってKubernetes API経由以外でリソースを削除した場合、一時的に削除されるがリコンサイルされて元に戻る。
逆にKubernetes APIを経由せずに作成したエンティティはKubernetes上では感知出来ないため、そのまま残り続ける。
deckの使い方として定常的に実行して設定のドリフトを検知・修正するという使い方があるが、Kubernetesリソースで管理する場合は矛盾が生じやすくなるため、deckは使わない方が良さそう。
検証 - KICを使う構成
Kong Ingress Controllerの特徴として、既存のKubernetesのkind: Serviceと紐づけてKong GatewayのServiceとして定義できる点がある。
これが適切にKong Operatorでも利用できるかも確認する。
先程はControl PlaneやData Planeを個別のカスタムリソースとして定義したが、ここではGatewayConfigurationリソースを使って一括で管理する方法を使ってみる。
準備
検証を進めやすくするために作成したリソースのうち、以下を削除する。
kubectl delete kongroute route-with-service -n kong
kubectl delete kongservice service -n kong
kubectl delete dataplane dataplane-example -n kong
上記のリソースに相当するリソースはGatewayリソースを作成する時に自動で生成される。
KongPluginは使うので残しておく。
Kong Operatorもインストールし直す。
恐らく元の設定でも問題なさそうだが、マイグレーションのページにあったオプションでインストールしなおす。
helm upgrade --install kong-operator kong/kong-operator \
-n kong-system \
--create-namespace \
--take-ownership \
--set env.ENABLE_CONTROLLER_KONNECT=true \
--set ko-crds.enabled=true \
--set global.conversionWebhook.enabled=true \
--set global.conversionWebhook.certManager.enabled=true
アクセス確認用のhttpbin.orgへのServiceを作成する。
echo '
apiVersion: v1
kind: Service
metadata:
name: httpbin-external
namespace: kong
annotations:
konghq.com/protocol: "https"
konghq.com/port: "443"
konghq.com/host-header: "httpbin.org"
spec:
type: ExternalName
externalName: httpbin.org
ports:
- name: https
port: 443
targetPort: 443
protocol: TCP
' | kubectl apply -f -
アノテーションはKong Gatewayに対する設定となる。
今回はプラグインはRouteに設定するのでHTTPRouteリソース側のアノテーションに記載するが、Serviceにプラグインを設定したい場合は上記のアノテーションに追加する形で設定する。
KICモードの設定
KICモードで動作させるため、KonnectGatewayControlPlaneのcluster_typeにCLUSTER_TYPE_K8S_INGRESS_CONTROLLERを設定する。
ここのパラメータはイミュータブルで変更できないので、一度リソースを削除してから作り直す。
kubectl delete konnectgatewaycontrolplane gateway-control-plane -n kong
echo '
kind: KonnectGatewayControlPlane
apiVersion: konnect.konghq.com/v1alpha2
metadata:
name: gateway-control-plane
namespace: kong
spec:
createControlPlaneRequest:
name: my-operator-demo
cluster_type: CLUSTER_TYPE_K8S_INGRESS_CONTROLLER
konnect:
authRef:
name: konnect-api-auth
' | kubectl apply -f -
次にGatewayConfigurationリソースを作成する。
これはKong Gatewayの設定を定義するもので、kind: GatewayClassと組み合わせて利用する。
echo '
kind: GatewayConfiguration
apiVersion: gateway-operator.konghq.com/v2beta1
metadata:
name: kong
namespace: kong
spec:
extensions:
- kind: KonnectExtension
name: my-konnect-config
group: konnect.konghq.com
controlPlaneOptions:
translation:
combinedServicesFromDifferentHTTPRoutes: enabled
' | kubectl apply -f -
spec.controlPlaneOptions.translation.combinedServicesFromDifferentHTTPRoutesをenabledに設定しているのはHTTPRouteとの統合のためで、設定の仕方はKOのアナウンスから流用した。
なお、この後Data Planeを作成するが、その仕様はspec.dataPlaneOptionsで指定することが出来る。(今回はデフォルト値を使用)
次にkind: Gatewayとkind: GatewayClassを作成するが、その前にData Planeで使うポートをk3dで穴あけしておく。
k3d cluster edit dev --port-add "8000:8000@loadbalancer"
上記のはk3d固有の話なので、別環境でやっている人は無視してOK。
GatewayClassとGatewayのManifestは以下となる。
echo '
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: kong
namespace: kong
spec:
controllerName: konghq.com/gateway-operator
parametersRef:
group: gateway-operator.konghq.com
kind: GatewayConfiguration
name: kong
namespace: kong
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: kong
namespace: kong
spec:
gatewayClassName: kong
listeners:
- name: http
protocol: HTTP
port: 8000
hostname: "proxy.hogehoge.info"
allowedRoutes:
namespaces:
from: All
' | kubectl apply -f -
Gatewayリソースを作成すると、自動的にDataPlaneリソースも作成される。
なお、ホスト名は解決できるようにしておくこと。(自分の場合はRoute53で管理しているドメインを指定した)
最後にHTTPRouteリソースを作成する。
echo '
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin-route
namespace: kong
annotations:
konghq.com/strip-path: "true"
konghq.com/plugins: rate-limit-5-per-min
spec:
parentRefs:
- name: kong
hostnames:
- "proxy.hogehoge.info"
rules:
- matches:
- path:
type: PathPrefix
value: /httpbin
backendRefs:
- name: httpbin-external
port: 443
' | kubectl apply -f -
spec.parentRefsには先程作成したGatewayの名前を指定し、spec.rules.backendRefsには先程作成したkind: Serviceの名前を指定する。
またアノテーションでRouteの設定(Strip Path)やPluginの適用(先程作成したKongPluginを指定)も行っている。
動作確認
Gatewayで公開したポートを指定してアクセスしてみる。
curl http://proxy.hogehoge.info:8000/httpbin/user-agent -i
正常に動作していれば以下のようなレスポンスが返ってくる。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 33
Connection: keep-alive
X-RateLimit-Limit-Minute: 5
X-RateLimit-Remaining-Minute: 4
RateLimit-Reset: 12
RateLimit-Remaining: 4
RateLimit-Limit: 5
Date: Tue, 02 Dec 2025 01:39:48 GMT
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
X-Kong-Upstream-Latency: 6
X-Kong-Proxy-Latency: 1
Via: 1.1 kong/3.11.0.6-enterprise-edition
X-Kong-Request-Id: 28d346624a6f401a0586ec1784234c55
{
"user-agent": "curl/8.7.1"
}
ヘッダからRate Limiting Pluginも適切に動作していることが分かる。
なお、Gatewayリソースの親子関係は以下となる。
$ kubectl tree gateway -n kong kong
NAMESPACE NAME READY REASON STATUS AGE
kong Gateway/kong - - 58m
kong ├─ControlPlane/kong-gzt66 True Ready Current 58m
kong │ └─Secret/controlplane-kong-gzt66-n2bkd - - 58m
kong ├─DataPlane/kong-5brt6 True Ready Current 58m
kong │ ├─Deployment/dataplane-kong-5brt6-tcfqp - - 58m
kong │ │ ├─ReplicaSet/dataplane-kong-5brt6-tcfqp-6f774b5965 - - 57m
kong │ │ │ └─Pod/dataplane-kong-5brt6-tcfqp-6f774b5965-xnt24 True Current 57m
kong │ │ ├─ReplicaSet/dataplane-kong-5brt6-tcfqp-7476549495 - - 58m
kong │ │ └─ReplicaSet/dataplane-kong-5brt6-tcfqp-797d8f68c4 - - 57m
kong │ ├─Secret/dataplane-kong-5brt6-bt8mz - - 58m
kong │ ├─Service/dataplane-admin-kong-5brt6-gkmq4 - - 58m
kong │ │ └─EndpointSlice/dataplane-admin-kong-5brt6-gkmq4-gwcm2 - - 58m
kong │ └─Service/dataplane-ingress-kong-5brt6-k2gff - - 58m
kong │ └─EndpointSlice/dataplane-ingress-kong-5brt6-k2gff-d2s57 - - 58m
kong └─NetworkPolicy/kong-5brt6-limit-admin-api-qcb6n - - 57m
Control PlaneとData Planeが作成された事が分かる。
作成したリソースがプラグイン含めて確認できる。
まとめ
Kong Operatorを使うと以下の2つのアプローチが出来ることを確認した。
- カスタムリソースを使ってKong GatewayのエンティティをManifestとして管理できる
- Kubernetesのネイティブリソースを使ってKong GatewayのエンティティをManifestとして管理できる
所感としてはKong Gateway OperatorとKong Ingress Controllerがいい感じで統合されていて、使い勝手とかはそれ程変わっていなさそうだった。
それぞれ使っている人は違和感なく移行できそうに思えた。
一方で、ドキュメントが不足しているようでカスタムリソースのYAMLの書き方が分からず結構苦労して検証した。
実運用に組み込む場合はある程度の試行錯誤が必要そうなので、十分に検証してから導入するのが良さそうだ。

