Kuberenetes上にベーシックな機能セットのAPIプラットフォームを作ってみよう

2025年10月7日(火)
松田 元輝 (まつだ げんき)
第2回の今回は、Kubernetes上にAPIプラットフォームのベーシックな機能セットを構築する手順を解説します。

はじめに

前回はプラットフォームエンジニアリングの概要を学びました。今回は、実際にAPIプラットフォームを構築してみましょう。PC1台で完結するシンプルな構成なので、ぜひ皆さんも試してみてください。

今回、構築するプラットフォームの機能は以下の通りです。

  • マルチテナント: 複数のチームが同じプラットフォームを利用しても、互いに影響を与えないようにする
  • APIゲートウェイ: Gateway APIを利用してルーティングを行う
  • 簡単なAPI認可: 「Kuadrant」と「Keycloak」でAPIへのアクセスを認証されたクライアントのみに制限する

構築する構成の説明

プラットフォームエンジニアリングでは、プラットフォームの構築・運用をプラットフォームチームが行い、そのプラットフォームを使ってプロダクトチームがアプリケーションを開発・運用します。そのため、今回の構成もプラットフォームチームが構築する部分と、プロダクトチームが構築する部分に分けています。

今回の構成は以下の通りです。

構成図

【補足】実際の通信経路はこの通りにはなりませんが、まずはこのようなイメージで理解していただくと分かりやすいです。通信経路に関して違和感を覚えた方は、実際に構築して確認してみましょう。

構成要素は以下の通りです。

  • Kubernetes: コンテナのデプロイや管理を自動化するプラットフォーム。Namespaceによるマルチテナントを実現する。また、Operatorによりプラットフォームの機能追加を容易にする
  • Gateway API: 柔軟なAPIゲートウェイ機能をKubernetes上で提供する
  • Gateway: Gateway APIで定義されるリソース。APIゲートウェイの本体となり、外部からの通信を受け付ける
  • HTTPRoute: Gateway APIのリソース。Gatewayへのリクエストを各Serviceへルーティングするルールを定義する
  • Kuadrant: Gateway APIに認証認可や流量制御などの機能を追加するプロダクト
  • Authorino: Kuadrantの認証認可エンジン。APIリクエストに対する認可ポリシーの適用などを行う
  • AuthPolicy: Kuadrantで利用する認証認可ポリシー定義用のリソース
  • Keycloak: オープンソースのIDアクセス管理システム。Kuadrantと連携し、APIの認可を提供する

今回はKubernetes上にAPIプラットフォームを構築します。主なNamespaceの構成は以下の通りです。

  • shared-gateway: テナント間で共有されるGatewayリソースをデプロイする。プラットフォーム上で提供される機能へのアクセスは全てこのGatewayを通るよう構成する
  • kuadrant-system: Kuadrantのコンポーネントをデプロイする
  • keycloak: 認証認可機能を提供するKeycloakをデプロイする
  • echo(テナント): プロダクトチームのアプリケーションをデプロイする

shared-gateway Namespaceには、テナント間で共有して利用するGatewayリソースがあります。プラットフォームへの通信はすべてこのGatewayを経由させます。プラットフォームチームはこのGatewayに対して、デフォルトのセキュリティ設定や流量制御などを集中管理できます。

プロダクトチーム用のNamespaceには、各チームのアプリケーションとHTTPRouteリソースをデプロイします。HTTPRouteリソースでは、アプリケーションへのルーティングを定義します。

さらに、認証・認可機能を提供するために、keycloak NamespaceにKeycloakをデプロイします。

今回、プロダクトチームのアプリケーションへの通信は認証されたクライアントのみに制限します。クライアントが認証済みかどうかはトークンイントロスペクションで確認します。トークンイントロスペクションとはアクセストークンの有効性を確認する手段の1つで、トークンの失効や状態変化を即時反映できる仕組みです。GatewayからAuthorinoを経由してKeycloakのイントロスペクションエンドポイントを利用し、アクセストークンの有効性を確認します。

