kubernetes

kube API server and kubernetes python client

misankim 2023. 8. 13. 16:47

Although I briefly covered this in the previous post, I would like to think again about how to access the kube API server and Kubernetes, and briefly record how to interact with the Kubernetes cluster using python.

 

1. kube API server

As described in the k8s official documentation, the core of the various components of the control plane that makes up Kubernetes is the kube API server. It is a component that allows access to all k8s resources stored in etcd in API form and allows all actions such as inquiry/creation/delete.

 

Kubernetes cluster components (Source - kubernetes official documentation (https://kubernetes.io/ko/docs/))

 

2. kubectl

There is probably no one who uses k8s who does not know the kubectl command. If you are new to K8s, whether you are creating a self-managed K8s cluster or a managed K8s cluster from a Cloud Provider (AWS, GCP, Azure, etc.), the first thing you do after creating a cluster is to run the kubectl command to run the cluster. You will learn to interact with.

 

The kubectl command is a command line tool that allows API communication with the kube API server. Let’s take a look at the example below.

 

➜  [~] kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          34m

 

As anyone who reads this post will know, the above command is a command that queries the list of pods in the current namespace and its output. If you run the same command with the "-v=7" option to set the log level, you can see the output as below.

 

➜  [~] kubectl get pod -v=7
I0813 14:53:24.482339   46420 loader.go:372] Config loaded from file:  /Users/my-test-user/.kube/config
I0813 14:53:24.486938   46420 round_trippers.go:463] GET https://kubernetes.docker.internal:6443/api/v1/namespaces/default/pods?limit=500
I0813 14:53:24.486952   46420 round_trippers.go:469] Request Headers:
I0813 14:53:24.486960   46420 round_trippers.go:473]     Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json
I0813 14:53:24.486968   46420 round_trippers.go:473]     User-Agent: kubectl/v1.24.2 (darwin/arm64) kubernetes/f66044f
I0813 14:53:24.504753   46420 round_trippers.go:574] Response Status: 200 OK in 17 milliseconds
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          37m

 

 

As above, you can see that the "kubectl get pod" command is the same as calling the "/api/v1/namespaces/default/pods" path of the kube API server of the current k8s cluster using the GET method.

 

This time, let’s check the response value received from the kube API server. Below is the command and its output that search the pod list in the current namespace and output it in json format.

 

