GKEで手っ取り早くアプリのアップデート/ロールバックを試してみる

GKE初学者です。
GKEの実際の運用でアプリを作成〜更新する場面を想定しつつ、サクッとアプリの無停止アップデート（ローリングアップデート）を試してみたかったので流れをまとめます。

gcloudのインストール、プロジェクトの作成、gcloudの設定までは終わっている状態を想定しています。

# クラスターの作成
まずはアプリを乗せる３つのノードをもつクラスターを作成します。

```bash
$ gcloud container clusters create test --num-nodes=3
```

# アプリの作成
次にアプリと想定したコンテナイメージを作成します。

## アプリの作成
中身はただの静的なHTMLです。

```text:ディレクトリ構成
test_nginx
 ├ Dockerfile
 └ html
   └ index.html
```
```text:Dockerfile
FROM nginx
COPY html /usr/share/nginx/html
```
```html:index.html
<h1>Hello, k8s!</h1>
<p>Version 1.0</p>
```

## Google Container Registryにイメージをアップする

ではDockerfileをもとにイメージのビルドを行います。
（ちなみに自分はローカルにdockerコマンドを入れるのが面倒だったため、GCPのWebプラットフォームのコンソールで実行。）
コマンドの形式は「ホスト名(リージョン)/プロジェクトID/イメージ名:タグ」となります。

```bash:
$ docker build -t gcr.io/default-237604/test_nginx:v1.0 .
```

ホスト名は次の4つのオプションのいずれかを選択可能です。

* gcr.io （今のところアメリカ）
* us.gcr.io （アメリカ）
* eu.gcr.io　（ヨーロッパ）
* asia.gcr.io （アジア）

作成されているか確認します。

```bash:
$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
gcr.io/default-237604/test_nginx      v1.0                1acd6162e9ae        4 seconds ago       109MB
```

次にContainer Registryにアップします。

```bash:
$ docker push gcr.io/default-237604/test_nginx:v1.0
```
これでイメージの作成、アップロードが完了です。

## Deploymentの定義
作成したイメージを利用するためのDeploymentを定義します。

```yaml:nginx-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: gcr.io/default-237604/test_nginx:v1.0
        ports:
        - containerPort: 80
```
作成します。
（--recordをつけることでAnnotationにコメントやリビジョンを履歴として保存しておける。）

```bash:
$ kubectl create -f nginx-deploy.yaml --record
deployment "frontend" created
```
きちんとデプロイ出来ているか確認します。

```bash:
$ kubectl get deployment
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   2         2         2            0           3m
```

## Serviceの定義
次に公開するためのServiceを定義します。

```yaml:nginx-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
```
作成します。

```bash:
$ kubectl create -f nginx-service.yaml 
service "frontend" created
```
今回も確認します。
EXTERNAL-IPが外部に公開されるアドレスで、pendingの状態だとまだ完了していません。

```bash:
$ kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
frontend     LoadBalancer   10.11.245.168   XX.XX.XX.XX    80:32442/TCP   2m
```

## 動作確認
EXTERNAL-IPをWebブラウザからアクセスしてみます。
http://XX.XX.XX.XX/

![スクリーンショット 2019-04-28 17.33.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35884/354fc48b-3aa6-8b94-8c43-bd72274b963c.png)

このようなページが表示されれば成功です。

# アプリのバージョンアップを行う
ではここでindex.htmlを編集しサクッとバージョンを2.0にしてみます。

## アプリの更新
```html:index.html
<h1>Hello, k8s!</h1>
<p>Version 2.0</p>
```

## 更新したアプリをGoogle Container Registryにアップする
v1.0と同様にv2.0イメージを作成します。
（タグをv2.0に変更しています。）

```bash:
$ docker build -t gcr.io/default-237604/test_nginx:v2.0 .
$ docker push gcr.io/default-237604/test_nginx:v2.0
```
## Deploymentの変更
参照するイメージのタグのみを変更しています。

```yaml:nginx-deploy.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: gcr.io/default-237604/test_nginx:v2.0
        ports:
        - containerPort: 80
```
## デプロイ
ではいよいよ更新したアプリをデプロイします。

```bash:
$ kubectl apply -f nginx-deploy.yaml --record
deployment "frontend" configured
```
Replicasetsが切り替わっているか確認してみます。

```bash:
$ kubectl get replicasets
NAME                  DESIRED   CURRENT   READY     AGE
frontend-6bc9d49844   2         2         2         7m
frontend-7b65dbfb55   0         0         0         26m
```
新しい方に切り替わっているようです。
ブラウザでも確認してみます。

![スクリーンショット 2019-04-28 17.51.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35884/dbcaf83f-f27b-d2bd-431d-748149cf28fb.png)

無事にアップデートされました！

# アプリのロールバック
v2.0に何かしらの不具合がありロールバックを行う必要があったとしてv1.0に戻す作業を行います。
再度Deploymentを変更してapplyでも可能ですがここではロールバックで戻します。

## デプロイの履歴を確認
--recordオプションをつけていたので内容を確認することが出来ます。

```bash:
$ kubectl rollout history deployment frontend
deployments "frontend"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=nginx-deploy.yaml --record=true
2         kubectl apply --filename=nginx-deploy.yaml --record=true
```
前のリビジョン１の詳細を確認します。

```bash:
$ kubectl rollout history deployment frontend --revision=1
deployments "frontend" with revision #1
Pod Template:
  Labels:	app=nginx
	pod-template-hash=3621869611
  Annotations:	kubernetes.io/change-cause=kubectl create --filename=nginx-deploy.yaml --record=true
  Containers:
   nginx-container:
    Image:	gcr.io/default-237604/test_nginx:v1.0
    Port:	80/TCP
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>

```
## ロールバックの実行
履歴を確認できたところで前のリビジョンにロールバックしてみます。

```bash:
$ kubectl rollout undo deployment frontend --to-revision=1
deployment "frontend" rolled back
```
ロールバックされたようです。
ブラウザで確認したところ表示もv1.0に戻っていたことが確認できました。

# 参考
* [GKEでGoogle Container Registryを利用する](https://qiita.com/smiyaguchi/items/24bace6dd3b5f05ee986)
* [Kubernetes: Deployment の仕組み](https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a)