Knativeはサーバーレスのビルディングブロックであるという文脈でで説明されることが多く、本来メリットを受けることができるはずのKubernetes上のアプリケーションやマイクロサービスの開発者、運用・管理者などに便利な機能が伝わってない説があったりなかったりするようです。
サーバーレスや抽象化という「言葉から想像される何か」は人により異なり、共通の認識が築かれないまま議論がかわされ、具体的に検証されないのはとてももったいないことです。
以下はahmet alp balkan(@ahmetb)さんのKnative = Kubernetes Networking++を日本語訳したものです。この記事では、Kubernetes上のマイクロサービスを開発する中で発生する課題をKnativeがいかに解決するかが解説されています。Kubernetes Podcast #78 KUDO, with Gerred DillonのNews of the weekでも取り上げられてましたね。
説明に近い内容を日本語で一通り試せるワークショップも作ったのでそちらもよかったら見てみてください。
build-your-own-platform-with-knative
Knative = Kubernetes Networking++
Knativeプロジェクトは通常、«Kubernetes上のサーバーレス»のための構成要素として説明されます。この意味合いの結果として、ほとんどのKubernetesユーザーは、Knativeが非サーバーレスワークロードに対して何ができるかを認識していません。KnativeはKubernetes上のステートレスなマイクロサービスのオートスケールとネットワークを改善します。
«サーバーレス»をしばらく脇に置いておきましょう。ほとんどのKubernetesユーザーは12ファクターアップのマニフェストに従ってマイクロサービスを書きます。サービスはステートレスで、リクエスト/RPC越しに通信すべきです。
Kubernetesはそのような性質のマイクロサービスに対して適したレイヤーで動作しません。組み込みのKubernetesワークロードとネットワーク機能は、マイクロサービスの世界で必要となるいくつかの共通のタスクに不十分です。
- アプリケーションに2つのマニフェスト(Deployment、Service)を書く必要がある
- HTTP/gRPCのリクエスト/RPC単位のロードバランシングがない1
- トラフィック分割がなく、ブルーグリーンデプロイが簡単にできない
- CPU/メモリーベースのオートスケール(たいていスケールが遅く、求めているものではない)
- 同時実行数制御がない(例: Pod毎の最大リクエスト数)
KubernetesにインストールされたKnative 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コンポーネントのみをインストールできます。
- KnativeはIstioに依存しなくなりました。Knativeはルーティングとトラフィックの分割にロードバランサーを必要としますが、GlooやAmbassadorのような代替手段、またはKourierなどのKnative専用に構築されたゲートウェイプロキシを使用できます。
Knative «Service» のAPI
KnativeはKubernetes DeploymentとServiceを単一のService
タイプに結合します。
ほとんどのステートレスなKubernetesのDeployment
マニフェストは、apiVersion
/ kind
を変更し、いくつかの不要な部分をトリミングすることにより、非常に簡単にKnativeに移行できます。
# 変更前:
apiVersion: apps/v1
kind: Deployment
# 変更後:
apiVersion: serving.knative.dev/v1
kind: Service
Knative Service
を更新するたびに、新しいRevision
オブジェクトが作成されます。Revision
オブジェクトはイミュータブルであり、デプロイのスナップショットを強制的に持つことになります。そうすると、Revision
オブジェクトを使用して、ロールアウト中にトラフィックを分割したり、単純にロールバックしたりできます。
Knative Servingは、開発者が日々直面することのない他のAPIを提供します。たとえば、コードと構成を分離するConfigration
などです。(別の12ファクターアップの教え)
Knativeのオートスケール
開発者として、Kubernetesでマイクロサービスをオートスケールさせることについて頭を悩ませる問題が発生した場合、それには十分な理由があります。
Kubernetesのオートスケールは、主にHorizontal Pod Autoscalerを使用して行われます。HPAコントローラーはデフォルトでCPU/メモリー使用量(またはカスタムメトリクス)を確認し、長い時間枠で動作します。そのため、突然のトラフィックスパイクへの反応は遅くなります。
一方、Knativeのオートスケールは«処理中のリクエスト»(同時実行性)によって駆動されます。サービスが処理できるよりも多くのリクエストを突然受け取った場合、Knativeはより短い時間枠で動作するため、より多くのPodをすばやく追加します。そのため、反応が迅速です。
KnativeはPodに来るすべてのリクエストを把握しています。3Knativeを使用すると、ソフトターゲットである«ターゲット同時実行»レベル、または各Podに送信される最大の実行リクエスト数を保証するハード«同時実行»制限を持つようにアプリを構成できます。並行処理の設定を念頭に置いて、Knativeオートスケーラーは受信リクエストを厳密に監視し、追加するPodの数をすばやく決定します。
アプリがスケールアウトされている間、Knativeは受信リクエストを保持し、リクエストをドロップせずに新しいPodにプロキシします。これはKubernetesでは不可能です。
開発者として、私は長い行のHorizontalPodAutoscalingマニフェストで«平均70%のCPU使用率をターゲットにし、アプリを1〜30Podの間でスケーリングする»と書くのではなく、1行の構成で«アプリが一度に20リクエストを処理できる»と書ける方が嬉しいです。
さらに、Knativeはしばらくの間トラフィックを受信しないサービスに対して«0 Podへのスケールイン»を提供します(Kubernetesはネイティブにこれを行うことはできません4)。これは、Knative activatorコンポーネントを使用して実現されます。この機能はデフォルトでサーバーレス方式でオンになっているため、「コールドスタート」が発生します。ただし、簡単にオフにすることができます。
Knativeオートスケーラーの詳細については、このブログ投稿、[このドキュメント](this doc)、この例を参照してください。
KubernetesとしてのKnative
Knativeでデプロイされたアプリケーションは、依然としてKubernetes Serviceです。他のKubernetesサービスに接続でき、ネイティブKubernetesサービスディスカバリを使用して接続できます。
Knative ServicesはKubernetes Deploymentとしてデプロイされます。Knative Serviceオブジェクト(例)を介して引き続きPodSpecの値を指定できます(環境変数、ボリュームマウント、その他詳細)。
Kubernetes開発者にとって、Knativeの参入障壁は非常に低いです。Cloud Run on Anthosのようなマネージドサービスを使うと、クラウドとオンプレミスのどちらでも、クラスタでマネージドKnativeセットアップを使用できます。
多くのKubernetesユーザーは現在、Knativeプロジェクトがどのように彼らの役に立つのかを見逃していると思います。このKnativeの説明が気に入ったら教えてください。Knativeについての記事を続けていきたいと思っています。