【Tekton】Blue/Greenデプロイをやってみる

スポンサーリンク

はじめに

TektonでBlue/Greenデプロイをやってみます。

Blue/Greenデプロイ

Blue/Greenデプロイは、新しいバージョンを実行してからトラフィックを切り替えるデプロイ方法です。

メリットデメリット
・ダウンタイムがゼロ
・異なるバージョンが同時に実行されない
・すぐに旧バージョンに戻せる
・2倍のリソースが必要

Tektonを使わないBlue/Greenデプロイは下記で試しています。

【Kubernetes】DeploymentとServiceでブルーグリーンデプロイやってみる
はじめにKubernetesのデプロイ戦略として、DeploymentとServiceだけでブルーグリーンデプロイを動かしてみます。Kubernetesでの代表的なデプロイ戦略については、下記でざっくりまとめています。ブルーグリーン...

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.

作成するマニフェストは下記の通りです。

.
├── 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.yamlv1/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を利用します。

catalog/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.
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について簡単に紹介します。

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

参考

タイトルとURLをコピーしました