Deploy ConfigMaps and Secrets in Red Hat OpenShift Kubernetes Cluster

jasper475

Jasper Rodda

Posted on January 17, 2024

Deploy ConfigMaps and Secrets in Red Hat OpenShift Kubernetes Cluster

Applications are often written to connect to a database to read and write information. Hardcoding database connection details, port, username and password is the most insecure way and hard to manage at scale. So, Kubernetes solves this problem via ConfigMaps and Secrets.

Two ways to store data

  1. ConfigMap: Used to store insensitive data such as db-port via an Environment variable or a File or Volume Mounts.
  2. Secret: Used to store and encrypt sensitive data at rest such as database username or database password

etcd is a data store in Kubernetes which is used to store all created resources as objects. Resources such a Pod, Deployment, Service, Ingress, ConfigMap are stored in ectcd as plain objects. however, with secrets, etcd stored the information and encrypts the data at rest in base64 encoded.

1. Deploying ConfigMap in Kubernetes

- 1.a) ConfigMap via Env Variable

- 1.b) ConfigMap via File - Volume Mount


1.a) Deploy ConfigMap via Env Variable

  • create configmap.yml file as below
apiVersion: v1
kind: ConfigMap
metadata: 
  name: py-app-configmap
data: 
db-port: "3306"
Enter fullscreen mode Exit fullscreen mode
  • Login to OpenShift Kubernetes cluster
oc login --token=sha256~asf9879SDAF987sd987sdf --server=https://api.sandbox-m3.1530.p1.openshiftapps.com:6443
Enter fullscreen mode Exit fullscreen mode
  • Deploy Configmap
$ kubectl apply -f configmap.yml
configmap/py-app-configmap created
Enter fullscreen mode Exit fullscreen mode
  • verify ConfigMap - $ kubectl get cm | kubectl get configmap
$ kubectl get configmap
NAME                       DATA   AGE
config-service-cabundle    1      2d15h
config-trusted-cabundle    1      2d15h
kube-root-ca.crt           1      2d15h
openshift-service-ca.crt   1      2d15h
py-app-configmap           1      37s
Enter fullscreen mode Exit fullscreen mode

configmap created on OpenShift

configmap yaml on OpenShift

  • Kubectl Describe ConfigMap - $kubectl describe cm py-app-configmap
$kubectl describe cm  py-app-configmap
Name:         py-app-configmap
Namespace:    jasper475-dev
Labels:       <none>
Annotations:  <none>

Data
====
db-port:
----
3306

BinaryData
====

Events:  <none>
Enter fullscreen mode Exit fullscreen mode
  • Deploy Pods and appending configMap using deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata: 
  name: sample-python-deployment
  labels: 
    app: sample-python-app
spec: 
  replicas: 5
  selector: 
    matchLabels: 
      app: sample-python-app
  template: 
    metadata: 
      labels: 
        app: sample-python-app
    spec:
      containers:
      - name: python-app
        image: jasper475/d37-k8s-services-py-django-app:v2
        env:
          - name: DB-PORT
            valueFrom: 
              configMapKeyRef:
                name: py-app-configmap
                key: db-port
        ports:
        - containerPort: 8000
Enter fullscreen mode Exit fullscreen mode
  • deployed Pods
kubectl apply -f deploy.yaml
deployment.apps/sample-python-deployment created
Enter fullscreen mode Exit fullscreen mode
  • get Pods and ssh into it to get env variable
$ kubectl get pods
NAME                                        READY   STATUS    RESTARTS   AGE
sample-python-deployment-5787bd6b9f-5n779   1/1     Running   0          69s
sample-python-deployment-5787bd6b9f-h6th2   1/1     Running   0          69s
sample-python-deployment-5787bd6b9f-sft22   1/1     Running   0          69s
sample-python-deployment-5787bd6b9f-w7s56   1/1     Running   0          69s
sample-python-deployment-5787bd6b9f-xncfd   1/1     Running   0          69s
Enter fullscreen mode Exit fullscreen mode
  • SSH into a pod
  • Command: kubectl exec -it sample-python-deployment-5787bd6b9f-5n779 -- /bin/bash and Search(grep) for Env variable DB-PORT
  • Dev inside python app can retrieve via: OS.env("DB-PORT")
