Building stuff with the Kubernetes API (part 1) — Exploring API objects

Vladimir Vivien
Programming Kubernetes
5 min readFeb 27, 2018

--

Kubernetes is a formidable platform on (and with) which you can create all sorts of tools. Fortunately, there are many options when it comes to programming against the Kubernetes APIs. These options, however, can be overwhelming which can leave potential developers with no clear directions.

In this multipart series I will highlight the extensibility of Kubernetes as a platform. We will walkthrough the different options that are available when building solutions using the Kubernetes platform, from simple tools to extending Kubernetes with custom API types.

In this series, it is assume that you have installed, worked with, and understand the fundamentals of Kubernetes including using its kubectl command line tool.

When building tools around Kubernnetes platform, it is important to understand the geography of its API landscape. Before we write any code, let us look at ways to explore and work with API objects directly without any programming languages (that will come in later posts).

An API-first architecture

A Kubernetes cluster is comprised of several loosely-coupled components. Central to the cluster is the API server which manages access to stored (versioned) objects that represent the state of the cluster.

Control of the Kubernetes cluster can be achieved declaratively by creating or mutating existing objects (stored in etcd) that represent different aspects of Kubernetes. Cluster components and tools can observe these changes, in a reconciliation loop, and mutate the cluster itself until it reaches the desired state.

Read more about the Kubernetes API convention here.

Accessing API objects with kubectl get

One of the easiest ways to see the guts of the Kubernetes API objects is to use the kubectl get command with the raw flag. This lets you to explore your cluster’s API landscape without the need of additional tools. For instance, you can issue the following command to get a list of all API paths on the cluster:

$ kubectl get --raw /
{
"paths": [
"/api",
"/api/v1",
"/apis",
...
"/ui",
"/ui/",
"/version"
]
}

Using kubectl, you can explore all accessible objects for inspections. For instance, the following returns a NodeList resource which is a collection of all nodes represented in the cluster. The output of the command is piped to the Python json package for readability as shown (abbreviated).

kubectl get --raw /api/v1/nodes | python -m json.tool
{
"apiVersion": "v1",
"kind": "NodeList",
"metadata": {
"selfLink": "/api/v1/nodes"
}
"items": [
{
"metadata": {
"name": "master01",
"selfLink": "/api/v1/nodes/master01",
"uid": "5d9d8696-051a-11e8-b2f9-b827ebf4f498"
},
...
},
...
{
"metadata": {
"name": "node04",
"selfLink": "/api/v1/nodes/node04",
"uid": "6586ed51-0521-11e8-b2f9-b827ebf4f498"
},
...
}
],
}

And as you would expect, you can use kubectl get to retrieve API objects directly. For instance, the following shows the (abbreviated) JSON object representing node master01 in the cluster.

$ kubectl get --raw /api/v1/nodes/master01 | python -m json.tool
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"name": "master01",
"selfLink": "/api/v1/nodes/master01",
"uid": "5d9d8696-051a-11e8-b2f9-b827ebf4f498"
},
"spec": {
"externalID": "master01",
"taints": [
{
"effect": "NoSchedule",
"key": "node-role.kubernetes.io/master"
}
]
}
...
}

If you have worked with kubectl, you already know that you can use it to create new, or update the state of existing, API objects. For instance, let us submit the a JSON object to the API server with command kubectl create -f. This will cause the cluster to schedule the deployment of a 3-replica NGINX pod in the default namespace.

$ cat <<EOF | kubectl create -f -
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "nginx",
"labels": {
"app": "nginx"
}
},
"spec": {
"replicas": 3,
"selector": {
"matchLabels": {
"app": "nginx"
}
},
"template": {
"metadata": {
"labels": {
"app": "nginx"
}
},
"spec": {
"containers": [
{
"image": "nginx:1.7.9",
"name": "nginx",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
EOF
deployment "nginx" created

Note that kubectl can use either JSON or YAML formatted objects.

We can then use kubectl get — raw to inspect the deployment as follows:

$ kubectl get --raw \
/apis/apps/v1/namespaces/default/deployments/nginx \
| python -m json.tool

{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"annotations": {
"deployment.kubernetes.io/revision": "1"
},
"generation": 1,
"labels": {
"app": "nginx"
},
"name": "nginx",
"namespace": "default",
"resourceVersion": "571894",
"selfLink": "/apis/apps/v1/namespaces/default/deployments/nginx",
"uid": "d5d41646-1b43-11e8-a95e-b827ebf4f498"
},
...
}

Accessing the API server directly

With the proper setup, it is possible to access the API server directly over HTTP so you can use tool such as cURL, a web browser, or your favorite REST tool.

Using command kubectl proxy

If you need direct HTTP access to the API server, the recommended way is to use command kubectl proxy. The command sets up a proxy allowing authorized and authenticated access to the running API server. For instance, the following snippet starts the proxy to forward all requests to localhost port 8080 to the API server:

$ kubectl proxy --port=8080 &
$ Starting to serve on 127.0.0.1:8080

Once the proxy is running, it is possible to access the API server via HTTP using your favorite tool. The following uses cURL, to return all available API paths on the server:

$ curl http://localhost:8080/
{
"paths": [
"/api",
"/api/v1",
"/apis",
...
"/ui",
"/ui/",
"/version"
]
}

As we did before, we can get access resource lists or access API objects directly as shown below.

$ curl http://localhost:8080/api/v1/nodes
{
"kind": "NodeList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/nodes",
"resourceVersion": "602451"
},
"items": [
{
"metadata": {
"name": "master01",
"selfLink": "/api/v1/nodes/master01",
...
]
}

One can also submit API objects directly via HTTP requests. For instance, the following submits a JSON object to create a new Kubernetes namespace called test-ns:

$ curl http://localhost:8080/api/v1/namespaces/ -k -H "Content-Type: application/json" -XPOST -d '
{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {
"name": "test-ns"
}
}'

Direct server access

If using kubectl proxy is not an option, you can access the API server directly without proxied traffic. This implies, however, that you must provide the server with proper authorization credentials. To do this, we first scrape the the API server address and the default service account authorization token from Kubernetes using kubectl:

APISERVER=$(kubectl config view -o \
jsonpath='{.clusters[*].cluster.server}')
TOKEN=$(kubectl get secrets \
-o jsonpath='{.items[?(@.type=="kubernetes.io/service-account-token")].data.token}' \
| base64 --decode)

Then, use the APISERVER server address and the TOKEN as part of the HTTP request header as shown below:

$ curl $APISERVER/version --header "Authorization: Bearer $TOKEN" --insecure
{
"major": "1",
"minor": "9",
"gitVersion": "v1.9.2",
"gitCommit": "5fa2db2bd46ac79e5e00a4e6ed24191080aa463b",
"gitTreeState": "clean",
"buildDate": "2018-01-18T09:42:01Z",
"goVersion": "go1.9.2",
"compiler": "gc",
"platform": "linux/arm"
}

Summary

A Kubernetes cluster is a collection of API objects that are managed by loosely-coupled components. Creating tools on the Kubernetes platform requires that you understand how and know where to get access to these API objects. This first write up provides a walkthrough of simple ways you can use to explore Kubernetes API objects without using any programming languages.

What next

In the next post, we will start exploring programmatic usage of the Kubernetes API with Python, Node, and possibly Java.

References

https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/

https://kubernetes.io/docs/reference/kubectl/cheatsheet/

--

--