{"id":15804969,"url":"https://github.com/ddobrin/declarative-deployments-k8s","last_synced_at":"2025-04-21T08:31:47.188Z","repository":{"id":134175645,"uuid":"273331461","full_name":"ddobrin/declarative-deployments-k8s","owner":"ddobrin","description":"Demo for declarative deployments in Kubernetes - fixed, rolling, bluegreen, canary with Java services using Spring Cloud","archived":false,"fork":false,"pushed_at":"2020-06-29T14:27:11.000Z","size":1259,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-06T02:03:14.900Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ddobrin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-18T20:14:57.000Z","updated_at":"2023-07-31T09:17:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"afe703e6-f248-4aa9-8424-77ffd2502b3c","html_url":"https://github.com/ddobrin/declarative-deployments-k8s","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddobrin%2Fdeclarative-deployments-k8s","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddobrin%2Fdeclarative-deployments-k8s/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddobrin%2Fdeclarative-deployments-k8s/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddobrin%2Fdeclarative-deployments-k8s/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ddobrin","download_url":"https://codeload.github.com/ddobrin/declarative-deployments-k8s/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250023527,"owners_count":21362415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-05T02:02:33.001Z","updated_at":"2025-04-21T08:31:46.808Z","avatar_url":"https://github.com/ddobrin.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Patterns for Declarative Deployments in Kubernetes\n\nThe demo applications in this repo are leveraging Spring Cloud Kubernetes and *DO NOT* use a service discovery mechanism (Consul, Eureka, etc). It runs in any Kubernetes distribution.\n\n## Why do anything?\nIsolated environments can be provisioned in a self-service manner with minimal human intervention through the Kubernetes scheduler, however, with a growing number of microservices, continually updating and replacing them with newer versions becomes an increasing burden. \n\nProvisioning processes allow for some or no downtime, however at the expense of increased resource consumption. \n\nWe need to be aware that manual or scripted processes are error prone, respectively effort-intensive, which could bottleneck the software release process.\n\n\n## What are Declarative Deployments?\nThe concept of Deployments in Kubernetes encapsulates the upgrade and rollback processes of a group of containers and makes its execution a repeatable and automated activity. \n\nIt allows us to describe how an application should be updated, based on different strategies and allowing for the fine-tuning of the various aspects of the deployment process\n\n\n## How to explore Declarative Deployment Models:\n1. [Demo - Initial Setup](#1)\n2. [Demo - Install the Demo Client app](#2)\n\n### Zero downtime app deployments\n3. [Rolling Deployments](#3)\n4. [Fixed Deployments](#4)\n5. [Blue-Green Deployments](#5)\n\n### Deployments with app downtime \n6. [Canary Deployments](#6)\n\n\n\u003ca name=\"1\"\u003e\u003c/a\u003e\n## Demo Setup\n\n#### Clean-up previously deployed resources, if any\n```shell\n# clean-up previous images\ndocker images | grep message-service\ndocker images | grep message-service | awk '{print $3}' | xargs docker rmi -f\n\ndocker images | grep billboard-client\ndocker images | grep billboard-client | awk '{print $3}' | xargs docker rmi -f\n```\n\n#### Set up demo artifacts\n```shell\n# clone the Git repo\ngit clone git@github.com:ddobrin/declarative-deployments-k8s.git\ncd declarative-deployments-k8s/\n\n# build the source code\nmvn clean package\n\n# build images with tags 1.0 and 1.1 - for rolling, fixed, canary\ncd build\n./build-images.sh \n\ntriathlonguy/message-service                                                                    1.0                      fcc3c30ee3c5        2 minutes ago       302MB\ntriathlonguy/message-service                                                                    1.1                      fcc3c30ee3c5        2 minutes ago       302MB\ntriathlonguy/billboard-client                                                                   1.0                      47d6fe4af2dd        2 minutes ago       285MB\ntriathlonguy/billboard-client                                                                   1.1                      47d6fe4af2dd        2 minutes ago       285MB\n\n# build images with tags blue and greeen - for blue-green deployments\n./build-images-bluegreen.sh\n\ntriathlonguy/message-service                                                                    1.0                      fcc3c30ee3c5        6 minutes ago       302MB\ntriathlonguy/message-service                                                                    1.1                      fcc3c30ee3c5        6 minutes ago       302MB\ntriathlonguy/message-service                                                                    blue                     fcc3c30ee3c5        6 minutes ago       302MB\ntriathlonguy/message-service                                                                    green                    fcc3c30ee3c5        6 minutes ago       302MB\ntriathlonguy/billboard-client                                                                   1.0                      47d6fe4af2dd        6 minutes ago       285MB\ntriathlonguy/billboard-client                                                                   1.1                      47d6fe4af2dd        6 minutes ago       285MB\ntriathlonguy/billboard-client                                                                   blue                     47d6fe4af2dd        6 minutes ago       285MB\ntriathlonguy/billboard-client                                                                   green                    47d6fe4af2dd        6 minutes ago       285MB\n```\n#### Setup initial K8s resources for the demo\n```shell\nkubectl apply -f configmap.yaml\nkubectl apply -f security.yaml\n\n# substitute values for username and password\nkubectl apply -f dockercred.yaml \n```\n\n\u003ca name=\"2\"\u003e\u003c/a\u003e\n# Demo - Install the Demo Client app\n\n__Please note:__\n* this demo client is to be installed after an initial deployment for one of the 4 declarative deployment demo's illustrated in this repo.\n\nThe client app is a billboard-client app displaying quotes provided by the message-service.\n\n#### Set-up steps\n```shell\n# client app K8s resources\ncd \u003crepo_root\u003e/billboard-client/k8s\n\n# deploy the client app\nkubectl apply -f deployment.yml \n\n# expose the app with a LoadBalancer type of service\nkubectl apply -f service.yml \n\n# validate that the deployment is successful\nkubectl get deploy\n\n# example\nNAME               READY   UP-TO-DATE   AVAILABLE   AGE\nbillboard-client   1/1     1            1           15h\nmessage-service    3/3     3            3           15h\n\n# validate that the service is available and has a Public IP assigned to it\nkubectl get svc\n\n# example\nNAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)          AGE\nbillboard-client   LoadBalancer   10.0.14.43   34.70.147.241   8080:30423/TCP   15h\nmessage-service    NodePort       10.0.3.237   \u003cnone\u003e          8080:31787/TCP   15h \n```\n\n#### Testing the back-end service using the client app\nThe service can be queried at the external IP and the exposed port 8080 :\n```shell\ncurl \u003cexternal IP\u003e:8080/message\n\n# example\n\u003e curl 34.70.147.241:8080/message\nService version: 1.1 - Quote: The shortest answer is doing -- Lord Herbert\n```\n\n#### Access the billboard-client in the browser - at the ExternalIP of the service and the associated port\n![Client app](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/v1.0.png)  \n\n__Please note:__\n* The service for the client app will be available to route requests to the message-service and does not have to be restarted while testing the resepective deployment strategies\n\n#### Client-app cleanup \n```shell\nkubectl delete deploy billboard-client\n```\n\n\u003ca name=\"3\"\u003e\u003c/a\u003e\n# Rolling Deployment\n\n#### Pros:\n* Zero downtime during the update process\n* Ability to control the rate of a new container rollout\n\n#### Cons:\n* During the update, two versions of the container are running at the same time\n\n#### How does it work:\n* Deployment creates a new ReplicaSet and the respective Pods\n* Deployment replaces the old containers with the previous service version with the new ones\n* Deployment allows you to control the range of available and excess Pods\n\n![Rolling Deployment - Prior to Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/RD1.png)  \n\n\n![Rolling Deployment - During Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/RD2.png)  \n\n![Rolling Deployment - Post Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/RD3.png)  \n\n#### The Deployment configuration\nThe Deployment uses the `RollingUpdate` strategy and allows full control over hopw many instances are unavailable at any given moment in time:\n```yaml\nkind: Deployment\nmetadata:\n  name: message-service\nspec:\n  replicas: 3\n  strategy:\n    type: RollingUpdate\n    rollingUpdate: \n      maxSurge: 1\n      maxUnavailable: 1\n  selector:\n    matchLabels:\n      app: message-service\n  template:\n    metadata:\n      labels:\n        app: message-service\n...        \n```\n\nThe Service selects all nodes for the message-service matching the label:\n```yaml\nkind: Service\nmetadata:\n  labels:\n    app: message-service\n  name: message-service\n  namespace: default\nspec:\n...\n  selector:\n    app: message-service\n  type: NodePort\n```\n\n#### Clean-up resources before running this demo\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\n```\n\n#### Start deploying the resources for the Rolling Deployment demo\n```shell\n# deploy v 1.0\n\u003e cd \u003crepo_root\u003e/message-service/k8s/rolling\n\u003e kubectl deployment-rolling-1.0.yml\n\n# validate deployment\n\u003e k get pod\nNAME                               READY   STATUS    RESTARTS   AGE\nmessage-service-5566ff65cd-crf2j   1/1     Running   0          39s\nmessage-service-5566ff65cd-f4b46   1/1     Running   0          39s\nmessage-service-5566ff65cd-q4kd7   1/1     Running   0          39s\n\n\u003e k get deploy\nNAME              READY   UP-TO-DATE   AVAILABLE   AGE\nmessage-service   3/3     3            3           47s\n\n# deploy service exposing a NodePort\nkubectl apply -f service.yml\n\n# access the endpoints on the node and run some curl requests\nkubectl get ep\n# example\nNAME              ENDPOINTS                                         AGE\nkubernetes        35.222.17.85:443                                  2d5h\nmessage-service   10.24.0.38:8080,10.24.1.51:8080,10.24.1.52:8080   16s\n\n# you can access the service also via the NodePort IP of the Service\nkubectl get svc\n# example\nNAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE\nkubernetes        ClusterIP   10.0.0.1     \u003cnone\u003e        443/TCP          2d5h\nmessage-service   NodePort    10.0.3.237   \u003cnone\u003e        8080:31787/TCP   15m\n\n# run an NGINX pod to access the message-service using the NodePort\nkubectl run nginx --restart=Never --rm --image=nginx -it -- bash\ncurl \u003cselect-one-message-service-endpoint\u003e\ncurl \u003cselect-one-message-service-endpoint\u003e/quotes\nexit\n\n# example \n# Service version indicates : version 1.0 at this time\n# using pod endpoint\nroot@nginx:/# curl 10.24.0.38:8080\n{\"id\":3,\"quote\":\"Service version: 1.0 - Quote: Failure is success in progress\",\"author\":\"Anonymous\"}\n\nroot@nginx:/# curl 10.0.3.237:8080\n# Service version indicates : version 1.0 at this time\n# using service NodePort IP\n{\"id\":5,\"quote\":\"Service version: 1.0 - Quote: The shortest answer is doing\",\"author\":\"Lord Herbert\"}\n\n# Update the deployment to version 1.1 \n# for demo purposes, an environment variable is also modified to indicate the version\n# to update only the image, the K8s set image command can be used\nkubectl apply -f deployment-rolling-1.1.yml \n\n# example - how to update the image in a deployment\nset image deployment message-service message-service=triathlonguy/message-service:1.1\n\n\n# from the NGINX instance, run the curl command again the service again\n# note that version is 1.1\n# using service NodePort IP\n# Service version indicates : version 1.1 at this time\nroot@nginx:/# curl 10.0.3.237:8080\n{\"id\":2,\"quote\":\"Service version: 1.1 - Quote: While there's life, there's hope\",\"author\":\"Marcus Tullius Cicero\"\n```\n\n#### Clean-up resources after running this demo for rolling deployments\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\n```\n\n\u003ca name=\"4\"\u003e\u003c/a\u003e\n# Fixed Deployment\n\n#### Pros:\n* Single version serves requests at any moment in time\n* Simpler process for service consumers, as they do not have to handle multiple versions at the same time\n\n#### Cons:\n* There is downtime while old containers are stopped, and the new ones are starting\n\n#### How does it work:\n* Deployment stops first all containers deployed for the old version\n* Clients experience an outage, as no service is available to process requests\n* Deployment creates the new containers\n* Client accesses requests serviced by the new version\n\n![Fixed Deployment - Prior to Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/FD1.png)  \n\n![Fixed Deployment - During Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/FD2.png)  \n\n![Fixed Deployment - Post Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/FD3.png)  \n\n#### The Deployment configuration\nThe Deployment uses the `Recreate` strategy as it terminates all pods from a deployment before creating the pods for the new version:\n```yaml\nkind: Deployment\nmetadata:\n  name: message-service\nspec:\n  replicas: 3\n  strategy:\n    type: Recreate\n  selector:\n    matchLabels:\n      app: message-service\n...        \n```\n\nThe Service selects all nodes for the message-service matching the label:\n```yaml\nkind: Service\nmetadata:\n  labels:\n    app: message-service\n  name: message-service\n  namespace: default\nspec:\n...\n  selector:\n    app: message-service\n  type: NodePort\n```\n\n#### Clean-up resources before running this demo\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\n```\n#### Start deploying the resources for the Fixed Deployment demo\n```shell\n# deploy v 1.0\n\u003e cd \u003crepo_root\u003e/message-service/k8s/fixed\n\u003e kubectl apply -f deployment-fixed-1.0.yml\n\n# validate deployment\n\u003e k get pod\n# example\nNAME                                READY   STATUS    RESTARTS   AGE\nmessage-service-5566ff65cd-c5872    1/1     Running   0          19m\nmessage-service-5566ff65cd-gvcqj    1/1     Running   0          19m\nmessage-service-5566ff65cd-mxqmj    1/1     Running   0          19m\n\n\u003e k get deploy\n# example\nNAME              READY   UP-TO-DATE   AVAILABLE   AGE\nmessage-service   3/3     3            3           47s\n\n# deploy service exposing a NodePort\nkubectl apply -f service.yml\n\n# access the endpoints on the node and run some curl requests\nkubectl get ep\n# example\nNAME               ENDPOINTS                                         AGE\nbillboard-client   10.24.1.62:8080                                   17h\nkubernetes         35.222.17.85:443                                  2d23h\nmessage-service    10.24.0.41:8080,10.24.1.63:8080,10.24.1.64:8080   17h\n\n# you can access the service also via the NodePort IP of the Service\nkubectl get svc\n# example\nNAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)          AGE\nbillboard-client   LoadBalancer   10.0.14.43   34.70.147.241   8080:30423/TCP   17h\nkubernetes         ClusterIP      10.0.0.1     \u003cnone\u003e          443/TCP          2d23h\nmessage-service    NodePort       10.0.3.237   \u003cnone\u003e          8080:31787/TCP   17h\n\n# run an NGINX pod to access the message-service using the NodePort\nkubectl run nginx --restart=Never --rm --image=nginx -it -- bash\ncurl \u003cselect-one-message-service-endpoint\u003e\ncurl \u003cselect-one-message-service-endpoint\u003e/quotes\nexit\n\n# example \n# Service version indicates : version 1.0 at this time\n# using pod endpoint\nroot@nginx:/# curl 10.24.0.41:8080\n{\"id\":3,\"quote\":\"Service version: 1.0 - Quote: Failure is success in progress\",\"author\":\"Anonymous\"}\n\nroot@nginx:/# curl 10.0.3.237:8080\n# Service version indicates : version 1.0 at this time\n# using service NodePort IP\n{\"id\":5,\"quote\":\"Service version: 1.0 - Quote: The shortest answer is doing\",\"author\":\"Lord Herbert\"}\n\n# Update the deployment to version 1.1 \n# for demo purposes, an environment variable is also modified to indicate the version\n# to update only the image, the K8s set image command can be used\nkubectl apply -f deployment-fixed-1.1.yml \n\n# example - how to update the image in a deployment\nset image deployment message-service message-service=triathlonguy/message-service:1.1\n```\n\n#### Observe how all pods are being shut down, befoire the pods for the new version are being started\n```shell\n\u003e kubectl get pod\nNAME                                READY   STATUS        RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9   1/1     Running       0          17h\nmessage-service-5566ff65cd-c5872    1/1     Terminating   0          28m\nmessage-service-5566ff65cd-gvcqj    1/1     Terminating   0          28m\nmessage-service-5566ff65cd-mxqmj    1/1     Terminating   0          28m\n\nmessage-service-58744dd6c9-jsprc    0/1     Pending       0          0s\nmessage-service-58744dd6c9-jsprc    0/1     ContainerCreating   0          0s\nmessage-service-58744dd6c9-r5bkq    0/1     Pending             0          0s\nmessage-service-58744dd6c9-r5bkq    0/1     ContainerCreating   0          0s\nmessage-service-58744dd6c9-bxswq    0/1     Pending             0          0s\nmessage-service-58744dd6c9-bxswq    0/1     ContainerCreating   0          0s\nmessage-service-58744dd6c9-r5bkq    1/1     Running             0          2s\nmessage-service-58744dd6c9-bxswq    1/1     Running             0          2s\nmessage-service-58744dd6c9-jsprc    1/1     Running             0          2s\n\nbillboard-client-6f6d7858d9-qhmv9   1/1     Running   0          17h\nmessage-service-58744dd6c9-bxswq    1/1     Running   0          25s\nmessage-service-58744dd6c9-jsprc    1/1     Running   0          25s\nmessage-service-58744dd6c9-r5bkq    1/1     Running   0          25s\n```\n\n#### Observe the updated deployment by running a fresh curl request\n```shell\n# from the NGINX instance, run the curl command again the service again\n# note that version is 1.1\n# using service NodePort IP\n# Service version indicates : version 1.1 at this time\nroot@nginx:/# curl 10.0.3.237:8080\n{\"id\":2,\"quote\":\"Service version: 1.1 - Quote: While there's life, there's hope\",\"author\":\"Marcus Tullius Cicero\"\n```\n\n#### Clean-up resources after running this demo for fixed deployments\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\nkubectl delete svc message-service\n```\n\n\u003ca name=\"5\"\u003e\u003c/a\u003e\n# Blue-Green Deployment\n\n#### Pros:\n* Single version serves requests at any moment in time\n* Zero downtime during the update\n* Allows precise control of switching to the new version\n\n#### Cons:\n* Requires 2x capacity while both blue and green versions are up\n* Manual intervention for switch\n\n#### How does it work :\n* A second Deployment is created manually for the new version (green)\n* The new version (green) does not serve client requests yet and can be tested internally to validate the deployment\n* The Service Selector in K8s is being updated to route traffic to the new version (green), followed by the removal of the old (blue) Deployment\n\n![Blue-Green Deployment - Prior to Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/BGD1.png)  \n\n![Blue-Green Deployment - During Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/BGD2.png)  \n\n![Blue-Green Deployment - Post Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/BGD3.png)  \n\n#### The Deployment configuration\nThe Deployment does not provide a specific strategy, as the service exposing the deployment is the K8s resource participating in the deployment process which selects which pod instances are exposed to client requests. In this excerpt, 2 labels are used, `message-service` and `blue`:\n```yaml\nkind: Deployment\nmetadata:\n  name: message-service-blue\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: message-service\n  template:\n    metadata:\n      labels:\n        app: message-service\n        version: blue\n...        \n```\n\nThe Service selects all nodes for the message-service matching multiple labels: the app + the version. This allows the selection of the pods matching a specific version. This excerpt has the Service match 2 labels `message-service` and 'blue`:\n```yaml\nkind: Service\nmetadata:\n  labels:\n    app: message-service\n  name: message-service\n  namespace: default\nspec:\n...\n  selector:\n    app: message-service\n    version: blue\n  type: NodePort\n```\n\n#### Clean-up resources before running this Blue-green demo\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service-blue\nkubectl delete deploy message-service-green\n```\n\n#### Start deploying the resources for the Blue-green Deployment demo\n```shell\n# deploy v 1.0\n\u003e cd \u003crepo_root\u003e/message-service/k8s/bluegreen\n\u003e kubectl apply -f deployment-blue.yml\n\n# validate deployment\n\u003e kubectl get pod\n# example\nNAME                                     READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9        1/1     Running       0          20h\nmessage-service-blue-5f77f88f4f-hk99f    1/1     Running       0          3s\nmessage-service-blue-5f77f88f4f-j6hvg    1/1     Running       0          3s\nmessage-service-blue-5f77f88f4f-lgq9x    1/1     Running       0          3s\n\n\u003e kubectl get deploy\nNAME                   READY   UP-TO-DATE   AVAILABLE   AGE\nbillboard-client       1/1     1            1           20h\nmessage-service-blue   3/3     3            3           20s\n\n# deploy service exposing a NodePort\nkubectl apply -f service-blue.yml\n\n# access the endpoints on the node and run some curl requests\nkubectl get ep\n# example\nNAME               ENDPOINTS                                         AGE\nbillboard-client   10.24.1.62:8080                                   20h\nkubernetes         35.222.17.85:443                                  3d2h\nmessage-service    10.24.0.47:8080,10.24.0.48:8080,10.24.1.75:8080   6s\n\n# you can access the service also via the NodePort IP of the Service\nkubectl get svc\n# example\n# note the selector matching the label \"blue\"\nNAME               TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)          AGE    SELECTOR \nbillboard-client   LoadBalancer   10.0.14.43    34.70.147.241   8080:30423/TCP   21h    app=billboard-client\nkubernetes         ClusterIP      10.0.0.1      \u003cnone\u003e          443/TCP          3d3h   \u003cnone\u003e\nmessage-service    NodePort       10.0.11.113   \u003cnone\u003e          8080:32072/TCP   18m    app=message-service,version=blue\n\n# run an NGINX pod to access the message-service using the NodePort\nkubectl run nginx --restart=Never --rm --image=nginx -it -- bash\ncurl \u003cselect-one-message-service-endpoint\u003e\ncurl \u003cselect-one-message-service-endpoint\u003e/quotes\nexit\n\n# example \n# Service version indicates : version \"blue\" at this time\n# using pod endpoint\nroot@nginx:/#  curl 10.24.0.47:8080\n{\"id\":3,\"quote\":\"Service version: 1.0 - Quote: Failure is success in progress\",\"author\":\"Anonymous\"}\n\nroot@nginx:/# curl 10.0.11.113:8080\n# Service version indicates : version 1.0 at this time\n# using service NodePort IP\n{\"id\":5,\"quote\":\"Service version: 1.0 - Quote: The shortest answer is doing\",\"author\":\"Lord Herbert\"}\n```\n\n#### Update the deployment to version \"green\" \n```shell\n# for demo purposes, an environment variable is also modified to indicate the version \"green\"\n# to update only the image, the K8s set image command can be used\nkubectl apply -f deployment-green.yml \n\n# example - how to update the image in a deployment\nset image deployment message-service message-service=triathlonguy/message-service:green\n```\n\n#### Observe the newly added green deployment, in parallel with the blue deployment\n\n```shell\n\u003e kubectl get deploy\n# example\nNAME                    READY   UP-TO-DATE   AVAILABLE   AGE\nbillboard-client        1/1     1            1           21h\nmessage-service-blue    3/3     3            3           10m\nmessage-service-green   3/3     3            3           4s\n\n\u003e kubectl get pod\n# example\nNAME                                     READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9        1/1     Running   0          21h\nmessage-service-blue-5f77f88f4f-hk99f    1/1     Running   0          12m\nmessage-service-blue-5f77f88f4f-j6hvg    1/1     Running   0          12m\nmessage-service-blue-5f77f88f4f-lgq9x    1/1     Running   0          12m\nmessage-service-green-5b745bd4f6-2k9zz   1/1     Running   0          2m15s\nmessage-service-green-5b745bd4f6-n29gw   1/1     Running   0          2m15s\nmessage-service-green-5b745bd4f6-pp44h   1/1     Running   0          2m15s\n\n# from the NGINX instance, run the curl command again the service again\n# note that version is still \"blue\"\n# using service NodePort IP\n# Service version indicates : version blue at this time\nroot@nginx:/# curl 10.0.11.113:8080\n{\"id\":2,\"quote\":\"Service version: blue - Quote: While there's life, there's hope\",\"author\":\"Marcus Tullius Cicero\"}\n\n# you can use also the external IP of the billboard-client\n\u003e curl 34.70.147.241:8080/message\n# example\nService version: blue - Quote: Failure is success in progress -- Anonymous\n```\n\n#### The service still matches the same pods with selector \"blue\"\n\n#### Switch traffic by updating the service definition to use the selector \"green\"\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n ...\n  labels:\n    app: message-service\n  name: message-service\n  namespace: default\n  resourceVersion: \"1043830\"\n  selfLink: /api/v1/namespaces/default/services/message-service\n  uid: 41f79216-b260-11ea-bb6b-42010a800107\nspec:\n  clusterIP: 10.0.11.113\n  externalTrafficPolicy: Cluster\n  ports:\n  - nodePort: 32072\n    port: 8080\n    protocol: TCP\n    targetPort: 8080\n  selector:\n    app: message-service\n    version: green\n ...\n```\n\n```shell\n\u003e kubectl apply -f service-green.yml\n# example\n# Note the selector matching the label \"green\"\nNAME               TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)          AGE    SELECTOR\nbillboard-client   LoadBalancer   10.0.14.43    34.70.147.241   8080:30423/TCP   21h    app=billboard-client\nkubernetes         ClusterIP      10.0.0.1      \u003cnone\u003e          443/TCP          3d3h   \u003cnone\u003e\nmessage-service    NodePort       10.0.11.113   \u003cnone\u003e          8080:32072/TCP   18m    app=message-service,version=green\n\n# using the external IP of the billboard-client, we can validate that the traffic has moved to the green deployment\n\u003e curl 34.70.147.241:8080/message\nService version: green - Quote: Success demands singleness of purpose -- Vincent Lombardi\n\n# from within the NGINX instance, we can use the NodePort IP of the message-service\n\u003e curl 10.0.11.113:8080\n{\"id\":2,\"quote\":\"Service version: green - Quote: While there's life, there's hope\",\"author\":\"Marcus Tullius Cicero\"}\n```\n\n#### At this time, the Blue deployment can safely be removed\n```shell\n\u003e kubectl delete deploy message-service-blue\n```\n\n#### Clean-up resources after running this demo for rolling deployments\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service-blue\nkubectl delete deploy message-service-green\nkubectl delete svc message-service\n```\n\n\u003ca name=\"6\"\u003e\u003c/a\u003e\n# Canary Deployment\n\n\n#### Pros:\n* Reduces the risk of a new service version by controlling access to the new version to a subset of consumers\n* Allows precise control of full switch to the new version\n\n#### Cons:\n* Manual intervention for switch\n* Consumers failing to handle multiple versions simultaneously see failures\n\n#### How does it work:\n* A second Deployment is created manually for the new version (canary) with a small set of instances\n* Some of the client requests are now redirected to the canary version\n* Once there is confidence that the canary version works as expected, traffic is fully scaled up for the canary version and scaled to zero for the old one\n\n![Canary Deployment - Prior to Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/CD1.png)  \n\n![Canary Deployment - During Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/CD2.png)  \n\n![Canary Deployment - Post Deployment](https://github.com/ddobrin/declarative-deployments-k8s/blob/master/images/CD3.png)  \n\n#### The Deployment configuration\nThe Deployment does not provide a specific strategy, as the service exposing the deployment is the K8s resource participating in the deployment process which selects which pod instances are exposed to client requests. In this excerpt, 2 labels are used, `message-service` and `1.0`, the canary deployment will use `message-service` and `canary`, which will allow the Service to match pods by a single label `message-service`:\n```yaml\n# initial deployment\nkind: Deployment\nmetadata:\n  name: message-service\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: message-service\n  template:\n    metadata:\n      labels:\n        app: message-service\n        version: \"1.0\"\n...        \n# canary deployment\nkind: Deployment\nmetadata:\n  name: message-service-canary\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: message-service\n  template:\n    metadata:\n      labels:\n        app: message-service\n        version: canary\n...\n```\n\nThe Service selects all nodes for the message-service matching the labels for the app:\n```yaml\nkind: Service\nmetadata:\n  labels:\n    app: message-service\n  name: message-service\n  namespace: default\nspec:\n...\n  selector:\n    app: message-service\n  type: NodePort\n```\n\n#### Clean-up resources before running this Canary Deployment demo\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\n```\n#### Start deploying the resources for the Canary Deployment demo\n```shell\n# deploy v 1.0\n\u003e cd \u003crepo_root\u003e/message-service/k8s/canary\n\u003e kubectl apply -f deployment-1.0.yml\n\n# validate deployment\n\u003e kubectl get pod\n# example\nNAME                                READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9   1/1     Running   0          24h\nmessage-service-5566ff65cd-6mpgv    1/1     Running   0          23s\nmessage-service-5566ff65cd-jqxkq    1/1     Running   0          23s\nmessage-service-5566ff65cd-mjp4q    1/1     Running   0          23s\n\n\u003e kubectl get deploy\n# example\nNAME               READY   UP-TO-DATE   AVAILABLE   AGE\nbillboard-client   1/1     1            1           24h\nmessage-service    3/3     3            3           46s\n\n# deploy service exposing a NodePort\n\u003e kubectl apply -f service-canary.yml \n\n# access the endpoints on the node and run some curl requests\n\u003e kubectl get ep\n# example\nNAME               ENDPOINTS                                         AGE\nbillboard-client   10.24.1.62:8080                                   24h\nkubernetes         35.222.17.85:443                                  3d6h\nmessage-service    10.24.0.53:8080,10.24.1.81:8080,10.24.1.82:8080   20s\n\n# you can access the service also via the NodePort IP of the Service\n\u003e kubectl get svc\n# example\nNAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)          AGE\nbillboard-client   LoadBalancer   10.0.14.43   34.70.147.241   8080:30423/TCP   24h\nkubernetes         ClusterIP      10.0.0.1     \u003cnone\u003e          443/TCP          3d6h\nmessage-service    NodePort       10.0.15.51   \u003cnone\u003e          8080:30296/TCP   57s\n\n# access the message-service using the public IP of the billboard-client\n\u003e curl 34.70.147.241:8080/message\nService version: 1.0 - Quote: Never, never, never give up -- Winston Churchill\n\n# run an NGINX pod to access the message-service using the NodePort\n\u003e kubectl run nginx --restart=Never --rm --image=nginx -it -- bash\ncurl \u003cselect-one-message-service-endpoint\u003e\ncurl \u003cselect-one-message-service-endpoint\u003e/quotes\nexit\n\n# example \n# Service version indicates : version 1.0 at this time\n# using pod endpoint\nroot@nginx:/# curl 10.24.0.53:8080\n{\"id\":5,\"quote\":\"Service version: 1.0 - Quote: The shortest answer is doing\",\"author\":\"Lord Herbert\"}\n\nroot@nginx:/# curl curl 10.0.15.51:8080\n# Service version indicates : version 1.0 at this time\n# using service NodePort IP\n{\"id\":4,\"quote\":\"Service version: 1.0 - Quote: Success demands singleness of purpose\",\"author\":\"Vincent Lombardi\"}\n```\n#### Scale down version 1.0 of the service from 3 to 2 replicas and deploy the canary version, v 1.1, with one replica\n#### The number of instances running at the same time remains the same\n\n```shell\n\u003e kubectl scale deploy message-service --replicas=2\n\u003e kubectl get pod\n# example\nNAME                                READY   STATUS        RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9   1/1     Running       0          24h\nmessage-service-5566ff65cd-6mpgv    1/1     Running       0          9m11s\nmessage-service-5566ff65cd-jqxkq    1/1     Running       0          9m11s\nmessage-service-5566ff65cd-mjp4q    1/1     Terminating   0          9m11s\n\nNAME                                READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9   1/1     Running   0          24h\nmessage-service-5566ff65cd-6mpgv    1/1     Running   0          15m\nmessage-service-5566ff65cd-jqxkq    1/1     Running   0          15m\n\n# Create the deployment with version 1.1 with 1 single replica, to maintain the previous replica count\n# for demo purposes, an environment variable is also modified to indicate the version\n\u003e kubectl apply -f deployment-canary.yml \n# example\nNAME                                      READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9         1/1     Running   0          25h\nmessage-service-5566ff65cd-6mpgv          1/1     Running   0          36m\nmessage-service-5566ff65cd-jqxkq          1/1     Running   0          36m\nmessage-service-canary-747cbf84b7-jtrqx   1/1     Running   0          5s\n```\n\n#### Requests will be load-balanced between the old version (1.0) and the new one (canary) according to the ratio of the running pods\n```shell\n# run an NGINX pod to access the message-service using the NodePort\n\u003e kubectl run nginx --restart=Never --rm --image=nginx -it -- bash\ncurl \u003cselect-one-message-service-endpoint\u003e\ncurl \u003cselect-one-message-service-endpoint\u003e/quotes\nexit\n\n# example \n# Service version indicates : version 1.0 at this time\n# using pod endpoint\nroot@nginx:/# curl 10.24.1.81:8080 \n{\"id\":5,\"quote\":\"Service version: 1.0 - Quote: The shortest answer is doing\",\"author\":\"Lord Herbert\"}\n\n# Access the canary service : version \"canary\" at this time\nroot@nginx:/# curl 10.24.1.83:8080\n{\"id\":3,\"quote\":\"Service version: canary - Quote: Failure is success in progress\",\"author\":\"Anonymous\"}\n\n# exit the NGINX instance\nexit\n\n# accessing the service using the load balanced billboard-client service illustrates how requests are load balanced against the 1.0 respectively canary versions\n\u003e curl  34.70.147.241:8080/message\nService version: 1.0 - Quote: Never, never, never give up -- Winston Churchill\n\u003e curl  34.70.147.241:8080/message\nService version: canary - Quote: The shortest answer is doing -- Lord Herbert\n\u003e curl  34.70.147.241:8080/message\nService version: 1.0 - Quote: Success demands singleness of purpose -- Vincent Lombardi\n```\n\n#### The canary deployment can be scaled up to the original number of instances for version 1.0\n#### Version 1.0 instances can be deleted at this time, without downtime to service clients\n```shell\n\u003e kubectl scale deploy message-service-canary --replicas=3\n\u003e kubectl delete deploy message-service-canary\n\n# example\nNAME                                      READY   STATUS        RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9         1/1     Running       0          2d20h\nmessage-service-5566ff65cd-6mpgv          1/1     Terminating   0          43h\nmessage-service-5566ff65cd-jqxkq          1/1     Terminating   0          43h\nmessage-service-canary-747cbf84b7-b5xgj   1/1     Running       0          12s\nmessage-service-canary-747cbf84b7-jtrqx   1/1     Running       0          43h\nmessage-service-canary-747cbf84b7-sjdm4   1/1     Running       0          12s\n...\nNAME                                      READY   STATUS    RESTARTS   AGE\nbillboard-client-6f6d7858d9-qhmv9         1/1     Running   0          2d20h\nmessage-service-canary-747cbf84b7-b5xgj   1/1     Running   0          73s\nmessage-service-canary-747cbf84b7-jtrqx   1/1     Running   0          43h\nmessage-service-canary-747cbf84b7-sjdm4   1/1     Running   0          73s\n\n# number of pods reflect the desired replicas for the canary version of the service\n# the instances for the previous version have been deleted\n```\n\n#### Clean-up resources after running this demo for canary deployments\n```shell\nkubectl get deploy\nkubectl get svc\n\n# delete existing resources\nkubectl delete deploy message-service\nkubectl delete svc message-service\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddobrin%2Fdeclarative-deployments-k8s","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fddobrin%2Fdeclarative-deployments-k8s","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddobrin%2Fdeclarative-deployments-k8s/lists"}