手順の前提

プラットフォームそのものの構築方法とプラットフォームの利用方法を区別するため、以降の手順では「プラットフォームチームの作業」と「プロダクトチームの作業」を分けて説明します。

なお、手順で使用する各種マニフェストはGitHubリポジトリにもまとめて掲載しています。本記事にもマニフェストの内容を記載していますが、コピー&ペーストの手間を省きたい場合はリポジトリをクローンして利用すると便利です。

Kubernetesクラスタの準備
(プラットフォームチームの作業)

今回はAWSのEC2上でKubernetesクラスタを構築したため、ロードバランサーの関係で「K3s」を利用します。ローカルのPCで構築する場合は「kind」や「Docker Desktop」を利用しても問題ありません。

K3sは軽量なKubernetesディストリビューションで、特にリソースが限られた環境や開発環境での利用に適しています。また、デフォルトで「ServiceLB」というロードバランサーがインストールされているため、Gateway APIの検証にも便利です。

K3sのインストール

K3sのインストールは以下のコマンドで行います。今回はGateway APIを使いたいので、ノードのIPアドレスを使ってしまうTraefik(Ingress Controller)を無効化しておきます。

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" sh -s - --disable traefik

Docker Hubのプル制限対策

コンテナイメージを取得する際には「Docker Hub」を使うことが多いですが、Docker Hubにはイメージのプル制限があります。プル制限が気になる場合はミラーレジストリを設定することをおすすめします。

K3sのミラーレジストリの設定は/etc/rancher/k3s/registries.yamlで行います。

cat << EOF | sudo tee /etc/rancher/k3s/registries.yaml > /dev/null
mirrors:
  docker.io:
    endpoint:
      - "https://mirror.gcr.io"

設定したらK3sを再起動しましょう。

sudo systemctl restart k3s

kubeconfigの設定

K3sのkubeconfigは/etc/rancher/k3s/k3s.yamlに生成されるので、それを〜/.kube/配下にコピーします。

sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/

このままだとk3s.yamlの所有者はrootユーザーになっており、自分の操作ユーザーでは読み取れません。そのため、k3s.yamlの所有者を自分のユーザーに変更します。

sudo chown $(whoami) ~/.kube/k3s.yaml

環境変数KUBECONFIGに~/.kube/k3s.yamlを設定します。

export KUBECONFIG=~/.kube/k3s.yaml

既存のkubeconfigと共存させたい場合は、以下のように「:」でつなぐこともできます。

export KUBECONFIG=~/.kube/config:~/.kube/k3s.yaml

Gateway APIのインストール
(プラットフォームチームの作業)

Gateway APIはあくまでKubernetesが定義したフレームワークであり、実際のゲートウェイ実装は「NGINX Gateway Fabric」や「Envoy Gateway」「Istio」などのプロジェクトが提供します。今回はEnvoy Gatewayを利用します。

Envoy Gatewayをhelmでインストールします。また、Kuadrantを利用するため、EnvoyPatchPolicyを有効にします。

helm upgrade --install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.4.1 \
  --set config.envoyGateway.extensionApis.enableEnvoyPatchPolicy=true \
  -n envoy-gateway-system --create-namespace

以下のように、Envoy Gatewayがデプロイされていることが確認できます。

$ kubectl get po -n envoy-gateway-system
NAME                             READY   STATUS      RESTARTS   AGE
eg-gateway-helm-certgen-l7s9d    0/1     Completed   0          114s
envoy-gateway-7c88d4fff4-lg4dd   1/1     Running     0          23s

Gateway Classのマニフェストを作成します。Gateway Classは、Gateway APIの実装(Envoy GatewayやNGINX Fabric Gatewayなど)を指定するためのリソースです。Gatewayを作成する際にGateway Classを指定することで、どの実装を使うかを決定します。 今回はEnvoy Gatewayを指定します。

# envoy-gateway/gateway-class.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

作成したGateway Classをデプロイします。

