Kubernetes+kustomizeでmasterをスマートにさっとデプロイする

# 前提とゴール
- Kubernetesでのデプロイはなんとなくわかる  
DeploymentとServiceのyaml用意して、kubectl applyすればいいんでしょ？  
くらいで大丈夫です
- Github-Flowをしたい  
masterがマージされたら、production(staging)に即デプロイしたい
- yaml内にあるdocker imageのtagをスマートに書き換えたい  
置換用の文字を埋め込んでsedしたくない
- (ついでに) production, staging, development, localのyamlの差異をスマートに管理したい  
localだけ完全に別yaml、とか辞めたい  
**kustomizeを使う理由です**

# (Kubernetes) kustomizeについて
## 概要
(細かくは触れないのでググっていただくとして)
Kubernetesのyamlをいい感じにマージできます
超簡単に、基本的な設定と、共通の設定、環境ごとの設定を、コマンドでマージしてapplyできます

ほかの選択肢としてHelmがありますが、
kustomizeのほうが、Helmよりも気軽に使える、という認識です


## (2019/07) 2つあるので、超注意すること
1. 元々開発されていた、単体のコマンドとして `kustomize ...` で動くkustomize  
いろいろできる
2. kubectlコマンドに統合された `kubectl kustomize ...` のkustomize  
`kustomize build ...` 相当のことしかできないので、 **今回は使わない**

設定に関しては同じ項目が設定できる、はず(細かくは見切れていない)
https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#kustomize-feature-list
https://github.com/kubernetes-sigs/kustomize/blob/master/docs/fields.md

統合については、きちんと状態を追えていないので、よしなにご判断ください


# ディレクトリ構成
botを作った時の構成なので、serviceは含んでいません
適宜読み替えてお願いします

```
/ops/k8s
├── manifest
│   └── .gitignore
├── base
│   ├── deployment.yaml
│   └── kustomization.yaml
└── overlays
    ├── local
    │   ├── build.sh
    │   └── kustomization.yaml
    └── production
        ├── build.sh
        ├── deployment.yaml
        └── kustomization.yaml
```

ディレクトリ構成はこちらを参考に
https://github.com/kubernetes-sigs/kustomize/blob/master/README.md
baseとoverlaysでわけ、overlays内に環境(production, staging, ...)を置くようです
manifestは、マージ後のyaml確認用フォルダとして置いているので、なくても大丈夫です

## /ops/k8s/manifest
フォルダは管理対象にするが、フォルダ内のファイル管理は不要

```/ops/k8s/manifest/.gitignore
*
!.gitignore
```

## /ops/k8s/base
一見いろいろ足りてないですが、これで大丈夫です
最終的には、ほかの設定とマージしていい感じにします

```/ops/k8s/base/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-name
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: container-name
          image: app-image:latest
```

```/ops/k8s/base/kustomization.yaml
commonLabels:
  app: app-label
resources:
  - deployment.yaml
```

## /ops/k8s/overlays/production
yamlでは、docker imageもtagも **指定しません**

```/ops/k8s/overlays/production/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-name
spec:
  template:
    spec:
      imagePullSecrets:
        - name: secret-name
      containers:
        - name: container-name
          imagePullPolicy: Always
```

```/ops/k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- deployment.yaml
images:
- name: app-image
```

/ops/k8s/overlays/production/build.shを実行すると、
新しいdocker imageとtagで、/ops/k8s/overlays/production/kustomization.yamlが更新されます
これを用いて、CircleCIにてdocker imageとtagをスマートに差し替えます

```/ops/k8s/overlays/production/build.sh
#!/bin/sh
cd `dirname $0`

IMAGE_NAME="$1"
kustomize edit set image app-image="${IMAGE_NAME}"
kustomize build . > ../../manifest/prd-k8s.yaml
```

## /ops/k8s/overlays/local
詳細は割愛しますが、productionと似たような感じです

## /.circleci/config.yml
抜粋して書いていきます
全てのブランチでビルドまでしますが、デプロイはmasterのみです
リリースタグを切った時だけ、特定の環境にデプロイ、などもここでトリガーを書きます

```
workflows:
  version: 2
  build_and_deploy:
    jobs:
      - build:
          filters:
            branches:
              only: /.*/
      - deploy:
          requires:
            - build
          filters:
            branches:
              only: master
```

ビルドジョブの中で、gitのハッシュ値(CIRCLE_SHA1)でdocker tagを切り、
`kubectl apply ...` で新しいmasterを参照しなおすようにします

```
version: 2
jobs:
  (省略)
  deploy:
    (省略)
    steps:
      (省略)
      - run:
          name: docker build
          command: docker build -t ${APP_NAME}:${CIRCLE_SHA1} ops/app
      - run:
          name: docker registry push
          command: docker push ${APP_NAME}:${CIRCLE_SHA1}
      - run:
          name: k8s releasse
          command: |
            ./ops/k8s/overlays/production/build.sh "${APP_NAME}:${CIRCLE_SHA1}"
            kubectl apply -f ./ops/k8s/manifest/prd-k8s.yaml
```

# 残課題
## docker registryに参照されないimageが大量に増える
あとで調べてやります

## local、めんどくさい
kustomizeでは、どういう構成にするのがいいのか、については触れてませんが、
どうするのがいいのかというのは、いろいろ考えられます

  - localのvolumeを、k8sでmountしてアプリを参照する
  - docker imageはproductionに(近い)registryに置く
  - docker imageはlocalでdocker buildしたimageを使う

それに、アプリのビルドをして、docker imageのビルドをして、k8sにapplyして、、、
という一連の作業もめんどくさいです
これについてはskaffoldで改善させる予定です
https://qiita.com/yakisuzu/items/caf5557ba059bb88f0fe