1652947500
Kubernetesは近年デファクトスタンダードになり、DevOpsエンジニアと開発者の両方が日常的にKubernetesを使用しています。ただし、実行するタスクの多くは同じで、退屈で、自動化が簡単です。
多くの場合、一連のコマンドを使用して簡単なシェルスクリプトを作成するのは簡単kubectl
ですが、より複雑な自動化タスクの場合、bashだけでは不十分であり、Pythonなどの適切な言語の能力が必要です。
そのため、この記事では、Kubernetes Pythonクライアントライブラリを活用して、煩わしいKubernetesタスクを自動化する方法を見ていきます。
遊び場
Kubernetesクライアントでのプレイを開始する前に、まず、安全にテストできるプレイグラウンドクラスターを作成する必要があります。ここからインストールできるKinD(DockerのKubernetes)を使用します。
次のクラスター構成を使用します。
# kind.yaml
# https://kind.sigs.k8s.io/docs/user/configuration/
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: api-playground
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
上記の構成からクラスターを作成するには、次のコマンドを実行できます。
kind create cluster --image kindest/node:v1.23.5 --config=kind.yaml
kubectl cluster-info --context kind-api-playground
# Kubernetes control plane is running at https://127.0.0.1:36599
# CoreDNS is running at https://127.0.0.1:36599/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# api-playground-control-plane Ready control-plane,master 58s v1.23.5
# api-playground-worker Ready <none> 27s v1.23.5
# api-playground-worker2 NotReady <none> 27s v1.23.5
# api-playground-worker3 NotReady <none> 27s v1.23.5
クラスタが稼働している状態で、クライアントライブラリもインストールする必要があります(オプションで、仮想環境内に)。
python3 -m venv venv
source venv/bin/activate
pip install kubernetes
認証
Kubernetesクラスタ内でアクションを実行するには、最初に認証する必要があります。
長期間有効なトークンを使用するため、認証フローを繰り返し実行する必要はありません。ServiceAccount
有効期間の長いトークンは、 :を作成することで作成できます。
kubectl create sa playground
kubectl describe sa playground
Name: playground
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: playground-token-v8bq7
Tokens: playground-token-v8bq7
Events: <none>
export KIND_TOKEN=$(kubectl get secret playground-token-v8bq7 -o json | jq -r .data.token | base64 --decode)
サービスアカウントを使用すると、1人のユーザーに関連付けられないという利点もあります。これは、自動化の目的で常に推奨されます。
上記の出力からのトークンは、リクエストで使用できます。
curl -k -X GET -H "Authorization: Bearer $KIND_TOKEN" https://127.0.0.1:36599/apis
現在認証されていますが、多くのことを行う権限はありません。したがって、次に、を作成しRole
てバインドし、ServiceAccount
リソースに対してアクションを実行できるようにする必要があります。コードは次のとおりです。
kubectl create clusterrole manage-pods \
--verb=get --verb=list --verb=watch --verb=create --verb=update --verb=patch --verb=delete \
--resource=pods
kubectl -n default create rolebinding sa-manage-pods \
--clusterrole=manage-pods \
--serviceaccount=default:playground
上記は、サービスアカウントに、default
名前空間に限定されたポッドでのアクションを実行するためのアクセス許可を与えます。
常に役割を非常に狭く具体的に保つ必要がありますが、KinDで遊んでみると、以下に示すように、クラスター全体の管理者の役割を適用するのが理にかなっています。
kubectl create clusterrolebinding sa-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:playground
生のリクエスト
何が行われているのか、またクライアントが内部で何をしているのかをよりよく理解するために、kubectl
を使用した生のHTTPリクエストから始めますcurl
。
内部で行われている要求を見つける最も簡単な方法は、を使用して目的のkubectl
コマンドを実行すること-v 10
です。curl
これにより、以下に示す完全なコマンドが出力されます。
kubectl get pods -v 10
# <snip>
curl -k -v -XGET -H "Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json..." \
'https://127.0.0.1:36599/api/v1/namespaces/default/pods?limit=500'
# <snip>
の出力はloglevel 10
非常に冗長になりますが、どこかに上記のcurl
コマンドがあります。
Bearer
上記のコマンドに有効期間の長いトークンを使用してトークンヘッダーを追加すると、次のようなcurl
と同じアクションを実行できるようになります。kubectl
curl -s -k -XGET -H "Authorization: Bearer $KIND_TOKEN" -H "Accept: application/json, */*" -H "Content-Type: application/json" \
-H "kubernetes/$Format" 'https://127.0.0.1:36599/api/v1/namespaces/default/pods/example' | jq .status.phase
# "Running"
リクエスト本文が必要な場合は、リクエストに含める必要のあるフィールドを調べてください。たとえば、ポッドを作成するときに、ここで説明するAPIを使用できます。その場合、次のリクエストが表示されます。
curl -k -XPOST -H "Authorization: Bearer $KIND_TOKEN" -H "Accept: application/json, */*" -H "Content-Type: application/json" \
-H "kubernetes/$Format" https://127.0.0.1:36599/api/v1/namespaces/default/pods -d@pod.json
# To confirm
kubectl get pods
NAME READY STATUS RESTARTS AGE
example 0/1 Running 0 7s
オブジェクト属性については、 KubernetesAPIリファレンスを参照してください。さらに、次のコマンドを使用してOpenAPI定義を表示することもできます。
curl -k -X GET -H "Authorization: Bearer $KIND_TOKEN" https://127.0.0.1:36599/apis
REST APIを使用してKubernetesと直接やり取りするのは少し不格好かもしれませんが、それを使用することが理にかなっている場合があります。kubectl
状況によっては、OpenShiftなどのKubernetesの別のディストリビューションを使用しているときに、同等のコマンドを持たないAPIとやり取りすることが含まれます。これにより、kubectl
またはクライアントSDKでカバーされていない追加のAPIが公開されます。
Pythonクライアント
ここでPythonクライアント自体に移動します。kubectl
またはと同じ手順を実行する必要がありcurl
ます。最初のステップは認証です。これを以下に示します。
from kubernetes import client
import os
configuration = client.Configuration()
configuration.api_key_prefix["authorization"] = "Bearer"
configuration.host = "https://127.0.0.1:36599"
configuration.api_key["authorization"] = os.getenv("KIND_TOKEN", None)
configuration.verify_ssl = False # Only for testing with KinD!
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
ret = v1.list_namespaced_pod(namespace="default", watch=False)
for pod in ret.items:
print(f"Name: {pod.metadata.name}, Namespace: {pod.metadata.namespace} IP: {pod.status.pod_ip}")
# Name: example, Namespace: default IP: 10.244.2.2
まず、構成オブジェクトを定義します。これは、Bearer
トークンを使用して認証することをクライアントに通知します。KinDクラスターがSSLを使用していないことを考慮して、実際のクラスターではSSLを無効にします。ただし、決してそうすべきではありません。
構成をテストするためにlist_namespaced_pod
、APIクライアントのメソッドを使用してdefault
名前空間内のすべてのポッドを取得してから、それらの、、およびを出力name
しnamespace
ますIP
。
Deployment
ここで、より現実的なタスクのために、以下を作成しましょう。
deployment_name = "my-deploy"
deployment_manifest = {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": deployment_name, "namespace": "default"},
"spec": {"replicas": 3,
"selector": {
"matchLabels": {
"app": "nginx"
}},
"template": {"metadata": {"labels": {"app": "nginx"}},
"spec": {"containers": [
{"name": "nginx", "image": "nginx:1.21.6", "ports": [{"containerPort": 80}]}]
}
},
}
}
import time
from kubernetes.client.rest import ApiException
v1 = client.AppsV1Api(api_client)
response = v1.create_namespaced_deployment(body=deployment_manifest, namespace="default")
while True:
try:
response = v1.read_namespaced_deployment_status(name=deployment_name, namespace="default")
if response.status.available_replicas != 3:
print("Waiting for Deployment to become ready...")
time.sleep(5)
else:
break
except ApiException as e:
print(f"Exception when calling AppsV1Api -> read_namespaced_deployment_status: {e}\n")
の作成に加えてDeployment
、ポッドが利用可能になるのも待ちます。これを行うには、ステータスを照会し、Deployment
使用可能なレプリカの数を確認します。
また、などの関数名のパターンにも注意してcreate_namespaced_deployment
ください。それをより明確にするために、さらにいくつか見てみましょう:
replace_namespaced_cron_job
patch_namespaced_stateful_set
list_namespaced_horizontal_pod_autoscaler
read_namespaced_daemon_set
read_custom_resource_definition
これらはすべて、グローバルリソースの形式operation_namespaced_resource
または専用です。operation_resource
これらには、などのリソースステータスまたはなどのリソーススケールで操作を実行するメソッドの_status
接尾辞を付けることができます。_scaleread_namespaced_deployment_statuspatch_namespaced_stateful_set_scale
強調すべきもう1つの点は、上記の例では、にclient.AppsV1Api
属するすべてのリソースを操作できるようにするを使用してアクションを実行したことapiVersion: apps/v1
です。CronJobを使用する場合は、代わりにBatchV1Api
(apiVersion: batch/v1
YAML形式)を選択します。PVCの場合は、を選択しCoreV1Api
ますapiVersion: v1
。あなたは要点を理解します。
ご想像のとおり、これは多くの機能から選択できます。幸いなことに、それらはすべてドキュメントにリストされており、それらのいずれかをクリックして、その使用例を取得できます。
基本的なCRUD操作に加えて、オブジェクトの変更を継続的に監視することもできます。明らかな選択は見ることEvents
です:
from kubernetes import client, watch
v1 = client.CoreV1Api(api_client)
count = 10
w = watch.Watch()
for event in w.stream(partial(v1.list_namespaced_event, namespace="default"), timeout_seconds=10):
print(f"Event - Message: {event['object']['message']} at {event['object']['metadata']['creationTimestamp']}")
count -= 1
if not count:
w.stop()
print("Finished namespace stream.")
# Event - Message: Successfully assigned default/my-deploy-cb69f686c-2dspd to api-playground-worker2 at 2022-04-19T11:18:25Z
# Event - Message: Container image "nginx:1.21.6" already present on machine at 2022-04-19T11:18:26Z
# Event - Message: Created container nginx at 2022-04-19T11:18:26Z
# Event - Message: Started container nginx at 2022-04-19T11:18:26Z
ここでは、default
名前空間でイベントを監視することを選択しました。最初の10個のイベントを取得してから、ストリームを閉じます。リソースを継続的に監視したい場合は、timeout_seconds
とw.stop()
呼び出しを削除するだけです。
最初の例では、プレーンPythondict
を使用してDeploymentオブジェクトを定義し、それをクライアントに渡しました。または、以下に示すように、ライブラリによって提供されるAPIモデル(クラス)を使用して、より多くのOOPスタイルを使用することもできます。
v1 = client.AppsV1Api(api_client)
deployment_manifest = client.V1Deployment(
api_version="apps/v1",
kind="Deployment",
metadata=client.V1ObjectMeta(name=deployment_name),
spec=client.V1DeploymentSpec(
replicas=3,
selector=client.V1LabelSelector(match_labels={
"app": "nginx"
}),
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels={"app": "nginx"}),
spec=client.V1PodSpec(
containers=[client.V1Container(name="nginx",
image="nginx:1.21.6",
ports=[client.V1ContainerPort(container_port=80)]
)]))
)
)
response = v1.create_namespaced_deployment(body=deployment_manifest, namespace="default")
各議論にどのモデルを使用すべきかを理解しようとすることは敗戦であり、それは困難です。上記のようなリソースを作成するときは、常にモデルのドキュメントを使用し、個々のサブオブジェクトを作成するときにリンクをトラバースして、各フィールドで期待される値/タイプを把握する必要があります。
便利な例
これで、クライアントがどのように機能するかについての基本的な考え方がわかったはずです。そこで、毎日のKubernetes操作を自動化するのに役立つ可能性のあるいくつかの便利な例とスニペットを見てみましょう。
実行する可能性のある非常に一般的なことは、Deployment
ロールアウトです—通常はで実行されkubectl rollout restart
ます。ただし、これを行うためのAPIはありません。その方法kubectl
は、デプロイメントアノテーションを更新することです。具体的にkubectl.kubernetes.io/restartedAt
は、現在時刻に設定します。これが機能するのは、ポッド仕様に変更を加えると再起動が発生するためです。
Pythonクライアントを使用して再起動を実行する場合は、次の手順を実行する必要があります。
from kubernetes import dynamic
from kubernetes.client import api_client # Careful - different import - not the same as previous client!
import datetime
client = dynamic.DynamicClient(api_client.ApiClient(configuration=configuration))
api = client.resources.get(api_version="apps/v1", kind="Deployment")
# Even though the Deployment manifest was previously created with class model, it still behaves as dictionary:
deployment_manifest["spec"]["template"]["metadata"]["annotations"] = {
"kubectl.kubernetes.io/restartedAt": datetime.datetime.utcnow().isoformat()
}
deployment_patched = api.patch(body=deployment_manifest, name=deployment_name, namespace="default")
もう1つの一般的な操作は、スケーリングDeployment
です。幸い、これには使用できるAPI関数があります。これを以下に示します。
from kubernetes import client
api_client = client.ApiClient(configuration)
apps_v1 = client.AppsV1Api(api_client)
# The body can be of different patch types - https://github.com/kubernetes-client/python/issues/1206#issuecomment-668118057
api_response = apps_v1.patch_namespaced_deployment_scale(deployment_name, "default", {"spec": {"replicas": 5}})
トラブルシューティングの目的で、ポッドを調べて周りを見回し、場合によっては変数をexec
取得して正しい構成を確認することが理にかなっていることがよくあります。environment
そのためのコードは次のとおりです。
from kubernetes.stream import stream
def pod_exec(name, namespace, command, api_instance):
exec_command = ["/bin/sh", "-c", command]
resp = stream(api_instance.connect_get_namespaced_pod_exec,
name,
namespace,
command=exec_command,
stderr=True, stdin=False,
stdout=True, tty=False,
_preload_content=False)
while resp.is_open():
resp.update(timeout=1)
if resp.peek_stdout():
print(f"STDOUT: \n{resp.read_stdout()}")
if resp.peek_stderr():
print(f"STDERR: \n{resp.read_stderr()}")
resp.close()
if resp.returncode != 0:
raise Exception("Script failed")
pod = "example"
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
pod_exec(pod, "default", "env", v1)
# STDOUT:
# KUBERNETES_SERVICE_PORT=443
# KUBERNETES_PORT=tcp://10.96.0.1:443
# HOSTNAME=example
# HOME=/root
# ...
上記のスニペットでは、必要に応じてシェルスクリプト全体を実行することもできます。
したがって、Taint
問題のあるノードにを適用するとします。クラスタ管理指向のタスクに集中できます。Node Taints用の直接APIがないため、方法を見つけることができます。これが私たちを助けるためのコードです:
from kubernetes import client
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
# kubectl taint nodes api-playground-worker some-taint=1:NoSchedule
v1.patch_node("api-playground-worker", {"spec": {"taints": [{"effect": "NoSchedule", "key": "some-taint", "value": "1"}]}})
# kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints --no-headers
# api-playground-control-plane [map[effect:NoSchedule key:node-role.kubernetes.io/master]]
# api-playground-worker [map[effect:NoSchedule key:some-taint value:1]]
# api-playground-worker2 <none>
# api-playground-worker3 <none>
また、クラスターリソースの使用率を監視して、クラスターのスケーリングを自動化することもできます。通常は、kubectl top
インタラクティブに情報を取得するために使用しますが、クライアントライブラリを使用すると、次のことができます。
# https://github.com/kubernetes-sigs/kind/issues/398
# kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.5.0/components.yaml
# kubectl patch -n kube-system deployment metrics-server --type=json \
# -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
from kubernetes import client
api_client = client.ApiClient(configuration)
custom_api = client.CustomObjectsApi(api_client)
response = custom_api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes") # also works with "pods" instead of "nodes"
for node in response["items"]:
print(f"{node['metadata']['name']: <30} CPU: {node['usage']['cpu']: <10} Memory: {node['usage']['memory']}")
# api-playground-control-plane CPU: 148318488n Memory: 2363504Ki
# api-playground-worker CPU: 91635913n Memory: 1858680Ki
# api-playground-worker2 CPU: 75473747n Memory: 1880860Ki
# api-playground-worker3 CPU: 105692650n Memory: 1881560Ki
上記の例はmetrics-server
、クラスターにインストールされていることを前提としています。実行kubectl top
してそれを確認できます。KinDを使用している場合は、スニペットのコメントを使用してインストールしてください。
最後になりましたが、クラスター内のオブジェクトのデプロイまたは変更に使用するYAMLまたはJSONファイルが多数ある場合、またはクライアントで作成したものをエクスポートおよびバックアップする場合は、簡単な方法があります。YAML / JSONファイルをKubernetesオブジェクトに変換し、ファイルに戻す方法は次のとおりです。
# pip install kopf # (Python 3.7+)
import kopf
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
pods = []
# https://stackoverflow.com/questions/59977058/clone-kubernetes-objects-programmatically-using-the-python-api/59977059#59977059
ret = v1.list_namespaced_pod(namespace="default")
for pod in ret.items:
# Simple conversion to Dict/JSON
print(api_client.sanitize_for_serialization(pod))
# Conversion with fields clean-up
pods.append(kopf.AnnotationsDiffBaseStorage()
.build(body=kopf.Body(api_client.sanitize_for_serialization(pod))))
# Conversion from Dict back to Client object
class FakeKubeResponse:
def __init__(self, obj):
import json
self.data = json.dumps(obj)
for pod in pods:
pod_manifest = api_client.deserialize(FakeKubeResponse(pod), "V1Pod")
...
既存のオブジェクトをPythonディクショナリ(JSON)に変換する最初の方法はsanitize_for_serialization
、生成された/デフォルトのすべてのフィールドで生の出力を生成するを使用することです。より良いオプションは、kopf
ライブラリのユーティリティメソッドを使用することです。これにより、不要なフィールドがすべて削除されます。そこから、辞書を適切なYAMLまたはJSONファイルに変換するのは簡単です。
このプロセスを逆にしたい場合(辞書からクライアントオブジェクトモデルに移動するdeserialize
場合)、APIクライアントのメソッドを使用できます。ただし、このメソッドは引数に属性があることを想定しているdata
ため、そのような属性を持つコンテナークラスインスタンスを渡します。
Pythonクライアントで使用したいYAMLファイルがすでにある場合は、ユーティリティ関数を使用できますkubernetes.utils.create_from_yaml
。
すべてのライブラリ機能の完全な概要を取得するには、リポジトリ内のexamplesディレクトリを確認することをお勧めします。
また、ライブラリリポジトリの問題を確認することをお勧めします。ライブラリリポジトリには、イベントの並列処理やConfigMapの更新の監視など、クライアントの使用法の優れた例がたくさんあります。
結論
Pythonクライアントライブラリには何百もの関数が含まれているため、そこにあるすべての小さな機能やユースケースを網羅することは困難です。それらのほとんどは、数分後にライブラリの使用法をかなり自然にするはずの一般的なパターンに従います。
上記で示して参照したもの以外の例を探している場合は、Kubernetesオペレーターを作成するためのライブラリなど、PythonKubernetesクライアントを利用する他の一般的なツールを調べることをお勧めします。また、ライブラリ自体のテストを見てみると、その使用目的が示されているので非常に便利です。これが研究するのに優れたクライアントテストスイートです。
1652947500
Kubernetesは近年デファクトスタンダードになり、DevOpsエンジニアと開発者の両方が日常的にKubernetesを使用しています。ただし、実行するタスクの多くは同じで、退屈で、自動化が簡単です。
多くの場合、一連のコマンドを使用して簡単なシェルスクリプトを作成するのは簡単kubectl
ですが、より複雑な自動化タスクの場合、bashだけでは不十分であり、Pythonなどの適切な言語の能力が必要です。
そのため、この記事では、Kubernetes Pythonクライアントライブラリを活用して、煩わしいKubernetesタスクを自動化する方法を見ていきます。
遊び場
Kubernetesクライアントでのプレイを開始する前に、まず、安全にテストできるプレイグラウンドクラスターを作成する必要があります。ここからインストールできるKinD(DockerのKubernetes)を使用します。
次のクラスター構成を使用します。
# kind.yaml
# https://kind.sigs.k8s.io/docs/user/configuration/
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: api-playground
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
上記の構成からクラスターを作成するには、次のコマンドを実行できます。
kind create cluster --image kindest/node:v1.23.5 --config=kind.yaml
kubectl cluster-info --context kind-api-playground
# Kubernetes control plane is running at https://127.0.0.1:36599
# CoreDNS is running at https://127.0.0.1:36599/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# api-playground-control-plane Ready control-plane,master 58s v1.23.5
# api-playground-worker Ready <none> 27s v1.23.5
# api-playground-worker2 NotReady <none> 27s v1.23.5
# api-playground-worker3 NotReady <none> 27s v1.23.5
クラスタが稼働している状態で、クライアントライブラリもインストールする必要があります(オプションで、仮想環境内に)。
python3 -m venv venv
source venv/bin/activate
pip install kubernetes
認証
Kubernetesクラスタ内でアクションを実行するには、最初に認証する必要があります。
長期間有効なトークンを使用するため、認証フローを繰り返し実行する必要はありません。ServiceAccount
有効期間の長いトークンは、 :を作成することで作成できます。
kubectl create sa playground
kubectl describe sa playground
Name: playground
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: playground-token-v8bq7
Tokens: playground-token-v8bq7
Events: <none>
export KIND_TOKEN=$(kubectl get secret playground-token-v8bq7 -o json | jq -r .data.token | base64 --decode)
サービスアカウントを使用すると、1人のユーザーに関連付けられないという利点もあります。これは、自動化の目的で常に推奨されます。
上記の出力からのトークンは、リクエストで使用できます。
curl -k -X GET -H "Authorization: Bearer $KIND_TOKEN" https://127.0.0.1:36599/apis
現在認証されていますが、多くのことを行う権限はありません。したがって、次に、を作成しRole
てバインドし、ServiceAccount
リソースに対してアクションを実行できるようにする必要があります。コードは次のとおりです。
kubectl create clusterrole manage-pods \
--verb=get --verb=list --verb=watch --verb=create --verb=update --verb=patch --verb=delete \
--resource=pods
kubectl -n default create rolebinding sa-manage-pods \
--clusterrole=manage-pods \
--serviceaccount=default:playground
上記は、サービスアカウントに、default
名前空間に限定されたポッドでのアクションを実行するためのアクセス許可を与えます。
常に役割を非常に狭く具体的に保つ必要がありますが、KinDで遊んでみると、以下に示すように、クラスター全体の管理者の役割を適用するのが理にかなっています。
kubectl create clusterrolebinding sa-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:playground
生のリクエスト
何が行われているのか、またクライアントが内部で何をしているのかをよりよく理解するために、kubectl
を使用した生のHTTPリクエストから始めますcurl
。
内部で行われている要求を見つける最も簡単な方法は、を使用して目的のkubectl
コマンドを実行すること-v 10
です。curl
これにより、以下に示す完全なコマンドが出力されます。
kubectl get pods -v 10
# <snip>
curl -k -v -XGET -H "Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json..." \
'https://127.0.0.1:36599/api/v1/namespaces/default/pods?limit=500'
# <snip>
の出力はloglevel 10
非常に冗長になりますが、どこかに上記のcurl
コマンドがあります。
Bearer
上記のコマンドに有効期間の長いトークンを使用してトークンヘッダーを追加すると、次のようなcurl
と同じアクションを実行できるようになります。kubectl
curl -s -k -XGET -H "Authorization: Bearer $KIND_TOKEN" -H "Accept: application/json, */*" -H "Content-Type: application/json" \
-H "kubernetes/$Format" 'https://127.0.0.1:36599/api/v1/namespaces/default/pods/example' | jq .status.phase
# "Running"
リクエスト本文が必要な場合は、リクエストに含める必要のあるフィールドを調べてください。たとえば、ポッドを作成するときに、ここで説明するAPIを使用できます。その場合、次のリクエストが表示されます。
curl -k -XPOST -H "Authorization: Bearer $KIND_TOKEN" -H "Accept: application/json, */*" -H "Content-Type: application/json" \
-H "kubernetes/$Format" https://127.0.0.1:36599/api/v1/namespaces/default/pods -d@pod.json
# To confirm
kubectl get pods
NAME READY STATUS RESTARTS AGE
example 0/1 Running 0 7s
オブジェクト属性については、 KubernetesAPIリファレンスを参照してください。さらに、次のコマンドを使用してOpenAPI定義を表示することもできます。
curl -k -X GET -H "Authorization: Bearer $KIND_TOKEN" https://127.0.0.1:36599/apis
REST APIを使用してKubernetesと直接やり取りするのは少し不格好かもしれませんが、それを使用することが理にかなっている場合があります。kubectl
状況によっては、OpenShiftなどのKubernetesの別のディストリビューションを使用しているときに、同等のコマンドを持たないAPIとやり取りすることが含まれます。これにより、kubectl
またはクライアントSDKでカバーされていない追加のAPIが公開されます。
Pythonクライアント
ここでPythonクライアント自体に移動します。kubectl
またはと同じ手順を実行する必要がありcurl
ます。最初のステップは認証です。これを以下に示します。
from kubernetes import client
import os
configuration = client.Configuration()
configuration.api_key_prefix["authorization"] = "Bearer"
configuration.host = "https://127.0.0.1:36599"
configuration.api_key["authorization"] = os.getenv("KIND_TOKEN", None)
configuration.verify_ssl = False # Only for testing with KinD!
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
ret = v1.list_namespaced_pod(namespace="default", watch=False)
for pod in ret.items:
print(f"Name: {pod.metadata.name}, Namespace: {pod.metadata.namespace} IP: {pod.status.pod_ip}")
# Name: example, Namespace: default IP: 10.244.2.2
まず、構成オブジェクトを定義します。これは、Bearer
トークンを使用して認証することをクライアントに通知します。KinDクラスターがSSLを使用していないことを考慮して、実際のクラスターではSSLを無効にします。ただし、決してそうすべきではありません。
構成をテストするためにlist_namespaced_pod
、APIクライアントのメソッドを使用してdefault
名前空間内のすべてのポッドを取得してから、それらの、、およびを出力name
しnamespace
ますIP
。
Deployment
ここで、より現実的なタスクのために、以下を作成しましょう。
deployment_name = "my-deploy"
deployment_manifest = {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": deployment_name, "namespace": "default"},
"spec": {"replicas": 3,
"selector": {
"matchLabels": {
"app": "nginx"
}},
"template": {"metadata": {"labels": {"app": "nginx"}},
"spec": {"containers": [
{"name": "nginx", "image": "nginx:1.21.6", "ports": [{"containerPort": 80}]}]
}
},
}
}
import time
from kubernetes.client.rest import ApiException
v1 = client.AppsV1Api(api_client)
response = v1.create_namespaced_deployment(body=deployment_manifest, namespace="default")
while True:
try:
response = v1.read_namespaced_deployment_status(name=deployment_name, namespace="default")
if response.status.available_replicas != 3:
print("Waiting for Deployment to become ready...")
time.sleep(5)
else:
break
except ApiException as e:
print(f"Exception when calling AppsV1Api -> read_namespaced_deployment_status: {e}\n")
の作成に加えてDeployment
、ポッドが利用可能になるのも待ちます。これを行うには、ステータスを照会し、Deployment
使用可能なレプリカの数を確認します。
また、などの関数名のパターンにも注意してcreate_namespaced_deployment
ください。それをより明確にするために、さらにいくつか見てみましょう:
replace_namespaced_cron_job
patch_namespaced_stateful_set
list_namespaced_horizontal_pod_autoscaler
read_namespaced_daemon_set
read_custom_resource_definition
これらはすべて、グローバルリソースの形式operation_namespaced_resource
または専用です。operation_resource
これらには、などのリソースステータスまたはなどのリソーススケールで操作を実行するメソッドの_status
接尾辞を付けることができます。_scaleread_namespaced_deployment_statuspatch_namespaced_stateful_set_scale
強調すべきもう1つの点は、上記の例では、にclient.AppsV1Api
属するすべてのリソースを操作できるようにするを使用してアクションを実行したことapiVersion: apps/v1
です。CronJobを使用する場合は、代わりにBatchV1Api
(apiVersion: batch/v1
YAML形式)を選択します。PVCの場合は、を選択しCoreV1Api
ますapiVersion: v1
。あなたは要点を理解します。
ご想像のとおり、これは多くの機能から選択できます。幸いなことに、それらはすべてドキュメントにリストされており、それらのいずれかをクリックして、その使用例を取得できます。
基本的なCRUD操作に加えて、オブジェクトの変更を継続的に監視することもできます。明らかな選択は見ることEvents
です:
from kubernetes import client, watch
v1 = client.CoreV1Api(api_client)
count = 10
w = watch.Watch()
for event in w.stream(partial(v1.list_namespaced_event, namespace="default"), timeout_seconds=10):
print(f"Event - Message: {event['object']['message']} at {event['object']['metadata']['creationTimestamp']}")
count -= 1
if not count:
w.stop()
print("Finished namespace stream.")
# Event - Message: Successfully assigned default/my-deploy-cb69f686c-2dspd to api-playground-worker2 at 2022-04-19T11:18:25Z
# Event - Message: Container image "nginx:1.21.6" already present on machine at 2022-04-19T11:18:26Z
# Event - Message: Created container nginx at 2022-04-19T11:18:26Z
# Event - Message: Started container nginx at 2022-04-19T11:18:26Z
ここでは、default
名前空間でイベントを監視することを選択しました。最初の10個のイベントを取得してから、ストリームを閉じます。リソースを継続的に監視したい場合は、timeout_seconds
とw.stop()
呼び出しを削除するだけです。
最初の例では、プレーンPythondict
を使用してDeploymentオブジェクトを定義し、それをクライアントに渡しました。または、以下に示すように、ライブラリによって提供されるAPIモデル(クラス)を使用して、より多くのOOPスタイルを使用することもできます。
v1 = client.AppsV1Api(api_client)
deployment_manifest = client.V1Deployment(
api_version="apps/v1",
kind="Deployment",
metadata=client.V1ObjectMeta(name=deployment_name),
spec=client.V1DeploymentSpec(
replicas=3,
selector=client.V1LabelSelector(match_labels={
"app": "nginx"
}),
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels={"app": "nginx"}),
spec=client.V1PodSpec(
containers=[client.V1Container(name="nginx",
image="nginx:1.21.6",
ports=[client.V1ContainerPort(container_port=80)]
)]))
)
)
response = v1.create_namespaced_deployment(body=deployment_manifest, namespace="default")
各議論にどのモデルを使用すべきかを理解しようとすることは敗戦であり、それは困難です。上記のようなリソースを作成するときは、常にモデルのドキュメントを使用し、個々のサブオブジェクトを作成するときにリンクをトラバースして、各フィールドで期待される値/タイプを把握する必要があります。
便利な例
これで、クライアントがどのように機能するかについての基本的な考え方がわかったはずです。そこで、毎日のKubernetes操作を自動化するのに役立つ可能性のあるいくつかの便利な例とスニペットを見てみましょう。
実行する可能性のある非常に一般的なことは、Deployment
ロールアウトです—通常はで実行されkubectl rollout restart
ます。ただし、これを行うためのAPIはありません。その方法kubectl
は、デプロイメントアノテーションを更新することです。具体的にkubectl.kubernetes.io/restartedAt
は、現在時刻に設定します。これが機能するのは、ポッド仕様に変更を加えると再起動が発生するためです。
Pythonクライアントを使用して再起動を実行する場合は、次の手順を実行する必要があります。
from kubernetes import dynamic
from kubernetes.client import api_client # Careful - different import - not the same as previous client!
import datetime
client = dynamic.DynamicClient(api_client.ApiClient(configuration=configuration))
api = client.resources.get(api_version="apps/v1", kind="Deployment")
# Even though the Deployment manifest was previously created with class model, it still behaves as dictionary:
deployment_manifest["spec"]["template"]["metadata"]["annotations"] = {
"kubectl.kubernetes.io/restartedAt": datetime.datetime.utcnow().isoformat()
}
deployment_patched = api.patch(body=deployment_manifest, name=deployment_name, namespace="default")
もう1つの一般的な操作は、スケーリングDeployment
です。幸い、これには使用できるAPI関数があります。これを以下に示します。
from kubernetes import client
api_client = client.ApiClient(configuration)
apps_v1 = client.AppsV1Api(api_client)
# The body can be of different patch types - https://github.com/kubernetes-client/python/issues/1206#issuecomment-668118057
api_response = apps_v1.patch_namespaced_deployment_scale(deployment_name, "default", {"spec": {"replicas": 5}})
トラブルシューティングの目的で、ポッドを調べて周りを見回し、場合によっては変数をexec
取得して正しい構成を確認することが理にかなっていることがよくあります。environment
そのためのコードは次のとおりです。
from kubernetes.stream import stream
def pod_exec(name, namespace, command, api_instance):
exec_command = ["/bin/sh", "-c", command]
resp = stream(api_instance.connect_get_namespaced_pod_exec,
name,
namespace,
command=exec_command,
stderr=True, stdin=False,
stdout=True, tty=False,
_preload_content=False)
while resp.is_open():
resp.update(timeout=1)
if resp.peek_stdout():
print(f"STDOUT: \n{resp.read_stdout()}")
if resp.peek_stderr():
print(f"STDERR: \n{resp.read_stderr()}")
resp.close()
if resp.returncode != 0:
raise Exception("Script failed")
pod = "example"
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
pod_exec(pod, "default", "env", v1)
# STDOUT:
# KUBERNETES_SERVICE_PORT=443
# KUBERNETES_PORT=tcp://10.96.0.1:443
# HOSTNAME=example
# HOME=/root
# ...
上記のスニペットでは、必要に応じてシェルスクリプト全体を実行することもできます。
したがって、Taint
問題のあるノードにを適用するとします。クラスタ管理指向のタスクに集中できます。Node Taints用の直接APIがないため、方法を見つけることができます。これが私たちを助けるためのコードです:
from kubernetes import client
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
# kubectl taint nodes api-playground-worker some-taint=1:NoSchedule
v1.patch_node("api-playground-worker", {"spec": {"taints": [{"effect": "NoSchedule", "key": "some-taint", "value": "1"}]}})
# kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints --no-headers
# api-playground-control-plane [map[effect:NoSchedule key:node-role.kubernetes.io/master]]
# api-playground-worker [map[effect:NoSchedule key:some-taint value:1]]
# api-playground-worker2 <none>
# api-playground-worker3 <none>
また、クラスターリソースの使用率を監視して、クラスターのスケーリングを自動化することもできます。通常は、kubectl top
インタラクティブに情報を取得するために使用しますが、クライアントライブラリを使用すると、次のことができます。
# https://github.com/kubernetes-sigs/kind/issues/398
# kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.5.0/components.yaml
# kubectl patch -n kube-system deployment metrics-server --type=json \
# -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
from kubernetes import client
api_client = client.ApiClient(configuration)
custom_api = client.CustomObjectsApi(api_client)
response = custom_api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes") # also works with "pods" instead of "nodes"
for node in response["items"]:
print(f"{node['metadata']['name']: <30} CPU: {node['usage']['cpu']: <10} Memory: {node['usage']['memory']}")
# api-playground-control-plane CPU: 148318488n Memory: 2363504Ki
# api-playground-worker CPU: 91635913n Memory: 1858680Ki
# api-playground-worker2 CPU: 75473747n Memory: 1880860Ki
# api-playground-worker3 CPU: 105692650n Memory: 1881560Ki
上記の例はmetrics-server
、クラスターにインストールされていることを前提としています。実行kubectl top
してそれを確認できます。KinDを使用している場合は、スニペットのコメントを使用してインストールしてください。
最後になりましたが、クラスター内のオブジェクトのデプロイまたは変更に使用するYAMLまたはJSONファイルが多数ある場合、またはクライアントで作成したものをエクスポートおよびバックアップする場合は、簡単な方法があります。YAML / JSONファイルをKubernetesオブジェクトに変換し、ファイルに戻す方法は次のとおりです。
# pip install kopf # (Python 3.7+)
import kopf
api_client = client.ApiClient(configuration)
v1 = client.CoreV1Api(api_client)
pods = []
# https://stackoverflow.com/questions/59977058/clone-kubernetes-objects-programmatically-using-the-python-api/59977059#59977059
ret = v1.list_namespaced_pod(namespace="default")
for pod in ret.items:
# Simple conversion to Dict/JSON
print(api_client.sanitize_for_serialization(pod))
# Conversion with fields clean-up
pods.append(kopf.AnnotationsDiffBaseStorage()
.build(body=kopf.Body(api_client.sanitize_for_serialization(pod))))
# Conversion from Dict back to Client object
class FakeKubeResponse:
def __init__(self, obj):
import json
self.data = json.dumps(obj)
for pod in pods:
pod_manifest = api_client.deserialize(FakeKubeResponse(pod), "V1Pod")
...
既存のオブジェクトをPythonディクショナリ(JSON)に変換する最初の方法はsanitize_for_serialization
、生成された/デフォルトのすべてのフィールドで生の出力を生成するを使用することです。より良いオプションは、kopf
ライブラリのユーティリティメソッドを使用することです。これにより、不要なフィールドがすべて削除されます。そこから、辞書を適切なYAMLまたはJSONファイルに変換するのは簡単です。
このプロセスを逆にしたい場合(辞書からクライアントオブジェクトモデルに移動するdeserialize
場合)、APIクライアントのメソッドを使用できます。ただし、このメソッドは引数に属性があることを想定しているdata
ため、そのような属性を持つコンテナークラスインスタンスを渡します。
Pythonクライアントで使用したいYAMLファイルがすでにある場合は、ユーティリティ関数を使用できますkubernetes.utils.create_from_yaml
。
すべてのライブラリ機能の完全な概要を取得するには、リポジトリ内のexamplesディレクトリを確認することをお勧めします。
また、ライブラリリポジトリの問題を確認することをお勧めします。ライブラリリポジトリには、イベントの並列処理やConfigMapの更新の監視など、クライアントの使用法の優れた例がたくさんあります。
結論
Pythonクライアントライブラリには何百もの関数が含まれているため、そこにあるすべての小さな機能やユースケースを網羅することは困難です。それらのほとんどは、数分後にライブラリの使用法をかなり自然にするはずの一般的なパターンに従います。
上記で示して参照したもの以外の例を探している場合は、Kubernetesオペレーターを作成するためのライブラリなど、PythonKubernetesクライアントを利用する他の一般的なツールを調べることをお勧めします。また、ライブラリ自体のテストを見てみると、その使用目的が示されているので非常に便利です。これが研究するのに優れたクライアントテストスイートです。