Pages

Tuesday, August 7, 2018

Control Plane Component - Api Server

Share it Please
Since we know that etcd stores data and for having consistent data we should not be allowing multiple things to modify data. This is where Api server comes into picture.

The job of the api server is to mediate the communication between etcd and other clients. The Api server is the only one who have access to the Etcd server. When a client makes a call, he does that to Api server which in turn connects to the etcd server to obtain data. Api server provides CRUD interface for querying and modifying state over a RESTful API calls.

Besides Storing data,it also performs validation of those objects like pod manifest etc. One another things that the API server does is to handle the locking, so that call to change an objects from the API server are handled correctly and never overridden by other clients in the event of concurrent update.

Here are the steps as what happens inside the API Server,
Authentication - Once the client request for creating a resource ( pod, Deployment ) comes to API , authentication is done to identify the client. Multiple authentication plugins are configured in the Api server which will read the request one after the another until one identifies the client sending the request. Since the request are in Rest format which is HTTP request, Api server will read the headers and identify the client by extracting the user name , user id, group id etc

Authorization - Once the authentication is done, authorization comes into picture. More than one authorization plugins are configured to determine that the authenticated user can perform the action suggested in the request. For example, if a request for pod creation comes these plugins check if that user has the permission to create a pod, if so in what namespace etc

Admission Control Plugins - In the 3 stage admission control plugins comes into picture.  The Job of these is intercept the request before the Api server persistent. It intercepts the request and validates if anything is missing, values. For example, let's say if we have a Namespace which has resource set and if we are creating a pod in this namespace , these controls will set the limits for the pod based on the namespace.

Etcd - The request is then stored in the etcd.

One important thing that we need to know is that Api server gets the request from client and once all above steps are done it will save to the etcd server. It will full-fill any requests like , if Api server receives a request for pod creation it performs all the above steps and then store in the Etcd. It will not create the pod or tell to any other component to create the pod.

One another important job that the Api server does is sending notifications to the clients. For example , if a client request for creation of the pod, the data is saved into the etcd and a notification is sent to the interested parties. These interested parties can either create the pod or perform some other action based on the notification sent by the APi server. These interested parties can request for the changes from Api server that they are interested in.

Clients or interested parties open a Http connection to the Api Server and using this connection the client will receive notification ( like modification , creation ) to the resources that are interested in or requested for.

Kubectl is one of the client for Api server.Using the command “kubectl get pods --watch”, we don’t need to poll for the list of pods in turn Api server will send the details to the kubectl which is watching.

Exploring the Api Interface - Api is a library of functions and procedures whose interfaces are exposed so that external applications can use them. If we need to develop any scripts we need to have an understanding of the functionality that is exposed by Kubernetes.
In many cases we will use the kubectl command to talk with  the api server in performing the tasks. K8 Api is based on Rest implementation which means we can make a Rest call to the api server using linux command line tools like curl or any other tool that can make a rest call.

Api provides Url for endpoints and these endpoints provide access to specific functionality. Let use the kubectl command to access Api. for this we will run the “kubectl proxy” command from where we want to access the Api.
[root@manja17-I13330 ~]# kubectl proxy --port=8001 &
[1] 20120
[root@manja17-I13330 ~]# Starting to serve on 127.0.0.1:8001

