Kubernetes + GCP 技術調査 まとめ


`gcloud` が便利なので設定しておく。
参考: https://cloud.google.com/pubsub/docs/quickstart-cli

### kubernetesのversionを確認して起動

```sh

gcloud container get-server-config --zone asia-northeast1-a # versionのリストを確認
gcloud container clusters create k8s --cluster-version 1.13.7-gke.8 --zone asia-northeast1-a --num-nodes 3
```
- `--cluster-version 1.10.5-gke.3` ValidMasterVersionsにあるVersionを入力
- `--zone asia-northeast1-a` Kubernetesを設置するRegion
- `--num-nodes 3` ノード数。
- `--project`プロジェクトID

### kubernetesのCredentialを`~/.kube/config`に保存



```sh

gcloud container clusters get-credentials k8s --zone asia-northeast1-a
```
以降`kubectl`で管理ができるように

### Kubectl Command一覧

```sh

kubectl get [リソースの種別] [リソース名] # podの一覧取得
kubectl describe [リソースの種別] [リソース名] # 詳細を確認
kubectl top [リソースの種別] # リソース使用量を確認
kubectl -n kube-system top [リソースの種別] # kube-system namespaceで起動しているものの使用量を確認
kubectl exec -it sample-pod /bin/sh # Resource内のコンテナでコマンドを実行
kubectl logs sample-pod # sample-podでの標準出力をログとして出力
kubectl cp sample-pod:/etc/hostname . # Containerのファイルをローカルにコピー
kuberctl port-forward pod-A 12345:8080 # localhost-port:container-portという書式。
```

各種コマンドも他にオプションが色々確認あるので複雑なことをやろうと思ったら確認してみる。


### リソースの更新, 作成, 削除

```shell

kubectl apply -f sample-pod.yaml --prune --all # update, create, or delete
```
- `--record` アップデートの更新を記録する


削除の際に`error: error pruning nonNamespaced object /v1, Kind=Namespace: namespaces "kube-system" is forbidden: this namespace may not be deleted`というエラーが出る模様。削除は出来ているが気になる...
参考: https://github.com/kubernetes/kubernetes/issues/66430

### Pod

```yaml:sample-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
  annotations:
    annotation1: val1 # Annotation 各種設定がこれを通して行われたりする。
    annotation2: val2 # Annotation
spec:
  containers:
    - name: nginx-container
      image: nginx:1.12
      command: ["/bin/sleep"] # dockerfileのCMDの上書き
      args: ["3600"] # dockerfileのCMDの上書き
      ports:
        - containerPort: 80 # Containerのポート番号を指定
      env: # 環境変数を設定
        - name: MAX_CONNECTION
          value: "100"
```

```shell

kubectl exec -it sample-pod env # 環境変数を確認
```


### ReplicaSet

```yaml

apiVersion: apps/v1
kind: ReplicaSet # 複製する場合
metadata:
  name: sample-rs
spec:
  replicas: 3 # 複製の個数
  selector:
    matchLabels:
      app: sample-app # ラベルで複製するものを選ぶ
  template:
    metadata:
      labels:
        app: sample-app # 複製識別ラベル。一致していないと作成できない。
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.13
          ports:
            - containerPort: 80
```

```shell

kubectl describe rs sample-rs # Podの増減の履歴確認Command
```

### Deployment

```yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  strategy:
    type: Recreate # RollingUpdateがDefault。ここら辺は設定できるパラメーターも多い。minReadySecond, etc...
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80
```


```shell

kubectl get replicasets # -o yaml 生成されたReplicasets/Deploymentを確認
kubectl get deployments # -o yaml 生成されたReplicasets/Deploymentを確認
kubectl rollout status deployment sample-deployment # アップデートの状況
kubectl rollout history deployment sample-deployment # --revision [番号] 詳細表示
kubectl rollout undo deployment sample-deployment # --to-revision [番号] ロールバック先を指定
kubectl rollout pause deployment sample-deployment # 更新の適用を一時停止
kubectl rollout resume deployment sample-deployment # 更新の適用の一時停止を解除
```

### DaemonSet

```yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: sample-ds-ondelete
spec:
  updateStrategy:
    type: OnDelete # 削除された時に新しく作成する RollingUpdateとかもある。
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
```

```sh

kubectl delete pod [podリソース名] # 削除された時のセルフヒーリングで更新を行う。
```

### StatefulSet

```yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-stateful-set
spec:
  podManagementPolicy: Parallel # Podを並列的に作成。Defaultは一つずつ
  updateStrategy:
    type: OnDelete # RollingUpdateもある。
  serviceName: sample-statefulset
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80
          volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: www
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1G

```

```sh

kubectl get persistentvolumeclaims # 確保した永続化領域の確認
kubectl delete persistentvolumeclaims [リソース名] # podを削除するだけでは永続化領域までは削除されないので気をつける。
```

