Kubernetes・コンテナ

Kubernetes APIとは?URLの確認方法・エンドポイント構造とcurl実例【1.37で実測】

Kubernetes APIとは?URLの確認方法・エンドポイント構造とcurl実例【1.37で実測】

Kubernetes APIは、Pod・Service・Deploymentなどクラスタ内のすべてのリソースを操作するHTTPのREST APIで、kube-apiserverが公開しています。kubectlもCI/CDツールもOperatorも、このAPIのURLへリクエストを送ってクラスタを動かしています。この記事では、APIサーバーのURLを手元・Pod内・EKS/GKE/AKSで調べる方法、/api/v1と/apisから始まるパスの読み方、curlで叩いたときの401と403の違いを、kube-apiserver v1.37.0を手元で起動して取った実際の応答とあわせて整理します。Kubernetesそのものの全体像はKubernetes(クバネティス)とは?仕組み・メリット・デメリットとDockerとの違いをわかりやすく解説が入口です。

まとめ:Kubernetes APIのURL・パス・認証と拡張の要点

  • URL:手元はkubeconfigのserver欄(kubectl cluster-infoで表示)、Pod内はhttps://kubernetes.default.svc。自己構築クラスタの既定ポートは6443。
  • パス:コアは/api/v1、それ以外は/apis/<group>/<version>。Namespacedのリソースだけ/namespaces/{ns}が挟まる。
  • 処理順:認証、認可(RBAC)、Admission、etcd保存。不正なトークンは401、権限不足は403。資格情報なしの要求は、匿名認証が有効ならsystem:anonymousとして403、無効なら401。
  • バージョン:GAはメジャー版の中で削除されない。Betaは非推奨から9か月または3マイナーの長い方で提供終了。削除済みのURLは404になる。
  • 使い分け:人はkubectl、プログラムはクライアントライブラリ、curlは学習と障害の切り分けに限る。
  • 拡張:CRDで独自リソースを足すか、API集約で別サーバーを/apis配下に組み込む。

Kubernetes APIの位置づけ:kube-apiserverが公開するREST API

Kubernetes APIは、リソースの取得をGET、作成をPOST、全体の置き換えをPUT、部分更新をPATCH、削除をDELETEで行うRESTful APIです。これに変更を流し続けるwatchが加わり、コントローラーやスケジューラーはwatchでリソースの変化を受け取って動きます。RESTの原則そのものはRESTとは?REST APIの仕組みと6原則・SOAP/GraphQLとの違いを実装目線で解説で整理しています。

APIを処理するkube-apiserverは、クラスタの状態を保存するetcdに読み書きできる唯一のコンポーネントです。スケジューラーもkubeletもetcdには触れず、必ずAPIサーバーを経由します。混同されやすいのが、コンテナ内のアプリがServiceやIngressで公開する業務用のAPIです。Kubernetes APIはクラスタを管理するための別の窓口で、URLも認証も別物です。

リクエストの処理順:認証・認可・Admissionとetcd保存

たとえばPOST /api/v1/namespaces/default/podsでPodを作るリクエストは、公式「Controlling Access to the Kubernetes API」のとおり次の順に通ります。

  1. 認証:クライアント証明書、Bearerトークン、OIDCなどで送信者を確かめる。
  2. 認可:主にRBACで、その送信者がその操作をしてよいかを判定する。設計の考え方はRBACとは?ロールベースアクセス制御の仕組みと導入判断を実装視点で解説と共通です。
  3. Admission:既定値の補完やサイドカーの注入(Mutating)と、ポリシー違反やクォータ超過の拒否(Validating)を行う。旧PodSecurityPolicyもこの段の仕組みで、v1.25で削除されPod Security Admissionに置き換わった。
  4. etcd保存:通過したオブジェクトだけが保存され、watchしている各コントローラーへ変更が届く。

401と403の見分け方:kube-apiserver v1.37.0での実際の応答

トークン認証とRBACを有効にしたkube-apiserver v1.37.0に、同じURL(/api/v1/namespaces/default/pods)を4通りで送った結果です。

