アリババクラウドコンテナサービスを利用したKubernetesクラスタの展開戦略

本記事シリーズでは、**Alibaba Cloud Container Service**を利用したCI/CD（Continuous Integration and **Deployment**：継続的インテグレーションとデプロイメント）のライフサイクルを実装するための実践的なアプローチを紹介しています。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/deploy-docker-image-to-alibaba-cloud-container-service-cicd-automation-on-container-service-3_594541)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

#1 前提条件
ステップバイステップのガイドを通過する前に、ユーザーは以下の前提条件を持っている必要があります。

- まともなコンピュータまたはラップトップ。
- ウェブブラウザ、Google Chromeをお勧めします。
- 安定したインターネット接続
- Alibaba Cloudアカウント
- Linuxオペレーティングシステムの操作に関する基本的な知識。
- DockerコンテナとDockerfileの使い方についての理解と基礎知識。
- Kubernetesの基礎知識。
- GitHubのアカウント。


#2つのリソース

このチュートリアルでは、サンプルアプリケーションのソースコードを含む多くのサードパーティのリソースを使用しています。GitHub のアプリケーションソースコードを提供してくれた Satya Depareddy さんに感謝します。
[https://github.com/depareddy/java-webapp-docker](https://github.com/depareddy/java-webapp-docker?spm=a2c65.11461447.0.0.6ac21fa81ZqRok)

#3 はじめに
本連載の[前回](https://www.alibabacloud.com/blog/continuous-deployment-automation-on-alibaba-cloud-cicd-automation-on-container-service-2_594540?spm=a2c65.11461447.0.0.6ac21fa8aBtxop)は、クラウド上での継続的なデプロイの流れを自動化するための手順を解説しました。今回は、Alibaba Cloud [Container Service](https://www.alibabacloud.com/ja/product/kubernetes)を利用したKubernetesクラスタ上でのデプロイ戦略を中心としたDevOpsシリーズの第3回目、最終回です。

#4 ローリングアップデート
これは、Kubernetesクラスタ上でのデフォルトのデプロイ戦略です。ローリングアップデートでは、Podsインスタンスを新しいものでインクリメンタルに更新することで、デプロイメントの更新をダウンタイムゼロで行うことができます。新しいPodsは、利用可能なリソースを持つノード上でスケジュールされます。

この戦略は、更新を実行している間、どの時点でも少なくともいくつかのインスタンスを稼働させておくことで、アプリケーションのダウンタイムを防ぐことを目的としています。古いポッドがシャットダウンされるのは、新しいデプロイメントバージョンの新しいポッドが起動してトラフィックを処理できるようになってからです。


#5 カナリアリリース
カナリアデプロイメントは、新しいソフトウェアのバージョンをリリースする際のリスクを軽減するためのパターンです。しかし、ソフトウェアでは、継続的なデリバリを採用しているチームにとって、カナリアをリリースすることは戦略的な戦術となります。このアイデアは、新しいリリースを、安定版と並んで小さなサブセットのサーバに少しずつロールアウトするというものです。一旦テストを行った後、インフラストラクチャの残りの部分に変更をロールアウトすることができます。これにより、効果的に新しいバージョンを少数のユーザに公開することができ、障害が発生する可能性がある場合の早期の指標として機能します。カナリアは問題のあるデプロイやユーザーの怒りを避けるために非常に便利です。カナリアのデプロイに失敗しても、他のサーバには影響がないので、単にそれを捨てて根本的な原因を修正することができます。

Alibaba Cloudでカナリアリリースプロセスを体験するには、ソースコードの異なるバージョンに基づいて新しいイメージを作成することになります。

###5.1 新しいブランチを作成する
タグから新しいブランチを作成する場合

```
$git branch release-v2.0-branch release-v2.0
```

###5.2 ブランチからのコーデをチェックする
新しいブランチのソースコードをチェックアウト

```
$git checkout release-v2.0-branch
```

###5.3 既存のソースコードを変更する
プロジェクトのソースコードのホームディレクトリ - java-webapp-docker に移動します。ディレクトリを src/main/webapp に変更します。vi や vim などのエディタで index.jsp を開きます。ヘッダー **＜h2＞**を "Welcome to Alibaba Cloud DevOps v2.0" に変更し、ヘッダーのフォントを赤に変更します。

```
<html>
<body>
<h2><font color="red">Welcome to Alibaba Cloud DevOps v1.0</font></h2>
</body>
</html>
```

###5.4 変更のコミットと新しいタグの作成
次のように入力して、コミットするファイルをGitに追加します。

```
$git add index.jsp
```
そして、リポジトリにコードをコミットします。

```
$git commit -m "changed header text”
```

###5.5 新しいタグの作成
前のタグを削除するには次のように入力します。

```
$git tag -d release-v2.0
```


新しいタグを作成するには、次のように入力します。

```
$git tag release-v2.0
```

###5.6 リモートリポジトリへのプッシュタグ
リモートでタグを削除して再作成します。

```
$git push origin :release-v2.0
```

新しいタグを作成するには次のように入力します。

```
$git push origin release-v2.0
```

Container Registry->Build ページに戻って、イメージの最新のビルドを確認します。ビルドが成功した場合、タグ 2.0 の最新のイメージがリストに表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/cdc24ade-166e-3b12-9009-4c654377304f.png)


次に、Kubernetesクラスタへのイメージの新しいデプロイメントを作成する必要があります。

###5.7 新規デプロイメントの作成
「コンテナサービス」→「デプロイ」ページに移動します。テンプレートで作成」ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/cd6cd746-3917-ec29-bdbd-92ecdfa05434.png)