kubectl apply -f envoy-gateway/gateway-class.yaml

共有Gatewayの作成
(プラットフォームチームの作業)

次に、共有Gatewayを作成します。共有Gatewayは、複数のプロダクトチームが共通で利用するAPIゲートウェイです。これにより、各プロダクトチームは自分たちのNamespace内でHTTPRouteを定義し、共有Gatewayを通じて外部と通信できるようになります。

共有Gatewayデプロイ

shared-gateway Namespaceを作成します。

kubectl create namespace shared-gateway

共有Gatewayのマニフェストを作成します。

# shared-gateway/shared-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: shared-gateway
spec:
  gatewayClassName: envoy-gateway
  listeners:
    # Keycloak用リスナー
    - name: keycloak
      protocol: HTTP
      port: 80
      # 172.32.4.127 はEC2インスタンスのIPアドレスです。実際の環境に合わせて変更してください。
      hostname: keycloak.172.32.4.127.nip.io
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              kubernetes.io/metadata.name: keycloak
        kinds:
          - kind: HTTPRoute
    # echo API用リスナー(テナント)
    - name: echo
      protocol: HTTP
      port: 80
      hostname: "echo.172.32.4.127.nip.io"
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              kubernetes.io/metadata.name: echo
        kinds:
          - kind: HTTPRoute

このマニフェストでは、2つのリスナーを定義しています。

  • Keycloak用リスナー: KeycloakのAPIにアクセスするためのリスナー。KeycloakのNamespaceにデプロイされたHTTPRouteのみを許可する
  • echo API用リスナー: プロダクトチームが管理するecho APIにアクセスするためのリスナー。echo APIのNamespaceにデプロイされたHTTPRouteのみを許可する

【補足】nip.ioはワイルドカードDNSで、例えばホスト名がecho.172.32.4.127.nip.ioだと172.32.4.127が返ってきます。これにより、/etc/hostsやDNSサーバーを使わずともホスト名を使ったルーティングが可能になります。

共有Gatewayをデプロイします。

kubectl apply -f shared-gateway.yaml

以下のように、共有Gatewayが作成されていることが確認できます。172.32.4.127はEC2インスタンスのIPアドレスです。K3sのServiceLBはノードのIPアドレスをServiceに割り当てます。

$ kubectl get gateway -n shared-gateway
NAME             CLASS           ADDRESS        PROGRAMMED   AGE
shared-gateway   envoy-gateway   172.32.4.127   True         5d22h

プロダクトチームのアプリケーションデプロイ
(プロダクトチームの作業)

ここではプロダクトチームは「echo API」というアプリケーションを作っているとしましょう。echo APIの実装はecho-basicです。echo-basicはHTTPリクエストを受け取り、レスポンスを返すだけのシンプルなアプリケーションです。プロダクトチームは自分たちのNamespace echoにアプリケーションをデプロイします。

【補足】今回はKubernetesに対するすべての操作をcluster-admin権限(最も強い権限)で行っていますが、実際にプラットフォームを構築する場合は、プロダクトチームの各開発者に対してNamespace内に絞った権限を付与する必要があります。

echo APIデプロイ

プロダクトチーム用のNamespaceを作成します(プラットフォームチームがNamespaceを作成することもあります)。

kubectl create namespace echo

echo APIのマニフェストを作成します。

# echo/echo-basic.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo-svc
  namespace: echo
  labels:
    example: http-routing
spec:
  ports:
    - name: http
      port: 8080
      targetPort: 3000
  selector:
    app: echo-backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-backend
  namespace: echo
  labels:
    app: echo-backend
    example: http-routing
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo-backend
  template:
    metadata:
      labels:
        app: echo-backend
    spec:
      containers:
        - name: echo-backend
          image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          resources:
            requests:
              cpu: 10m

echo APIをデプロイします。

kubectl apply -f echo/echo-basic.yaml

HTTPRouteを作成して、共有Gatewayを通じてecho APIにアクセスできるようにします。

# echo/httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-route
  namespace: echo
