Access K8s API within Container

Modern applications are transformed to microservice based, API driven architecture. When the application is containerized, it needs to access other applications’ published APIs and K8s cluster API for its business logic.

In this article, an python application is containerized, its container image is built and pushed into docker register, an kubernetes manifest is created and deployed into K8s cluster and the containerized python application will access K8s cluster APIs to get cluster’s deployed pods’ configuration information. Each application running within containers of pods needs to be authenticated and authorized by K8s API server before each API access. When the application is containerized and deployed into Kubernetes cluster, each pod belongs to a specific namespace with its according service account (SA). Each service account has its own authentication secret, which can be used to authenticate with K8s API server. Role Based Access Control (RBAC) can then be used to control the authorization of associated service accounts. This service account secret is mounted to a pre-configured path, which can be accessed by the application running within the pod. The Kubernetes python package hides all those details from user python application code to make all those complicated processes easier to achieve.

Step One: Build the container image for the python application

Create a Dockerfile with following content :

controlplane $ cat Dockerfile 
FROM ubuntu
RUN apt-get update
FROM python:latest
RUN mkdir /src
COPY . /src
WORKDIR /src
RUN pip install -r requirements.txt
controlplane $ cat requirements.txt 
Kubernetes

Create following python program in the same directory of above Dockerfile based on this example:

from kubernetes import client, config

def main():
    config.load_incluster_config()

    v1 = client.CoreV1Api()
    print("Listing pods with their IPs:")
    ret = v1.list_pod_for_all_namespaces(watch=False)
    for i in ret.items:
        print("%s\t%s\t%s" %
              (i.status.pod_ip, i.metadata.namespace, i.metadata.name))

if __name__ == '__main__':
    main()

Then, build the docker image and push it to the docker registry:

controlplane $ docker build -t podapiaccess:v1 .
controlplane $ docker tag podapiaccess:v1 twintiger/podapiaccess:v1
controlplane $ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: <Your Docker Registry User Name>
Password: <Your according password>

Login Succeeded
controlplane $ docker push twintiger/podapiaccess:v1
The push refers to repository [docker.io/twintiger/podapiaccess]
084f4d130a72: Pushed 
d32ebda4ee0b: Pushed 
db8d0fe6cf95: Mounted from library/python 
00901a4c6fc7: Mounted from library/python 
7e7decd61f68: Mounted from library/python 
aedcb370b058: Mounted from library/python 
c3a0d593ed24: Mounted from library/python 
26a504e63be4: Mounted from library/python 
8bf42db0de72: Mounted from library/python 
31892cc314cb: Mounted from library/python 
11936051f93b: Mounted from library/python 
v1: digest: sha256:795b7425e878f4478147a6c136eba6994fc0113cf8c7537fbe42954fb1257582 size: 2631
controlplane $ 

Step Two: Test the newly built docker image

Test the above docker image by following steps:

controlplane $ docker run twintiger/podapiaccess:v1
Unable to find image 'twintiger/podapiaccess:v1' locally
v1: Pulling from twintiger/podapiaccess
0e29546d541c: Pull complete 
9b829c73b52b: Pull complete 
cb5b7ae36172: Pull complete 
6494e4811622: Pull complete 
6f9f74896dfa: Pull complete 
fcb6d5f7c986: Pull complete 
290438add9da: Pull complete 
ab11df61f44a: Pull complete 
de4793a5fa46: Pull complete 
b7c69d9f5717: Pull complete 
4d8c3388b53a: Pull complete 
ceea8c40c3ea: Pull complete 
Digest: sha256:5c800bd260a1cea55d71c4454166c49597c8f35d3520eadf7e82e8643f198cbf
Status: Downloaded newer image for twintiger/podapiaccess:v1
Traceback (most recent call last):
  File "/src/podapiaccess.py", line 16, in <module>
    main()
  File "/src/podapiaccess.py", line 5, in main
    config.load_incluster_config()
  File "/usr/local/lib/python3.10/site-packages/kubernetes/config/incluster_config.py", line 121, in load_incluster_config
    try_refresh_token=try_refresh_token).load_and_set(client_configuration)
  File "/usr/local/lib/python3.10/site-packages/kubernetes/config/incluster_config.py", line 54, in load_and_set
    self._load_config()
  File "/usr/local/lib/python3.10/site-packages/kubernetes/config/incluster_config.py", line 62, in _load_config
    raise ConfigException("Service host/port is not set.")
kubernetes.config.config_exception.ConfigException: Service host/port is not set.
controlplane $ 

Based on the above testing, the python code failed. Why ? The authentication with K8s API server failed. There is no corresponding authentication secret which is needed for the python application code to be authenticated with K8s API server.

