0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes Python Clientを使いPodからのデプロイメント

Posted at

はじめに

アプリケーションサービスからk8s環境にDeployment、Podリソースを提供する方法を調査し実装する。アプリケーションサービスもk8s上に提供されPodとして稼働する前提とします。

実装に利用する環境について

実装に利用したk8s環境は以下の通りです。

$ kubectl get nodes
NAME        STATUS     ROLES           AGE   VERSION
master      Ready      control-plane   42d   v1.31.6
worker1     NotReady   <none>          14h   v1.31.6
worker2     Ready      <none>          14h   v1.31.6
$ kubectl version
Client Version: v1.31.6
Kustomize Version: v5.4.2
Server Version: v1.31.5
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"31", GitVersion:"v1.31.6", GitCommit:"6b3560758b37680cb713dfc71da03c04cadd657c", GitTreeState:"clean", BuildDate:"2025-02-12T21:31:09Z", GoVersion:"go1.22.12", Compiler:"gc", Platform:"linux/amd64"}

Kubernetes Python Client 用Podの作成

アプリケーションサービスを作成するのは面倒なので、今回はPythonを実行可能なPodを作成し、手動でpythonコマンドを実行することで実装します。Python用のPodは、以下の定義で作ります。

pod-python.yaml
apiVersion: v1
kind: Pod
metadata:
  name: python-pod
spec:
  containers:
  - name: python-container
    image: python:3.11-slim
    command: ["python3"]
    tty: true
$ kubectl create -f pod-python.yaml 
pod/python-pod created
$ kubectl get pods
NAME                                READY   STATUS        RESTARTS   AGE
python-pod                          1/1     Running       0          6s
$ kubectl exec -it python-pod -- bash
root@python-pod:/# python
Python 3.11.11 (main, Feb 25 2025, 02:38:55) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello Python!")
Hello Python!
>>> 
root@python-pod:/# exit

Kubernetes Python Client モジュールのインストール

手動であらかじめ必要なパッケージをダウンロードしておき、python-dpo:/tmp配下にコピーしておきます。

$ kubectl exec -it python-pod -- bash
root@python-pod:/# python3 -m venv myenv
root@python-pod:/# source myenv/bin/activate
(myenv) root@python-pod:/# cd /tmp
(myenv) root@python-pod:/tmp# pip install certifi-2025.1.31-py3-none-any.whl six-1.17.0-py2.py3-none-any.whl charset_normalizer-2.0.12-py3-none-any.whl urllib3-1.26.20-py2.py3-none-any.whl pyasn1-0.5.1-py2.py3-none-any.whl rsa-4.9-py3-none-any.whl idna-3.10-py3-none-any.whl durationpy-0.9-py3-none-any.whl websocket_client-1.3.1-py3-none-any.whl oauthlib-3.2.2-py3-none-any.whl requests-2.27.1-py2.py3-none-any.whl requests_oauthlib-2.0.0-py2.py3-none-any.whl cachetools-4.2.4-py3-none-any.whl pyasn1_modules-0.3.0-py2.py3-none-any.whl python_dateutil-2.9.0.post0-py2.py3-none-any.whl google_auth-2.22.0-py2.py3-none-any.whl 
〜省略〜
Installing collected packages: durationpy, websocket-client, urllib3, six, pyasn1, oauthlib, idna, charset-normalizer, certifi, cachetools, rsa, requests, python-dateutil, pyasn1-modules, requests-oauthlib, google-auth
Successfully installed cachetools-4.2.4 certifi-2025.1.31 charset-normalizer-2.0.12 durationpy-0.9 google-auth-2.22.0 idna-3.10 oauthlib-3.2.2 pyasn1-0.5.1 pyasn1-modules-0.3.0 python-dateutil-2.9.0.post0 requests-2.27.1 requests-oauthlib-2.0.0 rsa-4.9 six-1.17.0 urllib3-1.26.20 websocket-client-1.3.1
(myenv) root@python-pod:/tmp# gzip -dc PyYAML-6.0.1.tar.gz | tar xf -
(myenv) root@python-pod:/tmp# cd PyYAML-6.0.1
(myenv) root@python-pod:/tmp/PyYAML-6.0.1# python3 setup.py install
〜省略〜
gcc -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/myenv/include -I/usr/local/include/python3.11 -c yaml/_yaml.c -o build/temp.linux-x86_64-cpython-311/yaml/_yaml.o
Error compiling module, falling back to pure Python
〜省略〜
Installed /myenv/lib/python3.11/site-packages/PyYAML-6.0.1-py3.11-linux-x86_64.egg
Processing dependencies for PyYAML==6.0.1
Finished processing dependencies for PyYAML==6.0.1
(myenv) root@python-pod:/tmp/PyYAML-6.0.1# cd ..
(myenv) root@python-pod:/tmp# pip install kubernetes-32.0.1-py2.py3-none-any.whl 
〜省略〜
Installing collected packages: kubernetes
Successfully installed kubernetes-32.0.1
(myenv) root@python-pod:/tmp# 

