Ansible Pilot

Configure a Pod to Use a Volume for Storage - Ansible module k8s

How to assign an emptyDir Volume to a Pod on Kubernetes K8s or OpenShift OCP Containers and Pods with Ansible Playbook using module k8s.

How to Configure a Pod to Use a Volume for Storage on Kubernetes K8s or OpenShift OCP?

I’m going to show you a live demo and some simple Ansible code. I’m Luca Berton and welcome to today’s episode of Ansible Pilot.

A Container’s file system lives only as long as the Container does. So when a Container terminates and restarts, filesystem changes are lost. For more consistent storage that is independent of the Container, you can use a Volume. This is especially important for stateful applications, such as key-value stores (such as Redis) and databases.

Ansible creates Kubernetes or OpenShift service

Let’s talk about the Ansible module k8s. The full name is kubernetes.core.k8s, which means that is part of the collection of modules of Ansible to interact with Kubernetes and Red Hat OpenShift clusters. It manages Kubernetes (K8s) objects.

Parameters

There is a long list of parameters of the k8s module. Let me summarize the most used. Most of the parameters are very generic and allow you to combine them for many use-cases. The name and namespace specify object name and/or the object namespace. They are useful to create, delete, or discover an object without providing a full resource definition. The api_version parameter specifies the Kubernetes API version, the default is “v1” for version 1. The kind parameter specifies an object model. The state like for other modules determines if an object should be created - present option, patched - patched option, or deleted - absent option. The definition parameter allows you to provide a valid YAML definition (string, list, or dict) for an object when creating or updating. If you prefer to specify a file for the YAML definition, the src parameter provides a path to a file containing a valid YAML definition of an object or objects to be created or updated. You could also specify a YAML definition template with the template parameter. You might find useful also the validate parameter in order to define how to validate the resource definition against the Kubernetes schema. Please note that requires the kubernetes-validate python module.

demo

How to Configure a Pod to Use a Volume for Storage on Kubernetes K8s or OpenShift OCP with Ansible Playbook. I’m going to create a Pod that runs one Container. This Pod has a Volume of type emptyDir that lasts for the life of the Pod, even if the Container terminates and restarts.

code

---
- name: k8s volume demo
  hosts: localhost
  gather_facts: false
  connection: local
  vars:
    myproject: "volume-example"
  tasks:
    - name: create {{ myproject }} namespace
      kubernetes.core.k8s:
        kind: Namespace
        name: "{{ myproject }}"
        state: present
        api_version: v1

    - name: create k8s pod
      kubernetes.core.k8s:
        state: present
        namespace: "{{ myproject }}"
        definition:
          apiVersion: v1
          kind: Pod
          metadata:
            name: redis
          spec:
            containers:
            - name: redis
              image: redis
              volumeMounts:
              - name: redis-storage
                mountPath: /data/redis
            volumes:
            - name: redis-storage
              emptyDir: {}

execution

ansible-pilot $ ansible-playbook kubernetes/configure-volume.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'
PLAY [k8s volume demo] ****************************************************************************
TASK [create volume-example namespace] ************************************************************
changed: [localhost]
TASK [create k8s pod] *****************************************************************************
changed: [localhost]
PLAY RECAP ****************************************************************************************
localhost                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-pilot $

idempotency

ansible-pilot $ ansible-playbook kubernetes/configure-volume.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'
PLAY [k8s volume demo] ****************************************************************************
TASK [create volume-example namespace] ************************************************************
ok: [localhost]
TASK [create k8s pod] *****************************************************************************
ok: [localhost]
PLAY RECAP ****************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-pilot $

before execution

ansible-pilot $ kubeadmin get pod --namespace=volume-example
No resources found in volume-example namespace.
ansible-pilot $
ansible-pilot $ oc login -u kubeadmin https://api.crc.testing:6443
Logged into "https://api.crc.testing:6443" as "kubeadmin" using existing credentials.
You have access to 64 projects, the list has been suppressed. You can list all projects with 'oc projects'
Using project "default".
ansible-pilot $ oc project volume-example
error: A project named "volume-example" does not exist on "https://api.crc.testing:6443".
Your projects are:
* default
[...]
ansible-pilot $ oc get pod --namespace=volume-example
No resources found in volume-example namespace.
ansible-pilot $

ansible module k8s before execution

after execution