$ kubectl exec -it sample-python-deployment-c57769684-749ct -- /bin/bash
groups: cannot find name for group ID 1011150000
1011150000@sample-python-deployment-c57769684-749ct:/app$ ls
db.sqlite3  demo  devops  manage.py  requirements.txt
1011150000@sample-python-deployment-c57769684-749ct:/app$ env | grep DB
DB-PORT=3306
NSS_SDB_USE_CACHE=no
Enter fullscreen mode Exit fullscreen mode
  • Edit DB-PORT via configmap.yml
apiVersion: v1
kind: ConfigMap
metadata: 
  name: py-app-configmap
data: 
  db-port: "3307"
Enter fullscreen mode Exit fullscreen mode
  • Apply changes kubectl apply -f configmap.yml
$ kubectl apply -f configmap.yml
configmap/py-app-configmap configured
Enter fullscreen mode Exit fullscreen mode
  • Notice config map Port changed to 3307 config map - Port 3307
  • SSH into the POD to get Environment variable value. Notice it is still having OLD Value. DB-PORT=3306
1011150000@sample-python-deployment-c57769684-749ct:/app$ env | grep DB
DB-PORT=3306
NSS_SDB_USE_CACHE=no
1011150000@sample-python-deployment-c57769684-749ct:/app$
Enter fullscreen mode Exit fullscreen mode

We cannot afford to restart pod in production - to Fix this issue we use Volume Mounts and persist config values and decouple it from pod application restarts and config changes.

1.b) Deploy ConfigMap via File - Volume Mount

  • deploy_volume_Mount.yaml file

  • create volume under container

spec: 
      containers: 
      |
      |
      volumes: 
        - name: db-connection
          configMap: 
            name: py-app-configmap

Enter fullscreen mode Exit fullscreen mode
  • Mount volume: provide volume name and volume path
    spec:
      containers:
      - name: python-app
        image: jasper475/d37-k8s-services-py-django-app:v2
        volumeMounts:
          - name: db-connection
          mountPath: /opt
Enter fullscreen mode Exit fullscreen mode
  • apply deployment changes
$ kubectl apply -f deploy_volume_mount.yaml
deployment.apps/sample-python-deployment configured
Enter fullscreen mode Exit fullscreen mode
  • Get Pods kubectl get pods
$ kubectl get pods
NAME                                        READY   STATUS    RESTARTS   AGE
sample-python-deployment-7b67d95fdc-56p4g   1/1     Running   0          37s
sample-python-deployment-7b67d95fdc-5sdcx   1/1     Running   0          37s
sample-python-deployment-7b67d95fdc-5v2r8   1/1     Running   0          35s
sample-python-deployment-7b67d95fdc-74sb8   1/1     Running   0          35s
sample-python-deployment-7b67d95fdc-cs9px   1/1     Running   0          37s
Enter fullscreen mode Exit fullscreen mode
  • SSH into pod as see ENV variable:
  • Notice: There is no environment variable - because deploy_volume_mount.yaml file, since we removed it.
$ kubectl exec -it sample-python-deployment-7b67d95fdc-56p4g -- /bin/bash
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ env | grep DB
NSS_SDB_USE_CACHE=no
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$
Enter fullscreen mode Exit fullscreen mode
  • Notice : volume is mounted on /opt path and port: 3307
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ ls /opt
db-port
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ cat /opt/db-port | more
3307
Enter fullscreen mode Exit fullscreen mode
  • Change Port to 3308 in configmap.yml and apply
apiVersion: v1
kind: ConfigMap
metadata: 
  name: py-app-configmap
data: 
  db-port: "3308"