➜  [~] kubectl get pod -o json
{
    "apiVersion": "v1",
    "items": [
        {
            "apiVersion": "v1",
            "kind": "Pod",
            "metadata": {
                "creationTimestamp": "2023-08-13T05:15:57Z",
                "labels": {
                    "run": "nginx"
                },
                "name": "nginx",
                "namespace": "default",
                "resourceVersion": "61398",
                "uid": "8144c120-b73c-4786-9137-df0b938f034c"
            },
            "spec": {
                "containers": [
                    {
                        "image": "nginx",
                        "imagePullPolicy": "Always",
                        "name": "nginx",
                        "resources": {},
                        "terminationMessagePath": "/dev/termination-log",
                        "terminationMessagePolicy": "File",
                        "volumeMounts": [
                            {
                                "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
                                "name": "kube-api-access-lh6rs",
                                "readOnly": true
                            }
                        ]
                    }
                ],
                "dnsPolicy": "ClusterFirst",
                "enableServiceLinks": true,
                "nodeName": "docker-desktop",
                "preemptionPolicy": "PreemptLowerPriority",
                "priority": 0,
                "restartPolicy": "Always",
                "schedulerName": "default-scheduler",
                "securityContext": {},
                "serviceAccount": "default",
                "serviceAccountName": "default",
                "terminationGracePeriodSeconds": 30,
                "tolerations": [
                    {
                        "effect": "NoExecute",
                        "key": "node.kubernetes.io/not-ready",
                        "operator": "Exists",
                        "tolerationSeconds": 300
                    },
                    {
                        "effect": "NoExecute",
                        "key": "node.kubernetes.io/unreachable",
                        "operator": "Exists",
                        "tolerationSeconds": 300
                    }
                ],
                "volumes": [
                    {
                        "name": "kube-api-access-lh6rs",
                        "projected": {
                            "defaultMode": 420,
                            "sources": [
                                {
                                    "serviceAccountToken": {
                                        "expirationSeconds": 3607,
                                        "path": "token"
                                    }
                                },
                                {
                                    "configMap": {
                                        "items": [
                                            {
                                                "key": "ca.crt",
                                                "path": "ca.crt"
                                            }
                                        ],
                                        "name": "kube-root-ca.crt"
                                    }
                                },
                                {
                                    "downwardAPI": {
                                        "items": [
                                            {
                                                "fieldRef": {
                                                    "apiVersion": "v1",
                                                    "fieldPath": "metadata.namespace"
                                                },
                                                "path": "namespace"
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    }
                ]
            },
            "status": {
                "conditions": [
                    {
                        "lastProbeTime": null,
                        "lastTransitionTime": "2023-08-13T05:15:57Z",
                        "status": "True",
                        "type": "Initialized"
                    },
                    {
                        "lastProbeTime": null,
                        "lastTransitionTime": "2023-08-13T05:16:05Z",
                        "status": "True",
                        "type": "Ready"
                    },
                    {
                        "lastProbeTime": null,
                        "lastTransitionTime": "2023-08-13T05:16:05Z",
                        "status": "True",
                        "type": "ContainersReady"
                    },
                    {
                        "lastProbeTime": null,
                        "lastTransitionTime": "2023-08-13T05:15:57Z",
                        "status": "True",
                        "type": "PodScheduled"
                    }
                ],
                "containerStatuses": [
                    {
                        "containerID": "docker://61b89cc6c5899a16b490686a2c86469aaddb1288240ed95b4d4eb64a939fdc78",
                        "image": "nginx:latest",
                        "imageID": "docker-pullable://nginx@sha256:67f9a4f10d147a6e04629340e6493c9703300ca23a2f7f3aa56fe615d75d31ca",
                        "lastState": {},
                        "name": "nginx",
                        "ready": true,
                        "restartCount": 0,
                        "started": true,
                        "state": {
                            "running": {
                                "startedAt": "2023-08-13T05:16:04Z"
                            }
                        }
                    }
                ],
                "hostIP": "192.168.65.4",
                "phase": "Running",
                "podIP": "10.1.0.100",
                "podIPs": [
                    {
                        "ip": "10.1.0.100"
                    }
                ],
                "qosClass": "BestEffort",
                "startTime": "2023-08-13T05:15:57Z"
            }
        }
    ],
    "kind": "List",
    "metadata": {
        "resourceVersion": ""
    }
}

 

And let's call the "/api/v1/namespaces/default/pods" path the same way by adding the "--raw" option to call the raw URI of the kube API server. Below are the commands and output.

 

➜  [~] kubectl get --raw /api/v1/namespaces/default/pods | jq
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "64656"
  },
  "items": [
    {
      "metadata": {
        "name": "nginx",
        "namespace": "default",
        "uid": "8144c120-b73c-4786-9137-df0b938f034c",
        "resourceVersion": "61398",
        "creationTimestamp": "2023-08-13T05:15:57Z",
        "labels": {
          "run": "nginx"
        },
        "managedFields": [
          {
            "manager": "kubectl-run",
            "operation": "Update",
            "apiVersion": "v1",
            "time": "2023-08-13T05:15:57Z",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:metadata": {
                "f:labels": {
                  ".": {},
                  "f:run": {}
                }
              },
              "f:spec": {
                "f:containers": {
                  "k:{\"name\":\"nginx\"}": {
                    ".": {},
                    "f:image": {},
                    "f:imagePullPolicy": {},
                    "f:name": {},
                    "f:resources": {},
                    "f:terminationMessagePath": {},
                    "f:terminationMessagePolicy": {}
                  }
                },
                "f:dnsPolicy": {},
                "f:enableServiceLinks": {},
                "f:restartPolicy": {},
                "f:schedulerName": {},
                "f:securityContext": {},
                "f:terminationGracePeriodSeconds": {}
              }
            }
          },
          {
            "manager": "kubelet",
            "operation": "Update",
            "apiVersion": "v1",
            "time": "2023-08-13T05:16:05Z",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:status": {
                "f:conditions": {
                  "k:{\"type\":\"ContainersReady\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  },
                  "k:{\"type\":\"Initialized\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  },
                  "k:{\"type\":\"Ready\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  }
                },
                "f:containerStatuses": {},
                "f:hostIP": {},
                "f:phase": {},
                "f:podIP": {},
                "f:podIPs": {
                  ".": {},
                  "k:{\"ip\":\"10.1.0.100\"}": {
                    ".": {},
                    "f:ip": {}
                  }
                },
                "f:startTime": {}
              }
            },
            "subresource": "status"
          }
        ]
      },
      "spec": {
        "volumes": [
          {
            "name": "kube-api-access-lh6rs",
            "projected": {
              "sources": [
                {
                  "serviceAccountToken": {
                    "expirationSeconds": 3607,
                    "path": "token"
                  }
                },
                {
                  "configMap": {
                    "name": "kube-root-ca.crt",
                    "items": [
                      {
                        "key": "ca.crt",
                        "path": "ca.crt"
                      }
                    ]
                  }
                },
                {
                  "downwardAPI": {
                    "items": [
                      {
                        "path": "namespace",
                        "fieldRef": {
                          "apiVersion": "v1",
                          "fieldPath": "metadata.namespace"
                        }
                      }
                    ]
                  }
                }
              ],
              "defaultMode": 420
            }
          }
        ],
        "containers": [
          {
            "name": "nginx",
            "image": "nginx",
            "resources": {},
            "volumeMounts": [
              {
                "name": "kube-api-access-lh6rs",
                "readOnly": true,
                "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
              }
            ],
            "terminationMessagePath": "/dev/termination-log",
            "terminationMessagePolicy": "File",
            "imagePullPolicy": "Always"
          }
        ],
        "restartPolicy": "Always",
        "terminationGracePeriodSeconds": 30,
        "dnsPolicy": "ClusterFirst",
        "serviceAccountName": "default",
        "serviceAccount": "default",
        "nodeName": "docker-desktop",
        "securityContext": {},
        "schedulerName": "default-scheduler",
        "tolerations": [
          {
            "key": "node.kubernetes.io/not-ready",
            "operator": "Exists",
            "effect": "NoExecute",
            "tolerationSeconds": 300
          },
          {
            "key": "node.kubernetes.io/unreachable",
            "operator": "Exists",
            "effect": "NoExecute",
            "tolerationSeconds": 300
          }
        ],
        "priority": 0,
        "enableServiceLinks": true,
        "preemptionPolicy": "PreemptLowerPriority"
      },
      "status": {
        "phase": "Running",
        "conditions": [
          {
            "type": "Initialized",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:15:57Z"
          },
          {
            "type": "Ready",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:16:05Z"
          },
          {
            "type": "ContainersReady",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:16:05Z"
          },
          {
            "type": "PodScheduled",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:15:57Z"
          }
        ],
        "hostIP": "192.168.65.4",
        "podIP": "10.1.0.100",
        "podIPs": [
          {
            "ip": "10.1.0.100"
          }
        ],
        "startTime": "2023-08-13T05:15:57Z",
        "containerStatuses": [
          {
            "name": "nginx",
            "state": {
              "running": {
                "startedAt": "2023-08-13T05:16:04Z"
              }
            },
            "lastState": {},
            "ready": true,
            "restartCount": 0,
            "image": "nginx:latest",
            "imageID": "docker-pullable://nginx@sha256:67f9a4f10d147a6e04629340e6493c9703300ca23a2f7f3aa56fe615d75d31ca",
            "containerID": "docker://61b89cc6c5899a16b490686a2c86469aaddb1288240ed95b4d4eb64a939fdc78",
            "started": true
          }
        ],
        "qosClass": "BestEffort"
      }
    }
  ]
}

 

 