途中でgccコマンドエラーが出力されていましたが、最終的にSuccessfullyのため一旦無視することにします。

呼び出し用 Deployment マニフェストの準備

Clientが呼び出すマニフェストは、以下内容をもとにPython Clientコードへ変換していきます。変換後のPythonコードはapply_in_cluster.pyになります。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
apply_in_cluster.py
from kubernetes import client, config

config.load_incluster_config()

deployment = client.V1Deployment(
    api_version="apps/v1",
    kind="Deployment",
    metadata=client.V1ObjectMeta(name="nginx-deployment"),
    spec=client.V1DeploymentSpec(
        replicas=3,
        template=client.V1PodTemplateSpec(
            spec=client.V1PodSpec(
                containers=[
                    client.V1Container(
                        name="nginx",
                        image="nginx:latest",
                        ports=[client.V1ContainerPort(container_port=80)],
                    )
                ]
            ),
        ),
    ),
)

api = client.AppsV1Api()
namespace = "default"

try:
    existing_deployment = api.read_namespaced_deployment("nginx-deployment", namespace)
    print("Deployment already exists, Updating...")
    api.patch_namespaced_deployment(name="nginx-deployment", namespace=namespace, body=deployment)
    print("Deployment updated successfully.")
except client.exceptions.ApiException as e:
    if e.status == 404:
        print("Deployment does not exist. Creating...")
        api.create_namespaced_deployment(namespace=namespace, body=deployment)
        print("Deployment created successfully.")
    else:
        print(f"Error: {e}")

Clientスクリプトの実行

先程のpython-podにapply_in_cluster.pyをコピーします。

$ kubectl cp apply_in_cluster.py python-pod:/

python-podに接続し先程のPythonファイルを実行してみます。

$ kubectl exec -it python-pod -- bash
root@python-pod:/# source myenv/bin/activate
(myenv) root@python-pod:/# python apply_in_cluster.py 
Error: (403)
Reason: Forbidden
HTTP response headers: HTTPHeaderDict({'Audit-Id': 'd598c4ea-0d8c-46cc-a90f-10fdf4de33d5', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'X-Content-Type-Options': 'nosniff', 'X-Kubernetes-Pf-Flowschema-Uid': '2094e53b-be81-4453-a7ce-d136df21c242', 'X-Kubernetes-Pf-Prioritylevel-Uid': 'e4dae82a-0dd7-4ac1-b0ef-4ad9e5a9b3a6', 'Date': 'Mon, 10 Mar 2025 09:53:29 GMT', 'Content-Length': '373'})
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"deployments.apps \"nginx-deployment\" is forbidden: User \"system:serviceaccount:default:default\" cannot get resource \"deployments\" in API group \"apps\" in the namespace \"default\"","reason":"Forbidden","details":{"name":"nginx-deployment","group":"apps","kind":"deployments"},"code":403}


(myenv) root@python-pod:/# 

エラーが出力されました。どうやら403なので権限不足が原因の様です。エラーメッセージを整形し内容を確認してみる。HeaderとBodyパートに分かれているようです。ヘッダーにはエラー原因に繋がる内容は特になさそうです。Bodyにはいくつか情報がありそうです。

User "system:serviceaccount:default:default":

リクエストを実行したユーザーは default ネームスペースの default サービスアカウント みたいです。KubernetesではPodがAPIを操作する際、デフォルトのサービスアカウントとして (system:serviceaccount::default) が使用される様です。

"cannot get resource "deployments" in API group "apps":

deployments リソース(apps APIグループ)の get 操作(情報取得)を実行しようとしたが、権限がないようです。deployments get以外にも、Pods リソースのget操作やcreate操作等々、必要な感じがします。

Error: (403)
Reason: Forbidden
HTTP response headers: HTTPHeaderDict(
{
    'Audit-Id': 'd598c4ea-0d8c-46cc-a90f-10fdf4de33d5', 
    'Cache-Control': 'no-cache, 
    private', 
    'Content-Type': 'application/json', 
    'X-Content-Type-Options': 'nosniff',
    'X-Kubernetes-Pf-Flowschema-Uid': '2094e53b-be81-4453-a7ce-d136df21c242',
    'X-Kubernetes-Pf-Prioritylevel-Uid': 'e4dae82a-0dd7-4ac1-b0ef-4ad9e5a9b3a6',
    'Date': 'Mon, 10 Mar 2025 09:53:29 GMT',
    'Content-Length': '373'
}
)
HTTP response body: {
    "kind":"Status",
    "apiVersion":"v1",
    "metadata":{},
    "status":"Failure",
    "message":"deployments.apps "nginx-deployment" is forbidden: User "system:serviceaccount:default:default" cannot get resource "deployments" in API group "apps" in the namespace "default"",
    "reason":"Forbidden",
    "details":{
        "name":"nginx-deployment",
        "group":"apps",
        "kind":"deployments"
    },
    "code":403
}