ansible-pilot $ kubeadmin get pod --namespace=volume-example
NAME    READY   STATUS    RESTARTS   AGE
redis   1/1     Running   0          38s
ansible-pilot $ kubeadmin get pod --namespace=volume-example --output=yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: Pod
  metadata:
    annotations:
      k8s.v1.cni.cncf.io/network-status: |-
        [{
            "name": "openshift-sdn",
            "interface": "eth0",
            "ips": [
                "10.217.0.129"
            ],
            "default": true,
            "dns": {}
        }]
      k8s.v1.cni.cncf.io/networks-status: |-
        [{
            "name": "openshift-sdn",
            "interface": "eth0",
            "ips": [
                "10.217.0.129"
            ],
            "default": true,
            "dns": {}
        }]
      openshift.io/scc: anyuid
    creationTimestamp: "2022-04-19T14:32:03Z"
    name: redis
    namespace: volume-example
    resourceVersion: "354824"
    uid: 6726f836-e601-430a-a829-1575ede0a781
  spec:
    containers:
    - image: redis
      imagePullPolicy: Always
      name: redis
      resources: {}
      securityContext:
        capabilities:
          drop:
          - MKNOD
      terminationMessagePath: /dev/termination-log
      terminationMessagePolicy: File
      volumeMounts:
      - mountPath: /data/redis
        name: redis-storage
      - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
        name: kube-api-access-qprq2
        readOnly: true
    dnsPolicy: ClusterFirst
    enableServiceLinks: true
    imagePullSecrets:
    - name: default-dockercfg-8h4dp
    nodeName: crc-8rwmc-master-0
    preemptionPolicy: PreemptLowerPriority
    priority: 0
    restartPolicy: Always
    schedulerName: default-scheduler
    securityContext:
      seLinuxOptions:
        level: s0:c26,c25
    serviceAccount: default
    serviceAccountName: default
    terminationGracePeriodSeconds: 30
    tolerations:
    - effect: NoExecute
      key: node.kubernetes.io/not-ready
      operator: Exists
      tolerationSeconds: 300
    - effect: NoExecute
      key: node.kubernetes.io/unreachable
      operator: Exists
      tolerationSeconds: 300
    volumes:
    - emptyDir: {}
      name: redis-storage
    - name: kube-api-access-qprq2
      projected:
        defaultMode: 420
        sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token
        - configMap:
            items:
            - key: ca.crt
              path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
            - fieldRef:
                apiVersion: v1
                fieldPath: metadata.namespace
              path: namespace
        - configMap:
            items:
            - key: service-ca.crt
              path: service-ca.crt
            name: openshift-service-ca.crt
  status:
    conditions:
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:03Z"
      status: "True"
      type: Initialized
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:10Z"
      status: "True"
      type: Ready
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:10Z"
      status: "True"
      type: ContainersReady
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:03Z"
      status: "True"
      type: PodScheduled
    containerStatuses:
    - containerID: cri-o://4bffa22f092c5c7808b39af757a5df46b214e5037f891e1bee8f7bd95f1ae26b
      image: docker.io/library/redis:latest
      imageID: docker.io/library/[email protected]:1b36e146475b71ee04da1ce60f201308392ff8468107f91615885d2e49536010
      lastState: {}
      name: redis
      ready: true
      restartCount: 0
      started: true
      state:
        running:
          startedAt: "2022-04-19T14:32:09Z"
    hostIP: 192.168.126.11
    phase: Running
    podIP: 10.217.0.129
    podIPs:
    - ip: 10.217.0.129
    qosClass: BestEffort
    startTime: "2022-04-19T14:32:03Z"
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
ansible-pilot $
ansible-pilot $ oc project volume-example
Now using project "volume-example" on server "https://api.crc.testing:6443".
ansible-pilot $ oc get pod --namespace=volume-example
NAME    READY   STATUS    RESTARTS   AGE
redis   1/1     Running   0          38s
ansible-pilot $ oc get pod --namespace=volume-example --output=yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: Pod
  metadata:
    annotations:
      k8s.v1.cni.cncf.io/network-status: |-
        [{
            "name": "openshift-sdn",
            "interface": "eth0",
            "ips": [
                "10.217.0.129"
            ],
            "default": true,
            "dns": {}
        }]
      k8s.v1.cni.cncf.io/networks-status: |-
        [{
            "name": "openshift-sdn",
            "interface": "eth0",
            "ips": [
                "10.217.0.129"
            ],
            "default": true,
            "dns": {}
        }]
      openshift.io/scc: anyuid
    creationTimestamp: "2022-04-19T14:32:03Z"
    name: redis
    namespace: volume-example
    resourceVersion: "354824"
    uid: 6726f836-e601-430a-a829-1575ede0a781
  spec:
    containers:
    - image: redis
      imagePullPolicy: Always
      name: redis
      resources: {}
      securityContext:
        capabilities:
          drop:
          - MKNOD
      terminationMessagePath: /dev/termination-log
      terminationMessagePolicy: File
      volumeMounts:
      - mountPath: /data/redis
        name: redis-storage
      - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
        name: kube-api-access-qprq2
        readOnly: true
    dnsPolicy: ClusterFirst
    enableServiceLinks: true
    imagePullSecrets:
    - name: default-dockercfg-8h4dp
    nodeName: crc-8rwmc-master-0
    preemptionPolicy: PreemptLowerPriority
    priority: 0
    restartPolicy: Always
    schedulerName: default-scheduler
    securityContext:
      seLinuxOptions:
        level: s0:c26,c25
    serviceAccount: default
    serviceAccountName: default
    terminationGracePeriodSeconds: 30
    tolerations:
    - effect: NoExecute
      key: node.kubernetes.io/not-ready
      operator: Exists
      tolerationSeconds: 300
    - effect: NoExecute
      key: node.kubernetes.io/unreachable
      operator: Exists
      tolerationSeconds: 300
    volumes:
    - emptyDir: {}
      name: redis-storage
    - name: kube-api-access-qprq2
      projected:
        defaultMode: 420
        sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token
        - configMap:
            items:
            - key: ca.crt
              path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
            - fieldRef:
                apiVersion: v1
                fieldPath: metadata.namespace
              path: namespace
        - configMap:
            items:
            - key: service-ca.crt
              path: service-ca.crt
            name: openshift-service-ca.crt
  status:
    conditions:
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:03Z"
      status: "True"
      type: Initialized
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:10Z"
      status: "True"
      type: Ready
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:10Z"
      status: "True"
      type: ContainersReady
    - lastProbeTime: null
      lastTransitionTime: "2022-04-19T14:32:03Z"
      status: "True"
      type: PodScheduled
    containerStatuses:
    - containerID: cri-o://4bffa22f092c5c7808b39af757a5df46b214e5037f891e1bee8f7bd95f1ae26b
      image: docker.io/library/redis:latest
      imageID: docker.io/library/[email protected]:1b36e146475b71ee04da1ce60f201308392ff8468107f91615885d2e49536010
      lastState: {}
      name: redis
      ready: true
      restartCount: 0
      started: true
      state:
        running:
          startedAt: "2022-04-19T14:32:09Z"
    hostIP: 192.168.126.11
    phase: Running
    podIP: 10.217.0.129
    podIPs:
    - ip: 10.217.0.129
    qosClass: BestEffort
    startTime: "2022-04-19T14:32:03Z"
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
ansible-pilot $
1:C 19 Apr 2022 14:48:44.365 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 19 Apr 2022 14:48:44.366 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 19 Apr 2022 14:48:44.366 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 19 Apr 2022 14:48:44.366 * monotonic clock: POSIX clock_gettime
1:M 19 Apr 2022 14:48:44.367 * Running mode=standalone, port=6379.
1:M 19 Apr 2022 14:48:44.367 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 19 Apr 2022 14:48:44.367 # Server initialized
1:M 19 Apr 2022 14:48:44.368 * Ready to accept connections

ansible module k8s after execution

Recap

Now you know how to Configure a Pod to Use a Volume for Storage on Kubernetes K8s or OpenShift OCP with Ansible.

Subscribe to the YouTube channel, Medium, Website and Twitter to not miss the next episode of the Ansible Pilot.

Academy

Learn the Ansible automation technology with some real-life examples in my

My book Ansible By Examples: 100+ Automation Examples For Linux and Windows System Administrator and DevOps

Want to keep this project going? Please donate

Trustpilot
Follow me

Subscribe not to miss any new releases

April 19, 2022

FREE Top 10 Best Practices

Top 10 Best Practices of Ansible Automation: save time, reduce errors and stress