送り方 HTTP 応答のmessage
Authorizationヘッダ無し 403 User “system:anonymous” cannot list…
存在しないトークン 401 Unauthorized
正しいトークン・権限無し 403 User “dev-user” cannot list…
system:mastersのトークン 200 PodList

読み方は3つです。401は「誰か分からない」、403は「誰かは分かったが許可が無い」。今回の環境では匿名認証が有効なため、ほかの資格情報も送らずにトークンを省くと、匿名ユーザーとして扱われて403になりました。匿名認証が無効な環境では401になります。403のmessageにsystem:anonymousと出ていたら、権限ではなくトークンの渡し方を疑ってください。なお同じ匿名リクエストでも/version・/livez・/readyzは200で返りました。既定のRBACが疎通確認用のパスを未認証のユーザーにも開けているためです。

Admissionの使い分け:Webhookと組み込みのCELポリシー

独自のAdmissionルールを足す方法は、外部サーバーを呼ぶAdmission Webhookと、APIサーバー内でCEL式を評価するポリシーの2つです。ValidatingAdmissionPolicyはv1.30で、MutatingAdmissionPolicyはv1.36でGAになり、v1.37.0では両方がadmissionregistration.k8s.io/v1で提供されていることをkubectl api-resourcesで確認しました。ラベル必須やレプリカ数の上限のように、リクエストの中身だけで判定できるルールはCELポリシーで書けます。Webhookサーバーの運用が要らず、そのサーバーが落ちてAPIリクエストが止まる事故も起きません。外部のデータを参照する判定や、OPA・Kyvernoのような既存のポリシーエンジンを使う場合はWebhookを残します。KyvernoがCELへ寄せている動きはKyvernoとは?Kubernetesのポリシー適用の仕組みとCEL移行を解説で扱っています。

APIサーバーのURLを確認する方法:kubeconfig・Pod内・クラウド別

調べる場所は、どこから接続するかで3つに分かれます。

手元の端末:kubeconfigとkubectl cluster-info

kubectlの接続先は、kubeconfig(既定は~/.kube/config)のclusters[].cluster.serverに書かれています。kube-apiserverの--secure-portの既定値は6443なので、自己構築クラスタではhttps://<IP>:6443の形になりやすく、マネージドではポート表記の無いURLが入ります。

# 現在のコンテキストが指すAPIサーバーのURL
kubectl cluster-info
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'

# 登録済みの全クラスタの名前とURL
kubectl config view -o jsonpath='{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

最後のコマンドは公式「Access Clusters Using the Kubernetes API」の例を短くしたものです。複数のクラスタを切り替える環境では、操作の前にこの一覧で向き先を確かめます。

Pod内:kubernetes.default.svcとServiceAccountのトークン

公式「Accessing the Kubernetes API from a Pod」によると、APIサーバーはdefault Namespaceのkubernetes Serviceとして公開され、kubernetes.default.svcで名前解決できます。環境変数KUBERNETES_SERVICE_HOSTとKUBERNETES_SERVICE_PORT_HTTPSにも同じ宛先が入るので、URLを設定ファイルに書く必要はありません。

SA=/var/run/secrets/kubernetes.io/serviceaccount
NS=$(cat $SA/namespace)
curl --cacert $SA/ca.crt \
  -H "Authorization: Bearer $(cat $SA/token)" \
  "https://kubernetes.default.svc/api/v1/namespaces/$NS/pods?limit=5"

公式ドキュメントは、証明書がkubernetes.default.svcというホスト名に対して有効である保証は無く、$KUBERNETES_SERVICE_HOSTのホストかIPには有効な証明書が提示されると注記しています。証明書エラーが出たら宛先を環境変数側へ切り替えます。

EKS・GKE・AKS:クラウドCLIによるエンドポイント取得

kubeconfigを作る前の段階や、CIで接続先を組み立てる場合は各クラウドのCLIから取ります。返る値の形式がそろっていません。

