GKE でアプリケーションを動かすまでのメモ

Kubernetes2 Advent Calendar 2017 16日目です(10日遅れました..)
https://qiita.com/advent-calendar/2017/kubernetes2

GKE を触る機会があったので GKE を使って kubenetes クラスタを使ってサンプルアプリが動くまでのコマンドなどを(ほぼ自分用に)忘備録としてまとめてみました
初めて GCP+Kubenetes を使ってどっちもよくわからんて状態だったのでとりあえず最終的にココらへんのコマンドを使ったら動く所まで行ったというメモ程度な感じです

結構やることが多かったので結構コマンドをそのまま列挙する感じになってしまっています

### やりたいこと

- Kubenetes(GKE)
- MySQL(Cloud SQL)

を使ったWEBアプリケーション環境の構築


### プロジェクト作成 ~ gcloud コマンドに設定

GCP にはプロジェクト単位で課金をするアカウントを設定する必要があるようなので、あらかじめ以下のコマンドで課金を行うアカウントのIDの取得しておきます

```
gcloud beta billing accounts list
```

取得したらプロジェクトの作成と gcloud コマンドの設定を行う

```
gcloud projects create sample-app # プロジェクト作成
gcloud beta billing projects link sample-app --billing-account=XXXXXX-XXXXXX-XXXXXX # 決済アカウントと紐付け(XXXXXX-XXXXXX-XXXXXX の部分は先ほど取得したアカウントIDに置き換える)
gcloud config set project sample-app # gcloud コマンドで操作するデフォルトのプロジェクトを設定
gcloud config set compute/zone asia-northeast1-b # gcloud コマンドで利用するデフォルトのリージョンを設定
```


### Cloud SQL

サンプルで作成するアプリケーションでは Cloud SQL の MySQL を利用してみようと思うので gcloud コマンドから Cloud SQL API の有効化します
GCP ではプロジェクト単位で各 API を利用する際に予め有効化しておく必要があるようです

https://cloud.google.com/service-management/enable-disable

```
gcloud service-management enable sqladmin.googleapis.com
```

有効化したので Cloud SQL インスタンスを起動

```
gcloud beta sql instances create sample-app \
  --tier db-f1-micro \
  --backup \
  --backup-start-time '19:00' \
  --database-version MYSQL_5_7 \
  --activation-policy ALWAYS \
  --enable-bin-log \
  --region asia-northeast1 \
  --gce-zone asia-northeast1-b \
  --maintenance-release-channel production \
  --maintenance-window-day MON \
  --maintenance-window-hour 18 \
  --storage-auto-increase \
  --storage-type SSD \
  --database-flags character_set_server=utf8
```

起動後に MySQL の root アカウントのパスワード設定やアプリケーション用ユーザーの作成を行います

```
root_password=$(pwgen -sy 16 1)
app_user_password=$(pwgen -sy 16 1)
gcloud sql instances set-root-password sample-app --password $root_password
gcloud beta sql users create app_user cloudsqlproxy~% --instance=sample-app --password=$app_user_password
```

### サービスアカウント

Cloud SQL への接続にはいくつかの方法があるようだが GKE を利用する場合に Cloud SQL Proxy を使った方法が紹介されていたのでこの方法を使って接続できるようにしてみます
https://cloud.google.com/sql/docs/mysql/connect-container-engine


GKE のコンテナ上で Cloud SQL Proxy を利用するにはサービスアカウントが必要なようなので作成を行います
https://cloud.google.com/iam/docs/service-accounts

個人的な雑な理解としてはサービスアカウント=AWSでいう所のIAMロールぽく使えるものみたいな認識です

```
gcloud iam service-accounts create cloud-sql
```

作成したサービスアカウントの email を取得

```
gcloud iam service-accounts list
```

更にこのサービスアカウントを利用して Cloud SQL Proxy で接続できるようにするために Cloud SQL クライアント権限を付与

```
gcloud projects add-iam-policy-binding sample-app \
  --member='serviceAccount:cloud-sql@sample-app.iam.gserviceaccount.com' \
  --role='roles/cloudsql.client'
```

`cloud-sql@sample-app.iam.gserviceaccount.com` の部分を取得した email に差し替えて実行する


権限を付与したらこのサービスアカウントを利用するための秘密鍵を生成

```
gcloud iam service-accounts keys create ~/cloud-sql-key.json --iam-account cloud-sql@sample-app.iam.gserviceaccount.com
```


https://cloud.google.com/sql/docs/mysql/connect-admin-proxy#install
を参考に環境にあわせた Cloud SQL Proxy をインストールして Cloud SQL Proxy を起動する

```
cloud_sql_proxy \
  -instances=sample-app:asia-northeast1:sample-app=tcp:3306 \
  -credential_file="~/cloud-sql-key.json"
```

`-credential_file="~/cloud-sql-key.json"` で渡した json ファイルのサービスアカウントで Cloud SQL Proxy が立ち上がるようです