The above command starts a proxy to the Kubernetes API server.Now use the curl command to access the Api as,
[root@manja17-I13330 ~]# curl http://localhost:8001/
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
    "/apis/admissionregistration.k8s.io",
    "/apis/admissionregistration.k8s.io/v1beta1",
    "/apis/apiextensions.k8s.io",
    "/apis/apiextensions.k8s.io/v1beta1",
    "/apis/apiregistration.k8s.io",
    "/apis/apiregistration.k8s.io/v1",
    "/apis/apiregistration.k8s.io/v1beta1",
    "/apis/apps",
    "/apis/apps/v1",
    "/apis/apps/v1beta1",
    "/apis/apps/v1beta2",
    "/apis/authentication.k8s.io",
    "/apis/authentication.k8s.io/v1",
    "/apis/authentication.k8s.io/v1beta1",
    "/apis/authorization.k8s.io",
    "/apis/authorization.k8s.io/v1",
    "/apis/authorization.k8s.io/v1beta1",
    "/apis/autoscaling",
    "/apis/autoscaling/v1",
    "/apis/autoscaling/v2beta1",
    "/apis/batch",
    "/apis/batch/v1",
    "/apis/batch/v1beta1",
    "/apis/certificates.k8s.io",
    "/apis/certificates.k8s.io/v1beta1",
    "/apis/events.k8s.io",
    "/apis/events.k8s.io/v1beta1",
    "/apis/extensions",
    "/apis/extensions/v1beta1",
    "/apis/networking.k8s.io",
    "/apis/networking.k8s.io/v1",
    "/apis/policy",
    "/apis/policy/v1beta1",
    "/apis/rbac.authorization.k8s.io",
    "/apis/rbac.authorization.k8s.io/v1",
    "/apis/rbac.authorization.k8s.io/v1beta1",
    "/apis/storage.k8s.io",
    "/apis/storage.k8s.io/v1",
    "/apis/storage.k8s.io/v1beta1",
    "/healthz",
    "/healthz/autoregister-completion",
    "/healthz/etcd",
    "/healthz/ping",
    "/healthz/poststarthook/apiservice-openapi-controller",
    "/healthz/poststarthook/apiservice-registration-controller",
    "/healthz/poststarthook/apiservice-status-available-controller",
    "/healthz/poststarthook/bootstrap-controller",
    "/healthz/poststarthook/ca-registration",
    "/healthz/poststarthook/generic-apiserver-start-informers",
    "/healthz/poststarthook/kube-apiserver-autoregistration",
    "/healthz/poststarthook/rbac/bootstrap-roles",
    "/healthz/poststarthook/start-apiextensions-controllers",
    "/healthz/poststarthook/start-apiextensions-informers",
    "/healthz/poststarthook/start-kube-aggregator-informers",
    "/healthz/poststarthook/start-kube-apiserver-informers",
    "/logs",
    "/metrics",
    "/openapi/v2",
    "/swagger-2.0.0.json",
    "/swagger-2.0.0.pb-v1",
    "/swagger-2.0.0.pb-v1.gz",
    "/swagger.json",
    "/swaggerapi",
    "/version"
  ]
}

