Kubernetes Series: Getting practical with the Kubernetes API

Sylia CHIBOUB
Level Up Coding
Published in
13 min readMar 30, 2023

--

Introduction

This articles explains the structure and the functioning of Kubernetes API. Section 1 gives an overview of the Kubernetes API. Section 2 explains the requirements needed to follow up with this article . Furthermore, Section 3 explains the Kubernetes API terminology. Moreover, Section 4 goes up through some practical examples. Section 5 covers more advanced queries. Finally, this article ends with a conclusion.

1. API Overview

Kubernetes (k8s) in an API driven client/server architecture where the API Server exposes an HTTP API that lets end users, different parts of your cluster, and external components communicate with one another. [1]

For a better understanding of this article, basic knowledge of k8s architecture is required.

It is worth remembering that the API server is the single entry to the k8s cluster. It is the only component responsible for receiving clients queries. and forwarding them to the rest of k8s components then responding to the clients.

The k8s components that the API server communicates with are the following:

Kubernetes Architecture
  • The Controller-Manager which contains k8s controllers (deployments, statefulsetsetc.) responsible for keeping the current state of k8s objects in sync with the declared desired state.
  • The etcd a consistent and highly-available b+tree key-value store. that logs the cluster’s state, networking and other important information about the cluster.
  • The Kube Scheduler which determines based on the information stored in the etcd on which node to schedule a pod.

k8s clients (users) can query and manipulate the state of API objects in k8s using client libraries, or by making REST requests. For example, the following can be used:

  • kubectl is a command line tool provided by k8s. It establishes communication with the API server and makes API calls with it in our behalf so that we can easily access and manage k8s API resources.
  • curl is a command line tool designed to exchange data with a server. In k8s, the server is the k8s API server.

The default serialization for k8s API calls is JSON. Eventhough we tend to use YAML format, It is important to know that YAML is converted to and from JSON.

For a communication to be established with the API-server. The API-server performs authentication , authorization and admission control operations for each API call.

  • authentication is established when a client presents a valid TLS certificate ( a valid TLS certificate is a certificate signed by the cluster’s CA and trusted by the API server ).
  • authorization is established through RBAC (Role, RoleBinding, ClusterRole, ClusterRoleBinding) that specifies if the client can or cannot perform CRUD operations (verbs) on certain k8s objects. In this step, the client identity is determined from the common name field in the subject of the certificate for example CN=Alice .
  • admission control is established if the CRUD operationperformed by the client on the k8s object is legitimate. For example, when the cluster has a ResourceQuota object that limits the number of podsthat can be created in a namespace dev to 2. If the user tries to create 3d pod then its request will be rejected.

It is worth noting that k8s does not have objects which represent clients accounts. clients cannot be added to a cluster through an API call.

2. Requirements

When curl is used. a client passes its TLS certificates and the cluster’s CA on command line as follows:

curl $server/api/v1/ --cacert ./ca.crt --cert ./client.crt --key ./client.key

When kubectl is used, It uses a config file already configured by k8s admin which contains clients TLS certificates and the cluster’s CA. This file should be available under $HOME/.kube/config

cat $HOME/.kube/config

For a better understanding of k8s APIs , curl command will be used through out this article.

For the seek of simplicity and to avoid dealing with HTTPS and TLS certificates for authentication. The kubectl proxy command will be used to create a proxy server.

The kubectl proxy command creates a proxy server or application-level gateway between localhost and the Kubernetes API server. It also allows serving static content over specified HTTP path. All incoming data enters through one port and gets forwarded to the remote Kubernetes API server port.

  • To create a proxy server, simply run:
kuebctl proxy
  • To communicate with the API Server, simply run:
curl http://127.0.0.1:8001
  • For a better JSON formatting and visualization, the jq command will be used as follows:
curl http://127.0.0.1:8001 | jq '.'

3. API terminology

3.1. API Resources Types and Objects

Kubernetes API operates in term of resources. But what is a resource ?

A resource is a unique representation of a k8s object. Each resource has its own representation. This representation is known as a resource type (kind ).

A resource type (kind ) can be pods, configmaps, services etc. You can see from the underlying code that resources of different kinds are represented differently.

# an example of an nginx pod resource
# kubectl run nginx --image=nginx --dry-run -o yaml > pod.yaml
# pod.yaml

apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
restartPolicy: Always
# an example of an nginx configmap resource
# kubectl create configmap nginx --dry-run -o yaml > configmap.yaml
# configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: nginx

