fbpx

連載: Kubernetesでカスタムコントローラを作ろう! ~第3回 Kubernetesのverbについて~

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

前回に引き続きKubernetesとおしゃべりしていきます。 前回はPodやDeploymentなどのKubernetesに保存されたリソースへアクセスするためのパスについて説明しました。 そして実際にDeploymentのリストを取得をしました。今回はその続きとなっております。

Kubernetesのサブリソースについて

KubernetesはHTTP経由でアクセス可能なRESTのインターフェースを持ちます。 一般的なPOST, PUT, PATCH, DELETE, GETなどでリクエストすることで、Kubernetes上のオブジェクトを作成・更新・削除・取得などの操作が可能です。 そしてPodなどの一部のリソースでは、1つ以上のサブリソースを持ちます。 例えばコンテナのログを取得したり、エフェメラルコンテナを追加するなどもPodのサブリソースから操作するものです。

コンテナのログを取得してみましょう。ログには以下のようなパスでアクセスできます。 /api/v1/namespaces/NAMESPACE名/pods/POD名/log?container=CONTAINER名 前回kube-apiserverへアクセスした時と同様に、kubectl proxyを実行している状態でコマンドを実行してみてください。 例えばkube-system上のcoredns-565d847f94-sqvdsというPodのcorednsコンテナのログを取得する例です。

$ curl "http://127.0.0.1:8001/api/v1/namespaces/kube-system/pods/coredns-565d847f94-sqvds/log?container=coredns"
.:53
[INFO] plugin/reload: Running configuration SHA512 = 591cf328cccc12bc490481273e738df59329c62c0b729d94e8b61db9961c2fa5f046dd37f1cf888b953814040d180f52594972691cd6ff41be96639138a43908
CoreDNS-1.9.3
linux/amd64, go1.18.2, 45b0a11

verbによるアクセス制御

Kubernetesではほとんどのリソースで標準的なHTTPのGET, POST, PUT, DELETE, PATCHなどのverbをサポートしていますが、それらの操作をさらに細かく分類した独自のverbを定義しています。 例えば、getlistと呼ばれるverbがあります。 KubernetesのverbはHTTPリクエストのverbと区別するために小文字で表現されます。 これらはHTTPのGETの処理をさらに細かく分類したものの一部です。 単一のオブジェクトを取得する場合はgetで、複数のオブジェクトを取得する場合はlistとして表現されます。 KubernetesのRBACの仕組みでは、これらverbを用いてRoleやClusterRoleでアクセスコントロールができます。

以下はcorednsのためのClusterRoleリソースの例です。 いくつかのリソースタイプに対して、listやwatchといったverbが利用可能であることが定義されています。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: system:coredns
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - services
  - pods
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch

効率的な変更検知の仕組み

コントローラを作成する上でリソースの変更検知は重要になってきます。 例えばkube-controller-managerに含まれるStatefulSetコントローラは、StatefulSetの.spec.templateにより、作成するPodの内容を決定します。 そしてそれが変更された場合、Podをローリングアップデートします。ほとんどのコントローラでは、こういった変更を検知する必要があります。 変更を検知する方法として、検知したいオブジェクトを取得し、変更があるかどうかを比較するという単純な方法があります。 しかしその方法では、例えばStatefulSetコントローラでは全NamespaceのStatefulSetの変更を見ているため、定期的に全てのStatefulSetのリストを取得し、変更を確認するという方法では非効率です。

Kubernetesでは便利なことにwatchと呼ばれるverbが多くのリソースに対して利用可能です。 これはリソースの変更があったものだけ通知される便利なものです。 Kubernetesのwatchの実態はHTTPのGETリクエストでwatchというクエリパラメータを付与したものです。

ここからwatchを実際にcurlで試してみましょう。 watchを試すために、専用のnamespaceを作成し、そのnamespace上に作成されたdeploymentsをwatchします。 まず初めにテスト用のnamespaceを作成します。

$ kubectl create ns watch-test

watchのテスト用に新しいnamespaceであるwatch-testを作成しました。 これ以降のコマンドはkubectl proxyを実行した状態で、別ターミナルで実行してください。

その時点でのListのresourceVersionを知ることができます。

$ curl -sSfL http://127.0.0.1:8001/apis/apps/v1/deployments | jq -r '.metadata.resourceVersion'
47142

KubernetesのオブジェクトはresourceVersionというものを持ち、この値の変更の有無で変化を検知できます。 resourceVersionはListの結果にも含まれており、そのListのresourceVersionをwatchリクエストに送ることで、そのList以降に発生したイベントを取得することができます。 上記のresourceVersionからされた変更をwatchします。

$ curl -X GET "http://127.0.0.1:8001/apis/apps/v1/namespaces/watch-test/deployments?watch&resourceVersion=47142"

上記コマンドを実行すると、最初は何も表示されないまま、curlコマンドが終了されず、kube-apiserverと接続されたままになっていると思います。 こちらは実行したまま、以下のコマンドをさらに別ターミナルで実行してください。

$ kubectl -n watch-test create deployment --image=nginx nginx

するとcurlを実行していたターミナル上に新しいDeploymentが作成されたことや、作成後Deploymentが何かしらによって修正されたことがイベントとして流れてきているはずです。

{"type":"ADDED","object":{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"nginx","namespace":"watch-test","uid":"1f66911c-be9d-4fe5-8329-72ac8a610227",...(省略)}}}
{"type":"MODIFIED","object":{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"nginx","namespace":"watch-test","uid":"1f66911c-be9d-4fe5-8329-72ac8a610227",...(省略)}}}
...(省略)

この仕組みを使うことで、変更差分だけを効率的に検知できます。

またこの仕組みは多少の切断にも強固な作りになっています。 ここでwatchリクエストを投げていたcurlコマンドを終了し、再度実行してみてください。すると同じwatchのイベントが表示されるはずです。 resourceVersionのおかげで、そのresourceVersionからの変更からwatchのイベントを受信できます。 受信したイベントの中に含まれるresourceVersionを指定すると、そのイベントから受信を再開できます。 通常コントローラ内でwatchを扱う場合、イベントに含まれるこのresourceVersionを内部的に記録することで、どこまでのイベントを処理したかを追えるようにしています。 そのため、一次的にネットワークの切断があった場合でも、自分が処理できたイベントまでのresourceVersionを知っているため、そこからwatchを再開でき、多少のネットワークの切断に強固になっているのです。 詳しくは公式ページのこちらのドキュメントをご参照ください。

最後に

前回に続きKubetnetesのAPIについて説明しました。今回はKubernetesのverbや効率的な変更検知についての仕組みについて説明しました。 どちらもKubernetesのコントローラを作成したり、KubernetesをAPIで操作する上で重要な知識となります。

クリエーションラインではKubernetesに関する様々なトレーニングを提供しております。 詳しくはこちらをご参照ください。

また、この連載の更新通知を受け取りたい方は、Twitterアカウントのフォローや、このページ下部にリンクのあるCL LAB MAIL MAGAZINEへの登録をぜひお願いします!

新規CTA