Redmine を GKE(k8s) + CloudSQL + FileStore  でデプロイ

このページは 5/18（土） 14:00 - 18:00 開催予定のGCPUG Shimane #04 のハンズオンセッションの資料となります。
https://gcpug-shimane.connpass.com/event/128517/

# 目的

今回のハンスおんはGKEにRedmineをデプロイする作業を通して、FileStore(nfs), CloudSQL(mysql)との連携を体験していただくことがメインとなります。

# アジェンダ
- 資料の説明（ハンズオンの内容説明）
- GCPの構成の説明（コンテナ、kubernetesの説明）
- RedmineをGKEをベースに動かしてみる（ハンズオン）
  - 事前準備
  - Cluster 作成
  - FileStore 作成
  - CloudSQL 作成
  - Redmine デプロイ
- 運用管理について
- 削除方法

# 構成の説明

![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29570/79108cee-ae7d-b44f-71ea-76cb8cd52231.png)

# RedmineをGKEをベースに動かしてみる（ハンズオン）

## kubectl （よく使うコマンド）

```bash
kubectl get xxx
# (pod, svc, deploy, pv, pvc, secrets, etc)

kubectl describe xxx [xxx_name]
# (pod, svc, deploy, pv, pvc, secrets, etc)

kubectl logs [pod_name]

# ssh(pod)
kubectl get pod
kubectl exec -it [pod_name] /bin/bash

# ssh(node)
kubectl get nodes
gcloud compute ssh [node_name]

# 削除系のコマンド
kubectl delete svc [service_name]
kubectl delete deployment [deployment_name]
kubectl delete pv [pv_name]
kubectl delete pvc [pvc_name]

```


## 1. 事前準備

- プロジェクトの作成
- 課金の有効化
- APIの有効化
  - Kubernetes Engine API
  - Cloud SQL
  - Cloud SQL Admin API
  - Cloud Filestore API

**注）以降の手順（コマンド）はCloud Shellにアクセスして行う**

```bash
# componentsのインストール
gcloud components install kubectl
# プロジェクトの選択
gcloud config set project [project-id]
# リージョンの選択（東京リージョンを選択）
gcloud config set compute/zone asia-northeast1-a

# 設定情報の確認 上記設定が反映されているか確認してみてください。（任意で実行）
$ gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = xxxxxxx@gmail.com
disable_usage_reporting = False
pass_credentials_to_gsutil = true
project = [project-id]
```

## 2. Cluster 作成

クラスター名は [ redmine-cloudsql-filestore ] で作成する。

```bash
gcloud container clusters create redmine-cloudsql-filestore --num-nodes=3
```

### preemptibleを利用する場合(参考)


```bash

# g1-smallタイプのnode(GCEインスタンス)をpreemptible(強制停止あり)で2つ立ち上げる
gcloud container clusters create redmine-cloudsql-filestore --preemptible --machine-type=g1-small --num-nodes=2

# 一台はpreemptibleでないnodeを使用することによって無停止で運用
gcloud container node-pools create default-pool --cluster redmine-cloudsql-filestore --machine-type=g1-small --num-nodes=1
```

### 作成済みのclusterを利用する場合（クラスターの選択）

```bash
# クラスターのリストを取得
gcloud container clusters list
---
NAME                     LOCATION           MASTER_VERSION               MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
persistent-disk-redmine  asia-northeast1-a  1.11.8-gke.6 (55 days left)  35.221.66.68  n1-standard-1  1.11.8-gke.6  3          ERROR
---
# kubectlで操作するクラスターを選択する
gcloud config set container/cluster redmine-cloudsql-filestore
```

作成完了後、コンソールから確認してみてください。
* 「コンピューティング > Kubernetes Engine」


## 3. Docker Image 作成

GKEで動かすアプリケーションのDocker imagesを作成します。
今回は事前にRedmineを GKE 向けにDocker image化したリポジトリーを準備しましたので、それを使用します。
https://github.com/yoshiokaCB/redmine/tree/gcp/master

以下の手順で CloudShell 上でDocker image をbuildし、GCPのレジストリーサービス(Container Registry)にpushする。

```bash
# リポジトリーをダウンロード
git clone -b gcp/master https://github.com/yoshiokaCB/redmine.git ./redmine_gke && cd $_
# Docker imagesをbuild
docker-compose build
# ビルドされたか確認
docker images
# アップロードようにtag付け
docker tag redmine_gke_app asia.gcr.io/[project-id]/redmine_app:v1
# Buildしたイメージをpush
docker push asia.gcr.io/[project-id]/redmine_app
```

作成完了後、コンソールから確認してみてください。
*「ツール > Container Registry」


## 4. FileStore 作成 （ストレージサービス）

データの永続化をするために、FileStore(nfs)を作成し、kubernetesのからアクセスできるよに PersistentVolume で volume を定義し, Pod でま mount できるように PersistentVolumeClaim を作成します。

```bash
gcloud filestore instances create redmine-nfs-server \
    --project=[project-id] \
    --zone=asia-northeast1-a \
    --tier=STANDARD \
    --file-share=name="vol01",capacity=1TB \
    --network=name="default"
```

FileStoreのIPアドレスを確認し、メモしておく

```bash
gcloud filestore instances list

INSTANCE_NAME       ZONE               TIER      CAPACITY_GB  FILE_SHARE_NAME  IP_ADDRESS    STATE  CREATE_TIME
redmine-nfs-server  asia-northeast1-a  STANDARD  1024         vol01            10.171.14.xx  READY  2019-05-xxT03:24:36

```

先ほどメモしたIPアドレスを追記する

```bash
vi redmine-pv.yaml

# x.x.x.x を先ほどメモしたIPに変更する
# ...
# nfs:
#   path: /vol01
#   server: x.x.x.x
```

kubernetesで接続できるようにvolumeを作成



```bash
# PersistentVolume の作成（volumeを定義）
kubectl apply -f redmine-pv.yaml

# PersistentVolumeClaim（volumeとpodを紐付け）
kubectl apply -f redmine-volumeclaim.yaml
```

参考
https://cloud.google.com/kubernetes-engine/docs/concepts/persistent-volumes?hl=ja


作成完了後、コンソールからそれぞれのストレージを確認してみてください。
* 「コンピューティング > Kubernetes Engine > ストレージ」
* 「ストレージ > FileStore」
注） FireStoreとFileStoreは別物です。

## 5. CloudSQL 作成

データベースの作成し、パスワードをkubernetesに暗号化して登録します。（今回はMySQLを使用）
また今回はDBへのアクセスにSQLProxyを使用しますので、DB作成後、アクセス用に権限(iam)の設定も行います。
権限を作成後をcredentialファイルを一度ダウンロードし、パスワードと同じくkubernetesに暗号化して登録します。

注意) [password]は任意の文字列に変更してください。

```bash
# cloudsql instance 作成
gcloud sql instances create redmine-mysql \
    --region=asia-northeast1 \
    --database-version=MYSQL_5_6 \
    --root-password=[password] \
    --cpu=1 \
    --memory=3840MiB \
    --storage-type=SSD \
    --storage-size=10GB \
    --storage-auto-increase

# k8sへファイルを暗号化して保存しておく
# mysqlというnameの中にpasswordというkeyで値を保存します。
# 後ほどredmineをデプロイするときに環境変数の呼び出しで利用します。
kubectl create secret generic mysql --from-literal=password=[password]

# sqlproxyのコンテナからcloudsql へのアクセスための iam を作成
gcloud iam service-accounts create cloudsql --display-name="cloudsql"

# cloudsql(iam) へ権限追加
gcloud projects add-iam-policy-binding [project-id] \
    --member="serviceAccount:cloudsql@[project-id].iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

# credential ファイルのダウンロード（アクセスkeyとpasswordが記入されたjsonファイル）
gcloud iam service-accounts keys create $HOME/cloudsql-credentials.json \
    --iam-account cloudsql@[project-id].iam.gserviceaccount.com

# kubernetes へ　credential ファイルを登録
# 後ほどredmineのデプロイするときにファイルを参照できるように登録します
kubectl create secret generic cloudsql-instance-credentials \
    --from-file=credentials.json=$HOME/cloudsql-credentials.json

```

作成完了後、コンソールから確認してみてください。
* 「ストレージ > SQL」


## 6. Redmine デプロイ

