|
@@ -0,0 +1,150 @@
|
|
|
+# This workflow performs a "blue-green" deployment, while preserving the original deployment object
|
|
|
+# and name. It accomplishes this by temporarily redirecting traffic to a *clone* of the original
|
|
|
+# deployment. Then after upgrading the original deployment to a later version, redirects traffic
|
|
|
+# back to the original (now upgraded) deployment.
|
|
|
+apiVersion: argoproj.io/v1alpha1
|
|
|
+kind: Workflow
|
|
|
+metadata:
|
|
|
+ generateName: k8s-bluegreen-
|
|
|
+ annotations:
|
|
|
+ argocd.argoproj.io/hook: Sync
|
|
|
+spec:
|
|
|
+ entrypoint: k8s-bluegreen
|
|
|
+ arguments:
|
|
|
+ parameters:
|
|
|
+ - name: deployment-name
|
|
|
+ - name: service-name
|
|
|
+ - name: new-deployment-manifest
|
|
|
+ - name: new-service-manifest
|
|
|
+
|
|
|
+ templates:
|
|
|
+ - name: k8s-bluegreen
|
|
|
+ steps:
|
|
|
+ # 1. Create a parallel Kubernetes deployment with tweaks to name and selectors
|
|
|
+ - - name: create-blue-deployment
|
|
|
+ template: clone-deployment
|
|
|
+
|
|
|
+ # 2. Wait for parallel deployment to become ready
|
|
|
+ - - name: wait-for-blue-deployment
|
|
|
+ template: wait-deployment-ready
|
|
|
+ arguments:
|
|
|
+ parameters:
|
|
|
+ - name: deployment-name
|
|
|
+ value: '{{workflow.parameters.deployment-name}}-blue'
|
|
|
+
|
|
|
+ # 3. Patch the named service to point to the parallel deployment app
|
|
|
+ - - name: switch-service-to-blue-deployment
|
|
|
+ template: patch-service
|
|
|
+
|
|
|
+ # 4. Update the original deployment (receiving no traffic) with a new version
|
|
|
+ - - name: apply-green-deployment
|
|
|
+ template: apply-manifest
|
|
|
+ arguments:
|
|
|
+ parameters:
|
|
|
+ - name: manifest
|
|
|
+ value: '{{workflow.parameters.new-deployment-manifest}}'
|
|
|
+
|
|
|
+ # 5. Wait for the original deployment, now updated, to become ready
|
|
|
+ - - name: wait-for-green-deployment
|
|
|
+ template: wait-deployment-ready
|
|
|
+ arguments:
|
|
|
+ parameters:
|
|
|
+ - name: deployment-name
|
|
|
+ value: '{{workflow.parameters.deployment-name}}'
|
|
|
+
|
|
|
+ # dummy approval step for demo purposes. Sleeps for 30 seconds
|
|
|
+ - - name: approve
|
|
|
+ template: approve
|
|
|
+
|
|
|
+ # 6. Patch the named service to point to the original, now updated app
|
|
|
+ - - name: switch-service-to-green-deployment
|
|
|
+ template: apply-manifest
|
|
|
+ arguments:
|
|
|
+ parameters:
|
|
|
+ - name: manifest
|
|
|
+ value: '{{workflow.parameters.new-service-manifest}}'
|
|
|
+
|
|
|
+ # 7. Remove the cloned deployment (no longer receiving traffic)
|
|
|
+ - - name: delete-cloned-deployment
|
|
|
+ template: delete-deployment
|
|
|
+
|
|
|
+# end of steps
|
|
|
+
|
|
|
+ # clone-deployment creates a "blue" clone of an existing deployment. The string -blue is appended to:
|
|
|
+ # - metadata.name
|
|
|
+ # - spec.selector.matchLabels
|
|
|
+ # - spec.template.metadata.labels
|
|
|
+ - name: clone-deployment
|
|
|
+ container:
|
|
|
+ image: argoproj/argoexec:v2.1.1
|
|
|
+ command: [sh, -c, -x]
|
|
|
+ args:
|
|
|
+ - kubectl get --export -o json deployment.apps/{{workflow.parameters.deployment-name}} > /original-deploy &&
|
|
|
+ jq -r '.metadata.name+="-blue" |
|
|
|
+ .spec.template.metadata.labels += (.spec.template.metadata.labels | to_entries | map(select(.key != "applications.argoproj.io/app-name")) | map(.value+="-blue") | from_entries) |
|
|
|
+ .spec.selector.matchLabels += (.spec.selector.matchLabels | to_entries | map(select(.key != "applications.argoproj.io/app-name")) | map(.value+="-blue") | from_entries)'
|
|
|
+ /original-deploy > /cloned-deploy &&
|
|
|
+ cat /cloned-deploy &&
|
|
|
+ kubectl apply -o yaml -f /cloned-deploy
|
|
|
+
|
|
|
+ # apply-manifest takes a kubernetes manifest and carrys over the app-name label (if present)
|
|
|
+ # before running `kubectl apply`. The label is used by ArgoCD for monitoring.
|
|
|
+ - name: apply-manifest
|
|
|
+ inputs:
|
|
|
+ parameters:
|
|
|
+ - name: manifest
|
|
|
+ artifacts:
|
|
|
+ - name: manifest-file
|
|
|
+ path: /manifest
|
|
|
+ raw:
|
|
|
+ data: '{{inputs.parameters.manifest}}'
|
|
|
+ container:
|
|
|
+ image: argoproj/argoexec:v2.1.1
|
|
|
+ command: [sh, -c, -x]
|
|
|
+ args:
|
|
|
+ - cp /manifest /manifest-new &&
|
|
|
+ APP_NAME=$(kubectl get -n default -f /manifest-new -o json | jq -r '.metadata.labels."applications.argoproj.io/app-name"') &&
|
|
|
+ if [ "$APP_NAME" != "null" ]; then
|
|
|
+ jq -r --arg APP_NAME "$APP_NAME" '.metadata.labels."applications.argoproj.io/app-name" = $APP_NAME' /manifest-new > /manifest-new.tmp &&
|
|
|
+ mv /manifest-new.tmp /manifest-new &&
|
|
|
+ if [ "$(jq -r .spec.template.metadata.labels /manifest-new)" != "null" ]; then
|
|
|
+ jq -r --arg APP_NAME "$APP_NAME" '.spec.template.metadata.labels."applications.argoproj.io/app-name" = $APP_NAME' /manifest-new > /manifest-new.tmp &&
|
|
|
+ mv /manifest-new.tmp /manifest-new ;
|
|
|
+ fi ;
|
|
|
+ fi &&
|
|
|
+ cat /manifest-new &&
|
|
|
+ kubectl apply -f /manifest-new
|
|
|
+
|
|
|
+ # wait-deployment-ready waits for a deployment to become fully deployed and ready, using the
|
|
|
+ # `kubectl rollout` command
|
|
|
+ - name: wait-deployment-ready
|
|
|
+ inputs:
|
|
|
+ parameters:
|
|
|
+ - name: deployment-name
|
|
|
+ container:
|
|
|
+ image: argoproj/argoexec:v2.1.1
|
|
|
+ command: [kubectl, rollout, status, --watch=true, 'deployments/{{inputs.parameters.deployment-name}}']
|
|
|
+
|
|
|
+ # patch-service updates the service selector labels to point to the "blue" deployment
|
|
|
+ - name: patch-service
|
|
|
+ container:
|
|
|
+ image: argoproj/argoexec:v2.1.1
|
|
|
+ command: [sh, -c, -x]
|
|
|
+ args:
|
|
|
+ - kubectl get -n default service {{workflow.parameters.service-name}} --export -o json > /original-svc &&
|
|
|
+ jq '.spec.selector = (.spec.selector | with_entries(.value+="-blue"))' /original-svc > /blue-svc &&
|
|
|
+ kubectl apply -o yaml -f /blue-svc
|
|
|
+
|
|
|
+ - name: delete-deployment
|
|
|
+ resource:
|
|
|
+ action: delete
|
|
|
+ manifest: |
|
|
|
+ apiVersion: apps/v1
|
|
|
+ kind: Deployment
|
|
|
+ metadata:
|
|
|
+ name: {{workflow.parameters.deployment-name}}-blue
|
|
|
+
|
|
|
+ - name: approve
|
|
|
+ container:
|
|
|
+ image: argoproj/argoexec:v2.1.1
|
|
|
+ command: [sleep, "30"]
|