はじめに
TektonでBlue/Greenデプロイをやってみます。
Blue/Greenデプロイ
Blue/Greenデプロイは、新しいバージョンを実行してからトラフィックを切り替えるデプロイ方法です。
メリット | デメリット |
---|---|
・ダウンタイムがゼロ ・異なるバージョンが同時に実行されない ・すぐに旧バージョンに戻せる | ・2倍のリソースが必要 |
Tektonを使わないBlue/Greenデプロイは下記で試しています。

TektonでBlue/Greenデプロイ
実際にTektonでBlue/Greenデプロイをやっていきたいと思います。
Tektonを使ってBlue/Greenデプロイをする場合は、catalogから下記を利用します。新しいバージョンのマニフェストを提供する方法はいくつかありますが、今回はConfigMapを利用します。
作成するマニフェストは下記の通りです。
.
├── blue-green-deploy-taskrun.yaml
├── blue-green-deploy.yaml
├── clusterbinding.yaml
├── v1
│ ├── blue-deployment.yaml
│ └── service.yaml
└── v2
└── green-deployment.yaml
ClusterRoleBindingの作成
まずは、ServiceAccountにClusterRoleをバインドします。
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
kubectl apply -f clusterbinding.yaml
デモアプリv1のデプロイ
version1のアプリをデプロイします。
v1/blue-deployment.yaml
とv1/service.yaml
は下記の通りです。ラベルのversion: "v1"
でバージョンを表しています。
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" # version1
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 # version1を指定
kubectl apply -f v1
デプロイしたアプリを確認してみます。
❯ kubectl get deploy,pod,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp-v1 1/1 1 1 44s
NAME READY STATUS RESTARTS AGE
pod/myapp-v1-584d5bb7d-j5gmk 1/1 Running 0 44s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/myapp NodePort 10.107.209.241 <none> 3000:31863/TCP 44s
ブラウザからアプリにアクセスしてみるとversion1であることがわかります。
Blue-Green-Deploy Taskの作成
Blue/Greenデプロイはcatalogの下記のTaskを利用します。
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: blue-green-deploy
labels:
app.kubernetes.io/version: "0.1"
annotations:
tekton.dev/pipelines.minVersion: "0.12.1"
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
- name: kubeconfig-dir
description: Can be used in case kubeconfig file is mounted
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
steps:
- name: change-deployment
image: quay.io/openshift/origin-cli:4.6
workingDir: $(workspaces.manifest-dir.path)
script: |
#!/usr/bin/env bash
set -u -o pipefail
if [[ -f $(workspaces.kubeconfig-dir.path)/kubeconfig ]]; then
export KUBECONFIG=$(workspaces.kubeconfig-dir.path)/kubeconfig
fi
getReasons() {
local deploy_name=$1
kubectl 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 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 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
}
main() {
local deploy_name=$(params.SERVICE_NAME)-$(params.NEW_VERSION)
local service=$(params.SERVICE_NAME)
local version=$(params.NEW_VERSION)
kubectl 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
kubectl patch svc ${service} \
-p "{\"spec\":{\"selector\": {\"app\": \"${service}\", \"version\": \"${version}\"}}}"
echo "Done."
fi
}
main "[email protected]"
kubectl apply -f blue-green-deploy.yaml
デモアプリv2の用意
version2のアプリのマニフェストはConfigMapに格納します。
v2/green-deployment.yaml
の内容は下記の通りです。
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" # version2
spec:
containers:
- name: myapp
image: quay.io/vinamra2807/social-client:v2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http
このファイルを使って、ConfigMapを作成します。
kubectl create configmap manifests --from-file="v2/green-deployment.yaml"
TaskRunの作成
TaskRunを作成して、Tektonを実行します。
blue-green-deploy-taskrun.yaml
は下記の通りです。
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: blue-green-deploy-taskrun
spec:
taskRef:
name: blue-green-deploy
params:
- name: SERVICE_NAME
value: myapp # Service名
- name: NEW_VERSION
value: v2 # 新しいバージョンの名前
workspaces:
- name: manifest-dir
configMap:
name: manifests
- name: kubeconfig-dir
emptyDir: {}
kubectl apply -f blue-green-deploy-taskrun.yaml
実行結果の確認
実行した結果を確認します。
まずはTaskRunのログを見てみます。
❯ tkn tr logs blue-green-deploy-taskrun
[change-deployment] deployment.apps/myapp-v2 created
[change-deployment] service/myapp patched
[change-deployment] Done.
Deployment、Pod、Serviceを確認するとversion2のアプリがデプロイされ、Serviceのセレクタがversion2に切り替わっているのが確認できます。
❯ kubectl get deploy,pod,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp-v1 1/1 1 1 73m
deployment.apps/myapp-v2 1/1 1 1 51s
NAME READY STATUS RESTARTS AGE
pod/myapp-v1-584d5bb7d-j5gmk 1/1 Running 0 73m
pod/myapp-v2-fcd8b6f98-g4q7q 1/1 Running 0 51s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/myapp NodePort 10.107.209.241 <none> 3000:31863/TCP 73m
❯ kubectl describe svc myapp
Name: myapp
Namespace: default
Labels: app=myapp
Annotations: <none>
Selector: app=myapp,version=v2
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.209.241
IPs: 10.107.209.241
Port: http 3000/TCP
TargetPort: 3000/TCP
NodePort: http 31863/TCP
Endpoints: 10.1.4.169:3000
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
最後にブラウザからアプリにアクセスしてみると、version2になっていることが確認できます。
blue-green-deploy Task
catalogのblue-green-deploy TaskのWorkspaceとParameterについて簡単に紹介します。
Workspaces
manifest-dir
: マニフェストがあるworkspace(emptyDir
だとマニフェストなし)kubeconfig-dir
: 別のクラスタにデプロイする場合にkubeconfigをマウントするworkspace(emptyDir
だとkubeconfigなし)
Parameters
SERVICE_NAME
: 対象のDeploymentと紐づいているSerivce名(必須)NEW_VERSION
: 新しくデプロイするアプリのバージョン名(必須)MANIFEST
: マニフェストのURL