Nicolas Kovacs

Nicolas Kovacs

kubernetes-workshop-2 thumbnail image

Kubernetes Workshop - Part 2

k8s

We will explore K8S functionalities further.

Prerequisites and objectives

We will assume here that you have followed the full Kubernetes(K8S) Workshop - Part 1

Let's do it

Start Minikube

$ minikube start

We have now a Deployment with 3 pods running

$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-dw8zj   1/1     Running   0          87m
nginx-deployment-5d59d67564-ltj8s   1/1     Running   0          87m
nginx-deployment-5d59d67564-nrn9f   1/1     Running   0          87m

Some concepts

DEPLOYMENTS, PODS AND REPLICAS

Let's try something, what happens If we delete a pod and list them just after

$ kubectl delete pod nginx-deployment-5d59d67564-dw8zj

pod "nginx-deployment-5d59d67564-dw8zj" deleted
$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-76885   1/1     Running   0          14s
nginx-deployment-5d59d67564-ltj8s   1/1     Running   0          88m
nginx-deployment-5d59d67564-nrn9f   1/1     Running   0          88m

The pod is recreated by K8S, because we specified 3 replicas (see the first one AGE : 14s)

K8S will always do its best to automatically computes changes to respect what you declared and apply with kubectl apply command

In our case we created a Deployment and asked 3 replicas, the Deployment object is still available, so K8S will create missing pods automatically to respect our specs.

$ kubectl get deploy # shortcut of deployments

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           92m

# Delete the deployment
$ kubectl delete deployment nginx-deployment

deployment.apps "nginx-deployment" deleted

# List all deployments
$ kubectl get deploy

No resources found in default namespace.

# List all pods
$ kubectl get pods

No resources found in default namespace.

As you see we have no deployments/pods available anymore.

Now we will bring back the deployment and scale down some replicas.

  • Apply the deployment

Apply deployment

$ kubectl apply -f nginx-deployment.yml

deployment.apps/nginx-deployment created

$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-mgqvl   1/1     Running   0          78s
nginx-deployment-5d59d67564-mvwt4   1/1     Running   0          78s
nginx-deployment-5d59d67564-phx64   1/1     Running   0          78s
  • Modify the nginx-deployment.yml created earlier and set the replicas number to 2.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  • Apply It again and you will see pods decreasing to 2. The status of one pod will be Terminating, and It will be deleted after
$ kubectl apply -f nginx-deployment.yml

deployment.apps/nginx-deployment configured

$ kubectl get pods

NAME                                READY   STATUS        RESTARTS   AGE
nginx-deployment-5d59d67564-mgqvl   1/1     Running       0          3m21s
nginx-deployment-5d59d67564-mvwt4   0/1     Terminating   0          3m21s
nginx-deployment-5d59d67564-phx64   1/1     Running       0          3m21s

$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-mgqvl   1/1     Running   0          4m28s
nginx-deployment-5d59d67564-phx64   1/1     Running   0          4m28s

VOLUMES & CONFIGMAPS

We have nginx pods running based on nginx image but how can we customize some files in runnings containers ?

ConfigMaps can be used to inject some config to container with environment variables for example, or files. When using file, we will also use VOLUMES to mount the file into the container.

Let's try to change the default index.html of the nginx docker image for each pod we created through our deployment.

  • Edit the nginx-deployment.yml file and add a volume mount into it
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
            - containerPort: 80
          volumeMounts:
            - name: custom-index
              mountPath: /usr/share/nginx/html/index.html
              subPath: index.html
              readOnly: true
      volumes:
        - name: custom-index
          configMap:
            name: custom-index-config-map
  • volumeMounts : is used to mount a volume into the container. The goal here is to mount a file to replace the default one provided by nginx docker image. More information above

  • mountPath: where we want to mount the volume

  • subPath : which file to use

List pods

$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-mgqvl   1/1     Running   0          4m28s
nginx-deployment-5d59d67564-phx64   1/1     Running   0          4m28s

# Open a shell into one pod (container)
$ kubectl exec -it nginx-deployment-5d59d67564-mgqvl -- sh

# Navigate to /usr/share/nginx/html
$ cd /usr/share/nginx/html
$ ls

50x.html  index.html

# We have the default index.html
# Display It
$ cat index.html

<!DOCTYPE html>
<html>
<head>
  <title>Welcome to nginx!</title>
  <style>
    body {
    width: 35em;
    margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif;
  }
  </style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
  working. Further configuration is required.</p>

<p>For online documentation and support please refer to
  <a href="http://nginx.org/">nginx.org</a>.<br/>
  Commercial support is available at
  <a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
  • volumes : allow us to create volume, in our case a ConfigMap to let it be mounted to containers

Now create a new file called nginx-configmap.yml and paste the content below

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-index-config-map
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <head>
    <title>Hello World K8S</title>
    </head>
    <body>
      <h1>K8S NGINX</h1>
    </body>
    </html>
  • Apply It