### Job

```yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
spec:
  completions: 5 # タスク実行回数。指定しないこともできる。タスクを実行し続けるワークキューになる。
  parallelism: 2 # 並列実行度
  backoffLimit: 10 # 失敗を許容する回数。Defaultは6
  template:
    spec:
      containers:
        - name: sleep-container
          image: centos:6
          command: ["sleep"]
          args: ["60"]
      restartPolicy: Never # OnFailure -> 失敗時に新規Podを作成 Never -> 失敗時に同一のPodで実行
```

```sh

kubectl get jobs # 状況を確認
```

### CronJob

```yaml

---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: sample-cronjob
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow # Forbid 前のジョブが終了していない場合に次のジョブを実行しない
  # Replace 前のジョブを削除して次のジョブを実行する
  startingDeadlineSeconds: 30
  successfulJobsHistoryLimit: 5 # 成功したジョブを保存する数
  failedJobsHistoryLimit: 3 # 失敗したジョブを保存する数
  suspend: false # 停止する時はここを変更する
  jobTemplate:
    spec:
      completions: 10
      parallelism: 2
      backoffLimit: 10
      template:
        spec: 
          containers:
            - name: sleep-container
              image: centos:6
              command: ["sh", "-c"]
              args: ["sleep 40; data + '%N' | cut -c 9 | egrep '[1|3|5|7|9]'"]
          restartPolicy: Never
```

```sh

kubectl get cronjobs
kubectl get jobs
kubectl delete jobs --all #削除
```

### Service

```yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  # strategy:
  #   type: Recreate # RollingUpdateがDefault。ここら辺は設定できるパラメーターも多い。minReadySecond, etc...
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  # clusterIP: 10.11.253.81 静的に指定することも可能。
  # externalIPs: externalIPsも設定できる。利用可能なものは`kubectl get nodes -o custom-columns="NAME:{metadata.name},IP:{status.addresses[].address}"`とかで確認可能
  #  - 10.240.0.7 Exnternal IPsを設定することでGKEのkubernetesネットワークの外側からもアクセスできるようになる。
  #  - 10.240.0.8
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

```

```sh

kubectl get pods -l app=sample-app -o custom-columns="NAME:{metadata.name},IP:{status.podIP}"
kubectl get services sample-clusterip
kubectl describe svc sample-clusterip
```

### NodePort

```yaml

apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  externalTrafficPolicy: Local # defaultがClusterでNode到達後にもNodeを飛び越えてPodにロードバランシングを行う。
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080 # ClusterのPort
      targetPort: 80 # PodのPort
      nodePort: 30080 # 全NodeでLisetenするPort。30000~32767の間で設定できる。指定しなかったら自動的に選択。
  selector:
    app: sample-app

```

### Load Balancer

```yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80
            
---
apiVersion: v1
kind: Service
metadata:
  name: sample-load-balancer
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local # defaultがClusterでNode到達後にもNodeを飛び越えてPodにロードバランシングを行う。
  loadBalancerIP: XXX.XXX.XXX.XXX # 静的に指定することも可能
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30082
  selector:
    app: sample-app
  loadBalancerSourceRanges:
    - 10.0.0.0/8 # LoadBalancerのアクセス制限。Defaultは0.0.0.0/0

```

```shell

kubectl get services # External IPを確認 [ExternalIP] + [NodePort]で接続
```

GCPでは自分が所有していないアドレスはloadBalancerIPとして指定できない。[VPCネットワーク] - [外部IPアドレス]から事前に予約するべし。


### ExternalName Service

```yaml

apiVersion: v1
kind: Service
metadata:
  name: sample-external-name-service
  namespace: default
spec:
  type: ExternalName
  externalName: external.example.com # 外部サービスのCNAME。Clusterから外部サービスへのアクセスの切り替えがKubenetesで切り替えられるようになる。
```

### Ingress

動かない。なぜだろう...?

```yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app


---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sample-ingress
spec:
  rules:
    - host: sample.example.com
      http:
        paths:
          - path: /path1/*
            backend:
              serviceName: sample-nodeport
              servicePort: 8080
          - path: /path2/*
            backend:
              serviceName: sample-nodeport
              servicePort: 8080
  backend:
    serviceName: sample-nodeport
    servicePort: 8080
  # tls:
  #   - hosts:
  #     - sample.example.com
  #       secretName: tls-sample
```

```shell

kubectl get ingresses sample-ingress # 払い出されたIDアドレスの確認
```

### Secret

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-pull-secret
spec:
  containers:
    - name: sample-pull-secret
      image: REGISTRY_NAME/secret-image:latest
      envFrom: # SecretのKeyを全て環境変数として設定
        - secretRef:
            name: sample-db-auth
      env:
        - name: DB_USERNAME
          valueFrom: # Secretから環境変数を作成
            secretKeyRef:
              name: sample-db-auth
              key: username
  imagePullSecrets: # ここで指定してPullできるように
    - name: sample-registry-auth
