GKEを使用している時に陥りがちな罠とトラブルシューティングについて

網羅されている訳ではないので、もっと色々な事例見つけたら教えてください。また個人の運用に経験に基づく知見なので、誤りがあれば指摘していただけると嬉しいです。

**中にはある程度知識を持った上で使わないと危険なコマンドがありますので、検証は自己責任でお願いします。特にGKEのAutoScalerなどと不整合を起こす可能性のあるコマンドもあるため、注意してください**

# Kubernetes初期設定と操作クラスタの切り替え

1. 前提としてクラスタを管理しているマシン上で[master components](https://kubernetes.io/docs/concepts/overview/components/)が起動している必要がある(minikubeを使うとVM上に,GKE上ではGUI・CUIでクラスタを作ると勝手に立つと思われる。これは簡潔性のために同ホストで全てのmaster componentsを立ち上げるが個別の起動や自作サービスとの共存も可能(ただしやらない方がいいと書いてある))。
参考:
The Kubernetes Master is a collection of three processes that run on a single node in your cluster, which is designated as the master node. Those processes are: kube-apiserver, kube-controller-manager and kube-scheduler.

2. kubectlコマンドはmaster componentsのうちkube-apiserverを指定・経由して操作したいホスト(クラスタ)に変更を加える(REST)
(各ホストではkubeletとkube-proxyというデーモンが操作を待ち受けている or 新たに作成されたnode上でkubele, kube-proxyを起動させる「GCPの場合は、おそらくGKEクラスタの作成時にそれぞれのnode上でkubeletを起動させている」)

3. kubectlで操作されるホスト群(クラスタ)の設定は(デフォルトでは)$HOME/.kube/configから読まれる

4. 以下のコマンド

```
gcloud container clusters get-credentials $CLUSTER_NAME --zone=$ZONE_OF_CLUSTER
```
を実行するとkubectlの操作対象をGKE上のクラスタに設定できる - ここの設定は、.kubeを見る限りkubectlのREST先を切り替えている。minikube startを使うとリクエスト先がVMになる。)

5.  .kube/configに一度設定したクラスタはcontextというkeyのデータに格納される。

```
kubectl config use-context $CLUSTER_NAME
```
で操作対象のクラスタを自由に変えることができる。

## 参考(https://github.com/kubernetes/kubernetes/issues/25383)
### 手動でkubectlの操作先を決定する最小の設定方法
```
$ kubectl config set-cluster --server=xxx
$ kubectl config set-credentials xx --username=xxx password=xxxx
$ kubectl config set-context xxx --cluster=xxx --user=xxx --namespace=xxxx
$ kubectl config use-context xxxx
```
`gcloud container clusters get-credentials` は、以上のコマンドを簡略化してくれていると思われる
### 現在kubectl実行環境に設定されているcontextのリストを確認する方法
```
kubectl config view -o jsonpath='{.contexts[*].name}'
```

また、kubectlのcontextの向き先を設定すればgoogle cloud console上のkuberctlでも操作が可能

## 設定情報を見る

dash_boardを使うと一覧できる。
gcp上のkubenetesの設定を確認したい場合は認証情報が必要なため、クレデンシャルを設定した端末でkubectl proxyを実行し、
```
$host:8001/ui
```
でアクセスすることにより本番環境の設定情報にもアクセスできる。gcp上の端末だとIPなどで弾かれるのでローカルマシンのkubectl proxyからlocalhostで接続するのが無難。

## minikube

ローカルで実行する際はminikubeを使うとよい
minikube startを実行するとvirtual box上にkubernetesのnodeを作成できるコンテキストを自動生成する

## 異常なpodがないかチェック
```
kubectl get pods --all-namespaces | grep -v Running
```
## Does not have minimum availability

こいつが起きるときはリソース不足だけとは限りません。コンテナの起動が失敗していたり、ヘルスチェックが失敗して、サービスからバックエンドのpodの接続が切断されてる時などにも表示されます。ちゃんとdescribeやイベントやステータスやログを読みましょう。特にこのエラーが出てる時に気づきにくい原因が以下二通り。

###  そもそもコンテナのビルドやデーモンの起動に失敗している

