Apply Complex Deployment Strategies to A K8s Cluster For More Reliability

Deploying Apps Through a Few Strategies

A Service like istio is developed to help address networking issues that may be "beyond the scope" of kubernetes. Istio, or something like it, can help manage network traffic for things like canary deployments.

Recreate with Downtime

First, destroy all existing versions of the app.
Then, release new versions.
Problem: App goes down during the process.

Rolling Update without Downtime

Take down 1 instance of the app at a time.
App never goes down, better than recreate. This is the default in k8s :)

Deploy Then Test Then Redirect Traffic With Blue-Green

The new app gets deployed along-side the old one.
Routing helps here - tests are run on the new version. Once tests pass, routing is redirected to the new app.
Service Meshes can be helpful here.

How does this work in K8s?

Understand the Current state

  • a deployment of pods of the app is present
  • a service routing traffic to the pods is also present
flowchart 
  direction TB
  DP["Deployment"]
  SVC["Service"]

  SVC --> DP

Include labels to the Deployment and the service

Here something like "version:1". Note the wrapper boxes here are just to "tie" the labels to the objects visually.
The label on the deployment should also be included in the selector in the service definition file. This step can also be done when creating the objects initially: maybe including a version: 1 on both the deployment and service to begin with.

flowchart 
  direction TB
  
  LB1>"version:1"]
  LB2>"version:1"]
  DP["Deployment Object"]
  SVC["Service Object"]

  subgraph Deployment
    LB1
    DP
  end

  subgraph SVC1["Prod Service"]
    LB2
    SVC
  end

  SVC1 --> Deployment

Create A New Deployment With The New App Version

Deploy a new Deployment of the new version of the app.
Have the deploymet accessible through a new service, accessible by some folks to test with, perphaps internal dogfooding or something.

flowchart 
  direction TB
  
  %%%
  %%%  second section
  %%%
  LB3V2>"version:2"]
  LB4V2>"version:2"]
  DP2["Deployment Object"]
  SVC2["Service Object"]

  subgraph DPG2[" QA/Test Deployment"]
    LB3V2
    DP2
  end

  subgraph SVCG2["QA/Test Service"]
    LB4V2
    SVC2
  end


  %%%
  %%%  first section
  %%%
  LB1>"version:1"]
  LB2>"version:1"]
  DP["Deployment Object"]
  SVC["Service Object"]

  subgraph PRDP["Prod Deployment"]
    LB1
    DP
  end

  subgraph SVC1["Prod Service"]
    LB2
    SVC
  end

  SVCG2 --> DPG2
  SVC1 --> PRDP

Adjust the Label Selector on the Production Service

Tell the Production service to matchLabels for the new version.
This will leave...

  • the qa service talking to the qa/latest instance of the app stil
  • the prod service talking to the latest instance of the app
  • the previous deployment of the app "dangling" without incoming traffic
flowchart 
  direction TB
  
  %%%
  %%%  second section
  %%%
  LB3V2>"version:2"]
  LB4V2>"version:2"]
  DP2["Deployment Object"]
  SVC2["Service Object"]

  subgraph DPG2[" (was) QA/Test Deployment"]
    LB3V2
    DP2
  end

  subgraph SVCG2["QA/Test Service"]
    LB4V2
    SVC2
  end


  %%%
  %%%  first section
  %%%
  LB1>"version:1"]
  LB2>"version:1"]
  DP["Deployment Object"]
  SVC["Service Object"]

  subgraph PRDP["(was) Prod Deployment"]
    LB1
    DP
  end

  subgraph SVC1["Prod Service"]
    LB2
    SVC
  end

  SVCG2 --> DPG2
  SVC1 --> DPG2

See This In Code

App Deployment Def V1

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  labels:
    app: webapp
    type: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      version: 1
  template:
    metadata:
      name: webapp-pod
      labels:
        version: 1
    spec:
      containers:
        - name: webapp-box
          image: web-api:1

App Service Def V1

apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    version: 1

App Deployment Def V2

This would be the new version of the deployment.
Here, Colors can be used to decipher the deployments from one-another. ROYGBIV might be useful for matching colors to objects in "order".
The differences here from the first version are:

  • the dpeloyment name
  • the deployment selector label value
  • the pod label
  • the container image version
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment-green
  labels:
    app: webapp
    type: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      version: 2
  template:
    metadata:
      name: webapp-pod
      labels:
        version: 2
    spec:
      containers:
        - name: webapp-box
          image: web-api:2

Change Label On Service Def

Same file as v1 above, just a new spec:selector:version value.

apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    version: 2

Canary

  • Canary, like blue-green, deploys both current + "future" instances of the app
  • Canary, unlike blue-green, routes traffic to both the "current" and "future" version of the app && the "future" version is referred to as the canary version
  • Canary routes a little bit of traffic to the new version for a time, retaining traffic to the current version as well

A Point of Knowledge here: Services distribute traffic "evenly" across pods: 4 pods, each get 25% of the traffic - 5 pods, each get 20%, etc.

  • spin up the current version deployment, the current service, and a new deployment with the new canary version of the pods/apps
  • Add a common label to both deployments, something like midCanary: true or something
  • here, if both deployments have the same number of pods, the service will route 50/50 to each deployment
    • One way to do this: leverage the "natural order" of how services route traffic by having something like 4 pods in the "current" deployment and 1 pod in the "canary" deployment
    • Another way,
Page Tags:
Kubernetes
k8s
deployment
canary
blue/green
deployment strategies