As shown above, there is a slight difference between the two commands (whether the kind is List or PodList), but you can see that they are ultimately commands with the same content. 

 

3. Access kube API server with curl command

This time, let’s make the same request to the kube API server using the curl command rather than the kubectl command. Check the address of the kube API server to be called using the "kubectl config view" command to view the settings of the kubeconfig file. In the test environment, you can see that the address of the kube API server is "https://kubernetes.docker.internal:6443".

 

➜  [~] kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubernetes.docker.internal:6443
  name: docker-desktop
contexts:
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-desktop
current-context: docker-desktop
kind: Config
preferences: {}
users:
- name: docker-desktop
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

 

Let's call the API using the curl command using the address of the confirmed kube API server. Below is the command and its output.

 

➜  [~] curl -k -X GET https://kubernetes.docker.internal:6443/api/v1/namespaces/default/pods                                                                                       (docker-desktop/default)
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "pods is forbidden: User \"system:anonymous\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "kind": "pods"
  },
  "code": 403
}%

 

A 403 authorization error occurs because there is no "Authorization" header for authentication in the request header. This time, include the authentication token in the request and call it as follows. Below is the command and its output.

 

➜  [~] TOKEN=$(kubectl create token default)

curl -k --header "Authorization: Bearer ${TOKEN}" -X GET https://kubernetes.docker.internal:6443/api/v1/namespaces/default/pods
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "pods is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "kind": "pods"
  },
  "code": 403
}%

 