A list of instances of a resource type is known as a collection. Moreover, a single instance of a resource type is called a resource, and also usually represents an object.

All objects created via the API have a unique object name to allow idempotent creation and retrieval.

3.2. API Resources Verbs

Different resource types exposes different CRUD verbs (create, read, update,delete) that can be performed on objects they manage. Almost all object resource types support the standard HTTP verbs — GET, POST, PUT, PATCH, and DELETE.

Kubernetes also uses its own verbs, which are often written lowercase to distinguish them from HTTP verbs:list ,get , watch , create , update , delete , create , patch

  • list : returns a collection of resources.
  • get : returns a single resource
  • watch : continuously list and get resources in real time. It is like the Linux watch command. Thus, when defining RBACverbs, it is worth noting that watch cannot be put alone. It must be put along with get and list verbs. If you sent an HTTP GET request with the ?watch query parameter, k8s calls this a watchand not a get .
  • For PUT requests, Kubernetes internally classifies these as either createor updatebased on the state of the existing object.

3.3. API Groups

Different resource types belong to different API groups. The API group is specified in a REST path and in the apiVersion field of a serialized object.

The API groups can be classified as either core groups or named groups.

k8s API Groups Categories

3.3.1. The core group

The core group is found at REST path /api/v1.

k8s API Core Group

The core group is specified as part of the apiVersion field. For example: apiVersion: v1.

# an example of an nginx configmap resource
apiVersion: v1 # here
kind: ConfigMap
metadata:
name: nginx

3.3.2 The named group

The named groups are at REST path /apis/$GROUP_NAME/$VERSION/$PLURALNAME

k8s API Named Group

The named group is specified as part of the apiVersion field as apiVersion: $GROUP_NAME/$VERSION . For example:apiVersion: batch/v1

- apiVersion: batch/v1 #here
kind: CronJob
metadata:

Each API group within the named group can have different versions.

  • Alpha: (i.e v1alpha1) This version present many bugs as it is not tested yet. it is disabled by default. Alpha features are susceptible to change or disappear at any time.
  • Beta: (i.e v1beta1) This is enabled by default as it has been tested. but It still presents some bugs.
  • Stable: (i.e v1) This version is stable. It is enabled by default.

Resources type of the core group are always under the v1 version.

Make sure to always use objects that belong to a stable API group. For example:

We have an object of resource type CronJob that belongs to the API group batch classified under the named group and available under two version: v1 and v1beta1 . Make sure, in such case, to always use v1 if it is available ( by default, k8s consider v1 as its preferred version ).

- apiVersion: batch/v1        # this is the perefered version
kind: CronJob
metadata:

...
- apiVersion: batch/v1alpha1 # this is an available version but not the most
# perefered
kind: CronJob
metadata:

4. API Calls Exploration

Let’s list the available k8s API in our k8s cluster

curl http://127.0.0.1:8001 | jq '.'

# OUTPUT :
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/batch",
"/apis/batch/v1",
"/apis/storage.k8s.io",
"/apis/storage.k8s.io/v1",
...
...

# REMEMBER :
# core group: /api/v1
# named group: /apis/$GROUP_NAME/$VERSION
# EXPLANATION :
# We can see that we have different resource types grouped into different
# groups, Some of these groups are made available under the core group and
# others under the named group and are available under a specific version.
# EXAMPLE :
# The resource group: batch is available under the named group (apis). it
# groups a certain resource types of version v1.

4.1. The core Group

  • Let’s explore an API core group
curl http://127.0.0.1:8001/api | jq '.'

# OUTPUT
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "X.X.X.X/x",
"serverAddress": "X.X.X.X/X"
}
]
}
# EXPLANATION
# we can see that the only version available for the resource types under
# this API GROUP is v1. Thus, all resources of these group are of version v1.
  • Let’s list resource-types of version v1under the core API group.
curl http://127.0.0.1:8001/api/v1 | jq '.resources[].name'

# OUTPUT :
...
"configmaps"
...
"pods"
"namespaces"
...
  • Let’s list collection or objects or resources available under the resource type pods
# get pods
curl http://127.0.0.1:8001/api/v1/pods | jq .items[].metadata.name

# get information about a specific pod using its name
curl http://127.0.0.1:8001/api/v1/pods/ | jq '.items[].metadata | select(.name=="nginx-a1Gv0")'
  • Let’s list collection or objects or resources available under the resource type namespace