spec:
  hostnames:
  - "echo.172.32.4.127.nip.io"
  parentRefs:
    # 共有Gatewayを指定
    - name: shared-gateway
      namespace: shared-gateway
      sectionName: echo
  rules:
    - name: root
      matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        # echo APIのServiceを指定
        - name: echo-svc
          port: 8080
          kind: Service
          weight: 1

動作確認を行います。

$ curl http://echo.172.32.4.127.nip.io
{
 "path": "/",
 "host": "echo.172.32.4.127.nip.io",
 "method": "GET",
 "proto": "HTTP/1.1",
 "headers": {
  "Accept": [
   "*/*"
  ],
  "User-Agent": [
   "curl/8.5.0"
  ],
  "X-Envoy-External-Address": [
   "10.42.0.1"
  ],
  "X-Forwarded-For": [
   "10.42.0.1"
  ],
  "X-Forwarded-Proto": [
   "http"
  ],
  "X-Request-Id": [
   "eb2f3a73-bb37-4ddc-8686-cf267ecf8f03"
  ]
 },
 "namespace": "echo",
 "ingress": "",
 "service": "",
 "pod": "echo-backend-7564b6f58c-z454r"
}

API認可の設定

ここまで設定すると、プロダクトチームが自分たちのアプリケーションとHTTPRouteをデプロイすることで、共有Gatewayを通じて外部からアクセスできるようになります。しかし、現状では誰でもアクセスできる状態なので、KuadrantとKeycloakを使ってAPIの認可を設定していきます。

Kuadrantインストール(プラットフォームチームの作業)

Kuadrantは、Kubernetes環境におけるAPIのセキュリティ設定や流量制御、トラフィック管理を可能とする拡張機能を提供するプロダクトです。Kuadrantの中で認証認可を行うコンポーネントが「Authorino」です。今回はAPIの認可をKeycloakと連携して行います。

Kuadrantをインストールします。

Kuadrantデプロイ

Kuadrantの前提であるcert-managerをインストールします。

helm upgrade --install \
  cert-manager cert-manager \
  --version v1.15.3 \
  --repo https://charts.jetstack.io \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

次に「Kuadrant Operator」をインストールします。Kuadrant Operatorは、Kuadrantのリソースを管理するためのコントローラーです。

helm upgrade --install \
  kuadrant-operator kuadrant-operator \
  --version 1.2.0 \
  --repo https://kuadrant.io/helm-charts/ \
  --create-namespace \
  --namespace kuadrant-system

Kuadrantのマニフェストを作成します。

# kuadrant/kuadrant.yaml
apiVersion: kuadrant.io/v1beta1
kind: Kuadrant
metadata:
  name: kuadrant
  namespace: kuadrant-system

Kuadrantをデプロイします。ここでデプロイされるのは、実際の通信に認証認可や流量制御などの処理を行うKuadrantコンポーネントです。

kubectl apply -f kuadrant/kuadrant.yaml

Kuadrant OperatorとKuadrantのコンポーネントがデプロイされていることを確認します。

$ kubectl get po -n kuadrant-system
NAME                                                     READY   STATUS    RESTARTS   AGE
authorino-744569f65-h7sds                                1/1     Running   0          13m
authorino-operator-55c46d7d78-vbxt8                      1/1     Running   0          51m
dns-operator-controller-manager-6dcd48c697-dqltr         1/1     Running   0          51m
kuadrant-operator-controller-manager-5d5f9bc775-zxtsj    1/1     Running   0          51m
limitador-limitador-84bdfb4747-tj7wv                     1/1     Running   0          13m
limitador-operator-controller-manager-5d5c4c478f-l6k6n   1/1     Running   0          51m

xxx-operatorはKuadrantのOperatorコンポーネントで、Kuadrantのリソースが作成されたときに対応する処理を行います。authorinoは認証認可を行うコンポーネントです。limitadorは流量制御を行います。

共有Gatewayにデフォルトで全トラフィックを拒否させる
(プラットフォームチームの作業)