Each of the above line talks about the interface that is being exposed by the kubernetes. Let's check for a specific interface,
[root@manja17-I13330 ~]# curl http://localhost:8001/api/v1 | less
{
  "kind": "APIResourceList",
  "groupVersion": "v1",
  "resources": [
    {
      "name": "bindings",
      "singularName": "",
      "namespaced": true,
      "kind": "Binding",
      "verbs": [
        "create"
      ]
    },
*******

If we can see that this is a APIResourceList kind which provides various resource bindings. If we grep the above command more specifically we can see,
[root@manja17-I13330 ~]# curl http://localhost:8001/api/v1 | grep name | grep -v namespaced
      "name": "bindings",
      "name": "componentstatuses",
      "name": "configmaps",
      "name": "endpoints",
      "name": "events",
      "name": "limitranges",
      "name": "namespaces",
      "name": "namespaces/finalize",
      "name": "namespaces/status",
      "name": "nodes",
      "name": "nodes/proxy",
      "name": "nodes/status",
      "name": "persistentvolumeclaims",
      "name": "persistentvolumeclaims/status",
      "name": "persistentvolumes",
      "name": "persistentvolumes/status",
      "name": "pods",
      "name": "pods/attach",
      "name": "pods/binding",
      "name": "pods/eviction",
      "name": "pods/exec",
      "name": "pods/log",
      "name": "pods/portforward",
      "name": "pods/proxy",
      "name": "pods/status",
      "name": "podtemplates",
      "name": "replicationcontrollers",
      "name": "replicationcontrollers/scale",
      "name": "replicationcontrollers/status",
      "name": "resourcequotas",
      "name": "resourcequotas/status",
      "name": "secrets",
      "name": "serviceaccounts",
      "name": "services",
      "name": "services/proxy",
      "name": "services/status",

This is the same that we will be using when defining a manifest file. For example if we are creating a name space ,we define the manifest file as
[root@manja17-I13330 kubenetes-config]# cat basic-namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: sample-testing

We can the first element that is being passed it the apiVersion: v1 which in turn uses these. This also talks about what operations that can be performed using the namespaces. If you hit the api server with above command and check for the namespace element, we see
 {
      "name": "namespaces",
      "singularName": "",
      "namespaced": false,
      "kind": "Namespace",
      "verbs": [
        "create",
        "delete",
        "get",
        "list",
        "patch",
        "update",
        "watch"
      ],
      "shortNames": [
        "ns"
      ]
    },

It gives all the operations that can be done on the namespaces. If you want to find out all the available namespaces currently exist we can use,
[root@manja17-I13330 kubenetes-config]# curl http://localhost:8001/api/v1/namespaces | grep name | grep -v "selfLink"
        "name": "default",
        "name": "kube-public",
        "name": "kube-system",

Using this “curl http://localhost:8001/api/v1/namespaces/default/pods” we can a list of all pods running in the default namespace

Now if we need to find more details about a pod running in a namespace we can use,
[root@manja17-I13330 kubenetes-config]# curl http://localhost:8001/api/v1/namespaces/default/pods/my-nginx
{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "my-nginx",
    "namespace": "default",
    "selfLink": "/api/v1/namespaces/default/pods/my-nginx",
    "uid": "e68afd34-8f0d-11e8-a791-020055e1ea1d",
    "resourceVersion": "7837400",
    "creationTimestamp": "2018-07-24T06:50:50Z",
    "labels": {
      "env": "dev"
    }
  },
  "spec": {
    "volumes": [
      {
        "name": "default-token-fx8mm",
        "secret": {
          "secretName": "default-token-fx8mm",
          "defaultMode": 420
        }
      }
    ],
    "containers": [
      {
        "name": "my-nginx",
        "image": "nginx",
        "ports": [
          {
            "containerPort": 80,
            "protocol": "TCP"
          }
        ],
        "resources": {

        },
        "volumeMounts": [
          {
            "name": "default-token-fx8mm",
            "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": "manja17-i14021",
    "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
      }
    ]
  },
  "status": {
    "phase": "Running",
    "conditions": [
      {
        "type": "Initialized",
        "status": "True",
        "lastProbeTime": null,
        "lastTransitionTime": "2018-07-24T06:49:26Z"
      },
      {
        "type": "Ready",
        "status": "True",
        "lastProbeTime": null,
        "lastTransitionTime": "2018-07-24T06:49:36Z"
      },
      {
        "type": "PodScheduled",
        "status": "True",
        "lastProbeTime": null,
        "lastTransitionTime": "2018-07-24T06:50:50Z"
      }
    ],
    "hostIP": "10.131.36.181",
    "podIP": "10.38.0.3",
    "startTime": "2018-07-24T06:49:26Z",
    "containerStatuses": [
      {
        "name": "my-nginx",
        "state": {
          "running": {
            "startedAt": "2018-07-24T06:49:36Z"
          }
        },
        "lastState": {

        },
        "ready": true,
        "restartCount": 0,
        "image": "docker.io/nginx:latest",
        "imageID": "docker-pullable://docker.io/nginx@sha256:4a5573037f358b6cdfa2f3e8a9c33a5cf11bcd1675ca72ca76fbe5bd77d0d682",
        "containerID": "docker://ac58e1679ffb9c74063431ae280fce7d35fa55e99883ef70c235d5e32b427416"
      }
    ],
    "qosClass": "BestEffort"
  }
}

No comments :

Post a Comment