# get namespaces
curl http://127.0.0.1:8001/api/v1/namespaces | jq .items[].metadata.name

# get information about a specific namespace using its name
curl http://127.0.0.1:8001/api/v1/namespaces/proxy| jq .
# get a list of existing pods within the selected namespace: proxy
# you can see that we've used a combination of the namespaces and pods resource types
curl http://127.0.0.1:8001/api/v1/namespaces/proxy/pods | jq .items[].metadata.name
# get information of a specific pod within the selected namespace : proxy
curl http://127.0.0.1:8001/api/v1/namespaces/proxy/pods | jq '.items[].metadata | select(.name=="nginx-a1Gv0")'

You can derive from the above examples that information about the pod nginx-a1Gv0 have been obtained using different REST Paths:

# get information of a specific pod within the selected namespace : proxy
curl http://127.0.0.1:8001/api/v1/namespaces/proxy/pods | jq '.items[].metadata | select(.name=="nginx-a1Gv0")'
# get information about a specific pod using its name
curl http://127.0.0.1:8001/api/v1/pods/ | jq '.items[].metadata | select(.name=="nginx-a1Gv0")'

4.2. The named Group

  • Let’s list the API groups made available under the named group
# REMEMBER :
# core group: /api/v1
# named group: /apis/$GROUP_NAME/$VERSION
# Let's list the groups available under the named group.
curl http://127.0.0.1:8001/apis/| jq .groups[].name

# OUTPUT :
...
"apps"
...
"batch"
...
"storage.k8s.io"
...
# EXPLANATION :
# We can see that we have different API groups available under the named group.

API groups are made available under a specific version or under a set of different versions with one specified as the preferred one. Thus, used by default in the apiVersion field of an object. Let’s explore versions of an API group

curl http://127.0.0.1:8001/apis/batch/ | jq . 

