Knative = Kubernetes Networking++の全訳

Knativeはサーバーレスのビルディングブロックであるという文脈でで説明されることが多く、本来メリットを受けることができるはずのKubernetes上のアプリケーションやマイクロサービスの開発者、運用・管理者などに便利な機能が伝わってない説があったりなかったりするようです。

サーバーレスや抽象化という「言葉から想像される何か」は人により異なり、共通の認識が築かれないまま議論がかわされ、具体的に検証されないのはとてももったいないことです。

以下は[ahmet alp balkan（@ahmetb）](https://twitter.com/ahmetb)さんの[Knative = Kubernetes Networking++](https://ahmet.im/blog/knative-better-kubernetes-networking/)を日本語訳したものです。この記事では、Kubernetes上のマイクロサービスを開発する中で発生する課題をKnativeがいかに解決するかが解説されています。Kubernetes Podcast [#78 KUDO, with Gerred Dillon](https://kubernetespodcast.com/episode/078-kudo/)のNews of the weekでも取り上げられてましたね。

説明に近い内容を日本語で一通り試せるワークショップも作ったのでそちらもよかったら見てみてください。
[build-your-own-platform-with-knative](https://github.com/toshi0607/build-your-own-platform-with-knative)


# Knative = Kubernetes Networking++

[Knativeプロジェクト](https://knative.dev/)は通常、«Kubernetes上のサーバーレス»のための構成要素として説明されます。この意味合いの結果として、ほとんどのKubernetesユーザーは、Knativeが非サーバーレスワークロードに対して何ができるかを認識していません。Knativeは**Kubernetes上のステートレスなマイクロサービスのオートスケールとネットワークを改善します。**

«サーバーレス»をしばらく脇に置いておきましょう。ほとんどのKubernetesユーザーは[12ファクターアップのマニフェスト](https://12factor.net/)に従ってマイクロサービスを書きます。サービスは**ステートレス**で、**リクエスト/RPC**越しに通信すべきです。

Kubernetesはそのような性質のマイクロサービスに対して適したレイヤーで動作しません。組み込みのKubernetesワークロードとネットワーク機能は、マイクロサービスの世界で必要となるいくつかの共通のタスクに不十分です。

* アプリケーションに2つのマニフェスト（Deployment、Service）を書く必要がある
* HTTP/gRPCのリクエスト/RPC単位のロードバランシングがない[^1]
* トラフィック分割がなく、ブルーグリーンデプロイが簡単にできない
* CPU/メモリーベースのオートスケール（たいていスケールが遅く、求めているものではない）
* 同時実行数制御がない（例: Pod毎の最大リクエスト数）

Kubernetesにインストールされた[Knative Serving](https://knative.dev/docs/serving/)はKubernetesのこれらの短所に直接対処します。

* KubernetesのServiceとDeploymentを組み合わせた«Service»オブジェクト
* HTTP/gPRCのリクエスト単位のロードバランシング
* リクエストベースの迅速なオートスケール、素早い反応
* Serviceのリビジョンを利用したトラフィック分割（ブルーグリーンデプロイ）
* 迅速なオートスケールはすぐに使え、高度に設定可能
  * 0 Podへのスケールイン（サスペンド）と0からN Podへのスケールアウト（アクティベーション）も設定できる
  * 同時実行数制御（Pod毎のリクエスト数制限）
* （オプション）HTTPメトリクス（レイテンシー、リクエスト数など）の自動監視サポート
* （オプション）自動TLS証明書と外部エンドポイントの終端

スタックのクリティカルパスにKnativeのような新しいコンポーネントを採用するのは難しい決断です。Knativeプロジェクトには、これについて気持ちを変えるかもしれないいくつかの側面があります。

* Knativeはv1.0間近で«安定»しつつあり、強固なコミュニティがあります。[^2]
* Knativeはモジュール式です。[Serving](https://knative.dev/docs/serving/)コンポーネントのみをインストールできます。
* KnativeはIstioに依存しなくなりました。Knativeはルーティングとトラフィックの分割にロードバランサーを必要としますが、[Gloo](https://docs.solo.io/gloo/latest/installation/knative/)や[Ambassador](https://www.getambassador.io/user-guide/knative/)のような代替手段、または[Kourier](https://github.com/3scale/kourier)などのKnative専用に構築されたゲートウェイプロキシを使用できます。

# Knative «Service» のAPI

KnativeはKubernetes DeploymentとServiceを単一の`Service`タイプに結合します。

ほとんどのステートレスなKubernetesの`Deployment`マニフェストは、`apiVersion` / `kind`を変更し、いくつかの不要な部分をトリミングすることにより、非常に簡単にKnativeに移行できます。

```yaml
# 変更前:
apiVersion: apps/v1
kind: Deployment

# 変更後:
apiVersion: serving.knative.dev/v1
kind: Service
```

Knative `Service`を更新するたびに、新しい`Revision`オブジェクトが作成されます。`Revision`オブジェクトは**イミュータブル**であり、デプロイのスナップショットを強制的に持つことになります。そうすると、`Revision`オブジェクトを使用して、ロールアウト中にトラフィックを分割したり、単純にロールバックしたりできます。

Knative Servingは、開発者が日々直面することのない[他のAPI](https://knative.dev/docs/serving/)を提供します。たとえば、コードと構成を分離する`Configration`などです。（別の12ファクターアップの教え）

# Knativeのオートスケール

開発者として、Kubernetesでマイクロサービスをオートスケールさせることについて頭を悩ませる問題が発生した場合、それには十分な理由があります。

Kubernetesのオートスケールは、主に[Horizo​​ntal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)を使用して行われます。HPAコントローラーはデフォルトでCPU/メモリー使用量（またはカスタムメトリクス）を確認し、長い時間枠で動作します。そのため、突然のトラフィックスパイクへの反応は*遅く*なります。

一方、Knativeのオートスケールは«処理中のリクエスト»（同時実行性）によって駆動されます。サービスが処理できるよりも多くのリクエストを突然受け取った場合、Knativeはより短い時間枠で動作するため、より多くのPodをすばやく追加します。そのため、反応が迅速です。

KnativeはPodに来るすべてのリクエストを把握しています。[^3]Knativeを使用すると、ソフトターゲットである«ターゲット同時実行»レベル、または各Podに送信される最大の実行リクエスト数を保証するハード«同時実行»制限を持つようにアプリを構成できます。並行処理の設定を念頭に置いて、Knativeオートスケーラーは受信リクエストを厳密に監視し、追加するPodの数をすばやく決定します。

アプリがスケールアウトされている間、Knativeは受信リクエストを保持し、リクエストをドロップせずに新しいPodにプロキシします。これはKubernetesでは不可能です。

開発者として、私は長い行のHorizo​​ntalPodAutoscaling[マニフェスト](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics)で«平均70％のCPU使用率をターゲットにし、アプリを1〜30Podの間でスケーリングする»と書くのではなく、1行の構成で«アプリが一度に20リクエストを処理できる»と書ける方が嬉しいです。

さらに、Knativeはしばらくの間トラフィックを受信しないサービスに対して«0 Podへのスケールイン»を提供します（Kubernetesはネイティブにこれを行うことはできません[^4]）。これは、Knative *activator*コンポーネントを使用して実現されます。この機能はデフォルトでサーバーレス方式でオンになっているため、「コールドスタート」が発生します。ただし、簡単にオフにすることができます。

Knativeオートスケーラーの詳細については、[このブログ投稿](https://medium.com/knative/knative-v0-3-autoscaling-a-love-story-d6954279a67a)、[このドキュメント](this doc)、[この例](https://knative.dev/docs/serving/samples/autoscale-go/)を参照してください。

# KubernetesとしてのKnative

Knativeでデプロイされたアプリケーションは、依然としてKubernetes Serviceです。他のKubernetesサービスに接続でき、ネイティブKubernetesサービスディスカバリを使用して接続できます。

Knative ServicesはKubernetes Deploymentとしてデプロイされます。Knative Serviceオブジェクト（[例](https://github.com/knative/docs/blob/768e738764f226da52c450381f4788b4f903343d/docs/serving/samples/secrets-go/service.yaml)）を介して引き続きPodSpecの値を指定できます（環境変数、ボリュームマウント、その他詳細）。

Kubernetes開発者にとって、Knativeの参入障壁は非常に低いです。[Cloud Run on Anthos](https://cloud.google.com/run/)のようなマネージドサービスを使うと、クラウドとオンプレミスのどちらでも、クラスタでマネージドKnativeセットアップを使用できます。

多くのKubernetesユーザーは現在、[Knative](https://knative.dev/)プロジェクトがどのように彼らの役に立つのかを見逃していると思います。このKnativeの説明が気に入ったら教えてください。Knativeについての記事を続けていきたいと思っています。

[^1]: KubernetesはTCPトラフィックのみを認識するため、個々のリクエストについては何も知りません。
[^2]: Google Cloud Platform、Red Hat、IBM CloudはすでにKnativeをサービスとして提供しています。
[^3]: Knativeは、サイドカープロキシ（queue-proxy）を挿入することで同時実行制御を実現します。
[^4]: 同様に、Azureの[Osiris](https://github.com/deislabs/osiris)プロジェクトも0 Podへのスケールインを実現するコントローラーを提供しています。