Enter fullscreen mode Exit fullscreen mode
  • apply changes: Kubectl apply -f configmap.yml
  • describe configmap : $ kubectl describe cm py-app-configmap
$ kubectl describe cm py-app-configmap
Name:         py-app-configmap
Namespace:    jasper475-dev
Labels:       <none>
Annotations:  <none>

Data
====
db-port:
----
3308

BinaryData
====

Events:  <none>
Enter fullscreen mode Exit fullscreen mode
  • Config Map: port 3308
  • SSH into POD and get Port info - kubectl exec -it sample-python-deployment-7b67d95fdc-56p4g -- /bin/bash
$ kubectl exec -it sample-python-deployment-7b67d95fdc-56p4g -- /bin/bash
groups: cannot find name for group ID 1011150000
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ env | grep DB
NSS_SDB_USE_CACHE=no
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ ls  /opt
db-port
1011150000@sample-python-deployment-7b67d95fdc-56p4g:/app$ cat /opt/db-port | more
3308
Enter fullscreen mode Exit fullscreen mode

2. Deploying ConfigMap in Kubernetes

  • create a Secret via CLI command
  • kubectl create secret generic - to store passwords
  • kubectl create secret tls - to store certificates
$ kubectl create secret generic empty-secret
secret/empty-secret created

$ kubectl get secret empty-secret
NAME           TYPE     DATA   AGE
empty-secret   Opaque   0      19s
Enter fullscreen mode Exit fullscreen mode

or

$ kubectl create secret genericmy-db-secret --from-literal=db-port="3308"
Enter fullscreen mode Exit fullscreen mode

or

  • Create secret via basic_secret.yaml file
apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin # required field for kubernetes.io/basic-auth
  password: t0p-Secret # required field for kubernetes.io/basic-auth
Enter fullscreen mode Exit fullscreen mode
  • apply : kubectl apply -f basicsecret.yaml
$ kubectl apply -f basic_secret.yaml                                     
secret/secret-basic-auth created
$  kubectl get secret secret-basic-auth
NAME                TYPE                       DATA   AGE
secret-basic-auth   kubernetes.io/basic-auth   2      17s
PS C:\Users\Jasper\OneDrive\Documents\CodeRepo\kubernetes\d41_ConfigMaps_Secrets> 
Enter fullscreen mode Exit fullscreen mode
  • Verify Secrets

Secret create

  • Describe Secret
kubectl describe secret secret-basic-auth
Name:         secret-basic-auth
Namespace:    jasper475-dev
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/basic-auth

Data
====
password:  10 bytes
username:  5 bytes
Enter fullscreen mode Exit fullscreen mode
  • Edit secret: kubectl edit secret secret-basic-auth
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  password: dDBwLVNlY3JldA==
  username: YWRtaW4=
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"secret-basic-auth","namespace":"jasper475-dev"},"stringData":{"password":"t0p-Secret","username":"admin"},"type":"kubernetes.io/basic-auth"}
  creationTimestamp: "2024-01-17T20:02:39Z"
  name: secret-basic-auth
  namespace: jasper475-dev
  resourceVersion: "1769551165"
  uid: 46bf94fa-c060-4f6b-83d6-9e08a4637b25
type: kubernetes.io/basic-auth
Enter fullscreen mode Exit fullscreen mode
  • Verify Secrets: By default, they are encoded Base64
Jasper@JASPERS-PC MINGW64 ~/OneDrive/Documents/CodeRepo/kubernetes/d41_ConfigMaps_Secrets
$ echo dDBwLVNlY3JldA== | base64 --decode
t0p-Secret
Jasper@JASPERS-PC MINGW64 ~/OneDrive/Documents/CodeRepo/kubernetes/d41_ConfigMaps_Secrets
$ echo YWRtaW4= | base64 --decode
admin
Enter fullscreen mode Exit fullscreen mode

Credits:-
Thanks to Abhishek Veeramalla

💖 💪 🙅 🚩
jasper475
Jasper Rodda

Posted on January 17, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related