Kuadrantの設定が終わり、各プロダクトチームが自分たちのAPIに認可をかけられるようになりました。あとは各プロダクトチームがAuthPolicyを作成し、HTTPRouteに認可設定を行うだけです。

しかし、認可設定をすべて各プロダクトチームに任せてしまって良いのでしょうか。もちろん、本記事を読むような意識の高い方々はきちんと認可設定を行うと思いますが、すべての開発者が高いセキュリティ意識を持っているわけではありません。そこで、各プロダクトチームが認可設定を忘れた場合にトラフィックが拒否されるよう、共有Gatewayにデフォルトで全てのトラフィックを拒否するポリシーを適用します。

認証認可のポリシーはKuadrantのAuthPolicyというリソースで定義します。AuthPolicyは、GatewayもしくはHTTPRouteに認証認可のポリシーを適用するためのリソースです。

共有Gatewayに対して全てのトラフィックを拒否するAuthPolicyを作成します。

共有GatewayにAuthPolicyを適用

まず、マニフェストを作成します。

# shared-gateway/authpolicy-deny-all.yaml
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: shared-gateway-auth-policy-deny-all
  namespace: shared-gateway
spec:
  targetRef:
    # ポリシーの適用対象を共有Gatewayにする
    group: gateway.networking.k8s.io
    kind: Gateway
    name: shared-gateway
  defaults:
    strategy: atomic # HTTPRoute側のポリシーで上書きできるようにする
    rules:
      authorization:
        deny-all:
          opa:
            # 全てのトラフィックを拒否するポリシー
            rego: |
              allow = false
      response:
        # レスポンスを設定
        unauthorized:
          headers:
            "content-type":
              value: application/json
          body:
            value: |
              {
                "error": "Forbidden",
                "message": "403 Forbidden: All traffic is denied by the AuthPolicy configuration for the Gateway."
              }

AuthPolicyをデプロイします。

kubectl apply -f shared-gateway/authpolicy-deny-all.yaml

これで、今までアクセスできていたecho APIに対して、全てのトラフィックが拒否されるようになります。実際にアクセスしてみましょう。

$ curl http://echo.172.32.4.127.nip.io
{
  "error": "Forbidden",
  "message": "403 Forbidden: All traffic is denied by the AuthPolicy configuration for the Gateway."
}

この設定を各HTTPRouteに対するAuthPolicyで上書きすることで、初めてアクセスが許可されるように設計します。

Keycloakのインストール(プラットフォームチームの作業)

KeycloakをインストールしてAPIを認可します。KeycloakはオープンソースのIDアクセス管理プロダクトで、APIプラットフォームに認証認可機能を提供します。

Keycloakデプロイ

KeycloakをHelmでインストールします。以下のコマンドを実行して、Keycloakをデプロイします。

helm upgrade --install keycloak \
  oci://registry-1.docker.io/bitnamicharts/keycloak \
  --version 26.3.2 \
  --set auth.adminUser=admin \
  --set auth.adminPassword=admin \
  -n keycloak --create-namespace

今回は管理者ユーザーのパスワードをadminに設定しています。実際の運用で同じことをすると非常に大きなセキュリティホールになるため、実運用では複雑なパスワードを設定するようにしてください。

続いて、KeycloakのHTTPRouteのマニフェストを作成します。

# keycloak/httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: keycloak-route
  namespace: keycloak
spec:
  hostnames:
  - keycloak.172.32.4.127.nip.io
  parentRefs:
    - name: shared-gateway
      namespace: shared-gateway
      sectionName: keycloak
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: keycloak
          port: 80
          kind: Service
          weight: 1

KeycloakのHTTPRouteをデプロイします。

kubectl apply -f keycloak/httproute.yaml

このままでは、共有Gatewayに設定したAuthPolicyにより、Keycloakへのトラフィックも拒否されてしまいます。Keycloakは自身で認証認可機能を持っているので、Keycloakへのアクセスを制御する必要はありません。そこで、Keycloakへの全てのトラフィックを許可するAuthPolicyを作成します。まず、マニフェストを作成します。

