CKAD Exam Preparation 3/4 - Configuration and Volumes

▶️ Introduction

This part summarizes Configuration and Volumes as key primitives of the CKAD Exam Curriculum. To learn more about the CKAD Exam please read this overview.

About this Series

During this blog series I summarize the main “study hooks” in order to be successful with your exam, as I was. The series is composed by the following articles:

All the examples have been developed using minikube on macOS Catalina with VirtualBox.

🖇️ Configuration Primitives

📘   https://kubernetes.io/docs/concepts/configuration/

Environment

For running a Pod with a certain set of environment variables (env var) the easiest way is with --env:

kubectl run p1 --image=busybox --env='var1=val1' --env='var2=val2' -- sh -c 'sleep 3600'

📌  one --env parameter is needed per env var set.

An environment can be also be populated with variables coming from Config Maps or Secrets.

Config Maps

The simplest way to create a Config Map is with the --literal command line option:

kubectl create configmap cm1 --from-literal='key1=value1' --from-literal='key2=value2'

📌  one --literal parameter is needed per name/value pair.

A Config Map can also be created from a properties or environment var file:

cat file.env
var1=val1
var2=val2

kubectl create configmap cm2 --from-env-file=file.env
kubectl describe configmap cm2

Name:         cm2
Namespace:    default

Data
====
var1:
----
val1
var2:
----
val2

A Config Map can also be created to include all the contents of a file:

cat myconfig.json
{
    "id": "a456",
    "type": "Configuration"
}

kubectl create configmap cm3 --from-file=myconfig.json
Name:         cm3
Namespace:    default

Data
====
config.json:
----
{
  "id": "a3456",
  "type": "Configuration"
}

The above command will create a Config Map with just one name/value pair (named myconfig.json) which value will be the content of the myconfig.json file.

📌  The difference between --from-env-file and --from-file.

Secrets

📌  Use Secrets for sensitive configurations, TLS, private keys, certificates, etc.

📌  Secrets are stored encrypted but finally exposed to trusted containers in plain mode.

The simplest way to create a generic Secret is with the --literal command line option:

kubectl create secret generic s1 --from-literal='username=jmcf' --from-literal='pwd=a123456'

📌  one --literal parameter is needed per Secret’s name/value pair.

kubectl describe secrets s1

Name:         s1
Namespace:    default

Type:  Opaque

Data
====
pwd:       7 bytes
username:  4 bytes

Assuming your K8s implementation encrypts Secrets using base64 (please do not do that in production) you can obtain a Secret’s value in plain mode as follows:

kubectl get secret s1 -o jsonpath='{.data.username}{"\n"}' | base64 -d

📌  You can create Secrets from env files and with file or folder content.

📌  Service Account tokens are also stored as Secrets of type kubernetes.io/service-account-token.

Pod Configuration through env vars

How to use a Config Map with direct mapping to env variables:

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   envFrom:
     - configMapRef:
         name: cm1
         optional: false
 dnsPolicy: ClusterFirst
 restartPolicy: Always

📌  An env var will be created for each ConfigMap’s name/value pair.

📌  The same can be done with Secrets using secretRef instead of configMapRef.

📌  Use optional: false to ensure you are using an already existent/right Config Map or Secret.

ConfigMap’s or Secret’s name/value pairs can also be mapped to custom env vars. In the example below the env var SECRET567 is mapped to the name/value pair pwd of the Secret s1.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   env:
     - name: SECRET567
       valueFrom:
         secretKeyRef:
           name: s1
           key: pwd
           optional: false
 dnsPolicy: ClusterFirst
 restartPolicy: Always

📌  The same can be done with Config Map using configMapKeyRef instead of secretKeyRef.

Pod configuration through Volumes

A Volume can be easily declared to reference a Config Map or a Secret. Then, such volume can be mounted to a folder by containers. Such folder will contain a file per name/value pair. The name of the file will correspond to the key name and the content of the file will be the key value.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 volumes:
   - name: v1
     configMap:
       name: cm1
       optional: false
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   volumeMounts:
     - name: v1
       mountPath: "/etc/foo"
 dnsPolicy: ClusterFirst
 restartPolicy: Always

