Components Hands-on

Find a quick summary on the kubernetes main concept to refresh your memory in a previous article, or go straight ahead with the hands-on examples.

Kubernetes setup

To get a better grasp of Kubernetes components and commands, it is recommended to have a working Kubernetes cluster running.

You can set up a Kubernetes cluster on your own following:

Or via docker desktop which provides one by default now.

You can also setup a temporary kubernetes configuration if you are working with a cloud provider by downloading the configuration file and set it up:

export KUBECONFIG=/path/to/the/kubeconfig

This should give you access to the cluster in that terminal session.

Kubernetes get started

Once you have a kubernetes cluster setup, you might want to test the following commands in a test cluster:

kubectl config set-context test

This will create the context test in your kubeconfig file. You can then switch to it using:

# To display the current contexts and their namespace
kubectl config get-contexts
# To use the created `test` context
kubectl config use-context test

To test it out, find the current context using

# To display the current context
kubectl config current-context

For the following example, the apiVersion in the presented yaml files are compatible with certain versions of Kubernetes. It might not be compatible with the version you are using, but the syntax should be fairly similar. I’ll try to keep it up to date, so if you find a deprecated notation, let me know in the comments. 🙂

Kubectl main commands

kubectl is a command line interface for running commands against Kubernetes clusters. In all of them, you can replace <resource> with any of the kubernetes resource (pods, node, deployments, services, …). You can also use kubectl explain pod to get the definition of what is a pod, but it’s not very useful in practice.

  • Describe will give you the detail information of the resource.
    If there is a name used for multiple resources, you can specify witch one using <resource>/<resource name>:
kubectl describe <resource name>
  • Give the basic information of the resource for a quick overview (name, status, Age, replicas)
kubectl get <resource>

# Get the logs for one application
kubectl logs -l app=my-app
  • To create a kubernetes resource defined inside the file.yaml. It uses the kubernetes API server to schedule the creation of the resource based on the yaml file.
kubectl create -f file.yaml
  • To remove a resource.
kubectl delete <resource name>
  • To edit a resource via an editor (like vim) you can use:
kubectl edit <resource> <resource name>
# example: kubectl edit deployment nginx-deployment
  • You can label kubernetes resources. It can then be used for network or access restrictions.
kubectl label <resource> <resource name> env=test

Pods

To create a pod, you need to specify its kind, name and then in spec the type of container that it is running.

apiVersion: v1
kind: Pod
metadata:
  name: poddy
spec:
  containers:
    - name: poddy
      image: gcr.io/google_containers/echoserver:1.4
      ports:
        - containerPort: 8080

Each created pod has its own ip, they also have labels that are used to identify and query them.

ImagePullPolicy, determines when to pull the image of the container(s) inside the pod. It is recommended to put “always” so it always pull the image (to avoid some trouble).

Replication controller

A ReplicationController ensures that a specified number of pod replicas are running at any one time. In other words, a ReplicationController makes sure that a pod or a homogeneous set of pods is always up and available.

kind: ReplicationController
metadata:
  name: poddy
spec:
  replicas: 2
  selector:
    app: poddy-app
  template:
    metadata:
      name: poddy
      labels:
        app: poddy-app
    spec:
      ... # Specs of the pod

Here we have 2 replicas of the specified pods named poddy-<random stuff> that will be created. The replicationController finds the pods using their label here poddy-app - called a ReplicaSet.

  • If you add another similar pod with the same label, the replicationController will associate it and terminates one pods since it will have 3.

Service

A Kubernetes Service is an abstraction that defines a logical set of Pods and a policy by which to access them. It is a group of pods acting as one, exposing the pods under one IP.

Since ReplicaSet can kill and recreate Pods, the pod ips can change meaning you never know how to reach them.

Basically a service has:

  • A static IP to reach the application
  • A Selector to select the pod part of the service
apiVersion: v1
kind: Service
metadata:
  name: poddy-service
spec:
  type: "NodePort"
  ports:
    - port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: poddy-app

In service spec you can find the type, it can be either:

  • ClusterIP: Exposes the service to a cluster-internal IP. Choosing this value makes the service only reachable from within the cluster using the {NodeIP}, it is the default.
  • NodePort: Exposes the service on each Node’s IP to a static port (the NodePort). You’ll be able to contact the NodePort service, from outside the cluster, by requesting {NodeIP}:{NodePort}.

Deployment

A deployment combines pods and replicaSets to create a desired state of the cluster.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  ... # Specs of the replicationController
  spec:
    ... # Specs of the pod(s)

Scale the deployment of an application, here the nginx app:

kubectl scale deployment nginx --replicas=3

Scale one or multiple deployments to 0 to remove any running resources in them:

# In default namespace (or use --namespace=<namespace name> on both side of '|' to scale down in a specific one)
kubectl get deployments -o jsonpath='{.items[*].metadata.name}' | xargs kubectl scale deployment --replicas=0
# To scale down all deployment in all namespace
kubectl get deployments --all-namespaces -o jsonpath='{range .items[*]}{@.metadata.namespace} {@.metadata.name} {end}' | xargs -n2 bash -c 'kubectl scale deployment $1 --replicas=0 --namespace=$0'

You can use the same command to scale them back up.

Namespace

A namespace is a way to separate your cluster or components. For example, having a prod and a test namespace can help deploy new software in the appropriate place.

Here is how we create a namespace

kubectl create namespace test

Or using a file, you can define your namespace like:

kind: Namespace
apiVersion: v1
metadata:
  name: test