# keycloak/authpolicy-allow-all.yaml
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: keycloak-authpolicy-allow-all
  namespace: keycloak
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: keycloak-route
  defaults:
    rules:
      authentication:
        "anonymous":
          anonymous: {}

マニフェストができたらデプロイします。

kubectl apply -f keycloak/authpolicy-allow-all.yaml

Keycloakへアクセスしてみましょう。ホスト名はHTTPRouteを参照してください。

$ kubectl get httproute -n keycloak keycloak-route
NAME             HOSTNAMES                          AGE
keycloak-route   ["keycloak.172.32.4.127.nip.io"]   29m

Webブラウザでhttp://keycloak.172.32.4.127.nip.ioにアクセスします。ユーザー名/パスワードは、Keycloakデプロイ時に設定したadmin/adminです。

Keycloakにログイン

KeycloakのRealm作成(プラットフォームチームの作業)

KeycloakのRealmはKeycloak内でのテナントのようなもので、各Realmごとにユーザーやクライアントを管理できます。今回はecho APIを呼び出すアプリケーション用のRealmを作成します。Realmの名前はechoとします。

まず、Manage realms(下図①)からCreate realm(下図②)をクリックします。

Realmの作成画面を開く

Realm name(下図①)にechoと入力し、Create(下図②)をクリックします。

Realmの作成

Realmを作成したら、自動的にechoに切り替わっているのがKeycloakの画面から確認できます。Keycloakのバージョンによって画面のどこに現在のRealmが表示されるのか異なりますが、v26.3.2では画面左上とManage realmsメニューから確認できます。切り替わってない場合は、Manage realmsメニューからechoを選択してください。

Realmをechoに切り替える

【補足】今回はecho APIを呼び出すアプリケーションを認証するためのRealmのみを作成しました。しかし、実際にプラットフォームを構築する場合はプラットフォームを利用する開発者を認証するためのRealmも作成し、Kubernetesをはじめとする各種プラットフォーム機能への認証認可も行う必要があります。

Keycloakのクライアント作成(プロダクトチームの作業)

Keycloakのクライアントとは、Keycloakが認証認可を提供するアプリケーションやサービスのことを指します。今回作成するクライアントは2つです。1つはAuthorino用のクライアントtoken-introspection、もう1つはecho APIを呼び出すアプリケーション用のクライアントechoです。

まず、Authorino用のクライアントtoken-introspectionを作成します。このクライアントはAuthorinoがKeycloakのイントロスペクションエンドポイントを呼び出すために使用します。 Keycloakの管理画面のClientsメニュー(下図①)からCreate client(下図②)をクリックします。

Clientsメニュー

Client ID(下図①)にtoken-introspectionと入力し、Next(下図②)をクリックします。

クライアントの基本設定

トークンイントロスペクションではクライアント認証を行うため、Client authenticationを有効にし(下図①)、Authentication flowは全てオフにします。Standard flow(下図①)がデフォルトでオンになっているので、チェックを外します。

クライアントのCapability config

Login settingsはデフォルトの設定で問題ありません。

クライアントのLogin settings

token-introspectionクライアント設定のCredentials(下図①)からClient Secret(下図②)をコピーしてメモします。

クライアントシークレットコピー

ここで、Authorino用のクライアントシークレットをKubernetesのSecretとして保存します。Kuadrantは、このSecretを使ってKeycloakのイントロスペクションエンドポイントを呼び出します。ただし、Secretはkuadrant-system Namespaceに作成する必要があります。プラットフォームチームのNamespaceに作成する必要があるため、kuadrant-systemの特定の名前のシークレットのみに権限を付与するなどの工夫が必要です。

kubectl create secret generic keycloak-echo-token-introspection-client-secret \
    -n kuadrant-system \
    --from-literal=clientID=token-introspection \
    --from-literal=clientSecret=<your client secret>