# Amazon EKS
aws eks describe-cluster --name <クラスタ名> --query cluster.endpoint --output text
# GKE
gcloud container clusters describe <クラスタ名> --location=<リージョンまたはゾーン> --format="value(endpoint)"
# AKS:公開FQDN
az aks show -g <リソースグループ> -n <クラスタ名> --query fqdn -o tsv
# プライベートFQDNを確認する場合
az aks show -g <リソースグループ> -n <クラスタ名> --query privateFqdn -o tsv
サービス 返る値 URLにするには
Amazon EKS https付きのURL そのまま使う
GKE IPアドレスのみ https://を前に付ける
AKS ホスト名(FQDN)のみ https://を前に付ける

EKSのdescribe-clusterリファレンスは、クラスタがACTIVEになるまでエンドポイントとCAデータを取得できないと書いています。GKEはgcloud container clusters describe、AKSはaz aks showが入口です。AKSの料金や作成手順はAKS(Azure Kubernetes Service)とは?料金の実額・Automaticとの違い・az aks createの手順【2026年10月】にまとめています。

エンドポイントのURL構造:/apiと/apis・サブリソース・クエリ

パスはグループとスコープで決まります。マニフェストのapiVersionとkindを手掛かりに、ディスカバリーでリソース名とスコープを確認するとURLを組み立てられます。

# コアグループ・Namespacedの一覧
/api/v1/namespaces/{ns}/pods
# 名前付きグループ・Namespacedの個別
/apis/apps/v1/namespaces/{ns}/deployments/{name}
# 全Namespace横断の一覧
/apis/apps/v1/deployments
# クラスタースコープ(namespacesが入らない)
/api/v1/nodes/{name}
# サブリソース
/api/v1/namespaces/{ns}/pods/{name}/log

コアグループ(Pod・Service・Node・Secretなど)だけが/api/v1で、apps・batch・networking.k8s.ioなどの名前付きグループは/apis/<group>/<version>です。API上のリソース種別は、グループ・バージョン・リソース名(GVR)で特定します。個々のオブジェクトを指定するには、さらに名前と、Namespacedの場合はNamespaceが必要です。マニフェストのapiVersionはこのグループとバージョンをつないだ値で、Deploymentならapps/v1、Podならグループが空なのでv1だけになります。

サブリソースとRBAC:pods/logはpodsと別の権限

個別リソースの後ろに続く/status・/log・/scale・/execはサブリソースで、RBACでは別の権限として扱われます。Podのget・list・watchだけを許したRoleを付けたServiceAccountのトークンで試すと、Pod一覧は200、同じNamespaceの/pods/<name>/logは403でした。kubectl auth can-i get pods --subresource=logもnoを返します。ログは読ませるがexecはさせない、という線引きはこの仕組みで作ります。

一覧の分割取得とwatch:limit・continue・resourceVersion

公式「Kubernetes API Concepts」にあるとおり、大きな一覧はlimitとcontinueで分割して取ります(分割取得はv1.29でGA)。ConfigMapを5個作って?limit=2で取ると、2件とremainingItemCount: 3、次ページ用のcontinueトークンが返りました。kubectlもkubectl get podsの裏で?limit=500を付けて分割取得しています。

変更を受け取り続けるには?watch=1を付けます。fieldSelector=metadata.name%3Dcm1で1件に絞ってwatchし、途中でそのConfigMapを書き換えると、ADDED(resourceVersion 222)に続いてMODIFIED(同244)が1行ずつ流れてきました。切断後は最後に受け取ったresourceVersionから再開できますが、履歴の保持範囲を過ぎると410 Goneになります。その場合は一覧を再取得して状態を同期し、その一覧のresourceVersionからwatchを再開します。過去の全イベントの再取得は保証されません。絞り込みにはlabelSelectorとfieldSelectorの2種類があります。

疎通確認と仕様取得:/livez・/readyz・/openapi/v3

/versionはサーバーの版、/livezと/readyzはAPIサーバー自身の死活と受付可否を返します。公式「Kubernetes API health endpoints」によれば/healthzはv1.16から非推奨で、機械判定ではHTTPステータスコードを見ます。人が中身を見るならkubectl get --raw='/readyz?verbose'で、項目ごとに[+]etcd okのような行が並びます。

