1
0
mirror of https://github.com/tektoncd/catalog.git synced 2024-11-21 05:55:35 +00:00

Add namespace support in blue-green-deployment

As of now blue-green-deployment task assumes the namespace to be the
current namespace in which pipeline is running but everytime this might
not be the case. Added an extra parameter, `NAMESPACE` which user needs
to supply as the parameter in which the existing deployment and service
are present.

Also added an optional parameter `IMAGE` in order to improve the
flexibility for the user

Signed-off-by: vinamra28 <jvinamra776@gmail.com>
This commit is contained in:
vinamra28 2023-01-29 17:54:08 +05:30 committed by tekton-robot
parent 5f899c0d54
commit bb5c8e97cf
10 changed files with 430 additions and 0 deletions

View File

@ -0,0 +1,89 @@
# Blue Green Deploy on Kubernetes
The following task can help you to deploy an application using the Blue-Green deployment strategy. This task gives you the flexibility to deploy a version of the application from the blue zone to the green zone without disturbing the previous version in the blue zone. It works by making the service point to the newer version of the application deployed.
- If you are deploying the first version of the application then it will go into the blue zone and for that you need to provide deployment and service manifests via the `workspaces` and in the `params` the service name and version. Sample Kubernetes manifests for version v1 can be found [here](./samples/v1-deploy).
- For further new deployments, it will get deployed in the other zone and then the current service will now point to the new deployment. For example if we have a deployment running in the blue zone then we will deploy the next deployment in the green zone and make the service point to the green zone or vice versa.
## Installing the Task
```
kubectl apply -f https://api.hub.tekton.dev/v1/resource/tekton/task/blue-green-deploy/0.2/raw
```
## Installing the ClusterRoleBinding
```
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/blue-green-deploy/0.2/support/clusterrolebinding.yaml
```
## Optional Workspaces
- **manifest-dir**: Manifest files can be provided via the workspaces
- **kubeconfig-dir**: If you want to deploy you application to another cluster then you can mount your `kubeconfig` file via this `workspace`
## Parameters
- **SERVICE_NAME**: The service name pointing to the existing deployment. (_Note_: The service name for the new deployment should be same)
- **NEW_VERSION**: The version of the deployment to be deployed in the green/blue zone
- **MANIFEST**: The deployment manifest URL file path provided in case the manifest is present on Github. (_Example_: "https://raw.githubusercontent.com/tektoncd/catalog/main/task/blue-green-deploy/0.2/samples/v1-deploy/blue-deployment.yaml")
- **NAMESPACE**: Target namespace in which operation needs to be performed
- **IMAGE**: Image which has kubectl binary present (_Default_: `quay.io/openshift/origin-cli:4.9`)
## Platforms
The Task can be run on `linux/amd64` platform.
# Usage
This TaskRun runs the Task to deploy the given Kubernetes resource in the green/blue zone and toggle the service to point to the new zone.
## Without using ConfigMap
TaskRun :-
```yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: blue-green-deploy-run
spec:
taskRef:
name: blue-green-deploy-k8s
params:
- name: SERVICE_NAME
value: myapp
- name: NEW_VERSION
value: v2
- name: MANIFEST
value: "https://raw.githubusercontent.com/tektoncd/catalog/main/task/blue-green-deploy/0.2/samples/v2-deploy/green-deployment.yaml"
```
## Using ConfigMap
1. Create the `ConfigMap`
```bash
kubectl create configmap manifests --from-file="green-deployment.yaml"
```
2. TaskRun:-
```yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: blue-green-deploy-run
spec:
taskRef:
name: blue-green-deploy-k8s
params:
- name: SERVICE_NAME
value: myapp
- name: NEW_VERSION
value: v2
workspaces:
- name: manifest-dir
configMap:
name: manifests
```

View File

