zlabjp/kubernetes-resource を使う

# 概要

今日は、[zlabjp/kubernetes-resource](https://github.com/zlabjp/kubernetes-resource) を実際に使うというのをやります。kubernetes-resource は、k8s にアプリケーションをデプロイするための concourse リソースです。ゼットラボで必要に迫られて作って、それから便利に使っています。@superbrothers による[初日の記事](https://qiita.com/superbrothers/items/1f95fda4631a21020ef5)の通りです。ツールというのは、あれこれ触ってみると理解が進みます。手元に k8s クラスタと concourse があって、GitHub と Docker Hub を利用できる環境であれば、同じことができるはずです。よかったら一緒にやりましょう。

# やること

あらかじめ k8s に nginx-1.13.6 をデプロイしておいて、concourse から nginx-1.13.7 にアップデートします。あらかじめの方は、手元の PC から個人の権限でデプロイします。アップデートの方は、GitHub リポジトリの Tag の作成をトリガーにして、concourse から ServiceAccount の権限でデプロイします。k8s と kubectl のバージョンは、ともに v1.7.11 を使います。大きくは、準備して実行するだけです。

# 準備

GitHub → k8s → concourse の順に準備します。

## GitHub

今回は、`bhiro/myproject` というリポジトリを新しく Private で作りました。README.md もリポジトリと一緒に作りました。これを手元の PC に git clone します。

```bash
$ git clone git@github.com:bhiro/myproject.git
$ cd myproject
```

このリポジトリを concourse から Read するための Deploy key を作成します。Deploy Key の実体は、SSH キーペアです。このキーペアは、concourse が使います。パスフレーズは設定してはいけません。`no passphrase` でないと concourse のジョブが失敗します。作成した鍵は、secrets ディレクトリに保存します。

```bash
$ mkdir secrets
$ ssh-keygen -t rsa -b 4096 -C "" -f secrets/id_rsa
```

公開鍵は、GitHub リポジトリの Deploy Keys に Read only の設定で登録します。秘密鍵は、あとで concourse パイプラインに登録します。

```
secrets
├── id_rsa
└── id_rsa.pub
```

secrets ディレクトリは、誤って Git にコミットしないように、`.gitignore` に書いておきます。

```
/secrets
```

## k8s

定義ファイルを作ってから、k8s リソースを作ります。

### 定義ファイル

k8s の定義ファイルを作ります。作り終えると、手元の PC のディレクトリは、以下のようになります。

```
myproject
├── .git
├── .gitignore
├── README.md
├── deploy
│   └── nginx-deploy.yaml
├── k8s
│   ├── 0_ns.yaml
│   ├── role.yaml
│   ├── rolebinding.yaml
│   └── sa.yaml
└── secrets
    ├── id_rsa
    └── id_rsa.pub
```

k8s ディレクトリには、クラスタ向けの定義ファイルを置きます。deploy ディレクトリには nginx 向けの定義ファイルを置きます。以下は、定義ファイルの一覧です。

| 定義ファイル | リソース種別 | リソース名 |
| --- | --- | --- | --- |
| k8s/ns.yaml | Namespace | dev |
| k8s/sa.yaml |Service Account | deploy |
| k8s/role.yaml | Role | deploy |
| k8s/rolebinding.yaml | RoleBinding | deploy |
| deploy/nginx-deploy.yaml | Deployment | nginx |

ファイルの内容は以下の通りです。

dev ネームスペースです。

```yaml:k8s/ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dev
```

deploy という ServiceAccount と Role を作って、それらをバインドします。これらのリソースは、dev ネームスペースに置きます。

```yaml:k8s/sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: deploy
  namespace: dev
```

```yaml:k8s/role.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: deploy
  namespace: dev
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["extensions"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["autoscaling"]
  resources: ["*"]
  verbs: ["*"]
```

```yaml:k8s/rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: deploy
  namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: deploy
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:serviceaccount:dev:deploy
  namespace: dev
```

nginx-1.13.6 をデプロイするための、Deployment です。

```yaml:deploy/nginx-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.13.6
        name: nginx
```

### リソース

kubectl コマンドを使って、手元の PC から k8s リソースを作ります。先にクラスタ向けのリソースを作ります。

```bash
$ kubectl apply -f k8s/
```

それから、nginx 向けのリソースを作ります。

```bash
$ kubectl apply -f deploy/ --namespace=dev
```

すでに定義ファイルがあるので簡単ですね。
pod が Running で nginx-1.13.6 のイメージなら OK です。

```bash
$ kubectl get pod --namespace=dev
NAME                     READY     STATUS    RESTARTS   AGE
nginx-1161159442-nlvzx   1/1       Running   0          5m

$ kubectl describe pod nginx-1161159442-nlvzx --namespace=dev |grep 'Image:'
Image:          nginx:1.13.6
```

## concourse

### 定義ファイル

concourse の定義ファイルを作ります。作り終えると、手元の PC のディレクトリは、以下のようになります。

```
myproject
├── .git
├── .gitignore
├── README.md
├── concourse
│   └── pipeline.yaml
├── deploy
│   └── nginx-deploy.yaml
├── k8s
│   ├── 0_ns.yaml
│   ├── role.yaml
│   ├── rolebinding.yaml
│   └── sa.yaml
└── secrets
    ├── concourse-secrets.yaml
    ├── id_rsa
    └── id_rsa.pub
```

concourse ディレクトリにパイプライン向けの定義ファイルを置きます。秘密情報は、Git にコミットされないように secrets ディレクトリに置きます。以下は、定義ファイルの一覧です。

| 定義ファイル |
| --- |
| concourse/pipeline.yaml |
| secrets/concourse-secrets.yaml |

pipeline.yaml の内容は以下の通りです。

最初に kubernetes-resource の定義があります。`tag` は、デプロイ先の k8s のマイナーバージョンに合わせてセットします。今回のデプロイ先は k8s-v1.7.11 なので、`tag: "1.7"` としています。`{{key}}` と `{{config}}` は秘密情報です。パイプライン作成時に、concourse-secrets.yaml から展開します。

```yaml:concourse/pipeline.yaml
resource_types:
- name: kubernetes
  type: docker-image
  source:
    repository: zlabjp/kubernetes-resource
    tag: "1.7"

resources:
- name: myproject
  type: git
  source:
    uri: git@github.com:bhiro/myproject.git
    branch: master
    tag_filter: v*
    private_key: {{key}}

- name: myk8s
  type: kubernetes
  source:
    kubeconfig: {{config}}

jobs:
- name: deploy
  serial: true
  plan:
  - get: myproject
    trigger: true
  - put: myk8s
    params:
      kubectl: apply -f myproject/deploy --namespace=dev
      wait_until_ready_selector: app=nginx
```

次に、concourse-secrets.yaml を作ります。

concourse-secrets.yaml には、GitHub リポジトリを Read するための Deploy Key と、k8s クラスタを操作するための kubeconfig を記入します。Deploy key には、GitHub の準備で作成した SSH キーペアの秘密鍵を使います。

```
$ cat secrets/id_rsa > secrets/concourse-secrets.yaml
$ chmod 600 secrets/concourse-secrets.yaml
```

kubeconfig は、[zlabjp/kubernetes-scripts](https://github.com/zlabjp/kubernetes-scripts) に含まれる create-kubeconfig というツールを使ってジェネレートします。create-kubeconfig の引数は ServiceAccount 名です。k8s の準備で dev ネームスペースに作成した deploy サービスアカウント を渡します。

```bash
$ curl -LO https://raw.githubusercontent.com/zlabjp/kubernetes-scripts/master/create-kubeconfig
$ chmod +x ./create-kubeconfig
$ mv ./create-kubeconfig /tmp/
$ /tmp/create-kubeconfig deploy --namespace=dev >> secrets/concourse-secrets.yaml
```

key と config をキーとして yaml に整形します。ここは、エディタの矩形編集を使って頑張ります。

```yaml:secrets/concourse-secrets.yaml
key: |
  -----BEGIN RSA PRIVATE KEY-----
  ....
  ....
  -----END RSA PRIVATE KEY-----

config: |
  apiVersion: v1
  ....
  ....
  token: ....
```

これで、全ての定義ファイルが揃いました。最後に GitHub に Push しておきます。

```bash
$ git add -A
$ git commit -m "create definition files"
$ git push origin master
```

### パイプライン

fly コマンドを使って、手元の PC から concourse のパイプラインを作ります。`${URL}` と `${TEAM}` は、環境に合わせてセット下さい。ターゲット名とパイプライン名は任意です。今回はそれぞれ dev, myproject としています。

```bash
$ fly --target dev login --concourse-url ${URL} --team-name ${TEAM}
$ fly --target dev set-pipeline --pipeline myproject --config concourse/pipeline.yaml --load-vars-from secrets/concourse-secrets.yaml
$ fly --target dev unpause-pipeline --pipeline myproject
```

すでに定義ファイルがあるので簡単ですね。
UI や コマンドラインからパイプラインが確認できれば OK です。

```bash
$ fly --target dev pipelines
```

# 実行

実行は、準備よりだいぶ簡単です。GitHub リポジトリ の Tag の作成をトリガーにして、k8s 上の nginx-1.13.6 を concourse から nginx-1.13.7 にアップデートします。

nginx-deploy.yaml を以下のように変更します。

```diff
git diff deploy/nginx-deploy.yaml
diff --git a/deploy/nginx-deploy.yaml b/deploy/nginx-deploy.yaml
index ec17270..4a8c9a9 100644
--- a/deploy/nginx-deploy.yaml
+++ b/deploy/nginx-deploy.yaml
@@ -14,5 +14,5 @@ spec:
         app: nginx
     spec:
       containers:
-      - image: nginx:1.13.6
+      - image: nginx:1.13.7
         name: nginx
```

コミットして push します。

```bash
$ git add deploy/nginx-deploy.yaml
$ git commit -m "update nginx"
$ git push origin master
```

最後に Tag を切ります。

```bash
$ git tag v0.0.1
$ git push origin v0.0.1
```

これを、トリガーに concourse の job が動きます。

```
$ fly --target dev builds|grep myproject
2674  myproject/deploy 1   succeeded  2017-12-06@20:04:26+0900  2017-12-06@20:04:48+0900  22s
```

22秒で終わりました。成功したようです。UI も job の成功を表す緑色になっています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96933/f3f53175-0954-caf5-f222-d052d7e39d4b.png)

k8s を確認します。

```bash
$ kubectl get pod --namespace=dev
NAME                     READY     STATUS    RESTARTS   AGE
nginx-3944497639-tzk71   1/1       Running   0          3m

$ kubectl describe pod nginx-3944497639-tzk71 --namespace=dev |grep 'Image:'
    Image:          nginx:1.13.7
```

確かに、nginx-1.13.7 にアップデートされています。

# まとめ

今日は、[zlabjp/kubernetes-resource](https://github.com/zlabjp/kubernetes-resource) を使って、concourse から k8s へのデプロイをやりました。CI や CD の準備というのは大変なものですが、組み終わって、動き出すと、なんとも気持ちのいいものです。ぜひ、お試しください。

# おわりに

このエントリは、弊社 Z Lab のメンバーによる [Z Lab Advent Calendar 2017](https://qiita.com/advent-calendar/2017/zlab) の十一日目として業務時間中に書きました。十二日目は @TakanariKo による OpenFaaS のお話です。