KnativeをIBM Cloud上で触ってみる 【前編】

ほんと久しぶりの投稿です。

社内のタスクで、Knativeの技術調査をこの間まで実施していました。せっかくなので、備忘録として作業ログを何回かに分けて書いていきます。

# Knativeとは
Google社やPivotal社、IBMが中心となって開発している、Kubernetesを基盤とした、Serverlessプラットフォーム構築のためのビルディング・ブロックです。ざっくりと言って、以下の3つの要素から構成されます：

- Builds
    - イメージのビルドに必要な仕組みを提供する
- Serving
    - リクエスト量に応じた動的なスケールアウト/インの仕組みの提供
    - Istioを活用した賢いルーティングの仕組みの提供
- Eventing
    - FaaSが必要とするイベント駆動のための仕組みを提供

<img width="401" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/101771/906fa8e2-ad32-c8a7-c761-e51941781127.png">

Knativeが出てきた背景などは以下のリンクの記事が詳しいので、是非目を通していただければと思います。自分も最初何度も読みました。

[最強のServerlessプラットフォーム？ Knativeを動かしてみるぞい](https://qiita.com/jacopen/items/d7f12703dcfa19242bcb)

# Knativeってどこで使われてる?
例えば、IBMがオープンソースとして寄与したApache OpenWhiskはKnativeを基盤に据える新アーキテクチャーが検討されています。プロジェクトKwskという名前です。(にしても「くわしく」ってw)

[Kwsk (Apache OpenWhisk on Knative)](https://github.com/projectodd/kwsk)

また、Riffと呼ばれるKubernetesのためのFaaS構成基盤もKnativeベースで構成されるオープンソースです。RiffをベースにPivotal社はPivotal Function Servicesのアルファ版を2018年12月前半に公開しています。

[riff is for functions](https://projectriff.io/)

他にも、CI/CDツールのJenkins XもKnativeを活用した構成をとるようです。

[Jenkins X で実現するサーバーレスJenkins](https://cloudbees.techmatrix.jp/serverless-jenkins-jenkins-x/)

調べると色々あるもんですね。。他にもあるのかな?

# Knative環境を用意しよう
では、実際にどんな環境なのかを構成して確認してみます。今回は普段使っているIKS (IBM Cloud Kubernetes Service)で構成してみます。
ただ、Knativeそのままだとやや使いにくい印象もあるので、今回はknctlコマンドライン・インターフェースを利用して導入・操作する手順をまとめておきます。

- [Knative Install on IBM Cloud Kubernetes Service (IKS)](https://github.com/knative/docs/blob/master/install/Knative-with-IKS.md)
- [Introducing Knctl: A simpler way to work with Knative](https://developer.ibm.com/blogs/2018/11/12/knctl-a-simpler-way-to-work-with-knative/)

> 注意
>
> - knctlコマンドライン・ツールは2018年12月時点ではKnative公式のコマンドライン・インターフェースではありません。
> - 今回の手順で導入されるKnativeに関して、Eventingは未導入です。

## 0. コマンドライン・インターフェースを準備する
まずはIBM Cloudを操作するためのコマンドライン・インターフェースを導入します。
すでに導入済みの人は良いですが、導入済みでない場合は以下のリンクを参考に導入し、ibmcloud loginでIBM Cloudにログインしておきます。

[IBM Cloud資料 - CLI と API のセットアップ](https://console.bluemix.net/docs/containers/cs_cli_install.html#cs_cli_install_steps)

kubectlコマンドライン・インターフェースも以下のリンクを参考に導入しておきます。

[Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)


## 1. Kubernetesクラスターを構成する
IBM Cloudのコンソール画面でも導入できますが、面倒なのでコマンドラインで導入します。以下は全てMac OSにて実施したログですので、Windowsの場合は参考程度に読み替えて実施してみてください。

まずはIBM Cloud上で利用可能なKubernetesのバージョンを`ibmcloud ks kube-versions`で確認します。

```
$ ibmcloud ks kube-versions
OK
バージョン
1.9.11 (推奨されておらず、13 日後にはサポート対象外)
1.10.11 (default)
1.11.5
1.12.3
バージョン間の差分を評価するには、https://console.bluemix.net/docs/containers/cs_versions.html#cs_versions を参照してください
```

上記より今回はデフォルトの1.10.11で導入します。

> 備考
> 1.11以降、コンテナー実行基盤がDockerdからContainerdに変更されています。
それが原因なのかはちゃんと調べきれてませんが、Istioに付属するPrometheus Serverの導入に失敗します(2018年12月14日確認時点)。
IBM CloudでKnativeを触ってみたい方は一旦1.10で試してもらった方が良いかもしれません。

続いて、デプロイ先のリージョンの指定やクラスター名を変数として設定します。
以下はIBM Cloudの東京リージョンにあるゾーンtok02を利用する例です。先ほど調べたバージョンは`CLUSTER_K8S_VERSION`に設定します。クラスター名は`knative`とします。

```
export CLUSTER_NAME=knative
export CLUSTER_REGION=ap-north
export CLUSTER_ZONE=tok02
export CLUSTER_K8S_VERSION=1.10.13
```

`ibmcloud ks region-set`で操作対象のリージョンをap-north、つまりは東京に切り替えます。`OK`と表示されたら先に進みます。

```
ibmcloud ks region-set $CLUSTER_REGION
```

デプロイの準備ができたので、`ibmcloud ks cluster-create`コマンドでクラスターを作成します。マシンタイプに関しては、今回はKnativeのドキュメントにある最小要件`b2c.4x16`(4 vCPU, 16GBメモリーの共有仮想サーバー)のワーカーノードを3台構成します。

```
ibmcloud ks cluster-create --name=$CLUSTER_NAME \
  --zone=$CLUSTER_ZONE \
  --kube-version=$CLUSTER_K8S_VERSION \
  --machine-type=b2c.4x16 \
  --workers=3
```

しばらく待てばクラスターが作成され、利用可能になります。`ibmcloud ks clusters`で一覧表示し、状態が`normal`になっていることを確認しましょう。

```
$ ibmcloud ks clusters
OK
名前      ID           状態     作成日時         ワーカー   ロケーション   バージョン     リソース・グループ名   
knative   xxxxxxx      normal   52 minutes ago   3          Tokyo          1.10.11_1536   default  
```

> 注意
> 最小要件を満たすクラスターを構成するためには利用しているIBM Cloudアカウントはクレジット登録しないとオーダーできませんのでご注意ください。今回の構成の場合、1時間ごとに約0.88ドルかかります。

## 2. Knative (Serving, Builds)を導入する
ここから本題のKnativeを導入していきます。
今回はKnctlコマンドライン・インターフェースで導入するのでシンプルです。

まずは`ibmcloud ks cluster-config <クラスター名>`でkubectlコマンドライン・インターフェースでアクセスするための構成情報をダウンロードします。

```
$ ibmcloud ks cluster-config knative
OK
knative の構成は正常にダウンロードされました。

環境変数をエクスポートして Kubernetes の使用を開始してください。

export KUBECONFIG=/Users/xxxx/.bluemix/plugins/container-service/clusters/knative/kube-config-tok02-knative.yml
```

表示された`export ~`の部分をターミナルで実行し、`kubectl get node`などでワーカーノード数などを確認し、問題なくアクセスできていることを確認します。

そうしたら、`knctl install`を実行し、Knativeを導入します。特にエラーなく、`Succeeded`と表示されればまずはOKです。

```
$knctl install
Installing Istio from 'https://github.com/knative/serving/releases/download/v0.2.1/istio.yaml'

namespace "istio-system" created
configmap "istio-galley-configuration" created
configmap "istio-statsd-prom-bridge" created
configmap "istio-security-custom-resources" created
configmap "istio" created
:
(中略)
:
Non-ready pods: activator-db79694db-bc2bv, autoscaler-86d954bffc-pwzjp

Non-ready pods: activator-db79694db-bc2bv, autoscaler-86d954bffc-pwzjp

Non-ready pods: autoscaler-86d954bffc-pwzjp

Non-ready pods: autoscaler-86d954bffc-pwzjp

Succeeded
```

念のため、ダウンしているPodがいないか`kubectl get pods --all-namespaces`で確認します。

```
$kubectl get po --all-namespaces
NAMESPACE            NAME                                                              READY     STATUS      RESTARTS   AGE
ibm-system           ibm-cloud-provider-ip-161-202-148-10-f67dc95c4-pvm8x              1/1       Running     0          3h
ibm-system           ibm-cloud-provider-ip-161-202-148-10-f67dc95c4-z6j62              1/1       Running     0          3h
ibm-system           ibm-cloud-provider-ip-161-202-148-13-8655c5cd99-nr2pp             1/1       Running     0          3h
ibm-system           ibm-cloud-provider-ip-161-202-148-13-8655c5cd99-w6zns             1/1       Running     0          3h
ibm-system           ibm-cloud-provider-ip-161-202-148-14-5f9f5696db-fq4d2             1/1       Running     0          4h
ibm-system           ibm-cloud-provider-ip-161-202-148-14-5f9f5696db-qq7nn             1/1       Running     0          4h
istio-system         istio-citadel-84fb7985bf-tk76n                                    1/1       Running     0          3h
istio-system         istio-cleanup-secrets-kqkt2                                       0/1       Completed   0          3h
istio-system         istio-egressgateway-bd9fb967d-dqzxr                               1/1       Running     0          3h
istio-system         istio-galley-655c4f9ccd-ps4xs                                     1/1       Running     0          3h
istio-system         istio-ingressgateway-688865c5f7-jxk5f                             1/1       Running     0          3h
istio-system         istio-pilot-6cd69dc444-6cvwk                                      2/2       Running     0          3h
istio-system         istio-policy-6b9f4697d-p5r7n                                      2/2       Running     0          3h
istio-system         istio-sidecar-injector-8975849b4-sr5zl                            1/1       Running     0          3h
istio-system         istio-statsd-prom-bridge-7f44bb5ddb-7px64                         1/1       Running     0          3h
istio-system         istio-telemetry-6b5579595f-7nrpw                                  2/2       Running     0          3h
istio-system         istio-telemetry-6b5579595f-9bmkb                                  2/2       Running     0          3h
istio-system         istio-telemetry-6b5579595f-lhnwm                                  2/2       Running     0          2h
istio-system         knative-ingressgateway-76b7677587-cltpn                           1/1       Running     0          3h
istio-system         zipkin-7f4b7b4988-2jnkj                                           1/1       Running     0          3h
knative-build        build-controller-747b8fd966-6f8nw                                 1/1       Running     0          3h
knative-build        build-webhook-6dc78d8f6d-p64f9                                    1/1       Running     0          3h
knative-monitoring   elasticsearch-logging-0                                           1/1       Running     0          3h
knative-monitoring   elasticsearch-logging-1                                           1/1       Running     0          3h
knative-monitoring   grafana-798cf569ff-xc4gc                                          1/1       Running     0          3h
knative-monitoring   kibana-logging-6bf7d8784d-d5jgk                                   1/1       Running     0          3h
knative-monitoring   kube-state-metrics-6d99fddb8d-lhbg4                               4/4       Running     0          3h
knative-monitoring   node-exporter-kw7jc                                               2/2       Running     0          3h
knative-monitoring   node-exporter-prlbs                                               2/2       Running     0          3h
knative-monitoring   node-exporter-ts6t5                                               2/2       Running     0          3h
knative-monitoring   prometheus-system-0                                               1/1       Running     0          3h
knative-monitoring   prometheus-system-1                                               1/1       Running     0          3h
knative-serving      activator-6cd459647f-p6bfk                                        2/2       Running     0          3h
knative-serving      activator-6cd459647f-sxhhd                                        2/2       Running     0          3h
knative-serving      activator-6cd459647f-v27kw                                        2/2       Running     0          3h
knative-serving      autoscaler-75b64694b-nxxpx                                        2/2       Running     0          3h
knative-serving      controller-54ffbcdfd4-kbmzq                                       1/1       Running     0          3h
knative-serving      webhook-5955555748-lmkvj                                          1/1       Running     0          3h
kube-system          calico-kube-controllers-6b79555b7-n99xt                           1/1       Running     0          4h
kube-system          calico-node-fl7g5                                                 2/2       Running     0          4h
kube-system          calico-node-v8rss                                                 2/2       Running     0          4h
kube-system          calico-node-vnl8m                                                 2/2       Running     0          4h
kube-system          heapster-74547548dd-kwcdt                                         2/2       Running     0          4h
kube-system          ibm-file-plugin-54b4ff5b8d-cdf7b                                  1/1       Running     0          4h
kube-system          ibm-keepalived-watcher-hrrs2                                      1/1       Running     0          4h
kube-system          ibm-keepalived-watcher-lsr2r                                      1/1       Running     0          4h
kube-system          ibm-keepalived-watcher-psmjl                                      1/1       Running     0          4h
kube-system          ibm-kube-fluentd-thsxp                                            1/1       Running     0          4h
kube-system          ibm-kube-fluentd-vjww7                                            1/1       Running     0          4h
kube-system          ibm-kube-fluentd-znpdn                                            1/1       Running     0          4h
kube-system          ibm-master-proxy-static-10.129.180.104                            2/2       Running     0          4h
kube-system          ibm-master-proxy-static-10.129.180.76                             2/2       Running     0          4h
kube-system          ibm-master-proxy-static-10.129.180.78                             2/2       Running     0          4h
kube-system          ibm-storage-watcher-65c9657c94-rvshv                              1/1       Running     0          4h
kube-system          kube-dns-amd64-84454d78cf-dg7fn                                   3/3       Running     0          4h
kube-system          kube-dns-amd64-84454d78cf-j4f8n                                   3/3       Running     0          4h
kube-system          kube-dns-autoscaler-87c768b59-vs9xt                               1/1       Running     0          4h
kube-system          kubernetes-dashboard-66ddf6bfd-q2kbd                              1/1       Running     0          4h
kube-system          public-cr8ab804382e104dd5a576c00c2a8c9c9e-alb1-67d987f5f9-5ncxk   4/4       Running     0          4h
kube-system          public-cr8ab804382e104dd5a576c00c2a8c9c9e-alb1-67d987f5f9-m95z5   4/4       Running     0          4h
kube-system          vpn-7747668c4b-bxq7x                                              1/1       Running     0          4h
```

## Knativeを試す (ざっくりと動作確認)
重要な機能やIBM Cloud固有の使い方の話は別途記載するとして、ざっくりとアプリがデプロイできるところを確認します。

`knctl deploy`コマンドでKnativeのサンプル・イメージを用いてアプリをデプロイします。

```
$ knctl deploy --service hello \
     --image gcr.io/knative-samples/helloworld-go \
     --env TARGET=Taro

Name  hello  

Waiting for new revision to be created...

Tagging new revision 'hello-00001' as 'latest'

Tagging new revision 'hello-00001' as 'previous'

Annotating new revision 'hello-00001'

Waiting for new revision 'hello-00001' to be ready for up to 5m0s (logs below)...

hello-00001 > hello-00001-deployment-5b596c876c-6rxf7 | 2018/12/17 15:41:05 Hello world sample started.

Revision 'hello-00001' became ready

Continuing to watch logs for 5s before exiting

Succeeded
```

デプロイされたアプリケーションはKnativeとしてはServiceと呼ばれる単位で管理されます。`knctl service list`でKnative Serviceの一覧を確認できます。

```
$ knctl service list
Services in namespace 'default'

Name   Domain                     Annotations  Conditions  Age  
hello  hello.default.example.com  -            3 OK / 3    2m  

1 services

Succeeded
```

Kubernetesの視点で見ると、アプリケーションはDeploymentでデプロイされています。

```
$ kubectl get deployment
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-00001-deployment   1         1         1            1           2m

$ kubectl get pods -n default
NAMESPACE            NAME                                                              READY     STATUS      RESTARTS   AGE
default              hello-00001-deployment-5b596c876c-6rxf7                           3/3       Running     0          2m
```

ここで、5分ほど放置すると、上記で確認したPodは削除されているのが確認できると思います。Servingのゼロ・スケーリングが発動したためです。

```
$kubectl get po 
No resources found.
```

利用していない時はリソースの無駄遣いをしないようにするためにPodを落とす、なかなかに素敵な動きをします。この状態でアプリケーションにアクセスするとどうなるでしょうか? `knctl curl --service <Knative Service名>`でアクセスしてみます。

```
$knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Taro!

Succeeded
```

体感的にやや時間がかかりましたが、きちんと応答が返ってきました。もう一度Podを確認すると、別のPodが作成されて稼働しているのが確認できます。

```
$kubectl get po 
NAME                                      READY     STATUS    RESTARTS   AGE
hello-00001-deployment-5b596c876c-n682h   3/3       Running   0          18s
```

次にアプリケーションを更新します。再び`knctl deploy`コマンドでデプロイします。
Knative Serviceであるhelloのリビジョンが上がったことが確認できます。

```
$ knctl deploy --service hello \
      --image gcr.io/knative-samples/helloworld-go \
      --managed-route=false \
      --env TARGET=Jiro
     
Name  hello  

Waiting for new revision (after revision 'hello-00001') to be created...

Tagging new revision 'hello-00002' as 'latest'

Tagging older revision 'hello-00001' as 'previous'

Annotating new revision 'hello-00002'

Waiting for new revision 'hello-00002' to be ready for up to 5m0s (logs below)...

hello-00002 > hello-00002-deployment-66dccfb4c5-fhbdm | 2018/12/17 16:06:49 Hello world sample started.

Revision 'hello-00002' became ready

Continuing to watch logs for 5s before exiting

Succeeded
```
再びアプリケーションに対しデプロイすると、結果が変わっていることが確認できます。
(環境変数`env`に合わせて出力が変わっていることが確認できます。)

```
$knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!

Succeeded
```

もう一度、デプロイしてみます。

```
$ knctl deploy --service hello \
      --image gcr.io/knative-samples/helloworld-go \
      --managed-route=false \
      --env TARGET=Saburo

Name  hello  

Waiting for new revision (after revision 'hello-00002') to be created...

Tagging new revision 'hello-00003' as 'latest'

Tagging older revision 'hello-00002' as 'previous'

Annotating new revision 'hello-00003'

Waiting for new revision 'hello-00003' to be ready for up to 5m0s (logs below)...

hello-00003 > hello-00003-deployment-f956864b4-wg8hp | 2018/12/17 16:21:59 Hello world sample started.

Revision 'hello-00003' became ready

Continuing to watch logs for 5s before exiting

Succeeded
```

そして`knctl rollout`コマンドを実行します。下記の例はlatest(envがSaburo)のリビジョンとprevious(envがJiro)のリビジョンのルーティング比を20%, 80%に指定しています。すなわち5回に1回は「Hello Saburo!」、他は「Hello Jiro!」と結果が帰るようなルーティング動作がこのコマンドで実現できます。

```
$knctl rollout --route hello -p hello:latest=20% -p hello:previous=80% -n default
Succeeded
```

数回アクセスしてみましたが、以下の結果を見る限りだいたい合ってそうですね。いつも綺麗な結果が出るわけではないですが、こうしたロールアウトの設定を少しずつ変更してカナリア・リリースを実現できます、ということですね。

```
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!

Succeeded
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!

Succeeded
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!

Succeeded
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!

Succeeded
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Saburo!

Succeeded
$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.default.example.com' 'http://161.202.103.219:80'

Hello Jiro!
```

# 終わりに
まずは導入編ということで、IBM CloudのIKS(IBM Cloud Kubernetes Service)でのKnativeのセットアップと簡単な動作確認を行いました。特にServingの動作をざっくり見てみたわけですが、Buildsの利用については別記事で整理したいと思います。