実際に接続できるかを確認してみる

```
mysql -h 127.0.0.1 -u sample-app -p$app_user_password
```


### GKE

Cloud SQL 周りの設定ができたので次は GKE 関連の設定などをおこなっていきます
GKE を利用するために compute API を有効化します

```
gcloud service-management enable compute.googleapis.com
```


Kubenetes のクラスタを作成

```
gcloud container clusters create sample-app \
  --cluster-version=1.7.8 \
  --image-type=COS \
  --machine-type=g1-small \
  --num-nodes=3 \
  --node-labels=role=sample-app-node \
  --enable-cloud-logging \
  --enable-cloud-monitoring
```

更に kubectl コマンド用に以下で credentials を取得しておく

```
gcloud container clusters get-credentials sample-app --zone asia-northeast1-b --project sample-app
kubectl config use-context gke_sample-app_asia-northeast1-b_sample-app
```

### Cloud SQL Proxy 向けの credentials を設定

`kubectl create secret generic` コマンドを使ってパスワードなど暗号化したいデータをコンテナに渡すことができるようなので設定しておく

```
kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json='~/cloud-sql-key.json'
```


### kubenates 関連情報の設定

ドメインを当てる場合に IP が切り替わると困るので static ip を取得して利用するようにする

```
gcloud compute addresses create sample-app-static-ip --global
```

### deployment 生成

kubenetes では自作した docker イメージを利用するようにしてみたいので Container Registory API を有効化する

```
gcloud service-management enable containerregistry.googleapis.com
```

有効にしたらデプロイしたいコンテナアプリケーションを

```
gcloud docker -- push asia.gcr.io/sample-app
```

のようにして Container Registory に push する

push したイメージを利用して以下のような deployment の mainifest ファイルを作成

```yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app
spec:
  minReadySeconds: 15
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 100%
      maxUnavailable: 0
  replicas: 2 # 作成する pod の数
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - image: asia.gcr.io/sample-app:latest # リリースするコンテナを設定
          name: app
          ports:
            - containerPort: 3000 # 起動するアプリケーションポートの番号
              name: app
          readinessProbe: # リクエスト送信可能かをチェックするヘルスチェックのパス情報を記載
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 60
            periodSeconds: 60
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 10
          livenessProbe: # コンテナが行きているかをチェックするヘルスチェックのパス情報を記載
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 60
            periodSeconds: 60
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 10
        - image: b.gcr.io/cloudsql-docker/gce-proxy:1.09 # Cloud SQL Proxy コンテナ
          name: cloudsql-proxy
          command: ["/cloud_sql_proxy",
                    "--dir=/cloudsql",
                    "-instances=sample-app:asia-northeast1:sample-app=tcp:3306", # 接続先情報 gcloud sql instances describe sample-app | grep connectionName のようにして取得できる
                    "-credential_file=/secrets/cloudsql-proxy/credentials.json"] # サービスアカウントの認証 json ファイル
          volumeMounts:
            - name: cloudsql-oauth-credentials
              mountPath: /secrets/cloudsql
              readOnly: true
            - name: ssl-certs
              mountPath: /etc/ssl/certs
            - name: cloudsql
              mountPath: /cloudsql
      volumes:
        - name: cloudsql-oauth-credentials
          secret:
            secretName: cloudsql-oauth-credentials # kubectl create secret generic で設定した秘密情報データを設定
        - name: ssl-certs
          hostPath:
            path: /etc/ssl/certs
        - name: cloudsql
          emptyDir:
```

作成したら

```
kubectl apply -f deployment.yml
```

のようにして kubenetes に設定を適用する
適用したら

```
kubectl get pods
```

のようにして manifest ファイルから起動した pod の情報を取得できる


### NodePort Service を作成

次に NodePort の作成を行う

```
kubectl expose deployment app --target-port=3000 --type=NodePort
```

### Ingress 作成

次に Ingress の作成を行う
deployment の時のように manifest ファイルを作成して適用する

```yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: sample-app-static-ip # gcloud compute addresses create の時の名前を指定する
spec:
  backend:
    serviceName: app
    servicePort: 3000
```

```
kubectl apply -f ingress.yaml
```

実行したら

```
watch kubectl get ingress
```

を実行して最終的に

```
NAME          HOSTS     ADDRESS          PORTS     AGE
app-ingress   *         11.111.111.111   80        1m
```

のように ADDRESS のIPアドレスが表示されるまで1~2分待つ
(何分も待っても表示されない場合何らかでうまく行ってない可能性が高い)

この時点で↑で表示されるIPアドレスにアクセスするとアプリケーションが表示されるはずです
うまく行っていれば対象IPにドメインを割り当てたりすれば良いかと思います
(ややこしかったのでSSL対応についてはスキップしてます)

### 感想

いきなり GCP も Kubenetes も慣れていない状態で GKE 触ろうとすると両方を覚えながらやらなきゃいけないので覚えることが多くてはじめに動かすところまで行くのが結構大変でした