The message is a little different this time, but a 403 permission error still occurs. This is because the call was made with the token of the default service account in the default namespace, but the service account does not have permission to view the pod list.

 

This time, grant cluster-admin privileges to the default service account in the default namespace. (The command below grants administrator privileges for the K8s cluster to the default service account, so after testing, be sure to use the "kubectl delete clusterrolebinding testrolebinding" command. It is recommended to delete the binding.)

 

kubectl create clusterrolebinding testrolebinding --clusterrole cluster-admin --serviceaccount=default:default

 

Now, try calling the kube API server again with the token of the default service account. Below is the curl command and its output.

 

➜  [~] TOKEN=$(kubectl create token default)

curl -k --header "Authorization: Bearer ${TOKEN}" -X GET https://kubernetes.docker.internal:6443/api/v1/namespaces/default/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "65454"
  },
  "items": [
    {
      "metadata": {
        "name": "nginx",
        "namespace": "default",
        "uid": "8144c120-b73c-4786-9137-df0b938f034c",
        "resourceVersion": "61398",
        "creationTimestamp": "2023-08-13T05:15:57Z",
        "labels": {
          "run": "nginx"
        },
        "managedFields": [
          {
            "manager": "kubectl-run",
            "operation": "Update",
            "apiVersion": "v1",
            "time": "2023-08-13T05:15:57Z",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:metadata": {
                "f:labels": {
                  ".": {},
                  "f:run": {}
                }
              },
              "f:spec": {
                "f:containers": {
                  "k:{\"name\":\"nginx\"}": {
                    ".": {},
                    "f:image": {},
                    "f:imagePullPolicy": {},
                    "f:name": {},
                    "f:resources": {},
                    "f:terminationMessagePath": {},
                    "f:terminationMessagePolicy": {}
                  }
                },
                "f:dnsPolicy": {},
                "f:enableServiceLinks": {},
                "f:restartPolicy": {},
                "f:schedulerName": {},
                "f:securityContext": {},
                "f:terminationGracePeriodSeconds": {}
              }
            }
          },
          {
            "manager": "kubelet",
            "operation": "Update",
            "apiVersion": "v1",
            "time": "2023-08-13T05:16:05Z",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:status": {
                "f:conditions": {
                  "k:{\"type\":\"ContainersReady\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  },
                  "k:{\"type\":\"Initialized\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  },
                  "k:{\"type\":\"Ready\"}": {
                    ".": {},
                    "f:lastProbeTime": {},
                    "f:lastTransitionTime": {},
                    "f:status": {},
                    "f:type": {}
                  }
                },
                "f:containerStatuses": {},
                "f:hostIP": {},
                "f:phase": {},
                "f:podIP": {},
                "f:podIPs": {
                  ".": {},
                  "k:{\"ip\":\"10.1.0.100\"}": {
                    ".": {},
                    "f:ip": {}
                  }
                },
                "f:startTime": {}
              }
            },
            "subresource": "status"
          }
        ]
      },
      "spec": {
        "volumes": [
          {
            "name": "kube-api-access-lh6rs",
            "projected": {
              "sources": [
                {
                  "serviceAccountToken": {
                    "expirationSeconds": 3607,
                    "path": "token"
                  }
                },
                {
                  "configMap": {
                    "name": "kube-root-ca.crt",
                    "items": [
                      {
                        "key": "ca.crt",
                        "path": "ca.crt"
                      }
                    ]
                  }
                },
                {
                  "downwardAPI": {
                    "items": [
                      {
                        "path": "namespace",
                        "fieldRef": {
                          "apiVersion": "v1",
                          "fieldPath": "metadata.namespace"
                        }
                      }
                    ]
                  }
                }
              ],
              "defaultMode": 420
            }
          }
        ],
        "containers": [
          {
            "name": "nginx",
            "image": "nginx",
            "resources": {},
            "volumeMounts": [
              {
                "name": "kube-api-access-lh6rs",
                "readOnly": true,
                "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
              }
            ],
            "terminationMessagePath": "/dev/termination-log",
            "terminationMessagePolicy": "File",
            "imagePullPolicy": "Always"
          }
        ],
        "restartPolicy": "Always",
        "terminationGracePeriodSeconds": 30,
        "dnsPolicy": "ClusterFirst",
        "serviceAccountName": "default",
        "serviceAccount": "default",
        "nodeName": "docker-desktop",
        "securityContext": {},
        "schedulerName": "default-scheduler",
        "tolerations": [
          {
            "key": "node.kubernetes.io/not-ready",
            "operator": "Exists",
            "effect": "NoExecute",
            "tolerationSeconds": 300
          },
          {
            "key": "node.kubernetes.io/unreachable",
            "operator": "Exists",
            "effect": "NoExecute",
            "tolerationSeconds": 300
          }
        ],
        "priority": 0,
        "enableServiceLinks": true,
        "preemptionPolicy": "PreemptLowerPriority"
      },
      "status": {
        "phase": "Running",
        "conditions": [
          {
            "type": "Initialized",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:15:57Z"
          },
          {
            "type": "Ready",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:16:05Z"
          },
          {
            "type": "ContainersReady",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:16:05Z"
          },
          {
            "type": "PodScheduled",
            "status": "True",
            "lastProbeTime": null,
            "lastTransitionTime": "2023-08-13T05:15:57Z"
          }
        ],
        "hostIP": "192.168.65.4",
        "podIP": "10.1.0.100",
        "podIPs": [
          {
            "ip": "10.1.0.100"
          }
        ],
        "startTime": "2023-08-13T05:15:57Z",
        "containerStatuses": [
          {
            "name": "nginx",
            "state": {
              "running": {
                "startedAt": "2023-08-13T05:16:04Z"
              }
            },
            "lastState": {},
            "ready": true,
            "restartCount": 0,
            "image": "nginx:latest",
            "imageID": "docker-pullable://nginx@sha256:67f9a4f10d147a6e04629340e6493c9703300ca23a2f7f3aa56fe615d75d31ca",
            "containerID": "docker://61b89cc6c5899a16b490686a2c86469aaddb1288240ed95b4d4eb64a939fdc78",
            "started": true
          }
        ],
        "qosClass": "BestEffort"
      }
    }
  ]
}%

 