APIの機械可読な仕様は/openapi/v3から取れます。英語版の公式ページではOpenAPI v3はv1.27から安定版(機能ゲートは撤去済み)ですが、2026年10月時点の日本語版ページは「Kubernetes v1.37では、OpenAPI v3によるAPI仕様をベータサポートとして提供しています」のまま更新されていません。版に関わる記述は英語版を正としてください。OpenAPIの読み方はOpenAPIとは?Swaggerとの違い(仕様とツール)とYAMLの書き方・3.0〜3.2の差分が参考になります。

kubectlが送っているHTTPリクエスト:詳細ログ・rawモード・api-resources

kubectlは、kubeconfigのURLと資格情報を使ってAPIを呼ぶクライアントです。kubectl get pods -v=6で実際のリクエストが見えます。v1.37.0では次の3本が出ました。

verb="GET" url="https://127.0.0.1:36443/api?timeout=32s" status="200 OK"
verb="GET" url="https://127.0.0.1:36443/apis?timeout=32s" status="200 OK"
verb="GET" url="https://127.0.0.1:36443/api/v1/namespaces/default/pods?limit=500" status="200 OK"

先の2本は、どのリソースがどのグループにあるかを調べるディスカバリーです。v1.30でGAになったAggregated discoveryにより、/apisへの1回の要求で、名前付きグループのリソース一覧(今回のv1.37.0環境では21グループ)が返ります。これはkubectlが集約ディスカバリー用のAcceptヘッダを送るためで、ヘッダを指定しないcurlの要求では非集約形式が返ります。コアグループは別途/apiから取得します。逆に任意のパスを取りたいときはkubectl get --raw /apis/apps/v1を使います。

リソースの一覧はkubectl api-resourcesです。アドオンを入れていないv1.37.0では71種類で、NAMESPACED列がtrueのものが34、false(クラスタースコープ)が37でした。APIVERSION列がそのままマニフェストのapiVersionとURLのグループ部分に対応します。正確なフィールドはkubectl explain deploymentで引けます。

curlで直接叩く手順:URL・CA証明書・トークン

トークン発行のkubectl create tokenはv1.24で追加されました。--durationを省くと有効期間はサーバーが決め、検証環境では3600秒、--duration=10mでは600秒のトークンが出ました。

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
kubectl config view --minify --raw --flatten \
  -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
TOKEN=$(kubectl create token default -n default)

curl --cacert ca.crt -H "Authorization: Bearer $TOKEN" "$APISERVER/version"
curl --cacert ca.crt -H "Authorization: Bearer $TOKEN" \
  "$APISERVER/api/v1/namespaces/default/pods?limit=5"

2本目は403になるのが普通です。default ServiceAccountにはPod一覧の権限が無く、認証は通って認可で止まっています。試す場合は、default NamespaceのPodに対するlistを許すRoleを作り、RoleBindingでdefault ServiceAccountに関連付けます。検証後は作成したRoleBindingとRoleを削除します。認証を丸ごと任せたいならkubectl proxyを起動し、curl http://localhost:8001/api/v1/namespaces/default/podsのように既定ポート8001へ送ります。

プログラムから使う:公式クライアントライブラリ

アプリから呼ぶなら生のHTTPを組み立てず、公式クライアントライブラリ(Goのclient-go、Pythonのkubernetesなど)を使います。kubeconfigやPod内のServiceAccountからURLと資格情報を読み込み、トークンの更新も任せられます。

# pip install kubernetes
from kubernetes import client, config

config.load_kube_config()          # Pod内なら config.load_incluster_config()
print(client.Configuration.get_default_copy().host)   # 接続先APIサーバーのURL

v1 = client.CoreV1Api()
for pod in v1.list_namespaced_pod("default", limit=5).items:
    print(pod.metadata.name, pod.status.phase)

Server-Side Apply:フィールドごとの所有者と競合

