Kubernetes Creates Pods: Imperative vs Declarative

Posted by Henry Du on Sunday, September 13, 2020

Kubernetes Creates Pods: Imperative vs Declarative

Introduction

Design and implementation microservices may have different approaches. During the design phase, we may need a quick proof of concept, or even multiple PoCs to compare. However, after we have a solid design, we may need to carefully plan how those microservices are working together in the cloud production. It demands us to specifically write Deployment, Service, ServiceAccount, ConfigMap etc. then, compile them to be a helm chart.

This article introduces comparison of some of kubernetes concepts, Pod vs Deployment, Declarative vs Imperative. Hope it can facilitate our daily work.

Pod vs. Deployment

Both Pod and Deployment are full-fledged objects in the Kubernetes API. After Kubernetes has a version 1.18 release at the end of March 2020, kubectl run only creates Pod, no more Deployment. If we need to create other resources in an imperative way, we can use the kubectl create command.

Available Commands:
  clusterrole         Create a ClusterRole.
  clusterrolebinding  Create a ClusterRoleBinding for a particular ClusterRole
  configmap           Create a configmap from a local file, directory or literal value
  cronjob             Create a cronjob with the specified name.
  deployment          Create a deployment with the specified name.
  job                 Create a job with the specified name.
  namespace           Create a namespace with the specified name
  poddisruptionbudget Create a pod disruption budget with the specified name.
  priorityclass       Create a priorityclass with the specified name.
  quota               Create a quota with the specified name.
  role                Create a role with single rule.
  rolebinding         Create a RoleBinding for a particular Role or ClusterRole
  secret              Create a secret using specified subcommand
  service             Create a service using specified subcommand.
  serviceaccount      Create a service account with the specified name

Pod is the smallest unit that kubernetes cluster is able to manage. Pod is the abstraction of the container. One pod can hold multiple containers, as Sidecar, Adapter or Ambassador pattern.

Deployment is the abstraction of the pod. For example, a deployment declares how many replicas of a pod should be running. It also declares how many CPUs and how much memories that limits to each pod.

In order to create a pod, we could write a Pod object YAML to apply to the K8S cluster. Or, We could use kubectl run --generator=run-pod/v1 ... to create a pod. However, we cannot expect kubernetes is able to clone more pods by means of ReplicaSets. It is unlikely that we will put Pod objects into helm charts and deploy it into production.

Deployment object will use template to create one or more application container(s) with many specifications. Service object is the abstraction of network utilities. The one reason we need a standalone Service object is because the Deployment may be killed or scale up. The network such as load balancer, node port or cluster IP should be kept persistently.

Imperative vs. Declarative

There are two approaches to manage Kubernetes objects: imperative and declarative. Imperative approach is to use kubectl run or other command to manage resources without manifest files (YAML definition). Declarative approach is to use kubectl apply command for predefined manifest files. The latter approach is using in production deployment.

Create a nginx imperatively

The following command creates a pod as well as a service, but no deployment object.

kubectl run --generator=run-pod/v1 web --image=nginx --labels app=web --expose --port 80

Both pod and service are created. However, there is no deployment.

> kubectl get pods
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
default       web                                1/1     Running   0          27h

> kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   8d
web          ClusterIP   10.103.242.17   <none>        80/TCP    27h

If there is an accident that the pod is deleted, Kubenetes will not bring it up because there is no target state defined in manifest file.

kubectl delete pod nginx

The pod will not be up again.

Create a nginx declaratively

The following Deployment and Service definition will declare the target state of ngnix service running in Kubernetes.

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

Service

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
    department: dev
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

There are three replicas of nginx pods. If one pod is deleted accidentally, Kubernetes will bring it up to meet target state defined in the Deployment manifest file.

Conclusion

Both kubectl run --generator and kubectl apply give us ways to create microservices. The imperative way will speed up the proof of concept work flow. In addition, the --dry-run and -o yaml options will save our time to write declarative YAML from scratch. Eventually, we need manifest files for all resources to be a helm chart to deploy to the production.