@ -0,0 +1,153 @@
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: blue-green-deploy
labels:
app.kubernetes.io/version: "0.2"
annotations:
tekton.dev/pipelines.minVersion: "0.17.0"
tekton.dev/categories: Deployment
tekton.dev/tags: deployment
tekton.dev/displayName: "blue green deployment"
tekton.dev/platforms: "linux/amd64"
spec:
description: >-
This task can be used to do Blue-Green deployment
workspaces:
- name: manifest-dir
description: Consisting of kubernetes manifests
optional: true
- name: kubeconfig-dir
description: Can be used in case kubeconfig file is mounted
optional: true
params:
- name: SERVICE_NAME
type: string
description: Name of the service which is pointing to existing deployment
- name: NEW_VERSION
type: string
description: The version of newer deployment which is to be patched to existing service
- name: MANIFEST
type: string
default: "."
description: The newer version of the deployment to be deployed in another zone
- name: NAMESPACE
type: string
description: Target namespace in which operation needs to be performed
- name: IMAGE
description: Image which has kubectl binary present
type: string
default: "quay.io/openshift/origin-cli:4.9"
steps:
- name: change-deployment
image: $(params.IMAGE)
script: |
#!/usr/bin/env bash
set -u -o pipefail
[[ "$(workspaces.manifest-dir.bound)" == "true" ]] && cd $(workspaces.manifest-dir.path)
if [[ "$(workspaces.kubeconfig-dir.bound)" == "true" ]] && [[ -f $(workspaces.kubeconfig-dir.path)/kubeconfig ]]; then
export KUBECONFIG=$(workspaces.kubeconfig-dir.path)/kubeconfig
fi
getReasons() {
local deploy_name=$1
kubectl -n $(params.NAMESPACE) get deploy "$deploy_name" \
-o jsonpath='{range .status.conditions[*]}{.reason}{"\n"}{end}'
}
# Getting the status at particular index
getStatusAtIndex() {
local deploy_name=$1
local index=$2
kubectl -n $(params.NAMESPACE) get deploy "$deploy_name" \
-o jsonpath='{range .status.conditions['$index']}{.status}{"\n"}{end}'
}
# Checking for unavailable pods in case the deployment is
# patched with a newer image
checkUnavailableReplicas() {
local deploy_name=$1
local replicasUnavailable=$(kubectl -n $(params.NAMESPACE) get deploy "$deploy_name" -o jsonpath='{.status.unavailableReplicas}')
[[ "$replicasUnavailable" == "" ]] && return 0 || return 1
}
getMinimumReplicaStatus() {
local deploy_name=$1
local INDEX=0
for reason in $(getReasons $deploy_name); do
if [[ "$reason" == "MinimumReplicasAvailable" ]]; then
local ready=$(getStatusAtIndex $deploy_name ${INDEX})
[[ "$ready" != "True" ]] && return 1 || return 0
fi
((INDEX++))
done
return 1
}
checkProgressDeadline() {
local deploy_name=$1
local INDEX=0
for reason in $(getReasons $deploy_name); do
if [[ "$reason" == "ProgressDeadlineExceeded" ]]; then
local status=$(getStatusAtIndex $deploy_name ${INDEX})
[[ "$status" == "True" ]] && return 0 || return 1
fi
((INDEX++))
done
return 1
}
switchService() {
kubectl -n $(params.NAMESPACE) patch svc ${service} \
-p "{\"spec\":{\"selector\": {\"app\": \"${service}\", \"version\": \"${version}\"}}}"
}
main() {
local deploy_name=$(params.SERVICE_NAME)-$(params.NEW_VERSION)
local service=$(params.SERVICE_NAME)
local version=$(params.NEW_VERSION)
kubectl -n $(params.NAMESPACE) apply -f $(params.MANIFEST)
# Wait until the Deployment is ready by checking the
# MinimumReplicasAvailable condition.
# Also checking for the ProgressDeadlineExceeded
# condition
while true; do
getMinimumReplicaStatus ${deploy_name}
[[ $? -eq 0 ]] && break
checkProgressDeadline ${deploy_name}
[[ $? -eq 0 ]] && break
sleep 5
done
# If ProgressDeadlineExceeded then break from the main
# and avoid checking for the next condition
checkProgressDeadline ${deploy_name}
if [[ $? -eq 0 ]]; then
echo "Failed to change the service" && break
fi
# If MinimumReplicas are available then check
# for the unavailable Pods
# Also checking for the ProgressDeadlineExceeded condition
while true; do
checkUnavailableReplicas ${deploy_name}
[[ $? -eq 0 ]] && break
checkProgressDeadline ${deploy_name}
[[ $? -eq 0 ]] && break
sleep 5
done
# Update the service selector with the new version
# if ProgressDeadline not exceeded
checkProgressDeadline ${deploy_name}
if [[ $? -eq 0 ]]; then
echo "Failed to change the service"
else
switchService ${service} ${version}
[[ $? -eq 0 ]] && echo "Done." || exit 1
fi
}
main "$@"