This time the response came normally. You can see that the contents of the response body are the same as the output of the "kubectl get --raw /api/v1/namespaces/default/pods" command above.

 

Although it has been described at length up to this point, the conclusion is that although there is a difference in the method whether you use the kubectl command, curl command, or any other method, ultimately interacting with k8s is through API communication with the kube API server.

 

4. kubernetes python client

In the same context as above, it is also possible to access k8s through python and control resources inside the cluster. To access k8s with python, of course, you can also call it through the requests library in the same way as interacting with the curl command above, but you can interact with k8s more easily by using the kubernetes python client library (hereinafter referred to as k8s python client).

 

First, install the k8s python client library using the pip command.

 

pip install kubernetes

 

Then, write a sample code that searches the list of pods in the default namespace as shown below. (In the sample code, the load_kube_config() function is used to read the system's default kubeconfig file (generally ~/.kube/config path) to obtain context information. Bring it.)

 

from kubernetes import client, config, dynamic
from kubernetes.client import api_client
import traceback

try:
    client = dynamic.DynamicClient(
        api_client.ApiClient(configuration=config.load_kube_config())
    )
    api_version = "v1"
    kind = "Pod"
    namespace = "default"
    api = client.resources.get(api_version=api_version, kind=kind)
    print(api.get(resource_version=0, namespace=namespace).items)
except:
    print("Error during list pod", flush=True)
    traceback.print_exc()

 

When running the sample script above, the output is displayed as follows.

 

➜  [~] python k8s_list_pod.py
[{'apiVersion': 'v1',
 'kind': 'Pod',
 'metadata': {'creationTimestamp': '2023-08-13T05:15:57Z',
 'labels': {'run': 'nginx'},
 'managedFields': [{'apiVersion': 'v1',
 'fieldsType': 'FieldsV1',
 'fieldsV1': {'f:metadata': {'f:labels': {'.': {}, 'f:run': {}}},
 'f:spec': {'f:containers': {'k:{"name":"nginx"}': {'.': {},
 'f:image': {},
 'f:imagePullPolicy': {},
 'f:name': {},
 'f:resources': {},
 'f:terminationMessagePath': {},
 'f:terminationMessagePolicy': {}}},
 'f:dnsPolicy': {},
 'f:enableServiceLinks': {},
 'f:restartPolicy': {},
 'f:schedulerName': {},
 'f:securityContext': {},
 'f:terminationGracePeriodSeconds': {}}},
 'manager': 'kubectl-run',
 'operation': 'Update',
 'time': '2023-08-13T05:15:57Z'},
                   {'apiVersion': 'v1',
 'fieldsType': 'FieldsV1',
 'fieldsV1': {'f:status': {'f:conditions': {'k:{"type":"ContainersReady"}': {'.': {},
 'f:lastProbeTime': {},
 'f:lastTransitionTime': {},
 'f:status': {},
 'f:type': {}},
 'k:{"type":"Initialized"}': {'.': {},
 'f:lastProbeTime': {},
 'f:lastTransitionTime': {},
 'f:status': {},
 'f:type': {}},
 'k:{"type":"Ready"}': {'.': {},
 'f:lastProbeTime': {},
 'f:lastTransitionTime': {},
 'f:status': {},
 'f:type': {}}},
 'f:containerStatuses': {},
 'f:hostIP': {},
 'f:phase': {},
 'f:podIP': {},
 'f:podIPs': {'.': {}, 'k:{"ip":"10.1.0.100"}': {'.': {}, 'f:ip': {}}},
 'f:startTime': {}}},
 'manager': 'kubelet',
 'operation': 'Update',
 'subresource': 'status',
 'time': '2023-08-13T05:16:05Z'}],
 'name': 'nginx',
 'namespace': 'default',
 'resourceVersion': '61398',
 'uid': '8144c120-b73c-4786-9137-df0b938f034c'},
 'spec': {'containers': [{'image': 'nginx',
 'imagePullPolicy': 'Always',
 'name': 'nginx',
 'resources': {},
 'terminationMessagePath': '/dev/termination-log',
 'terminationMessagePolicy': 'File',
 'volumeMounts': [{'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount',
 'name': 'kube-api-access-lh6rs',
 'readOnly': True}]}],
 'dnsPolicy': 'ClusterFirst',
 'enableServiceLinks': True,
 'nodeName': 'docker-desktop',
 'preemptionPolicy': 'PreemptLowerPriority',
 'priority': 0,
 'restartPolicy': 'Always',
 'schedulerName': 'default-scheduler',
 'securityContext': {},
 'serviceAccount': 'default',
 'serviceAccountName': 'default',
 'terminationGracePeriodSeconds': 30,
 'tolerations': [{'effect': 'NoExecute',
 'key': 'node.kubernetes.io/not-ready',
 'operator': 'Exists',
 'tolerationSeconds': 300},
                 {'effect': 'NoExecute',
 'key': 'node.kubernetes.io/unreachable',
 'operator': 'Exists',
 'tolerationSeconds': 300}],
 'volumes': [{'name': 'kube-api-access-lh6rs',
 'projected': {'defaultMode': 420,
 'sources': [{'serviceAccountToken': {'expirationSeconds': 3607, 'path': 'token'}},
             {'configMap': {'items': [{'key': 'ca.crt', 'path': 'ca.crt'}], 'name': 'kube-root-ca.crt'}},
             {'downwardAPI': {'items': [{'fieldRef': {'apiVersion': 'v1', 'fieldPath': 'metadata.namespace'},
 'path': 'namespace'}]}}]}}]},
 'status': {'conditions': [{'lastProbeTime': None,
 'lastTransitionTime': '2023-08-13T05:15:57Z',
 'status': 'True',
 'type': 'Initialized'},
                {'lastProbeTime': None,
 'lastTransitionTime': '2023-08-13T05:16:05Z',
 'status': 'True',
 'type': 'Ready'},
                {'lastProbeTime': None,
 'lastTransitionTime': '2023-08-13T05:16:05Z',
 'status': 'True',
 'type': 'ContainersReady'},
                {'lastProbeTime': None,
 'lastTransitionTime': '2023-08-13T05:15:57Z',
 'status': 'True',
 'type': 'PodScheduled'}],
 'containerStatuses': [{'containerID': 'docker://61b89cc6c5899a16b490686a2c86469aaddb1288240ed95b4d4eb64a939fdc78',
 'image': 'nginx:latest',
 'imageID': 'docker-pullable://nginx@sha256:67f9a4f10d147a6e04629340e6493c9703300ca23a2f7f3aa56fe615d75d31ca',
 'lastState': {},
 'name': 'nginx',
 'ready': True,
 'restartCount': 0,
 'started': True,
 'state': {'running': {'startedAt': '2023-08-13T05:16:04Z'}}}],
 'hostIP': '192.168.65.4',
 'phase': 'Running',
 'podIP': '10.1.0.100',
 'podIPs': [{'ip': '10.1.0.100'}],
 'qosClass': 'BestEffort',
 'startTime': '2023-08-13T05:15:57Z'}}]

 

This time, let's create a pod named nginx-test that runs the nginx:latest image in the default namespace.

 

from kubernetes import client, config, dynamic
from kubernetes.client import api_client
import traceback
import json
    
try:
    client = dynamic.DynamicClient(
        api_client.ApiClient(configuration=config.load_kube_config())
    )  
    manifest = '{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-test","creationTimestamp":null,"labels":{"run":"nginx"}},"spec":{"containers":[{"name":"nginx-test","image":"nginx:latest","resources":{}}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"},"status":{}}'
    api_version = "v1"
    kind = "Pod"
    namespace = "default"
    resource_manifest_dict = json.loads(manifest)
    api = client.resources.get(api_version=api_version, kind=kind)
    print(api.create(body=resource_manifest_dict, namespace=namespace))
except:
    print("Error during create pod", flush=True)
    traceback.print_exc()

 

When running the sample script above, the output is displayed as follows. And check the created pod.

 

➜  [~] python k8s_create_pod.py
ResourceInstance[Pod]:
  apiVersion: v1
  kind: Pod
  metadata:
    creationTimestamp: '2023-08-13T07:30:38Z'
    labels:
      run: nginx
    managedFields:
    - apiVersion: v1
      fieldsType: FieldsV1
      fieldsV1:
        f:metadata:
          f:labels:
            .: {}
            f:run: {}
        f:spec:
          f:containers:
            k:{"name":"nginx-test"}:
              .: {}
              f:image: {}
              f:imagePullPolicy: {}
              f:name: {}
              f:resources: {}
              f:terminationMessagePath: {}
              f:terminationMessagePolicy: {}
          f:dnsPolicy: {}
          f:enableServiceLinks: {}
          f:restartPolicy: {}
          f:schedulerName: {}
          f:securityContext: {}
          f:terminationGracePeriodSeconds: {}
      manager: OpenAPI-Generator
      operation: Update
      time: '2023-08-13T07:30:38Z'
    name: nginx-test
    namespace: default
    resourceVersion: '71336'
    uid: d42a3181-6191-476a-bb94-0cacaab67f65
  spec:
    containers:
    - image: nginx:latest
      imagePullPolicy: Always
      name: nginx-test
      resources: {}
      terminationMessagePath: /dev/termination-log
      terminationMessagePolicy: File
      volumeMounts:
      - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
        name: kube-api-access-xsp2s
        readOnly: true
    dnsPolicy: ClusterFirst
    enableServiceLinks: true
    preemptionPolicy: PreemptLowerPriority
    priority: 0
    restartPolicy: Always
    schedulerName: default-scheduler
    securityContext: {}
    serviceAccount: default
    serviceAccountName: default
    terminationGracePeriodSeconds: 30
    tolerations:
    - effect: NoExecute
      key: node.kubernetes.io/not-ready
      operator: Exists
      tolerationSeconds: 300
    - effect: NoExecute
      key: node.kubernetes.io/unreachable
      operator: Exists
      tolerationSeconds: 300
    volumes:
    - name: kube-api-access-xsp2s
      projected:
        defaultMode: 420
        sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token
        - configMap:
            items:
            - key: ca.crt
              path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
            - fieldRef:
                apiVersion: v1
                fieldPath: metadata.namespace
              path: namespace
  status:
    phase: Pending
    qosClass: BestEffort
    
➜  [~] kubectl get pod nginx-test
NAME         READY   STATUS    RESTARTS   AGE
nginx-test   1/1     Running   0          17s

 

 

Similar to the above, operations such as get, create, delete, and patch can be performed, so refer to the k8s python client github for more details.

 

5. In conclusion

Through simple samples, we took time to reflect and understand how users interact with the kube API server. Additionally, if you apply the k8s python client library well, you can create your own application that manages the k8s cluster according to user-specific usage patterns using functions that are not included in the management functions provided by existing k8s and various tools.