riff と Knative による サーバレス(FaaS)環境を Minikube 上に作ってみた

## 説明
Google Cloud Next '18 で Kubernetes を拡張してサーバレス環境(FaaS環境)を作る事ができる **Knative** が発表されましたね。
- [Googleによる紹介サイト](https://cloud.google.com/knative/)：https://cloud.google.com/knative/
- [Pivotalによる紹介サイト](https://pivotal.io/knative)：https://pivotal.io/knative/

 この Knative 上にオープンソースの FaaSツールである **[riff](https://projectriff.io)** 環境を作ってみます。

### Knative 概要
Knative は以下の３つのコンポーネントから成っていて、それぞれのコンポーネントが疎結合的にそれぞれの機能を担っています。
- Knative-Serving
- Knative-Build
- Knative-Eventing

#### Knative-Serving
Serving は、Kubernetes と Istio に作られ、サーバレスアプリケーションを稼働させます。
オートスケールし、使われない時には ゼロスケールします。

#### Knative-Build
Build は、ソースリポジトリ(GitHubなど)からソースを取得し、依存関係の取得、テスト実行、コンテナイメージのビルドを行います。その後、コンテナレジストリへのPushや、Kubernetesクラスタへのデプロイを行います。

#### Knative-Eventing
Pub/Sub デスティネーションや、KafkaやRabbitMQなどのバスといった様々なデータソースから、イベントストリームを Consume (または Publish) します。

## 前提
- 実行環境：macOS
- 仮想環境：HyperKit
- Kubernetes環境：Minikube

```
$ minikube version
minikube version: v0.28.2
```

## 手順
構築手順は、大きく次のようになります。

1. Kubernetes クラスタ (Minikube クラスタ)の作成
2. riff CLI のインストール
3. Knative のインストール
4. Kubernetes Secret の作成・適用
5. ファンクションの作成

### 1. Kubernetes クラスタ (Minikube クラスタ)の作成

以下のコマンドで Minikube クラスタを作成します。ここでは、前提に書いているように hyperkit を仮想環境に指定していますが(`--vm-driver=hyperkit`)、virtualbox や vmwarefusion など指定しても大丈夫でしょう。（動作確認はしてませんが。）

```sh
$ minikube start --memory=8192 --cpus=4 \
--kubernetes-version=v1.10.5 \
--vm-driver=hyperkit \
--bootstrapper=kubeadm \
--extra-config=controller-manager.cluster-signing-cert-file="/var/lib/localkube/certs/ca.crt" \
--extra-config=controller-manager.cluster-signing-key-file="/var/lib/localkube/certs/ca.key" \
--extra-config=apiserver.admission-control="LimitRanger,NamespaceExists,NamespaceLifecycle,ResourceQuota,ServiceAccount,DefaultStorageClass,MutatingAdmissionWebhook"
```

実行後、以下のようにpod が見えてきます。

```sh
$ kubectl get pod --all-namespaces

NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   etcd-minikube                           1/1       Running   0          11s
kube-system   kube-addon-manager-minikube             1/1       Running   0          1m
kube-system   kube-apiserver-minikube                 0/1       Pending   0          7s
kube-system   kube-controller-manager-minikube        1/1       Running   0          44s
kube-system   kube-dns-86f4d74b45-z76kh               3/3       Running   0          1m
kube-system   kube-proxy-4mzh7                        1/1       Running   0          1m
kube-system   kube-scheduler-minikube                 1/1       Running   0          32s
kube-system   kubernetes-dashboard-5498ccf677-kt285   1/1       Running   0          1m
kube-system   storage-provisioner                     1/1       Running   0          1m
```

### 2. riff CLI のインストール

riff CLI をインストールします。(バージョン：0.1.0 as of 2018/07/30)

```sh
$ curl -LO https://github.com/projectriff/riff/releases/download/v0.1.0/riff-darwin-amd64.tgz \
&& tar xvzf riff-darwin-amd64.tgz \
&& sudo mv riff /usr/local/bin/
```

```sh
$ riff version
Version
  riff cli: 0.1.0
```

### 3. Knative のインストール

```sh
$ riff system install --node-port
```

※ `Waiting for istio-sidecar-injector to start .Error: exit status 1` にようにエラーが出る事があるのですが、その場合は一度 minikbe クラスタを停止し(`qinikube stop`)、開始し直すとインストールできます。

```sh
$ riff system install --node-port
Installing Istio: https://storage.googleapis.com/riff-releases/istio-riff-0.1.0.yaml
Istio for riff installed

Waiting for istio-sidecar-injector to start .'Running'

Installing Knative Serving: https://storage.googleapis.com/riff-releases/release-no-mon-riff-0.1.0.yaml
Knative Serving for riff installed

Installing Knative Eventing: https://storage.googleapis.com/riff-releases/release-eventing-riff-0.1.0.yaml
Knative Eventing for riff installed

Applying Stub ClusterBus resource: https://storage.googleapis.com/riff-releases/release-eventing-clusterbus-stub-riff-0.1.0.yaml
clusterbus.channels.knative.dev "stub" created

riff system install completed successfully
```

```sh
$ kubectl get pod --all-namespaces                                                                                                                                                                                         

NAMESPACE          NAME                                       READY     STATUS      RESTARTS   AGE
istio-system       istio-citadel-7bdc7775c7-llxxq             1/1       Running     2          28m
istio-system       istio-cleanup-old-ca-v4m4b                 0/1       Completed   0          28m
istio-system       istio-egressgateway-795fc9b47-2476t        1/1       Running     3          28m
istio-system       istio-ingress-84659cf44c-tj9s6             1/1       Running     3          28m
istio-system       istio-ingressgateway-7d89dbf85f-l6j2s      1/1       Running     3          28m
istio-system       istio-mixer-post-install-d9ndt             0/1       Completed   0          28m
istio-system       istio-pilot-66f4dd866c-m86fs               2/2       Running     6          28m
istio-system       istio-policy-76c8896799-pzxmb              2/2       Running     4          28m
istio-system       istio-sidecar-injector-645c89bc64-5twvv    1/1       Running     8          28m
istio-system       istio-statsd-prom-bridge-949999c4c-czbsf   1/1       Running     2          28m
istio-system       istio-telemetry-6554768879-cmhj8           2/2       Running     4          28m
istio-system       knative-ingressgateway-5f5dc4b4cd-2nbwt    1/1       Running     1          10m
knative-build      build-controller-5cb4f5cb67-5stsw          1/1       Running     1          10m
knative-build      build-webhook-6b4c65546b-pdg4m             1/1       Running     3          10m
knative-eventing   controller-manager-7747d66d85-b4tv5        2/2       Running     2          2m
knative-eventing   eventing-controller-6cd984f789-m26hh       1/1       Running     0          2m
knative-eventing   eventing-webhook-7dfd9cfbd9-wrdgz          1/1       Running     0          2m
knative-eventing   stub-clusterbus-866c95f68d-qk9km           2/2       Running     0          2m
knative-serving    activator-7f5b67b69c-zztzj                 2/2       Running     0          2m
knative-serving    controller-868ff6d485-7frqr                1/1       Running     0          2m
knative-serving    webhook-6d9976c74f-gv7jg                   1/1       Running     0          2m
kube-system        etcd-minikube                              1/1       Running     0          4m
kube-system        kube-addon-manager-minikube                1/1       Running     2          31m
kube-system        kube-apiserver-minikube                    1/1       Running     0          4m
kube-system        kube-controller-manager-minikube           1/1       Running     0          4m
kube-system        kube-dns-86f4d74b45-2vmns                  3/3       Running     8          32m
kube-system        kube-proxy-nxxp5                           1/1       Running     0          3m
kube-system        kube-scheduler-minikube                    1/1       Running     2          31m
kube-system        kubernetes-dashboard-5498ccf677-vvqd6      1/1       Running     6          32m
kube-system        storage-provisioner                        1/1       Running     6          32m
```

### 4. Kubernetes Secret の作成・適用
DockerHubにコンテナイメージをpushするために使用する認証情報をKubernetes Secretで作成します。

以下のファイルを作成します。

```sh
$ vim push-cred.yaml
```

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: push-credentials
  annotations:
    build.knative.dev/docker-0: https://index.docker.io/v1/
type: kubernetes.io/basic-auth
data:
  username: <BASE64エンコードしたDockerHubユーザID>
  password: <BASE64エンコードしたDockerHubパスワード>
```

作成したファイルからsecretの定義情報を反映します。

```sh
$ kubectl apply -f push-cred.yaml
```

定義した secret を使用して namespace を初期化します。
ここでは、namespace を `default` としています。

```sh
$ riff namespace init default --secret push-credentials
```

### 5. Function の作成
Functionのサンプルとして、与えた数値を二乗する関数を Node.js で用意しました。

```js
module.exports = x => x ** 2;
```

上記のJSファイルをGitリポジトリに入れておきます。
ここでは、GitHubを使って以下のリポジトリとしました。

`https://github.com/shinyay/node-func-square.git`

最後に riff で Function を作成します。

```sh
$ riff function create node square \
  --git-repo https://github.com/shinyay/node-func-square.git \
  --artifact square.js \
  --image <DockerHubのユーザID>/node-func-square
```

上記コマンドを実行すると、DockerHub には、`<DockerHubのユーザID>/node-func-square` という名前でイメージが登録されます。
また、square-xxxxxという名前でPodが Minikubeクラスタ上に構成されます。

```sh
$ kubectl get pod --all-namespaces

NAMESPACE          NAME                                       READY     STATUS      RESTARTS   AGE
default            square-00001-deployment-696bf6c86b-fxkdr   3/3       Running     0          22s
default            square-00001-q4p97                         0/1       Completed   0          8m
istio-system       istio-citadel-7bdc7775c7-bfnww             1/1       Running     2          28m
istio-system       istio-cleanup-old-ca-4zmxq                 0/1       Completed   0          28m
istio-system       istio-egressgateway-795fc9b47-nl6g9        1/1       Running     3          28m
istio-system       istio-ingress-84659cf44c-nk7sp             1/1       Running     3          28m
istio-system       istio-ingressgateway-7d89dbf85f-vldmg      1/1       Running     3          28m
istio-system       istio-mixer-post-install-6ttqf             0/1       Completed   0          29m
istio-system       istio-pilot-66f4dd866c-26c9v               2/2       Running     4          28m
istio-system       istio-policy-76c8896799-f89zq              2/2       Running     4          28m
istio-system       istio-sidecar-injector-645c89bc64-cvnqj    1/1       Running     8          28m
istio-system       istio-statsd-prom-bridge-949999c4c-hjbkc   1/1       Running     2          28m
istio-system       istio-telemetry-6554768879-hn5c5           2/2       Running     4          28m
istio-system       knative-ingressgateway-5f5dc4b4cd-mwz4m    1/1       Running     2          24m
knative-build      build-controller-5cb4f5cb67-f7hwk          1/1       Running     1          25m
knative-build      build-webhook-6b4c65546b-f8dq8             1/1       Running     4          25m
knative-eventing   controller-manager-7747d66d85-k465k        2/2       Running     8          24m
knative-eventing   eventing-controller-6cd984f789-kstc5       1/1       Running     0          24m
knative-eventing   eventing-webhook-7dfd9cfbd9-nxw72          1/1       Running     1          24m
knative-eventing   stub-clusterbus-866c95f68d-dkwnc           2/2       Running     0          21m
knative-serving    activator-7f5b67b69c-b88xf                 2/2       Running     1          24m
knative-serving    controller-868ff6d485-rd7xs                1/1       Running     0          24m
knative-serving    square-00001-autoscaler-7c64497c6c-zn8qt   2/2       Running     0          22s
knative-serving    webhook-6d9976c74f-lmqvs                   1/1       Running     2          24m
kube-system        etcd-minikube                              1/1       Running     0          22m
kube-system        kube-addon-manager-minikube                1/1       Running     2          29m
kube-system        kube-apiserver-minikube                    1/1       Running     0          22m
kube-system        kube-controller-manager-minikube           1/1       Running     0          22m
kube-system        kube-dns-86f4d74b45-5ngd8                  3/3       Running     7          30m
kube-system        kube-proxy-vvxh7                           1/1       Running     0          21m
kube-system        kube-scheduler-minikube                    1/1       Running     2          29m
kube-system        kubernetes-dashboard-5498ccf677-xk7rd      1/1       Running     5          30m
kube-system        storage-provisioner                        1/1       Running     5          30m
```

### 6. Function の実行

以下のcurlコマンドを実行して、Functionを実行します。
MinikubeのIPアドレスは、`minikube ip` コマンドで確認できます。

```sh
$ curl \
       -w '\n' \
       -H 'Host: square.default.example.com' \
       -H 'Content-Type: text/plain' \
       http://<MinikubeのIPアドレス>:32380 \
       -d 5
25
```

Function に対して 数値 `5` を与えています。すると、結果として 二乗された値として `25` が返ってきます。

また、しばらく放置しておくと使用していないFunctionのPodが自動で終了します。

```sh
$ kubectl get pod --all-namespaces

NAMESPACE          NAME                                       READY     STATUS        RESTARTS   AGE
default            square-00001-deployment-696bf6c86b-fxkdr   0/3       Terminating   0          5m
```

終了している状態で、再度 Function を呼び出すと、Pod が再作成されて機能するようになります。

### 7. Function 及び Minikube クラスタのクリーンアップ

Function のクリーンアップは以下のコマンドで行います。
ここでは、Function名は、`square` です。

```sh
$ riff service delete <Function名>
```

Minikube クラスタのクリーンアップは、以下のコマンドで行います。

```sh
$ minikube delete
```

## まとめ
riff と Knative で簡単に FaaS 環境ができてしまいました。

今回は、riff のサイトにも掲載されているシンプルな Node.js のアプリを使用してみましたが、Java などの他の言語でも Function を作ってみようと思います。

### 今回のソース
- [https://github.com/shinyay/node-func-square](https://github.com/shinyay/node-func-square)