【Tekton】Blue/Greenデプロイをやってみる
はじめに
TektonでBlue/Greenデプロイをやってみます。
Blue/Greenデプロイ
Blue/Greenデプロイは、新しいバージョンを実行してからトラフィックを切り替えるデプロイ方法です。
メリット | デメリット |
---|---|
・ダウンタイムがゼロ ・異なるバージョンが同時に実行されない ・すぐに旧バージョンに戻せる | ・2倍のリソースが必要 |
Tektonを使わないBlue/Greenデプロイは下記で試しています。
【Kubernetes】DeploymentとServiceでブルーグリーンデプロイやってみる
はじめに Kubernetesのデプロイ戦略として、**DeploymentとServiceだけ
TektonでBlue/Greenデプロイ
実際にTektonでBlue/Greenデプロイをやっていきたいと思います。
Tektonを使ってBlue/Greenデプロイをする場合は、catalogから下記を利用します。新しいバージョンのマニフェストを提供する方法はいくつかありますが、今回はConfigMapを利用します。
catalog/task/blue-green-deploy/0.1 at main · tektoncd/catalog
Catalog of shared Tasks and Pipelines. Contribute to tektoncd/catalog development by creating an account on GitHub.
作成するマニフェストは下記の通りです。
1.
2├── blue-green-deploy-taskrun.yaml
3├── blue-green-deploy.yaml
4├── clusterbinding.yaml
5├── v1
6│ ├── blue-deployment.yaml
7│ └── service.yaml
8└── v2
9 └── green-deployment.yaml
ClusterRoleBindingの作成
まずは、ServiceAccountにClusterRoleをバインドします。
1apiVersion: rbac.authorization.k8s.io/v1
2kind: ClusterRoleBinding
3metadata:
4 name: default-blue-green
5subjects:
6 - kind: ServiceAccount
7 name: default
8 namespace: default
9roleRef:
10 kind: ClusterRole
11 name: cluster-admin
12 apiGroup: rbac.authorization.k8s.io
1kubectl apply -f clusterbinding.yaml
デモアプリv1のデプロイ
version1のアプリをデプロイします。
v1/blue-deployment.yaml
とv1/service.yaml
は下記の通りです。ラベルのversion: "v1"
でバージョンを表しています。
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: myapp-v1
5spec:
6 replicas: 1
7 selector:
8 matchLabels:
9 app: myapp
10 version: v1
11 template:
12 metadata:
13 labels:
14 app: "myapp"
15 version: "v1" # version1
16 spec:
17 containers:
18 - name: myapp
19 image: quay.io/vinamra2807/social-client:v1
20 imagePullPolicy: IfNotPresent
21 ports:
22 - containerPort: 3000
23 name: http
1apiVersion: v1
2kind: Service
3metadata:
4 name: myapp
5 labels:
6 app: myapp
7spec:
8 type: NodePort
9 ports:
10 - port: 3000
11 name: http
12 selector:
13 app: myapp
14 version: v1 # version1を指定
1kubectl apply -f v1
デプロイしたアプリを確認してみます。
1❯ kubectl get deploy,pod,svc
2NAME READY UP-TO-DATE AVAILABLE AGE
3deployment.apps/myapp-v1 1/1 1 1 44s
4
5NAME READY STATUS RESTARTS AGE
6pod/myapp-v1-584d5bb7d-j5gmk 1/1 Running 0 44s
7
8NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
9service/myapp NodePort 10.107.209.241 <none> 3000:31863/TCP 44s
ブラウザからアプリにアクセスしてみるとversion1であることがわかります。
Blue-Green-Deploy Taskの作成
Blue/Greenデプロイはcatalogの下記のTaskを利用します。
catalog/task/blue-green-deploy/0.1/blue-green-deploy.yaml at main · tektoncd/catalog
Catalog of shared Tasks and Pipelines. Contribute to tektoncd/catalog development by creating an account on GitHub.
1apiVersion: tekton.dev/v1beta1
2kind: Task
3metadata:
4 name: blue-green-deploy
5 labels:
6 app.kubernetes.io/version: "0.1"
7 annotations:
8 tekton.dev/pipelines.minVersion: "0.12.1"
9 tekton.dev/categories: Deployment
10 tekton.dev/tags: deployment
11 tekton.dev/displayName: "blue green deployment"
12 tekton.dev/platforms: "linux/amd64"
13spec:
14 description: >-
15 This task can be used to do Blue-Green deployment
16 workspaces:
17 - name: manifest-dir
18 description: Consisting of kubernetes manifests
19 - name: kubeconfig-dir
20 description: Can be used in case kubeconfig file is mounted
21 params:
22 - name: SERVICE_NAME
23 type: string
24 description: Name of the service which is pointing to existing deployment
25 - name: NEW_VERSION
26 type: string
27 description: The version of newer deployment which is to be patched to existing service
28 - name: MANIFEST
29 type: string
30 default: "."
31 description: The newer version of the deployment to be deployed in another zone
32 steps:
33 - name: change-deployment
34 image: quay.io/openshift/origin-cli:4.6
35 workingDir: $(workspaces.manifest-dir.path)
36 script: |
37 #!/usr/bin/env bash
38 set -u -o pipefail
39
40 if [[ -f $(workspaces.kubeconfig-dir.path)/kubeconfig ]]; then
41 export KUBECONFIG=$(workspaces.kubeconfig-dir.path)/kubeconfig
42 fi
43
44 getReasons() {
45 local deploy_name=$1
46 kubectl get deploy "$deploy_name" \
47 -o jsonpath='{range .status.conditions[*]}{.reason}{"\n"}{end}'
48 }
49
50 # Getting the status at particular index
51 getStatusAtIndex() {
52 local deploy_name=$1
53 local index=$2
54 kubectl get deploy "$deploy_name" \
55 -o jsonpath='{range .status.conditions['$index']}{.status}{"\n"}{end}'
56 }
57
58 # Checking for unavailable pods in case the deployment is
59 # patched with a newer image
60 checkUnavailableReplicas() {
61 local deploy_name=$1
62 local replicasUnavailable=$(kubectl get deploy "$deploy_name" -o jsonpath='{.status.unavailableReplicas}')
63 [[ "$replicasUnavailable" == "" ]] && return 0 || return 1
64 }
65
66 getMinimumReplicaStatus() {
67 local deploy_name=$1
68 local INDEX=0
69 for reason in $(getReasons $deploy_name); do
70 if [[ "$reason" == "MinimumReplicasAvailable" ]]; then
71 local ready=$(getStatusAtIndex $deploy_name ${INDEX})
72 [[ "$ready" != "True" ]] && return 1 || return 0
73 fi
74 ((INDEX++))
75 done
76 return 1
77 }
78
79 checkProgressDeadline() {
80 local deploy_name=$1
81 local INDEX=0
82 for reason in $(getReasons $deploy_name); do
83 if [[ "$reason" == "ProgressDeadlineExceeded" ]]; then
84 local status=$(getStatusAtIndex $deploy_name ${INDEX})
85 [[ "$status" == "True" ]] && return 0 || return 1
86 fi
87 ((INDEX++))
88 done
89 return 1
90 }
91
92 main() {
93 local deploy_name=$(params.SERVICE_NAME)-$(params.NEW_VERSION)
94 local service=$(params.SERVICE_NAME)
95 local version=$(params.NEW_VERSION)
96
97 kubectl apply -f $(params.MANIFEST)
98
99 # Wait until the Deployment is ready by checking the
100 # MinimumReplicasAvailable condition.
101 # Also checking for the ProgressDeadlineExceeded
102 # condition
103 while true; do
104 getMinimumReplicaStatus ${deploy_name}
105 [[ $? -eq 0 ]] && break
106 checkProgressDeadline ${deploy_name}
107 [[ $? -eq 0 ]] && break
108 sleep 5
109 done
110 # If ProgressDeadlineExceeded then break from the main
111 # and avoid checking for the next condition
112 checkProgressDeadline ${deploy_name}
113 if [[ $? -eq 0 ]]; then
114 echo "Failed to change the service" && break
115 fi
116 # If MinimumReplicas are available then check
117 # for the unavailable Pods
118 # Also checking for the ProgressDeadlineExceeded condition
119 while true; do
120 checkUnavailableReplicas ${deploy_name}
121 [[ $? -eq 0 ]] && break
122 checkProgressDeadline ${deploy_name}
123 [[ $? -eq 0 ]] && break
124 sleep 5
125 done
126
127 # Update the service selector with the new version
128 # if ProgressDeadline not exceeded
129 checkProgressDeadline ${deploy_name}
130 if [[ $? -eq 0 ]]; then
131 echo "Failed to change the service"
132 else
133 kubectl patch svc ${service} \
134 -p "{\"spec\":{\"selector\": {\"app\": \"${service}\", \"version\": \"${version}\"}}}"
135 echo "Done."
136 fi
137 }
138
139 main "$@"
1kubectl apply -f blue-green-deploy.yaml
デモアプリv2の用意
version2のアプリのマニフェストはConfigMapに格納します。
v2/green-deployment.yaml
の内容は下記の通りです。
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: myapp-v2
5spec:
6 replicas: 1
7 selector:
8 matchLabels:
9 app: myapp
10 version: v2
11 template:
12 metadata:
13 labels:
14 app: "myapp"
15 version: "v2" # version2
16 spec:
17 containers:
18 - name: myapp
19 image: quay.io/vinamra2807/social-client:v2
20 imagePullPolicy: IfNotPresent
21 ports:
22 - containerPort: 3000
23 name: http
このファイルを使って、ConfigMapを作成します。
1kubectl create configmap manifests --from-file="v2/green-deployment.yaml"
TaskRunの作成
TaskRunを作成して、Tektonを実行します。
blue-green-deploy-taskrun.yaml
は下記の通りです。
1apiVersion: tekton.dev/v1beta1
2kind: TaskRun
3metadata:
4 name: blue-green-deploy-taskrun
5spec:
6 taskRef:
7 name: blue-green-deploy
8 params:
9 - name: SERVICE_NAME
10 value: myapp # Service名
11 - name: NEW_VERSION
12 value: v2 # 新しいバージョンの名前
13 workspaces:
14 - name: manifest-dir
15 configMap:
16 name: manifests
17 - name: kubeconfig-dir
18 emptyDir: {}
1kubectl apply -f blue-green-deploy-taskrun.yaml
実行結果の確認
実行した結果を確認します。
まずはTaskRunのログを見てみます。
1❯ tkn tr logs blue-green-deploy-taskrun
2[change-deployment] deployment.apps/myapp-v2 created
3[change-deployment] service/myapp patched
4[change-deployment] Done.
Deployment、Pod、Serviceを確認するとversion2のアプリがデプロイされ、Serviceのセレクタがversion2に切り替わっているのが確認できます。
1❯ kubectl get deploy,pod,svc
2NAME READY UP-TO-DATE AVAILABLE AGE
3deployment.apps/myapp-v1 1/1 1 1 73m
4deployment.apps/myapp-v2 1/1 1 1 51s
5
6NAME READY STATUS RESTARTS AGE
7pod/myapp-v1-584d5bb7d-j5gmk 1/1 Running 0 73m
8pod/myapp-v2-fcd8b6f98-g4q7q 1/1 Running 0 51s
9
10NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
11service/myapp NodePort 10.107.209.241 <none> 3000:31863/TCP 73m
12
13❯ kubectl describe svc myapp
14Name: myapp
15Namespace: default
16Labels: app=myapp
17Annotations: <none>
18Selector: app=myapp,version=v2
19Type: NodePort
20IP Family Policy: SingleStack
21IP Families: IPv4
22IP: 10.107.209.241
23IPs: 10.107.209.241
24Port: http 3000/TCP
25TargetPort: 3000/TCP
26NodePort: http 31863/TCP
27Endpoints: 10.1.4.169:3000
28Session Affinity: None
29External Traffic Policy: Cluster
30Events: <none>
最後にブラウザからアプリにアクセスしてみると、version2になっていることが確認できます。
blue-green-deploy Task
catalogのblue-green-deploy TaskのWorkspaceとParameterについて簡単に紹介します。
catalog/task/blue-green-deploy/0.1 at main · tektoncd/catalog
Catalog of shared Tasks and Pipelines. Contribute to tektoncd/catalog development by creating an account on GitHub.
Workspaces
manifest-dir
: マニフェストがあるworkspace(emptyDir
だとマニフェストなし)kubeconfig-dir
: 別のクラスタにデプロイする場合にkubeconfigをマウントするworkspace(emptyDir
だとkubeconfigなし)
Parameters
SERVICE_NAME
: 対象のDeploymentと紐づいているSerivce名(必須)NEW_VERSION
: 新しくデプロイするアプリのバージョン名(必須)MANIFEST
: マニフェストのURL