次に、echo API用のクライアントechoを作成します。こちらは、実際にAPIを呼び出す側を認証するためのクライアントです。先ほどと同様に、Clientsメニュー(下図①)からクライアントを作成(下図②)します。

Clientsメニュー

Client ID(下図①)にechoと入力し、Next(下図②)をクリックします。

クライアントの基本設定

今回は単純に「クライアントクレデンシャルズグラント」を使ってトークンを発行します。これは、クライアントIDとクライアントシークレットを使ってアクセストークンを取得するOAuth2の認可フローです。ユーザーの介在なしにアプリケーションが自身を認証し、トークンを取得するために使用されます。

Client authenticationtを有効(下図①)にし、Authentication flowのService accounts roles(下図②)を有効にします。こちらもStandard flow(下図③)がデフォルトでオンになっているので、チェックを外してください。

クライアントのCapability config

Login settingsはデフォルトの設定で問題ありません。

クライアントのLogin settings

echoクライアント設定のCredentials(下図①)からClient Secret(下図②)をコピーしてメモしておいてください。APIを呼び出す際に必要です。

クライアントシークレットコピー

echo APIの認可設定(プロダクトチームの作業)

echo APIの認可設定をします。KuadrantのAuthPolicyを使って、Keycloakのトークンイントロスペクションを利用した認可を設定します。

echo APIにAuthPolicyを適用

HTTPRouteにAuthPolicyを設定すると、Gatewayに届いたトラフィックのうちecho APIへ向かうリクエストだけに、そのAuthPolicyが適用されます。今回の例では、GatewayはAuthorinoを通じてKeycloakにトークンイントロスペクションを行い、トークンの有効性を確認します。

AuthPolicyのマニフェストを作成します。

# echo/authpolicy.yaml
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: echo-authpolicy
  namespace: echo
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: echo-route
  defaults:
    rules:
      authentication:
        "keycloak-introspection":
          oauth2Introspection:
            # Keycloakのイントロスペクションエンドポイントを指定
            endpoint: http://keycloak.172.32.4.127.nip.io/realms/echo/protocol/openid-connect/token/introspect
            credentialsRef:
              # Keycloakから取得したクライアントIDとクライアントシークレットを格納したSecretを指定
              name: keycloak-echo-token-introspection-client-secret
      response:
        unauthenticated:
          headers:
            "content-type":
              value: application/json
          body:
            value: |
              {
                "error": "Unauthorized",
                "message": "401 Unauthorized: token introspection failed."
              }
        unauthorized:
          headers:
            "content-type":
              value: application/json
          body:
            value: |
              {
                "error": "Forbidden",
                "message": "403 Forbidden: policy check failed."
              }

AuthPolicyをデプロイします。

kubectl apply -f echo/authpolicy.yaml

動作確認

クライアントクレデンシャルズグラントを使って、echo APIにアクセスできることを確認します。まず、クライアント認証でKeycloakからアクセストークンを取得してください。

CLIENT_ID=echo
CLIENT_SECRET=<Keycloakから取得したechoクライアントのクライアントシークレット>
ACCESS_TOKEN=$(curl -X POST "http://keycloak.172.32.4.127.nip.io/realms/echo/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET" | jq -r '.access_token')

Keycloakから取得したアクセストークンを使ってecho APIにアクセスします。以下のコマンドを実行し、アクセストークンをAuthorizationヘッダーに設定してリクエストを送ってください。

curl -H "Authorization: Bearer $ACCESS_TOKEN" "http://echo.172.32.4.127.nip.io"

以下のようなレスポンスが返ってくれば、認可が成功しています。