```

```shell

kubectl create secret generic --save-config sample-db-auth --from-env-file .env # envファイルからSecretを作成
kubectl create secret generic --save-config docker-registry sample-registry-auth --docker-server=REGISTRY_SERVER --docker-username=REGISTRY_USERNAME --docker-password=DOCKER_PASSWORD --docker-email=REGISTRY_USER_EMAIL # PrivateImageをPullするためのSecretを作成
```

```text:.env

username=keijun
userbirthday=12345678
```

### ConfigMap

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-pull-secret
spec:
  containers:
    - name: sample-pull-secret
      image: REGISTRY_NAME/secret-image:latest
      envFrom: # ConfigMapのKeyを全て環境変数として設定
        - configMapRef:
            name: sample-db-auth
      env:
        - name: DB_USERNAME
          valueFrom: # ConfigMapから環境変数を作成
            configMapKeyRef:
              name: sample-db-auth
              key: connection.max
```

```shell

kubectl create configmap --save-config sample-configmap --from-file=./nginx.conf
kubectl get configmaps sample-configmap -o json | jq .data
```

### EmptyDir

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-emptydir
spec:
  containers:
    - image: nginx:1.12
      name: nginx-container
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}
```

### HostPath

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-hostpath
spec:
  containers:
    - image: nginx:1.12
      name: nginx-container
      volumeMounts:
        - mountPath: /etc
          name: cache-volume
  volumes:
    - name: cache-volume
      hostPath:
        path: /etc
        type: DirectoryOrCreate
```

### DownwardAPI

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-downward-api
spec:
  containers:
    - image: nginx:1.12
      name: nginx-container
      volumeMounts:
        - mountPath: /srv
          name: downward-api-volume
  volumes:
    - name: downward-api-volume
      downwardAPI:
        items:
          - path: "podname"
            fieldRef:
              fieldPath: metadata.name
          - path: "cpu-request"
            resourceFieldRef:
              containerName: "nginx-container"
              resource: requests.cpu
```

```shell

kubectl exec -it sample-downward-api ls /srv # 確認
```

### Projected

```yaml

apiVersion: v1
kind: Pod
metadata:
  name: sample-projected
spec:
  containers:
    - image: nginx:1.12
      name: nginx-container
      volumeMounts:
        - name: projected-volume
          mountPath: /srv
  volumes:
    - name: projected-volume
      projected:
        sources:
          - secret:
              name: sample-db-auth
              items:
                - key: username
                  path: secret/username.txt
          - configMap:
              name: sample-cnofigmap
              items:
                - key: nginx.conf
                  path: configmap/nginx.conf
          - downwardAPI:
              items:
                - path: "podname"
                  fieldRef:
                    fieldPath: metadata.name
          
```

```shell

kubectl exec -it sample-projected ls /srv # 確認
```

### PersistVolume

```yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: sample-pv
  labels:
    type: gce-pv
    environment: stg
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce # ReadWriteManyとかもある
  persistentVolumeReclaimPolicy: Retain # Claimの方で利用されなくなった時の挙動とか。Delete、Recycleとかがある。
  storageClassName: manual
  gcePersistentDisk:
    pdName: sample-gce-pv
    fsType: ext4
```

```shell

gcloud compute disks create --size=10GB sample-gce-pv --zone asia-northeast1-a
kubectl get persistentvolumes
```

### PersistentVolumeClaim

```yaml

---
apiVersion: v1
kind: Pod
metadata:
  name: sample-projected
spec:
  containers:
    - image: nginx:1.12
      name: nginx-container
      volumeMounts:
        - name: sample-pvc
          mountPath: /srv
  volumes:
    - name: projected-volume
      persistentVolumeClaim:
        claimName: sample-pvc

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: sample-pv
  labels:
    type: gce-pv
    environment: stg
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: manual
  gcePersistentDisk:
    pdName: sample-gce-pv
    fsType: ext4

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc
spec:
  selector:
    matchLabels:
      type: gce-pv
    matchExpressions:
      - {key: environment, operator: In, values: [stg]}
  resources:
    requests:
      storage: 4Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: manual
```

```shell

kubectl get persistentvolumeclaims
kubectl describe pvc sample-pvc
```

### StorageClass

```yaml

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc-provisioner
  annotations:
    volume.beta.kubernetes.io/storage-class: sample-storageclass
spec:
  resources:
    requests:
      storage: 3Gi
  accessModes:
    - ReadWriteOnce


---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sample-strageclass
parameters:
  type: pd-ssd
provisioner: kubernetes.io/gce-pd
reclaimPolicy: Delete

```

自動でPersistentColumeが作成されない....

### StatefulSet

使うと簡単にPersistentVolumeClaimが設定できそうである。