YAMLスクリプトを参照して、プライベートレジストリのイメージURLを元にイメージURLを修正します。

イメージレジストリのURLを覚えていない場合は、コンテナレジストリの詳細ページに移動し、VPCのリンクからコピーしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/93a1f14d-3662-f16a-8e54-d564881e99f7.png)

以下のYAMLスクリプトをテンプレートに貼り付け、デプロイをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/653197df-768e-67b3-aee0-c711bd995826.png)


###5.7.1 YAML デプロイメントスクリプト

```
apiVersion: apps/v1beta2 # for versions before 1.8.0 use apps/v1beta1
kind: Deployment
metadata:
  name: simplewebapp-v2
  labels:
    app: simplewebapp-default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simplewebapp-default
  template:
    metadata:
      labels:
        app: simplewebapp-default
    spec:
    #  nodeSelector:
    #    env: test-team
      containers:
      - name: simplewebapp-default
        imagePullPolicy: Always
        image: registry-intl-vpc.ap-southeast-3.aliyuncs.com/evanwong/simplewebapp:2.0
        ports:
        - containerPort: 8080
```

デプロイに成功すると、新しいバージョンのデプロイメントが[デプロイメント]リストに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/ec00c750-8423-bb7f-5d1e-cf6ff74d0b8a.png)


Pods セクションに行くと、新しいポッドが起動しています。この新しいポッドはバージョン2.0の最新のイメージをベースにしています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/9d1ebef0-24a1-213e-9040-e8d7405ba988.png)

###5.8 テストカナリアのリリース
ターミナルを使用して、以下のコマンドを入力し、エンドポイントの URL をロードバランサー外部エンドポイントの IP アドレスに置き換えます。URL は Container Service->Discovery と Load Balancing->Service で確認できます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/10ed0572-17dd-90cd-3ea0-7a9cc116fbfe.png)

```
$for x in {1..10}; do curl <URL of the LB endpoint>/simplewebapp; done
```

結果は以下のように表示されます。レスポンスは、コードのバージョンが異なるPodsからの出力になるでしょう。これはロードバランサーが平均的にPodsにリクエストを分散させているためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/802eb597-b4f9-7852-b38c-45354e426997.png)


新しいイメージ (2.0) に負荷を分散させるために、デプロイメント上のレプリカの数を増やすことができます。more->YAMLをクリックしてYAMLスクリプトファイルを開きます。replicas属性を1以上、例えば10に変更します。更新をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/48060a67-0c36-e81a-f43b-79d8e21ffb79.png)


Podsの数を確認すると、バージョン2.0をベースにした新しいPodsは1よりも多く表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/bcf452ea-fd01-1434-5935-562fa58276fb.png)

ターミナルに行って、CURLコマンドをもう一回実行してみてください。新しいPodsからは、より多くのレスポンスが出力されるでしょう。