{
 "path": "/",
 "host": "echo.172.32.4.127.nip.io",
 "method": "GET",
 "proto": "HTTP/1.1",
 "headers": {
  "Accept": [
   "*/*"
  ],
  "Authorization": [
   "Bearer <Keycloakから取得したechoクライアントのアクセストークン>"
  ],
  "User-Agent": [
   "curl/8.5.0"
  ],
  "X-Envoy-External-Address": [
   "10.42.0.1"
  ],
  "X-Forwarded-For": [
   "10.42.0.1"
  ],
  "X-Forwarded-Proto": [
   "http"
  ],
  "X-Request-Id": [
   "62bc0256-f27c-47e8-a16c-2229681b82e4"
  ]
 },
 "namespace": "echo",
 "ingress": "",
 "service": "",
 "pod": "echo-backend-7564b6f58c-z454r"
}

ソフトウェアのバージョン

今回利用したソフトウェアのバージョンは下表の通りです。

ソフトウェア名 バージョン
K3s v1.32.6+k3s1 (eb603acd)
Envoy Gateway v1.4.1
Kuadrant 1.2.0
cert-manager v1.15.3
Keycloak 26.3.2

まとめ

今回は、Kubernetes上にAPIプラットフォームのベーシックな機能セットを構築する手順を解説しました。

  • チームごとにNamespaceを分離し、マルチテナントを実現
  • Gateway APIでAPIのルーティングを実現
  • Kuadrant(Authorino)+KeycloakでAPIの認可を実現

今回の構成で、プロダクトチームはコンテナのデプロイ、ルーティング、簡単なAPI認可をKubernetesのインターフェースを通じて行うことができるようになりました。

しかし、ドキュメントは整備されていないので、プロダクトチームはプラットフォームチームのサポートなしでは各種リソースを作成することが難しいかもしれません。そのためプラットフォームエンジニアリングの成熟度モデルに当てはめると、インターフェースモデルのレベル1「暫定的である - 機能毎に独自の手順」からレベル2「戦略的である - 標準ツール」に相当します。インターフェースは一貫していますが、ドキュメントやテンプレートが整備されていない状態です。

また、今回作成したプラットフォームの導入前後での開発プロセスの変化を下表にまとめます。変わっていない箇所は今後の連載で改善していきます。

大プロセス 小プロセス プラットフォーム導入前 今回のプラットフォーム導入後
セキュリティ要件定義 認証要件の設計 手動 Keycloakで標準化
認可要件の設計 手動 Authorinoで部分自動化
OpenAPI仕様作成 OpenAPIの設計 手書き 手書き
セキュリティスキーマの定義 手書き Keycloak定義
API仕様の公開 メール送付など メール送付など
実装 APIの実装 手動実装 手動実装
認証・認可の実装 コード実装 Authorinoで自動化
ルーティング設定 手動設定 Gateway APIで宣言管理
デプロイと運用 デプロイ 手動 Kubernetesで自動化
監視・可観測性の導入 ログ出力と手動確認 ログ出力と手動確認
ポリシー準拠チェック 手動レビュー 手動レビュー
認可ポリシーの連携 手動実装 手動実装

次回以降では、この構成をベースにオブザーバビリティやCI/CDなど、さらに多くのプラットフォームの機能を追加していきます。まずは本記事の内容を実際に手を動かして試し、APIプラットフォームの基礎を体験してみてください。

著者
松田 元輝 (まつだ げんき)
株式会社日立製作所 AIサービス本部 アーキテクチャセンタ
2014年4月に日立製作所に入社し、CSI Driverの開発やレガシーアプリのモダナイズ、生成AIプラットフォームの開発を行う。Kubernetesをはじめとするクラウドネイティブ技術が好き。
X: https://x.com/maki_644

連載バックナンバー

システム開発技術解説
第2回

Kuberenetes上にベーシックな機能セットのAPIプラットフォームを作ってみよう

2025/10/7
第2回の今回は、Kubernetes上にAPIプラットフォームのベーシックな機能セットを構築する手順を解説します。
システム開発技術解説
第1回

「プラットフォームエンジニアリング」の基礎と「Kubernetes」開発基盤の要点を整理する

2025/7/18
第1回の今回は、プラットフォームエンジニアリングの基礎知識と、Kubernetesを前提とした開発基盤の要点について解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています