Application Lifecycle Management

Rolling Updates, Rollbacks, ConfigMaps, Env Variables, Secrets, Mutli container pods, Init Containers, Self Healing Applications

Rolling Updates and Rollbacks

Rollout and Versioning:

When you create a first deployment, it triggers a rollout. A new rollout creates a new deployment revision. Whenever an application is upgraded, a new deployment revision gets created. This helps us to track the changes made to our deployment. This enables us to roll back to the previous version of deployment if necessary.

# you can check the status rollout of deployement
kubectl rollout status <deployment-name>

# to see the history of rollout
kubectl rollout history <deployment-name>

2 Types of deployment strategies:

  1. Recreate Strategy: Destroy all the applications and recreate the newer version of the applications. During these processes applications are inaccessible to the users.

  2. Rolling updates: This is a default deployment strategy. We don't bring down all the applications in one go instead we take down the older version and bring up the newer version one by one. This way application never goes down and the upgrade is seamless.

Under the hood, a new replica set is created for hosting a new version of the application. It deletes one instance from replicaset1 and creates a new instance in replicaset2. It repeats this process until the desired state is reached.

Suppose after upgradation you realize there is something that is not working and you want to roll back to older version of deployment.

# rollback to previou version of deployment
kubectl rollout undo <deployment-name>

This will delete new replicaset 2 and bring the older ones up back in the replicaset1. And our application is back to its older version.


Configure Applications

It means configuring commands and arguments on applications, environment variables, and secrets.


Set Environment variable in Kubernetes

  1. ENV Property you can directly set environment variable in pod definition yaml file using env property. ENV is an array so every time starts with a dash under it.

     apiVersion: v1
     kind: Pod
     metadata:
       name: envar-demo
       labels:
         purpose: demonstrate-envars
     spec:
       containers:
       - name: envar-demo-container
         image: gcr.io/google-samples/node-hello:1.0
         # env property-like array; 
         env:
         - name: DEMO_GREETING
           value: "Hello from the environment"
         - name: DEMO_FAREWELL
           value: "Directly set env variables to pod defination yaml file"
    
  2. ConfigMaps:

When you have lots of pod definition files, it gets difficult to manage environment variables. So take these variables out of the file and manage centrally using ConfigMaps. So when the pod is created, the config map gets injected to make its key-value pairs available for the application to use it.

Imperative approach:

2.1 create configmap using the command line:

kubectl create configmap \
    <configmap_name> --from-literal=<key1>=<value1> \
                  --from-literal=<key2>=<value2> \
                  --from-literal=<key3>=<value3>

2.2 create configmap file:

kubectl create configmap
    <configmap_name> --from-file=<path-to-file>

example: app-config.properties file is created with key-value pair and then create configmap using the command line:

# app-config.properties
APP_NAME: front-end
APP_COLOR: blue
APP_STAGE: prod
kubectl create configmap
    app-config --from-file=app-config.properties

Declarative approach: create a config-map.yaml file with ConfigMap kind.

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # property-like keys; each key maps to a simple value
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"
  # file-like keys
  game.properties: 
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties:
    color.good=purple
    color.bad=yellow
    allow.textmode=true

You can create ConfigMap using kubectl command:

kubectl create -f config-map.yaml

You can create as many ConfigMap files according to your application like app-config, mysql-config, nginx-config etc.

Stats about ConfigMap:

# get list of configmap
kubectl get configmaps

# describe configmap
kubectl describe configmaps

Once you created the config maps, you can inject them into pod defination file under env property as a configMapKeyRef:

inject using a single environment variable:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # Define the environment variable
        - name: PLAYER_INITIAL_LIVES # Notice that the case is different here
                                     # from the key name in the ConfigMap.
          valueFrom:
            configMapKeyRef:
              name: game-demo        # The ConfigMap this value comes from.
              key: player_initial_lives # The key to fetch.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name

Inject using an environment variable:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      ports: 
        - containerPort: 8080 
      envFrom:
        - configMapRef:
              name: game-demo        # This is a ConfigMap Name, this value comes from the metadata.

inject the whole data as files in a volume:

volumes:
 - name: app-config-volume
   configMap:
      name: app-config
  1. Secrets: used to store critical information in an encoded format such as a password.

    Hence, these are not encrypted so anyone can decode secrets. So configure least-privilege access to Secrets- RBAC and avoid cheching-in secrets file on SCM. For security reasons store the secrets on AWS, Vault, Azure etc Providers and enable Encryption at Rest for Secrets so they can be stored encrypted in ETCD.

    For more advance, you can use Helm Secrets and HashiCorp Vault for handling sensitive data.

     # using command line 
     kubectl create secret generic \
         <secret-name> --from-literal=<key1>=<value1> \
         <secret-name> --from-literal=<key2>=<value2>
    
     # example:
     kubectl create secret generic \
         app-secret --from-literal=DB_USER=root \
                    --from-literal=DB_PASSWORD=password
    
     #-------------------------------------------
    
     # using a properties file  
     kubectl create secret generic \
         <secret-name> --from-file=<path-to-file>
    
     kubectl create secret generic \
         <secret-name> --from-file=app_secret.properties
    

    Lets create a secret file:

     apiVersion: v1
     kind: Secret
     metadata:
       name: app-secret
     data:
       DB_HOST: <encryptedText>
       DB_USER: <encryptedText>
       DB_PASSWORD: <encryptedText>
    
     #-----------------------------
     apiVersion: v1
     kind: Pod
     metadata:
       name: webapp
       labels: 
         name: webapp
     spec: 
       containers:
       - name: web
         image: simple-web
         ports:
           - containerPort: 8080
         envFrom: 
         # 1. Inject secrets as a envrionment variable
           - secretRef:
                  name: app-secret
    
     #------------------------------------
     # 2. Inject a single key using secretKeyRef
     env:
       - name: PLAYER_INITIAL_LIVES
         valueFrom:
             secretKeyRef:
               name: app-secret    
               key: DB_Password
    
     #------------------------------------
     # 3. Inject in a volume
     volumes:
      - name: app-config-volume
        secret:
           secretName: app-secret
    

    Some basic commands to check:

    
     #To encoce and decode text:
     echo -n "mySQL" | base64
     echo -n "encoded-text" | base64 --decode
    
     #To check secrets
     kubectl get secrets
     kubectl describe secrets
     kubectl get secret <secret-name> -o yaml
    

How to save secrets into etcd

Lets use etcd command line locally:

# install etcd-client 
apt-get install etcd-client

# now check etcd command line
etcdctl 

# check the pods
kubectl get pods -n kube-system

Add secrets to etcd:

all although it does not provide encryption. So Kubernetes provide an option to enable encryption at rest.

# check whether this option is enabled or not
ps -aux | grep kube-api | grep "encryption-provider-config"

# you can also check this option is set or not in configuration file
ls /etc/kubernetes/manifests
vi /etc/kubernetes/manifests/kube-apiserver.yaml

Now create your encryption config file (encyption.yaml): to encrypt your data

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - aescbc:
          keys:
            - name: key1
           # comd to create base 64 encoded secret object: head -c 32 /dev/urandom | base64
              secret: <BASE 64 ENCODED SECRET>
      - identity: {}

Now on your local machine create a path for mounting this encryption.yaml file with the pod

mkdir /etc/kubernetes/enc
mv encryption.yaml /etc/kubernetes/enc

# edit this yaml file and make 3 changes 
vi /etc/kubernetes/manifests/kube-apiserver.yaml

# 1.add this line under containers's command
# - --encryption-provider-config=/etc/kubernetes/enc/encryption.yaml 
# 2. add volume mount command
# 3. add volume host path
#------------------------------------------------------------
/etc/kubernetes/manifests/kube-apiserver.yaml file:
spec:
  containers:
  - command:
    - kube-apiserver
    ...
    - --encryption-provider-config=/etc/kubernetes/enc/encryption.yaml  # add this line
    volumeMounts:
    ...
    - name: enc                           # add this line
      mountPath: /etc/kubernetes/enc      # add this line
      readonly: true                      # add this line
    ...
  volumes:
  ...
  - name: enc                             # add this line
    hostPath:                             # add this line
      path: /etc/kubernetes/enc           # add this line
      type: DirectoryOrCreate             # add this line
  ...

# Now confirm the changes 
ps -aux | grep kube-api | grep encryption-provider-config

# Ensure all secrets are encrypted
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

For more refernce check this offical documentation: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/