kubectl apply --server-sideは、差分の計算をAPIサーバー側で行うServer-Side Apply(v1.22でGA)を使います。APIサーバーはフィールドごとに書いた主体(マネージャー)をmetadata.managedFieldsに記録します。ConfigMapのdata.colorをkubectlで適用したあと、別のマネージャー名(--field-manager=other-tool)で同じフィールドを書き換えようとすると、Apply failed with 1 conflict: conflict with "kubectl": .data.colorで止まりました。CIがServer-Side Applyで、HPAやOperatorなど別のマネージャーが持つフィールドの値を変えようとすると、この競合として表に出ます。kubectl editやapply以外のPUT・PATCH(managedFields上はUpdate)は所有権の競合では拒否されず、そのまま上書きして所有者が入れ替わります。上書きしてよいと判断したときだけ--force-conflictsを付けます。

APIグループとバージョン:Alpha・Beta・GAと非推奨ポリシー

APIは安定度に応じてAlpha、Beta、GAと進みます。公式「Kubernetes Deprecation Policy」のRule #4aは次のとおりです。

段階 既定 削除の扱い
Alpha 無効 予告なく削除され得る
Beta v1.24以降の新規Betaは無効 非推奨から9か月か3マイナーの長い方で終了
GA(v1など) 有効 メジャー版の中では削除されない

Betaは導入から9か月または3マイナーの長い方を超えないうちに非推奨にされる決まりです。v1.24より前からある既存のBetaは既定で有効のまま残っています。

削除済みAPIのURL:404応答と移行先

公式「Deprecated API Migration Guide」に削除の実例が並んでいます。extensions/v1beta1のDeploymentはv1.16、networking.k8s.io/v1beta1のIngressとCRDのapiextensions.k8s.io/v1beta1はv1.22、flowcontrol.apiserver.k8s.io/v1beta3はv1.32で提供を終えました。v1.37.0にこのうちDeployment・Ingress・FlowSchemaの旧URLを送ると3本とも404で、/apis/apps/v1/.../deploymentsは200です。古いURLを直書きしたスクリプトは、アップグレードした日から404を返し始めます。

削除される前に気づく手段は2つあります。非推奨のAPIを呼ぶと応答にWarningヘッダが付き、kubectlは画面に警告を出します。もう1つはAPIサーバーのメトリクスapiserver_requested_deprecated_apisで、どのグループ・バージョンが呼ばれ、どの版で削除予定かが記録されます。どちらもv1.19で入った仕組みです。

上流でサポートされているのは直近3マイナーで、2026年10月31日時点では1.35〜1.37です。1.34は2026年10月27日にサポートを終え、最新のパッチは2026年10月28日にGitHubで公開された1.37.2です。版ごとの期限と確認方法はKubernetesの最新バージョンは1.37|確認方法・EOL一覧とアップグレード手順で更新しています。

Kubernetes APIの拡張:CRDとAPI集約レイヤー

APIを拡張する方法は2つあります。CustomResourceDefinition(CRD)は、マニフェストを適用するだけでAPIサーバー自身に新しいリソースを足す方法です。apiextensions.k8s.io/v1はv1.16でGAになりました。手順の一次情報は公式「Extend the Kubernetes API with CustomResourceDefinitions」です。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: applications.example.com
spec:
  group: example.com
  scope: Namespaced
  names:
    plural: applications
    singular: application
    kind: Application
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          required: ["spec"]
          properties:
            spec:
              type: object
              properties:
                image: { type: string }
              required: ["image"]

適用すると/apis/example.com/v1/namespaces/{ns}/applicationsが生え、get・list・watch・create・patchなど8種類の操作が使えるようになりました。spec: {}のまま作ろうとするとspec.image: Required value、specごと省くとspec: Required valueで拒否されます。apiextensions.k8s.io/v1のCRDは構造的スキーマが必須ですが、requiredや値の制約を書かなければ、その条件に反する入力は保存されてしまいます。

CRDを定義しただけでは、オブジェクトが保存されるだけで何も動きません。作成を検知して処理するコントローラー(Operator)が要ります。Goでの雛形づくりはKubebuilderとは?kubectlとの違い、コントローラーとOperatorの違いと開発手順【v4.16】で扱っています。

