Knativeに触れることでKubernetesを勉強する

## サーバーレス気になる人間

はじめまして。サーバーレスが気になって夜も6時間しか眠れないサーバーレス気になる人間「ながの」と申します。

転職してきて8ヶ月、今までやったことがない事を多くやらせてもらって刺激的な毎日を過ごしています。

が、せっかくのアドベントカレンダーなので、仕事でもやったことないことに触れていこうと思い、題材に[Knative](https://knative.dev/)を選択しました。よろしければ、ご一緒にKnativeの知識を深めていきましょう。

## なぜ[Knative](https://knative.dev/)か？

<img width="1109" alt="Knative002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415280/b92e87c2-ece6-e83a-6f7f-5e2b1aeb0612.png">

なぜKnativeの知識を深めたいのか？これには明確な理由があります。

それは`Kubernetes`を学びたいからです。

えっ？だったらKubernetes勉強すればいいんじゃない？

はい。確かにそうなんです。が、今からKubernetesを勉強します！って言うのも出遅れた感が出すぎてしまって恥ずかしいですよね、、、加えて公私ともにKubernetes使うシチュエーションに出会っていないので、なかなか勉強する気持ちがわかないんですよね、、、

そこでKnativeです。(Knativeであれば、今更感はまだそこまでないですよね？？）

それにKnativeは何やらKubernetes上でサーバーレスな仕組みを構築できる、なんていう代物のようです。そうなんです！`Kubernetes上にサーバーレスな仕組み`なので、Knativeを学ぶためにはKubernetesも知らないといけないんですよね。

そうなってくると、Knativeを触ってみればKubernetesもサーバーレスも理解できる！一石二鳥です。（とか思ったのが誤りで、Knativeも難しれば、Kubernetesも難しいという末路が待っていたことに、このときの私は気付きようもなかった、、、）

ということで私はKnativeを選択したというわけです。

## まずはKnativeの知識を仕入れる

そもそもKnativeってなに？ってことになるかと思いますので、確認していきましょう。

参考にさせていただいたのは以下。全部メルペイの杉田さん([About Me – DoorLog](https://toshi0607.com/about/))のものです。

- [Kubernetes・Serverlessとの出会いと、Knative入門 - ログミーTech](https://logmi.jp/tech/articles/321365)

- [Knativeの歩き方 KubernetesからServerlessを訪ねて 第2版 #技術書典 - カエルと空 - BOOTH](https://toshi0607.booth.pm/items/1309468)

- [Knativeソースコードリーディング入門　Knativeで学ぶKubernetesのカスタムリソースとカスタムコントローラ - BOOTH](https://toshi0607.booth.pm/items/1568456)

私のKnativeの知識の120％は杉田さんのアウトプットからできています。

## で、結局Knativeとは何なのか？

最初は単純にKnativeってAWS LambdaのようなFaaSをAWS以外でも構築するためのOSSでしょって思ってました。

ところが色々と調べていくうちにそれだけではない、ということが分かりました。

誤解を恐れずに言えば、`Kubernetesを使って実現しようとしたことをさらに効率よく実現するためのオプション的なものがKnativeである`と私は理解しました。

ではKubernetesを使って実現しようとしたこととはなんでしょうか？

これは私のようなKubernetes未経験者でも、ちょっとKubernetesを気にしてセミナーに通っているような人であれば、セミナーでよく耳にするアレです。こんな事ができるからKubernetesって凄いんだよって紹介されているアレです。

- コンテナを使ったマイクロサービス
- ブルーグリーンデプロイメント
- カナリアリリース
- オートスケール
- ゼロスケール

Kubernetesでは上記なようなことが実現できるようになる、とはよく言われることですよね。

でもどうやら上記のようなことをKubernetesだけで実現するとなると結構大変な部分もあるようなんです。

私なんて最初はKubernetesを使うだけで上記に列挙したようなCloudNativeなことがすぐにできちゃんだろうな、すごいなあって思っていたのですが、Kubernetesが抽象化したのはインフラ部分のようで、Kubernetes上で作られたサービス(インフラより上層にあるサービス)が、CICDを行い、ブルーグリーンデプロイメントを実現したり、負荷が高まった時にオートスケールしたり、はたまたアクセスがない時はリソース節約のためにゼロスケールしたり、というようなことは割と手間をかけて実現しないといけなかったという事実があるようです。

そこで、せっかくKubernetesでインフラを抽象化して、インフラの細かくて面倒なことは気にしなくても、いい感じのリソースを利用できるようになったのだから、そこから先（インフラより上層のサービス）もいい感じに抽象化して、深いことを考えずにCloudNativeらしいことをしたいよね、ってことで出てきたのがKnativeだということらしいんです！！(諸説ありますが、私の理解です)

なので、杉田さんの本にも以下のように記載されています

> Knativeはつぎのような、コンテナアプリケーションを開発する上で必ずとおるが難しい課題を解決します
> - コンテナの迅速なデプロイ
> - ソースコードからURLをアクセスできるアプリケーションへ
> - ブルー/グリーンデプロイを伴うルーティングとトラフィック管理
> - オートスケーリングと需要に基づくワークロードのサイズ設定
> - 実行中のサービスをイベントエコシステムに結び付ける

ということで、少しずつKnativeの実態がどんなものなのか分かってきましたね。

## ひとまずKnativeを動かす

では、ひとまずKnativeを動かす環境をつくりましょう。

参考にさせて頂いたのは、こちらも杉田さんがServerlessDays Tokyo 2019で実施していたワークショップです。

- [GitHub - toshi0607/build-your-own-platform-with-knative: Knativeのコンポーネントを理解しながらFaaSプラットフォームをDIYするワークショップです](https://github.com/toshi0607/build-your-own-platform-with-knative)
  
今回は「クラスタの構築」と「各コンポーネントの理解」のServingのみだけですが試してみます。

### GCPを利用

KubernetesはGKEで作成します。(さっそくKubernetesの勉強になってますね)やり方はワークショップ通りで私は問題なく進めることができました。

[build-your-own-platform-with-knative/step0.md](https://github.com/toshi0607/build-your-own-platform-with-knative/blob/master/step0.md)

軽く補足すると(補足なので基本はワークショップの通り進めてくださいね)

- GCPでプロジェクトを作成
- Google Cloud Shellを起動
- 以下の3つの環境変数をセット
  - GCPプロジェクトのID
  - Kubernetesクラスタのクラスタ名
  - ゾーン
      - `GCPプロジェクトのID`はプロジェクト名と間違わないように！プロジェクト情報から見れます。
       ![knative001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415280/49615bbc-bcc1-1768-ef0d-48d54ce97d52.png)

- GCPプロジェクトIDをセット
- GKEとGCRを有効化
- Kubernetesクラスタの作成

Kubernetesクラスタの作成コマンドはこんな感じです。

```
gcloud beta container clusters create $CLUSTER_NAME \
  --addons=HorizontalPodAutoscaling,HttpLoadBalancing,Istio \
  --machine-type=n1-standard-2 \
  --cluster-version=latest --zone=$CLUSTER_ZONE \
  --enable-stackdriver-kubernetes --enable-ip-alias \
  --enable-autoscaling --min-nodes=1 --max-nodes=5 \
  --enable-autorepair \
  --scopes cloud-platform
```
確認します  

```
$ kubectl get nodes
NAME                                     STATUS   ROLES    AGE     VERSION
gke-knative-default-pool-743e9df0-8bg4   Ready    <none>   5m54s   v1.14.8-gke.17
gke-knative-default-pool-743e9df0-mbk3   Ready    <none>   5m53s   v1.14.8-gke.17
gke-knative-default-pool-743e9df0-z9bm   Ready    <none>   5m53s   v1.14.8-gke.17
```
おお！簡単にできました。Kubernetesクラスタがあっという間にできちゃいました。

次にここにKnativeをインストールしていきます。

```
$ kubectl apply --selector knative.dev/crd-install=true \
   --filename https://github.com/knative/serving/releases/download/v0.9.0/serving.yaml \
   --filename https://github.com/knative/eventing/releases/download/v0.9.0/release.yaml \
   --filename https://github.com/knative/serving/releases/download/v0.9.0/monitoring.yaml
```

むむ。`--selector knative.dev/crd-install=true`の意味はなんでしょうか？これはKnativeをKubernetes上で利用するためにカスタムリソースとして事前に登録しているコマンドのようです。Kubernetesはこんな感じでいろんなカスタムをすることで機能を追加していくんですね。

参考：[CRDについてのメモ – repl.info](https://repl.info/archives/2384/)

そして事前準備が完了したので、実際にKnativeのリソースを作ります。

```
$ kubectl apply --filename https://github.com/knative/serving/releases/download/v0.9.0/serving.yaml \
   --filename https://github.com/knative/eventing/releases/download/v0.9.0/release.yaml \
   --filename https://github.com/knative/serving/releases/download/v0.9.0/monitoring.yaml
```

確認しましょう。おお、全部うごいてます！！

```
$ kubectl get pods --namespace knative-serving
NAME                                READY   STATUS    RESTARTS   AGE
activator-68d9f95cd-qx4jk           2/2     Running   3          3m59s
autoscaler-5655c9fcfd-mp74z         2/2     Running   1          3m58s
autoscaler-hpa-8668fc6f68-97krf     1/1     Running   0          3m59s
controller-5b77c5596c-85hr2         1/1     Running   0          3m57s
networking-istio-6d7d44d879-f78mb   1/1     Running   0          3m56s
webhook-75b4fc9999-m62gq            1/1     Running   0          3m56s
```

```
$ kubectl get pods --namespace knative-eventing
NAME                                  READY   STATUS    RESTARTS   AGE
eventing-controller-db9b58855-v69nz   1/1     Running   0          4m21s
eventing-webhook-595c6b4fd8-gj2qh     1/1     Running   0          4m20s
imc-controller-7b9b7f9f66-4p78h       1/1     Running   0          4m19s
imc-dispatcher-775c96b5b5-rwzvl       1/1     Running   0          4m19s
sources-controller-78655cd9f9-dzvkn   1/1     Running   0          4m21s
```

```
$ kubectl get pods --namespace knative-monitoring
NAME                                  READY   STATUS    RESTARTS   AGE
elasticsearch-logging-0               1/1     Running   0          4m44s
elasticsearch-logging-1               1/1     Running   0          3m33s
fluentd-ds-fdcgj                      1/1     Running   0          4m43s
fluentd-ds-nkqjf                      1/1     Running   0          4m43s
fluentd-ds-wczz5                      1/1     Running   0          4m43s
grafana-85c86fb7b9-tndgp              1/1     Running   0          4m39s
kibana-logging-7cb6b64bff-wdjp8       1/1     Running   0          4m43s
kube-state-metrics-56f68467c9-t864j   4/4     Running   0          3m24s
node-exporter-k6drr                   2/2     Running   0          4m40s
node-exporter-qlh4q                   2/2     Running   0          4m40s
node-exporter-txv9k                   2/2     Running   0          4m40s
prometheus-system-0                   1/1     Running   0          4m37s
prometheus-system-1                   1/1     Running   0          4m37s
```
これでKubernetesクラスタとKnativeのインストールが完了です。次にワークショップの「各コンポーネントの理解」の中の「Serving」を試しみましょう。

## Serving

ここからワークショップ的には[build-your-own-platform-with-knative/step1.md](https://github.com/toshi0607/build-your-own-platform-with-knative/blob/master/step1.md)に入っていきます。

で、そもそもServingとは？ってことですが、Knativeの3大要素のうちの1つです。

- Serving
- Evneting
- Build（これはTektonとして独立したのでKnativeから外れた？）

そのうちの`Serving`の役割とは以下のようなものらしいです。

> オートスケールアウト・イン、トラフィックコントロール、バージョン管理などです。Servingは4つのコンポーネントから構成されます。
> ・Configuration: 最新の設定
> ・Revision: コードと設定の履歴
> ・Route: Revisionへのルーティング
> ・Service: RouteとConfigurationから構成されるサービス全体

うーん。なんとなくしか分からない、、、って感じなので、実際にServingを動かしてみましょう。

以下のように`serving.yaml`を作成します。

```
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: "gcr.io/knative-samples/helloworld-go"
            env:
            - name: "TARGET"
              value: "Knative serving"
```
そして実行します。

```
$ kubectl apply -f service.yaml
```

確認します

```
$ kubectl get ksvc,configuration,route
NAME                                     URL                                     LATESTCREATED      LATESTREADY        READY   REASON
service.serving.knative.dev/helloworld   http://helloworld.default.example.com   helloworld-w89zj   helloworld-w89zj   True
NAME                                           LATESTCREATED      LATESTREADY        READY   REASON
configuration.serving.knative.dev/helloworld   helloworld-w89zj   helloworld-w89zj   True
NAME                                   URL                                     READY   REASON
route.serving.knative.dev/helloworld   http://helloworld.default.example.com   True
```
これでKnativeのServingのコンポーネントである`service,configuration,route`ができたってことですね。下の図の赤枠で囲んだものができたものです。

<img width="733" alt="Screen Shot 2019-12-03 at 23.35.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415280/eef448a0-d2bf-a80a-25b9-32ad03a02b82.png">

参照元：https://github.com/knative/docs/tree/master/docs/serving

で、これで何ができるようになったの？ってなりますよね。なので、以下の確認をしていきます。

```
# IPアドレスの取得
$ export IP_ADDRESS=$(kubectl get svc istio-ingressgateway --namespace istio-system --output 'jsonpath={.status.loadBalancer.ingress[0].ip}')

# curlでリクエスト
$ curl -H "Host: helloworld.default.example.com" http://$IP_ADDRESS
Hello Knative serving!
```

`Hello Knative serving!`が無事に出力されました。

## これの何が凄いの？？

無事に`Hello Knative serving!`が出力されましたが、これの何が凄いのでしょうか？

まず最初に上記の15行くらいのyamlを実行しただけでKnativeが動くようになったということが挙げられます。

そして2つ目にKnativeのオブジェクトだけでなく、`組み込みのKubernetesオブジェクトまで動いている`というのが凄い点です。KnativeがKubernetes上にサーバーレスな仕組みを構築するものなので、当然といえば当然ですが、Kubernetesを意識することなくKubernetesのメリットを利用できるというのは凄いことなのではないでしょうか。

では、どんなKubernetesオブジェクトができているのか確認してみましょう。

```
$ kubectl get pod
NAME                                           READY   STATUS    RESTARTS   AGE
helloworld-t75c2-deployment-54755c9bb5-tf6vl   2/2     Running   0          42s
```
```
$ kubectl get replicaset
NAME                                     DESIRED   CURRENT   READY   AGE
helloworld-t75c2-deployment-54755c9bb5   1         1         1       21m
```
```
$ kubectl get deployment
NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
helloworld-t75c2-deployment   1/1     1            1           21m
```
```
$ kubectl get service
NAME                       TYPE           CLUSTER-IP   EXTERNAL-IP                                            PORT(S)             AGE
helloworld                 ExternalName   <none>       cluster-local-gateway.istio-system.svc.cluster.local   <none>              20m
helloworld-t75c2           ClusterIP      10.0.0.176   <none>                                                 80/TCP              21m
helloworld-t75c2-6l49j     ClusterIP      10.0.13.34   <none>                                                 80/TCP,8022/TCP     21m
helloworld-t75c2-metrics   ClusterIP      10.0.2.146   <none>                                                 9090/TCP,9091/TCP   21m
kubernetes                 ClusterIP      10.0.0.1     <none>                                                 443/TCP             45m
```
おおー。確かにKubernetesのオブジェクトが動いています。15行くらいのyamlを実行しただけなのに色々と動いてますね。

実はKubernetesの組み込みオブジェクトだけでこれと同じことをしようとすると結構な手間らしいんです。

- [Migrating from Kubernetes Deployment to Knative Serving](https://medium.com/google-cloud/migrating-from-kubernetes-deployment-to-knative-serving-bdc45ef1bb9e)

本当にそうなの？

と疑問に思ってくればしめたものです！本当に手間がかかるかの確認のために、Kubernetesの`Pod,ReplicaSet,Deplyment,ingress`などを調べて実際に動かしてみればいいのですから。こうしてKubernetesについて色々と知る必要がでてきました。

自分なりに少し調べてみた感じですと、まずKubernetesクラスタ内にコンテナをたてる部分、そしてそのコンテナに外部からアクセスする部分、それらを作成するだけでも15行以上のyamlを書く必要があるということです。そこにさらに外部からアクセスができるようになっている状況が15行のyamlで作成できてしまうというのは、凄いことなのかなと思っています。

## まとめ

いかがだったでしょうか？

KnativeをきっかけにしてKubernetesに興味を持とうという主旨は伝わりましたでしょうか。個人的にはこのやり方は割といいんじゃないかと思っています。それにKnativeのワークショップが充実しているので、そこを紐解いていけばKubernetesについても詳しくなれる！そんな気がしています。

今回はServingだけにしか触れていない(というか勉強できていない)ので、これからKnativeの他の部分についても触れていくことで、Kubernetesの勉強のきっかけを多く作って、Kubernetesの理解を深めていきたいと思います。