📌  The same can be done with Secret using secret instead of configMap.

It is also possible to map specific name/value pairs of a Secret or Config Map to a path. See below

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 volumes:
   - name: v1
     secret:
       secretName: s1
       items:
        - key: username
          path: "credentials/username.conf"
       optional: false
 containers:
 - image: busybox
   command: ["sh", "-c"]
   args: ["sleep 3600"]
   name: my-pod
   resources: {}
   volumeMounts:
     - name: v1
       mountPath: "/etc/foo"
 dnsPolicy: ClusterFirst
 restartPolicy: Always
kubectl exec my-pod-4 -it -n jmcf -- cat /etc/foo/credentials/username.conf

💽 Volumes

📘   https://kubernetes.io/docs/concepts/storage/

Transient Volumes

emptyDir allows to create a transient Volume for a Pod.

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-emptydir
  namespace: jmcf
spec:
  volumes:
    - name: logs
      emptyDir: {}
  containers:
    - name: app-container
      image: alpine
      command: ["/bin/sh"]
      args: ["-c", "while true; do date >> /var/log/app.txt; sleep 5; done"]
      volumeMounts:
        - name: logs
          mountPath: /var/log
kubectl exec -it -f emptydir-volume.yaml -- cat /var/log/app.txt

📌  Transient Volumes share a Pod’s lifetime.

Persistent Volumes

How to create a Persistent Volume (PV)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv-tutorial
  labels:
    release: stable
spec:
  storageClassName: canterafonseca
  capacity:
    storage: 20Mi
  hostPath:
    path: "/tmp/volume_test"
  accessModes:
    - ReadWriteOnce
    - ReadWriteMany
kubectl get pv mypv-tutorial
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS     REASON   AGE
mypv-tutorial   20Mi       RWO,RWX        Retain           Available           canterafonseca            25s

📌  PVs are cluster resources and have a lifecycle independent of any individual Pod that uses the PV.

📌  The reclaim policy for a PV tells the cluster what to do with the Volume after it has been released of its claim: Retain, Recycle, or Delete.

Persistent Volume Claims

A Persistent Volume Claim (PVC) is a request for storage by a user. PVCs consume PV resources. Claims can request specific size and access modes.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: pvclaim-t1
 namespace: jmcf
spec:
 storageClassName: canterafonseca 
 accessModes:
   - ReadWriteOnce
 volumeMode: Filesystem
 resources:
   requests:
     storage: 10Mi
 selector:
   matchLabels:
     release: stable
kubectl get pvc -n jmcf pvclaim-t1
NAME         STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS     AGE
pvclaim-t1   Bound    mypv-tutorial   20Mi       RWO,RWX        canterafonseca   20s

We can observe that our initial PV mypv-tutorial it is now bound to our PVC jmcf/pvclaim-t1.

kubectl get pv mypv-tutorial
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS     REASON   AGE
mypv-tutorial   20Mi       RWO,RWX        Retain           Bound    jmcf/pvclaim-t1   canterafonseca            17m

📌  The binding between a PV and a PVC happens provided there is a match with capacity, storage class and selector.

📌  There are storage classes marked as dynamically provisioned. In those cases PVs can be created dynamically to meet the demands of a PVC.

Mounting Persistent Volume Claims

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-pvc
  namespace: jmcf
spec:
  volumes:
    - name: logs
      persistentVolumeClaim:
        claimName: pvclaim-t1
  containers:
    - name: app-container
      image: alpine
      command: ["/bin/sh"]
      args: ["-c", "while true; do date >> /var/log/app.txt; sleep 5; done"]
      volumeMounts:
        - name: logs
          mountPath: /var/log
kubectl exec -it -f pod-pvc.yaml -- cat /var/log/app.txt

⏭️ Next in this series

Deployments, Services and Networking

🗒️ Feedback