もう1つのAPI集約レイヤーは、自作のAPIサーバーをAPIServiceで登録し、/apis/<group>へのリクエストをそのサーバーへ中継させる方法です。kubectl topが使うmetrics.k8s.io(metrics-server)が代表例です。etcd以外に保存したい、取得のたびに計算したいといった要件が無ければ、運用するサーバーが増えないCRDを選びます。

判断基準:呼び出し方とエンドポイントの公開範囲

手段 採用する場面 見送る場面
kubectl 人の日常操作・CIのデプロイ アプリからの常時呼び出し
クライアントライブラリ Operator・社内ツール・Pod内アプリ 一度きりの調査
curl直叩き 学習・障害の切り分け 本番の定常処理

curlを本番の定常処理に組み込むのはやめるべきです。トークンの更新、CA証明書の管理、非推奨APIへの追従を自前で抱えることになり、前述の削除済みURLのように版を上げた日に止まります。

APIサーバーのエンドポイントをインターネットに出すかも決めておきます。運用者の接続元を固定できるなら許可IPを絞り、社内ネットワークやVPN経由に限れるならプライベートエンドポイントにします。クラウドの外にあるCIから接続元を絞れない場合だけ、パブリックのまま短い有効期間のトークンと最小権限のRBACで守ります。URLが漏れても認証と認可の2段で止まることが前提です。クラスタ構築からエンドポイントの公開範囲、EKS・GKE・AKSの選定までの相談はインフラ構築(AWS・Google Cloud・Azure)で受けています。手元で同じ実験をしたい場合は、kindでクラスタを作るk8sをローカルで動かす手順|kindでクラスタ構築からDeployment公開・撤収までが使えます。

よくある質問

Kubernetes APIとkubectlの違いは?

Kubernetes APIはクラスタ側の窓口(kube-apiserverが公開するHTTPのAPI)で、kubectlはそれを呼ぶCLIクライアントです。kubectl get pods -v=6を付けると、送っているURLとHTTPメソッドが表示されます。

Kubernetes APIサーバーのURLとポート番号はどこで分かる?

手元ではkubectl cluster-infoか、kubeconfigのserver欄で分かります。kube-apiserverの既定ポートは6443で、EKS・GKE・AKSはポート表記の無いHTTPSのURLです。Pod内からはhttps://kubernetes.default.svcか環境変数KUBERNETES_SERVICE_HOSTを使います。

curlでKubernetes APIを叩くと403になるのはなぜ?

認証は通ったが、そのユーザーやServiceAccountに操作の権限が無いためです。messageにsystem:anonymousと出ている場合は、トークンが送られていないか、Authorizationヘッダの書き方が誤っています。トークン自体が不正なら403ではなく401が返ります。

apiVersionには何を書けばいい?

リソースが属する「グループ/バージョン」を書きます。Pod・Service・ConfigMapはv1、Deployment・StatefulSetはapps/v1、Ingress・NetworkPolicyはnetworking.k8s.io/v1、CronJobはbatch/v1です。kubectl api-resourcesのAPIVERSION列で確認できます。

Alpha版のAPIを本番で使ってよい?

使わないでください。Alphaは既定で無効で、予告なく変更・削除されます。本番はGAを使い、Betaを使う場合も非推奨から9か月または3マイナーで提供が終わる前提で、アップグレードのたびにリリースノートとapiserver_requested_deprecated_apisを確認します。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.09 テックブログ エディオン不正アクセス第2報:カード番号の対象確認とログ・決済画面の改ざん対策
  2. 2026.11.09 テックブログ LANSCOPE不正アクセス第2報:製品への影響と偽連絡の確認、停止中のライセンス認証の窓口
  3. 2026.11.10 テックブログ Claude Fable 5.2とは:料金・破壊的変更4点とFable 5.1からの移行手順【2026年11月】
  4. 2026.11.10 テックブログ ニトリの情報漏洩:最大3,762万件の対象確認となりすまし連絡の見分け方
  5. 2026.11.09 テックブログ ステップゴルフ不正アクセス:口座情報の対象確認と偽連絡・引き落としの注意点

RELATED POSTS 関連記事

目次