コンテナ内のログを読みましょう。意外と自分の実装したものがエラー出しててイメージビルドには成功しているが正しく起動してないバグは見落としがちですが、結構起きます。(ちゃんとCIでテストしていれば防げる話ですが。）

###  serviceのselectorで設定されているmatchLabelがマッチしていない、もしくは複数のPodsやContainerに該当して接続先が定まらない

kubeObjectのサービスはselectorで接続先を決定するので、実は設定ファイル外のPodsやコンテナを指定できる。陥りがちなのはこれによって接続先を間違えていたり複数存在させることによって、一意に定まらないケースで、問題が潜在化してしまうことが多いです。=> helmがいいです。

## リソース足りてない問題

- Sync Failed 
- Unscheduled
- Pending

以上の二つは正しくpodsの起動ができなかったりrolling updateができない時に起きる。二通りの方法を試してみる。

- リソースを増やして移行する

```
gcloud container clusters resize $cluster --node-pool default-pool --size 4 --zone asia-east1-c 
```

```
gcloud container node-pools create f1-micro-pool --machine-type f1-micro --num-nodes=5 --zone=asia-east1-c --cluster $cluster
```
https://qiita.com/superbrothers/items/d9766ebaff15d6954d03
https://qiita.com/tkusumi/items/946b0f31931d21a78058

- Errorになってるコンテナを無理やり消すなどをしてリソースを開けて、オブジェクトを作り直す(createはだいたいコマンドをcreateに置き換えるか、逆方向のコマンドを打てば良いので省略)
     - podsを減らす
        
        ```
        kubectl scale --replicas=1 deploy/$deploymentName
        ```
     - podsを強制削除
     
        ```
        kubectl delete pods $podName --force --grace-period=0
        ```
     
     - nodeを指定してpodを他のnodeに退避させる
     
         ```
          kubectl drain $nodeName [Option]
         ```

     - nodeを強制削除
        注意: AutoScalerなどと一緒に使うと表示がバグるので、gcloud CLIまたは、GCP Consoleからの操作をお勧めします。ゾンビ化して強制削除せざるをえない時などに使ってください。

         ```
         kubectl delete nodes/$nodeName --force --grace-period=0    
         ```

     - node-poolを削除する

        ```
        gcloud container node-pools delete default-pool --zone $zoneName --cluster $clusterName 
        ```

## ヘルスチェックうまく行っていない問題

### Error例など
- Unscheduled
- Does not have minimum availability

### よく陥りがちな罠
 
Ingressのヘルスチェックは接続先のサービスのヘルスチェックを再利用し、設定されていなければデフォルトで/にヘルスチェックを飛ばす。この時、status 200が/から返っていなければ、ingressが起動しません。特にdefault設定のままだと、stgにbasic認証を挟むなどの操作によって、気づかぬうちに、/がstatus 200を返さなくなるなどの理由で、PodがRunningであるのにserviceから接続がrejectされるなどの事故が起きます。(実話)この場合Cloud LoggingのIngressのヘルスチェックログが警告を発してるので、気づけます。基本的にhealth checkは設定推奨になります。

### 落ちた時にアラートを飛ばす
Stack Driver からUptime Checkを設定し、ヘルスチェックのパスにリクエストを飛ばしておくと良い

 設定抜粋例)

   ``` yaml
    readinessProbe:
        httpGet:
          path: /healthz
          port: 80
        periodSeconds: 1
        timeoutSeconds: 1
        successThreshold: 1
        failureThreshold: 10
   ```


## 高負荷に耐えられない問題

- podのスケーリング設定を行う色々なオプションがあるので、状況に合わせて使う
例)
        - 通常時の設定
             desired のpod数、scalingの条件などを設定する
     
            ```
           kubectl autoscale rc (or deployment) foo --min=2 --max=5 --cpu-percent=80
            ```

        - Rolling Update時の, Scaling 条件を設定する
            - maxSurge:  Rollin Updateの際にdesiredレプリカ数を基準に何個まで、podsをスケールさせてもよいか設定する。defaultは25%少数は切り上げ
            - maxUnavailable:  defaultは25%。少数は切り下げ。
 
## わざとリソースを食いつぶしてみる

練習としてcordon、drain、taintによって、podのスケジューリングできないnodeの数を調整し、その状態でどれくらいリソースを確保したら、クラスタが復元できるか検証してみる。

- cordon
 
NodeにPodsを置けなくする。Uncordonはその逆。

  ```
  kubectl cordon $nodeName [Option]
  kubectl uncordon $nodeName [Option]
  ```
- drain

NodeをUnscheduledに(cordonと同じ処理)してからpodをevictして他のNodeに移行する。

  ```
 kubectl drain $nodeName [Option]
  ```

- taint
   tolerationsという条件を設定して、条件を越えると、podsを他のNodeへevictionが走るようにできる
 
  ```taint.yaml
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
   ```

cordonしたNodeには新たなコンテナが配備できなくなるため、ReadyになっているNodeのみでリソース調整ができる。これによって、適度なNode数やpodsを配備した状態でエラーを起こしてみて、復旧をテストしてみる。

## Ingressがゾンビ化する

IngressはGKEのリソースではなく、GCEにロードバランサーと設定が立つため、イレギュラーな削除の仕方をするとゾンビ化する

特に 

```
kubectl delete ing $ingName
```
などのコマンドを実行してIngressを削除するよりも前に、GKEのクラスタを削除してしまうと、GCE上にGCEロードバランサーのインスタンスが稼働し続けstatic IPも解放されないという現象が起きるため、クラスタを削除する前に上記コマンドの実行を忘れないようにしておく必要がある。
なお、忘れてしまった場合は、GCE・VPCのコントロールから以下の設定を削除する必要がある。

- ネットワークサービス > 負荷分散 > ロードバランサ　> 該当のロードバランサを削除
- ネットワークサービス > 負荷分散 > バックエンド　> ロードバランサが設定されていないバックエンドを削除
- VCPネットワーク > ファイアウォールルール > 削除
- VCPネットワーク > 外部IPアドレス > `gcloud addresses delete $ip`
- GCE > ヘルスチェック > 未使用なものから検索をかける　> 削除
- GCE > インスタンスグループ > 未使用なものから検索をかける　> 削除


## 本番環境の設定忘れた・担当者が職場を去ったが設定してあるものがわからない

```
kubectl get $resorceType -o yaml
```

で設定を復元できます。ベンダーごとのメタデータがついてたり,secretはbase64設定されていますが、復元できます。

## 問題点を探す

- GKE上のダッシュボードで確認するそれぞれのkubeObjectに遷移しyaml,イベント,ログを確認
- Stack Driver Loggingやモニタリングをみる
- kubectl top $resorceType
- kubectl describe $resorceType

## set imageに失敗した失敗した失敗した失敗した失敗した…時

```
kubectl rollout history deployment/nginx-deployment --revision=2
kubectl rollout undo deployment/nginx-deployment 
kubectl rollout undo deployment/nginx-deployment --to-revision=2
```
のいずれかを使う。

## コンテナログイン

```
kubectl exec $pod-name -c $container-name  /bin/bash
```
## Nodeにログインする(裏技)
裏側知りたい時や調査以外で非推奨

```
gcloud instances list (NodeNameを調べる)
gcloud compute  ssh $nodeName
```