```
$for x in {1..10}; do curl <URL of the LB endpoint>/simplewebapp; done
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/1d288237-f779-4a31-1ca6-d319cee10a99.png)

#6 グレーのリリースとBlue-Greenデプロイメント
ここでは、Alibaba Cloud Container Service for Kubernetesが提供するIngress機能を利用して、グレーリリースやBlue-Greenデプロイメントを実装する方法を説明します。

グレーリリースまたはBlue-Greenデプロイメントでは、ターゲットソフトウェアの最新バージョンとそれ以前のバージョンの2つの同一の本番環境を作成することができます。そして、特定のルールを適用して、以前のバージョンのソフトウェアに影響を与えることなく、以前のバージョンから最新バージョンへのトラフィックをリルートすることができます。最新版のソフトウェアが一定期間例外なく動作した後、以前のバージョンから最新版へのトラフィックをすべてリルートすることができます。

A/Bテストは、比較・増分グレーリリースの一種です。具体的には、A/Bテストでは、一部のユーザーには以前のバージョンのサービスを利用させておき、他のユーザーのトラフィックを最新バージョンのサービスにリルートすることができます。最新版のサービスが指定された期間、例外なく動作していれば、すべてのユーザのトラフィックを徐々に最新版のサービスに迂回させることができます。

###6.1 シナリオ
**6.1.1 シナリオ1**

例えば、サービスAが既にオンラインで動作し、外部からアクセス可能なレイヤ7サービスを提供しており、このサービスの新機能を備えた新バージョン、すなわちサービスA'が開発されているとします。サービスA'をリリースしたいが、サービスAを直接直接置き換えたくない場合。さらに、リクエストヘッダーにfoo = barが含まれている、またはCookieにfoo = barが含まれているクライアントリクエストをサービスA 'に転送したい場合。サービスA'が特定の期間、例外なく実行された場合、すべてのトラフィックをサービスAからサービスA 'に再ルーティングし、サービスAをスムーズにオフラインにする必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/fa7d77fe-e296-c079-dd2f-d0b14bb605ca.png)


**6.1.2 シナリオ2**

例えば、サービスBという名前の以前のバージョンのサービスが、外部からアクセス可能なレイヤ7サービスを提供するためにオンラインで実行されているとします。ただし、それには既知の問題があります。新しいバージョンのService B 'は、問題が修正されて開発されています。この最新バージョンをリリースする必要がありますが最初はすべてのクライアントトラフィックの20％のみをサービスB 'に再ルーティングする必要があります。そして次に、サービスB'が例外なく一定期間実行された後、すべてのトラフィックをサービスから再ルーティングする必要があります。 BをサービスB 'に接続し、サービスBをスムーズにオフラインにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d902e844-5970-9f32-da87-1492262761be.png)

先行アプリケーションのリリース要件を満たすために、Alibaba Cloud Container Service for KubernetesではIngress機能を利用して、以下の4つの方法でトラフィックを分散させています。

A/Bテストでは

- リクエストヘッダに応じてトラフィックを分散させる
- クッキーに応じてトラフィックを分散させる
- クエリパラムに応じてトラフィックを分散させる


blue/green deploymentでは

- サービスウェイトに応じてトラフィックを分配


###6.2 サービスの作成
イングレスを作成する前に、まず最初にv1用とv2用の2つのNodePortサービスを作成する必要があります。Container Service->Serviceページに移動し、Createボタンをクリックします。

サービス名を入力し、NodePortを選択し、ドロップダウンボックスからsimplewebapp-defaultを選択します。サービスポートには80、コンテナポートには8080を入力し、NodePortは残してプロトコルとしてTCPを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/917d5196-2dc0-abda-7aeb-4d1bbf181a1c.png)


バージョン2の別サービスを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/e533ec1a-daaf-183a-934b-d5cf9e1706b8.png)

###6.3 イングレスの作成
Container Service] > [Ingress]に移動します。Createボタンをクリックして新しい入口を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/9b859088-3fe5-34d3-c570-c8724beb10cd.png)


ポップアップ画面で、以下の詳細を入力して「作成」をクリックします。

注意事項:
- ドメイン名については、コンテナサービスが提供するデフォルトのドメイン名は *.ca58e4d5834804a40b0723004c3c11370.ap-southeast-3.alicontainer.com/simplewebapp/ です。独自のカスタムドメインを使用することもできます。
- パス: simplewebapp
- サービス上で、既存の NodePort サービス - simplewebapp-v1 と simplewebapp-v2 - を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/4e5b5519-160b-0f59-0455-fc5402e6cf44.png)

イングレスが作成された後、新しいイングレスは以下のようにランディングページに表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/3606db29-6fe0-936a-c859-4231f044039c.png)

ウェブサイトを表示するには、ルール上のリンクをコピーし、*を以前にサービスを有効化するために使用したユーザー名に置き換えるだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/eaab90dd-7dc9-bfc4-1638-7d57c8f320a8.png)

この段階では、この流入用に2つのサービスが作成されているにもかかわらず、Webサイトがリフレッシュされると、バージョン1.0のアプリケーションにしかリクエストを送信してくれませんでした。これは、サービスの重みが有効になっていないためです。

両方のバージョンに負荷を分散させるためには、サービスウェイトを有効にする必要があります。そのためには、ingressでupdateをクリックします。ポップアップダイアログで、サービスウェイトを有効にするチェックボックスにチェックを入れます。負荷を均等に分散させるには、各サービスのウェイトを50に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/2789b8df-484c-7f01-9aea-5c537685b014.png)

これで、リクエストはランダムに両バージョンに転送されるようになりました。ingress では、特定のサービスにマッチするルールを指定することもできます。例えば、クエリパラメータが条件にマッチした場合、そのサービスにリクエストを転送します。

ingressページに戻り、Updateをクリックします。ポップアップダイアログで、グレーのルールセットを追加し、サービスとして simplewebapp-v2 を選択し、タイプとして Query を選択し、マッチングルールとして Value match を選択し、マッチする値として key in 2 を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/9c383a7c-b247-87b5-efa6-9ba53c35c196.png)


ブラウザでURLを更新すると、ヘッダテキストはアプリケーションv2から来ているはずです。 サービスウェイトが50/50で有効になっているため、レスポンスはアプリケーションv1から出力される可能性があります。新しいバージョン(v2)へのリクエストのみを転送する必要がある場合、simplewebapp-v1 のウェイトを 0 に減らすべきです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/0c4a1f4a-19ca-78ee-ab75-c404cd0d4ad2.png)

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*