とりあえずAPI group "apps"でdeplyomentsリソースに対しget操作可能なServiceAccountやRole/RoleBindingを作成しpython-podに適用してみます。

ServiceAccount リソース

sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account

Role リソース

role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: my-role
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get"]

RoleBinding リソース

作成したRoleリソースをServiceAccountリソースにバインドするためのRoleBindingリソースを作成します。

rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-rolebinding
  namespace: default
subjects:
  - kind: ServiceAccount
    name: my-service-account
    namespace: default
roleRef:
  kind: Role
  name: my-role
  apiGroup: rbac.authorization.k8s.io
python.yaml
apiVersion: v1
kind: Pod
metadata:
  name: python-pod
spec:
  serviceAccountName: my-service-account # 追加
  containers:
  - name: python-container
    image: python:3.11-slim
    command: ["python3"]
    tty: true

用意したファイルを用いて適用していきます。どうやらデプロイ済みのコンテナに適用するとエラーになるようです。具体的にはServiceAccount: "default"をServiceAccount: "my-service-account"に変更することが許可されていないみたいです。

ServiceAccount リソースの適用

$ kubectl apply -f sa.yaml -f role.yaml -f rolebinding.yaml -f python.yaml 
serviceaccount/my-service-account created
role.rbac.authorization.k8s.io/my-role created
rolebinding.rbac.authorization.k8s.io/my-rolebinding created
Warning: resource pods/python-pod is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
The Pod "python-pod" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,`spec.tolerations` (only additions to existing tolerations),`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
  core.PodSpec{
  	... // 7 identical fields
  	DNSPolicy:                    "ClusterFirst",
  	NodeSelector:                 nil,
- 	ServiceAccountName:           "default",
+ 	ServiceAccountName:           "my-service-account",
  	AutomountServiceAccountToken: nil,
  	NodeName:                     "worker2",
  	... // 21 identical fields
  }

# Role リソースへの create操作権限追加

一度python-podを再作成し適用したところ今度は、create操作権限が足りていないみたいです。最終的にRoleリソースを以下のように修正しました。

role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: my-role
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "create"]

初回作成は正常終了、再度実行した場合はpatch操作権限がなくエラーになるみたいです。

(myenv) root@python-pod:~# python apply_in_cluster.py 
Deployment does not exist. Creating...
Deployment created successfully.
(myenv) root@python-pod:~# python apply_in_cluster.py 
Deployment already exists, Updating...
Error: (403)
Reason: Forbidden
HTTP response headers: HTTPHeaderDict({'Audit-Id': '2f827d2b-d7e4-4d2e-9a75-d0015c7345b4', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'X-Content-Type-Options': 'nosniff', 'X-Kubernetes-Pf-Flowschema-Uid': '2094e53b-be81-4453-a7ce-d136df21c242', 'X-Kubernetes-Pf-Prioritylevel-Uid': 'e4dae82a-0dd7-4ac1-b0ef-4ad9e5a9b3a6', 'Date': 'Mon, 10 Mar 2025 11:29:33 GMT', 'Content-Length': '386'})
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"deployments.apps \"nginx-deployment\" is forbidden: User \"system:serviceaccount:default:my-service-account\" cannot patch resource \"deployments\" in API group \"apps\" in the namespace \"default\"","reason":"Forbidden","details":{"name":"nginx-deployment","group":"apps","kind":"deployments"},"code":403}


(myenv) root@python-pod:/tmp# 

Client実行により作成された nginx Pod

Deploymentリソースnginx-deploymentがk8s上に反映されていることを確認できました。

$ kubectl get deployments,pods
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           3m40s

NAME                                    READY   STATUS        RESTARTS   AGE
pod/nginx-deployment-54b9c68f67-d7hlb   1/1     Running       0          3m40s
pod/nginx-deployment-54b9c68f67-dj2sj   1/1     Running       0          3m40s
pod/nginx-deployment-54b9c68f67-h2nh7   1/1     Running       0          3m40s
pod/nginx-deployment-54b9c68f67-ls9xv   1/1     Terminating   0          20h
pod/python-pod                          1/1     Running       0          19m

動作確認

手を抜いてServiceリソースを作成していないため、対象ノードから直接Podに割り当てられたIPをリクエストしての動作確認になります。

$ kubectl describe pods nginx-deployment-54b9c68f67-d7hlb | grep -e ^Node: -e ^IP:
Node:             worker2/192.168.11.51
IP:               10.244.223.166
$ ssh worker2
$ curl http://10.244.223.166
〜省略〜
<h1>Welcome to nginx!</h1>
〜省略〜

おわりに

当初目的の通り、PodからDeploymentを作成することができました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?