This piece of python container code must be deployed into Kubernetes cluster, running from pod to make authentication process success.

Step Three: Deploy the containerized python application inside Kubernetes cluster

Create following Kubernetes deployment manifest:

controlplane $ cat deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-k8s-api
spec:
  selector:
    matchLabels:
      app: hello-k8s-api
  replicas: 1 
  template:
    metadata:
      labels:
        app: hello-k8s-api
    spec:
      containers:
      - name: hello-api
        image: twintiger/podapiaccess:v1
        command: ["/bin/sleep", "3650d"]
        imagePullPolicy: IfNotPresent
      restartPolicy: Always

Deploy above manifest and verify its status by following procedures:

controlplane $ kubectl apply -f deployment.yaml 
deployment.apps/hello-k8s-api created

controlplane $ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
hello-k8s-api-5769fb66fb-gtb2f   1/1     Running   0          29s

Test the python Kubernetes API access code by:

controlplane $ kubectl exec -it hello-k8s-api-5d5699b6b-b4v45 -- bash
root@hello-k8s-api-5d5699b6b-b4v45:/src# pwd
/src
root@hello-k8s-api-5d5699b6b-b4v45:/src# ls
Dockerfile  requirements.txt
podapiaccess.py

root@hello-k8s-api-5d5699b6b-b4v45:/src# python podapiaccess.py
Listing pods with their IPs:
10.32.0.193     default hello-k8s-api-5d5699b6b-b4v45
10.88.0.2       kube-system     coredns-fb8b8dccf-fmrd8
10.88.0.3       kube-system     coredns-fb8b8dccf-tzv4p
172.17.0.24     kube-system     etcd-controlplane
10.88.0.4       kube-system     katacoda-cloud-provider-776f5d7cb-g6r57
172.17.0.24     kube-system     kube-apiserver-controlplane
172.17.0.24     kube-system     kube-controller-manager-controlplane
172.17.0.26     kube-system     kube-keepalived-vip-r5xxf
172.17.0.26     kube-system     kube-proxy-5qdzn
172.17.0.24     kube-system     kube-proxy-wnzl6
172.17.0.24     kube-system     kube-scheduler-controlplane
172.17.0.26     kube-system     weave-net-9pjqm
172.17.0.24     kube-system     weave-net-wjt9x

The python K8s API access code works and the IP address of all pods are listed.

Now, let’s dig into the deployment and its according pod:

controlplane $ kubectl get deployments
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
hello-k8s-api   1/1     1            1           5m44s
controlplane $ kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
hello-k8s-api-5d5699b6b-b4v45   1/1     Running   0          5m50s
controlplane $ kubectl logs pod/hello-k8s-api-5d5699b6b-b4v45
controlplane $ kubectl describe pod/hello-k8s-api-5d5699b6b-b4v45
Name:               hello-k8s-api-5d5699b6b-b4v45
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               node01/172.17.0.26
Start Time:         Fri, 31 Dec 2021 19:12:46 +0000
Labels:             app=hello-k8s-api
                    pod-template-hash=5d5699b6b
Annotations:        <none>
Status:             Running
IP:                 10.32.0.193
Controlled By:      ReplicaSet/hello-k8s-api-5d5699b6b
Containers:
  hello-api:
    Container ID:  docker://750c20dd890cff333ccbb44de4b1be9ea59f73ffd7a9edf34474b30e6d4a8101
    Image:         twintiger/podapiaccess:v1
    Image ID:      docker-pullable://twintiger/podapiaccess@sha256:e809bd1c327941f9de5d4306c9cc9e507b84d0a3020fe8cd0c8dbe01736e058e
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sleep
      3650d
    State:          Running
      Started:      Fri, 31 Dec 2021 19:13:23 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-64mfb (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-64mfb:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-64mfb
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  6m17s  default-scheduler  Successfully assigned default/hello-k8s-api-5d5699b6b-b4v45 to node01
  Normal  Pulling    6m16s  kubelet, node01    Pulling image "twintiger/podapiaccess:v3"
  Normal  Pulled     5m40s  kubelet, node01    Successfully pulled image "twintiger/podapiaccess:v1"
  Normal  Created    5m40s  kubelet, node01    Created container hello-api
  Normal  Started    5m40s  kubelet, node01    Started container hello-api
controlplane $ 

Pay special attention to the following information of the pod within the deployment:

Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-64mfb (ro)

This secret is the service account’s secret within the namespace, which is used by pod to authenticate with K8s API server.

Keyuan Zhang

Published by Keyuan Zhang

Professional with intensive industry experience and knowledge on Cloud Computing, IoT and Embedded System.

Leave a comment