To create components inside that newly created “test” namespace:

kubectl create component-file.yaml -n test

To set that new namespace as the default namespace (When not specified default will be used as a namespace and for every command):

kubectl config set-context --current --namespace=<namespace>

To see the namespaces in your context, you can use:

kubectl get namespaces
#NAME              STATUS   AGE
#kube-system       Active   173m
#kube-public       Active   173m
#kube-node-lease   Active   173m
#default           Active   173m

Kubernetes starts with four namespaces: kube-system, kube-public, kube-node-lease, and default.

Volumes

Persistent Volume

A PersistentVolume (PV) is a piece of storage in the cluster that has been provisioned by an administrator. It is a resource in the cluster just like a node is a cluster resource.

The information there stays even if the pod dies, it’s independent.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
  labels:
    vol: mysql
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/mysql-poddy

Persistent Volume Claim

A PersistentVolumeClaim (PVC) is a request for storage by a user. It allows a user to consume abstract storage resources.

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: ""
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  selector:
    matchLabels:
      vol: "mysql"

Here the pvc claim the above mysql-pv one, using the label in order to select it, and the accessModes for how it is supposed to be accessed.

Delete all permanent volume claims using:

kubectl delete pvc --all -n <namespace>

It also works for permanent volumes, swap pvc by pv to delete them all as well.

Secrets

Kubernetes’ secrets allow users to define sensitive information outside of containers and expose that information to containers through environment variables as well as files within Pods.

apiVersion: v1
kind: Secret
metadata:
  name: poddy-secret
type: Opaque
data:
  dbname: d29yZHByZXNzX2Ri
  dbuser: d29yZHByZXNzX3VzZXI=
  dbpassword: d29yZHByZXNzX3Bhc3M=
  mysqlrootpassword: cm9vdHBhc3MxMjM=

Secrets must be encoded first into base64, and put into data.

Daemon Set

Daemon Set ensures that a copy of the pod runs on a selected set of nodes. By default, all nodes in the cluster are selected. A selection criteria may be specified to select a limited number of nodes.

kind: DaemonSet
metadata:
  name: poddy-daemonset
spec:
  selector:
    matchLabels:
      tier: monitoring
      name: poddy-exp
  template:
    metadata:
      labels:
        tier: monitoring
        name: poddy-exp
    spec:
      ... # Specs of the pod(s)

Jobs

A Job creates one or more pods and ensures that a specified number of them are successfully completed. A job keeps track of the pod’s successful completion. The job will start a new pod if the pod fails or is deleted due to hardware failure.

Jobs are complementary to the Replica Set. A Replica Set manages pods which are not expected to terminate (e.g. web servers), and a Job manages pods that are expected to terminate (e.g. batch jobs).

Non Parallel Job

Only one pod per job is started, unless the pod fails. Job is complete as soon as the pod terminates successfully.

apiVersion: batch/v1
kind: Job
metadata:
  name: poddy-job
spec:
  template:
    spec:
      ... # Specs of the pod(s)

Parallel Job

The number of pods to complete is defined by .spec.completions attribute (here 6) in the configuration file. The number of pods to run in parallel is defined by .spec.parallelism attribute (here 2 pods at the same time) in the configuration file.

The default value for both of these attributes is 1.

The job is complete when there is one successful pod for each value in the range in 1 to .spec.completions.

apiVersion: batch/v1
kind: Job
metadata:
  name: poddy-parallel-job
spec:
  completions: 6
  parallelism: 2
  template:
    metadata:
      name: poddy
    spec:
      ... # Specs of the pod with the same name as in template.metadata

Cron Job

A Cron Job is a job that runs on a given schedule, written in Cron format. There are two primary use cases:

  • Run jobs once at a specified point in time
  • Repeatedly at a specified point in time
apiVersion: batch/v1
kind: CronJob
metadata:
  name: poddy-cron
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: poddy-app
        spec:
          ... # Specs of the pod with the same name as in template.metadata

Ingress

An Ingress Controller - is an application that watches the Master Node’s API Server for changes in the Ingress resources and updates the Layer 7 load balancer accordingly. Ingress-Controller is a mandatory prerequisite for Ingress rules to start working in Kubernetes.

Kubernetes has different options of Ingress Controllers, and, if needed, we can also build our own. Most popular Ingress Controllers implementations are Haproxy, Traefic, Nginx.

Find all the ingresses in your cluster for all namespaces using:

kubectl get ingress -A 

This will list them, displaying among other things their name, hosts and ports.

NetworkPolicy

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
  namespace: policy-demo
spec:
  podSelector:
    matchLabels:
      run: nginx
  ingress:
    - from:
        - podSelector:
            matchLabels:
              run: access

Single service ingress

The simple ingress usage is with one address, and one service associated to it. The service associated needs to have CLusterIP in the spec.type.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: poddy-ingress
spec:
  rules:
    - host: poddy.com
      http:
        paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: poddy-service
                port:
                  number: 80

Simple fanout

This time we’re going to deploy Simple fanout type that exposing multiple services on the same host, but via different paths. This type is very handy when you running in CloudProvider and want to cut cost on creating LoadBalancers for each of your applications.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-rules
spec:
  rules:
    - host: poddy.com
      http:
        paths:
          - pathType: Prefix
            path: "/hello"      #poddy.com/hello
            backend:
              service:
                name: poddy-service-1
                port:
                  number: 80
          - pathType: Prefix
            path: "/world"     #poddy.com/world
            backend:
              service:
                name: poddy-service-2
                port:
                  number: 80