いよいよRedmineのデプロイです。
redmine.yaml（deployment）でPodを起動し、redmine-service.yaml(service)で外部へのエンドポイントを作成します。

secret_key_baseを設定する

```bash
# secrete_key_baseを編集する
docker-compose run --rm app bundle exec rake secret
vi redmine.yaml
```

```:redmine.yaml
- name: SECRET_KEY_BASE
  value: [secret_key_base]
```

Redmineのデプロイをする

```bash
# redmine+sqlproxy のpod作成
kubectl create -f redmine.yaml

# サービスの作成
kubectl create -f redmine-service.yaml
```

サービス作成後、サイトの確認をする。

```bash
kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
kubernetes   ClusterIP      10.47.xxx.xx   <none>          443/TCP        1d
mysql        ClusterIP      10.47.xxx.xx   <none>          3306/TCP       1d
redmine      LoadBalancer   10.47.xxx.xx   35.221.90.xx   80:30667/TCP   1d

# 35.221.90.xx にブラウザでアクセスする。

```
EXTERNL-IP（35.221.90.xx）にブラウザからアクセスする。
（ログインID,PASS共に`admin`でアクセス可能）

修正変更等を反省させる時は下記のように `apply -f` を使用する

```
kubectl apply -f redmine.yaml
```
# 更新

1. Redmineのコードに変更を加える。
2. ビルドの再実行
3. tagをv2に変更してpush
4. redmine.yamlのimageのtagをv2に変更して更新

# ロールバック

```bash
$ kubectl get deployment
--
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
redmine   1         1         1            1           8d
--
$ kubectl rollout history deployment redmine
deployments "redmine"
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>

# バージョンの詳細確認（yaml表示）
$ kubectl rollout history deployment redmine --revision=1
# ロールバック
$ kubectl rollout undo deployment redmine --to-revision=1
```
# スケール・オートスケール

コンソールで設定してアクセス負荷をかけてみる


# 無停止でnodeを更新する
```bash

# 新規 node-pool 作成
gcloud container node-pools create n1-pool --cluster redmine-cloudsql-filestore --machine-type=n1-standard-1 --num-nodes=2 --enable-autoscaling --min-nodes=2 --max-nodes=10 --zone asia-northeast1-a

# nodeのスケジューリングを停止する
for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do kubectl cordon "$node"; done

# nodeをdrainする
for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do
  kubectl drain --force --ignore-daemonsets --delete-local-data --grace-period=180 "$node";
done

# 古い node-pools 削除

gcloud container node-pools delete default-pool --cluster redmine-cloudsql-filestore --zone asia-northeast1-a

```


# 参考サイト

## gcloud リファレンス
https://cloud.google.com/sdk/gcloud/reference/

## kubectl リファレンス
https://kubernetes.io/docs/reference/kubectl/overview/

### kubernetes : kubectlコマンド一覧
https://qiita.com/suzukihi724/items/241f7241d297a2d4a55c

## GKE

### チュートリアル
https://cloud.google.com/kubernetes-engine/docs/tutorials/

### Using Persistent Disks with WordPress and MySQL
https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk

### Github（サンプルコード）
https://github.com/GoogleCloudPlatform/kubernetes-engine-samples

### GKE + FileStore
https://cloud.google.com/filestore/docs/accessing-fileshares
https://cloud.google.com/kubernetes-engine/docs/concepts/persistent-volumes?hl=ja

### GKE + CloudSQL
https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine?hl=ja#proxy

### EC2 インスタンスで運用している GitLab を、 GKE に移行してみた。（Kubernetes Engine + Cloud SQL + Cloud Filestore）
https://qiita.com/nagase/items/0ec9bf1702b222cb2e69

### Kubernetes: Deployment の仕組み
https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a

### GKEのインスタンスタイプを無停止で変更する
https://qiita.com/yagince/items/9d66a6cf58b2905c16db

https://cloud.google.com/kubernetes-engine/docs/concepts/node-pools?hl=ja
https://cloud.google.com/kubernetes-engine/docs/resize-cluster?hl=ja

### KubernetesにおけるLoadBalancerとIngressの違いについて
https://sff8.hatenablog.com/entry/2018/10/27/234757