# OUTPUT
{
"kind": "APIGroup",
"apiVersion": "v1",
"name": "batch",
"versions": [
{
"groupVersion": "batch/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "batch/v1",
"version": "v1"
}
}
# EXPLANATION
# The api group batch avaialble under the named group has version v1 which
# is also the preferred version. Thus, resources types avaiable under the api
# Group batch are of version v1.
  • Let’s list the resources types made available under the API group batch of version v1 .
curl http://127.0.0.1:8001/apis/batch/v1 | jq .resources[].name

# OUTPUT
"cronjobs"
"cronjobs/status"
"jobs"
"jobs/status"
# EXPLANATION
# resources types available under the API group batch of type v1 are cronjobs
# and jobs. These resources can be accessed using their REST PATH /cronjobs
# and can be explored further using their REST PATH and REST SUBPATH
# /cronjobs/status
  • Let’s list collections, resources , objects made available under the resources type cronjobsof the API group batch of version v1 .
curl http://127.0.0.1:8001/apis/batch/v1/cronjobs | jq .items[].metadata.name

# OUTPUT
"daily-cronjob-database-backuper"
"..."
"..."
# Explanation
# These are the list of running cronjobs objects

4.3. The resources types definition

Let’s explore how an API resource type is defined.

# Let's explore the api resources type available under the core group

# List the resources type
curl http://127.0.0.1:8001/api/v1/| jq .
# Select only resources of type pods
curl http://127.0.0.1:8001/api/v1/ | jq '.resources[] | select(.name=="pods")'
# OUTPUT
{
"name": "pods",
"singularName": "",
"namespaced": true,
"kind": "Pod",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"shortNames": [
"po"
],
"categories": [
"all"
],
"storageVersionHash": "xxxxxx"
}

From the above output we can derive that each resource type is defined by :

  • a plural name defined in the name field. It is the plural name that is available as an HTTP endpoint. We make API Calls with the available k8s resource types through the use of their plural names .
  • a kind defined in the kind field. It defines the resource type’s
  • a singular name defined in the singularName field.
  • a list of verbs defined in the verbsfield. It defines the list of operations that can be performed on collections, objects or resources managed by this resource type .
  • a list of short namesdefined in the shotNamesfield. It allows to query the resource type using its short name. For example:
kubectl get po # instead of: kubectl get pods
kubectl get deploy # intead of : kubectl get deployments

Finally, the nameSpacedfield defines whether or not the resource type is namespaced or cluster scoped.

For example:

objects of resource type pod are namespaced. To create or manage pods objects we should specify a namespace otherwise k8s consider them as part of the default namespace.

kubectl get pods -n develop

objects of resource type pv [persistent storage] are cluster scoped. To create or managepvs we don’t need to specify a namespace. k8s consider them as cluster scoped resources.

kubectl get pv

A simple way to check for API resources types names, shortnames, version, kind and whether or not they’re namespaced. You can use the following command. It performs API Calls with the API-server on our behalf to get these information.

kubectl api-resources

# watch the API Calls the kubectl establish with the API-server
kubectl api-resources -v 10

5. API Calls Advanced Queries

5.1 Using kubectl along with jsonPathand jq [5]

Note:

# Brief Resume of kubectl JsonPath and jq Operators: more can be found in [5]

- {"\n"} : the escape sequence
- {"\t"} : the space sequence
- range, end : to iterate a list of elements
- [,] : the union operator. for example, get two elements [a,b]
- [start:end:step]: subscript operator
- * : wildcard. Get all objects
- .. : recursive descent
- . or [] : child operator
- select () : to filter elements
- keys : to list json file keys for further queries
  • get the possible json attribute to query of the pod k8s resource
kubectl get pod -o json | jq 'keys'

# OUTPUT
[
"apiVersion",
"items",
"kind",
"metadata"
]
# Explanation
# We can see that we can query the above attributes, for example:
# If we want to list pods within the default namespace:

kubectl get pod -o json | jq '.items[]'

# Let's get the possible attributes to query for each pod within the
# list of items

kubectl get pod -o json | jq '.items[]' | jq 'keys'
kubectl get pod -o json | jq '.items[].metadata' | jq 'keys'
kubectl get pod -o json | jq '.items[].metadata.name'
  • get the name of pods within the default namespace
# option 1
kubectl get pod -o=jsonpath='{.items[*].metadata.name}'

# option 2
kubectl get pod -o=jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
  • get containers and image names of the nginx pod within the default namespace.

Important

JSONPath regular expressions are not supported. If you want to match using regular expressions, you can use a tool such as jq.

 kubectl get pods -o json | jq '.items[] | select (.metadata.name == "nginx") | [.spec.containers[].name, .spec.containers[].image]'
  • get containers and images names of the pod containing the nginx pattern in their names within the default namespace.
kubectl get pods -o json | jq '.items[] | select (.metadata.name | test ("nginx")) | [.spec.containers[].name, .spec.containers[].image]'
  • get pods names within the default namespaces and their corresponding starting date.
kubectl get pod -o json | jq '.items[] | [.metadata.name, .status.startTime]'
  • order pods by their staring time
kubectl get pod -o json | jq '.items[] | .status.startTime | sort_by(.startTime)'

# ERROR
Cannot iterate over string.

# COMMENT
The idea here is just to show the sort_by function usage.
  • for every pod within the default namespace, get its name, its requierement in terms of cpu, ram and derive its quality of service (qos)
kubectl get pod -o json | jq '.items[] | [.metadata.name, .spec.containers[].resources, .status.qosClass]'
# Additional Queries 

kubectl get pod -o json | jq .items[].spec.containers[].resources
kubectl get pod -o json | jq .items[].spec.containers[].resources.requests
kubectl get pod -o json | jq .items[].spec.containers[].resources.requests.cpu
kubectl get pod -o json | jq .items[].spec.containers[].resources.requests.memory
kubectl get pod -o json | jq .items[].spec.containers[].resources.limits
kubectl get pod -o json | jq .items[].spec.containers[].resources.limits.cpu
kubectl get pod -o json | jq .items[].spec.containers[].resources.limits.memory
kubectl get pod -o json | jq .items[].status.qosClass

Conclusion

The primary goal of this article was to give a deeper understanding of the Kubernetes API functioning through practical examples. This has been accomplished through giving a general overview about the k8s API then getting more practical through different examples using the curl and kubectlcommands.

References

[1] The Kubernetes API. (2022, October 24). Kubernetes. https://kubernetes.io/docs/concepts/overview/kubernetes-api/

[2] API Overview. (n.d.). Kubernetes. https://kubernetes.io/docs/reference/using-api/

[3] Kubernetes API Concepts. (2022, December 26). Kubernetes. https://kubernetes.io/docs/reference/using-api/api-concepts/

[4] KodeKloud. (2021, October 26). Kubernetes API Fundamentals You Must Know! [Video]. YouTube. https://www.youtube.com/watch?v=_65Md2qar14

[5] JSONPath Support. (2022, November 4). Kubernetes. https://kubernetes.io/docs/reference/kubectl/jsonpath/

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job

--

--