View File

@ -0,0 +1,16 @@
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: blue-green-deploy-run
spec:
taskRef:
name: blue-green-deploy
params:
- name: SERVICE_NAME
value: myapp
- name: NEW_VERSION
value: v2
- name: MANIFEST
value: "https://raw.githubusercontent.com/vinamra28/blue-green-deployment-k8s/master/deployment%2Bservice/green-deployment.yaml"
- name: NAMESPACE
value: default

View File

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v1
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: v1
template:
metadata:
labels:
app: "myapp"
version: "v1"
spec:
containers:
- name: myapp
image: quay.io/vinamra2807/social-client:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: myapp
labels:
app: myapp
spec:
type: NodePort
ports:
- port: 3000
name: http
selector:
app: myapp
version: v1

View File

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v2
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: v2
template:
metadata:
labels:
app: "myapp"
version: "v2"
spec:
containers:
- name: myapp
image: quay.io/vinamra2807/social-client:v2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http

View File

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: default-blue-green
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

View File

@ -0,0 +1,46 @@
#!/bin/bash
#This will create a sample deployment version v1 of the application and a service which will point to that deployment
cat <<EOF | kubectl apply -f- -n "${tns}"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v1
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: v1
template:
metadata:
labels:
app: "myapp"
version: "v1"
spec:
containers:
- name: myapp
image: quay.io/vinamra2807/social-client:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http
---
apiVersion: v1
kind: Service
metadata:
name: myapp
labels:
app: myapp
spec:
type: NodePort
ports:
- port: 3000
name: http
selector:
app: myapp
version: v1
EOF
kubectl -n "${tns}" wait --for=condition=available --timeout=600s deployment/myapp-v1

View File

@ -0,0 +1,37 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: blue-green-account
namespace: blue-green-deploy-0-2
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: blue-green-cluster-role
rules:
# Core API
- apiGroups: [""]
resources: ["services", "pods", "deployments", "configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
# Apps API
- apiGroups: ["apps"]
resources: ["deployments", "daemonsets", "jobs"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
# Tekton API
- apiGroups: ["tekton.dev"]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: blue-green-binding
subjects:
- kind: ServiceAccount
name: blue-green-account
namespace: blue-green-deploy-0-2
roleRef:
kind: ClusterRole
name: blue-green-cluster-role
apiGroup: rbac.authorization.k8s.io

View File

@ -0,0 +1,17 @@
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: blue-green-deploy-run
spec:
serviceAccountName: blue-green-account
taskRef:
name: blue-green-deploy
params:
- name: SERVICE_NAME
value: myapp
- name: NEW_VERSION
value: v2
- name: MANIFEST
value: "https://raw.githubusercontent.com/vinamra28/blue-green-deployment-k8s/master/deployment%2Bservice/green-deployment.yaml"
- name: NAMESPACE
value: blue-green-deploy-0-2