$ kubectl apply -f . # We use here the dot to apply all files in current directory

configmap/custom-index-config-map created
deployment.apps/nginx-deployment unchanged
service/nginx unchanged
  • List and observe pods
$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-866857c45d-gn4xc   1/1     Running   0          61s
nginx-deployment-866857c45d-vkhbp   1/1     Running   0          59s

$ kubectl exec -it nginx-deployment-866857c45d-gn4xc -- bash # we use bash because it is installed in nginx docker image

$ cd /usr/share/nginx/html/
$ cat index.html

<!DOCTYPE html>
<html>
<head>
<title>Hello World K8S</title>
</head>
<body>
  <h1>K8S NGINX</h1>
</body>
</html>
  • Let's try to use busybox to display it with the service
$ kubectl run -it --rm debug --image=busybox --restart=Never -- sh

$ wget http://nginx

Connecting to nginx (10.105.68.32:80)
saving to 'index.html'
index.html           100% |****************************************************************|   113  0:00:00 ETA
'index.html' saved

$ cat index.html

<!DOCTYPE html>
<html>
<head>
<title>Hello World K8S</title>
</head>
<body>
  <h1>K8S NGINX</h1>
</body>
</html>

SECRETS & ENVIRONMENT VARIABLES

We can inject env var into containers with keys values in the deployment file or with a ConfigMap.

We will never put sensitive data (api keys, secrets, etc.) into deployment file. Because these files are usually versioned, we will use K8S Secret Object to handle them.

  • Edit nginx-deployment.yml and add env: block and envFrom: block
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
            - containerPort: 80
          volumeMounts:
            - name: custom-index
              mountPath: /usr/share/nginx/html/index.html
              subPath: index.html
              readOnly: true
          env:
            - name: ENV_VAR_1
              value: "Hello from the environment"
            - name: ENV_VAR_2
              value: "Hello 2 from the environment"
          envFrom:
            - configMapRef:
                name: nginx-env-configmap
      volumes:
        - name: custom-index
          configMap:
            name: custom-index-config-map
  • Create a new file called nginx-env-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-env-configmap
data:
  ENV_VAR_3: "Hello 3 from the environment"
  ENV_VAR_4: "Hello 4 from the environment"
  • Apply changes
$ kubectl apply -f .

configmap/custom-index-config-map unchanged
deployment.apps/nginx-deployment configured
configmap/nginx-env-configmap created
service/nginx unchanged
  • Observe changes
$ kubectl exec -it nginx-deployment-54899785f6-294kh -- bash

# Display container's env vars
$ printenv

# Search your env vars
ENV_VAR_2=Hello 2 from the environment
ENV_VAR_3=Hello 3 from the environment
ENV_VAR_1=Hello from the environment
ENV_VAR_4=Hello 4 from the environment

To store sensitive data we will use secrets. Secrets are like a named bucket where we put inside key value pair data. Try to group your secrets together for example : service's credentials, api-secrets, etc.

  • Generate secrets using the CLI with kubectl create secret
$ kubectl create secret generic my-secret --from-literal=user=myUser --from-literal=password=myPass

secret/my-secret created

We use a generic type of secret with a name of my-secret, with then use --from-literal= to specific key=value secret. Here we want

user = myUser
password = myPass
  • Now we can use this secret inside our deployment, look for new env : entries
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
            - containerPort: 80
          volumeMounts:
            - name: custom-index
              mountPath: /usr/share/nginx/html/index.html
              subPath: index.html
              readOnly: true
          env:
            - name: USER
              valueFrom:
                secretKeyRef:
                  name: my-secret
                  key: user
            - name: PASSWORD
              valueFrom:
                secretKeyRef:
                  name: my-secret
                  key: password
            - name: ENV_VAR_1
              value: "Hello from the environment"
            - name: ENV_VAR_2
              value: "Hello 2 from the environment"
          envFrom:
            - configMapRef:
                name: nginx-env-configmap
      volumes:
        - name: custom-index
          configMap:
            name: custom-index-config-map

More information :

- name: USER
  valueFrom:
    secretKeyRef:
      name: my-secret
      key: user
  • name : is the name of the env var we want to create and put our secret inside

  • secretKeyRef : name of the secret we created

  • key : the key inside the secret for getting the value

Then we can apply changes, and observe the result

$ kubectl apply -f .

configmap/custom-index-config-map unchanged
deployment.apps/nginx-deployment configured
configmap/nginx-env-configmap unchanged
service/nginx unchanged

$ kubectl get pods

NAME                               READY   STATUS      RESTARTS   AGE
nginx-deployment-cd85d6486-7q6ws   1/1     Running     0          14s
nginx-deployment-cd85d6486-pr6ck   1/1     Running     0          12s

$ kubectl exec -it nginx-deployment-cd85d6486-7q6ws -- bash

# printenv and search your new env var
printenv
USER=plezi
PASSWORD=plezipass
← Back to home