オープンソースのサーバーレスコンテナフレームワークを解読する: イベント駆動型

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/decipher-the-open-source-serverless-container-framework-event-driven_602042?spm=a2c65.11461433.0.0.4ee35355zOUPsx)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

# イベント駆動型アーキテクチャとKnative Eventingの概要

## 著者: Yuanyi

### イベント駆動型の紹介
イベント駆動型（Event-driven）とは、分散システムにおける通信モデルの一種で、コンポーネント間の相互作用が直接的なリクエスト・レスポンス形式ではなく、イベント通知に基づいて行われるものです。非同期通信と疎結合が特徴です。EDA（Event-Driven Architecture）では、コンポーネントはイベントの発行と購読を通じて協力します。これらのイベントには、ユーザーの操作、システム状態の変化、センサーデータなどが含まれます。

### クラウドネイティブなサーバーレスイベント駆動フレームワーク：Knative Eventing
Knativeは、Kubernetesクラスターを基盤とするオープンソースのサーバーレスフレームワークで、サーバーレスアプリケーション向けにクラウドネイティブかつクロスプラットフォームのオーケストレーション標準を提供します。サーバーレスアーキテクチャにおける重要なイベント駆動機能として、Knative Eventingはクラウドネイティブなイベント駆動能力を提供します。Knative Eventingは独立したプラットフォームであり、標準的なKubernetesサービスやKnative Servingサービスなど、さまざまなワークロードをサポートします。HTTP POSTリクエストを使用してイベントプロデューサーとレシーバー間でイベントを送受信します。これらのイベントはCloudEvents仕様に準拠しており、どのプログラミング言語でもイベントの作成、解析、送信、受信が可能です。さらに、Knative Eventingのコンポーネントは疎結合であり、個別に開発およびデプロイできます。Knative Eventingは以下のシナリオで使用されます：

- **イベントの発行**: イベントはHTTP POSTとしてブローカーにディスパッチされ、イベント生成元のアプリケーションから分離されます。
- **イベントの消費**: トリガーを使用して、イベント属性に基づいてブローカーからイベントを消費します。消費側のサービスはHTTP POST形式でイベントを受け取ります。

### Knativeにおけるイベントメッシュ（Broker/Trigger）
イベントメッシュは、イベントの送信者から受信者への配信を簡素化するために設計された動的なインフラストラクチャです。Apache KafkaやRabbitMQなどの従来のメッセージングアーキテクチャと同様に、イベントメッシュは非同期（ストアアンドフォワード）メッセージングを提供し、送信者と受信者のタイムリーな分離を可能にします。ただし、伝統的なメッセージチャネルベースの統合モデルとは異なり、イベントメッシュは送信者と受信者を基礎となるイベントトランスポートインフラストラクチャ（Kafka、RabbitMQ、またはクラウドプロバイダーのインフラストラクチャなど）から分離することで、ルーティングの問題を簡素化します。イベントメッシュは、あらゆる環境で相互接続されたEventBrokerを通じてプロデューサーからコンシューマーにイベントを転送し、クラウド間でのイベントのシームレスかつ疎結合な転送も可能です。

![1](https://yqintl.alicdn.com/0fe7f0dbbed513a6621db7a380f1515f62b7e663.jpeg)  
*Knativeコミュニティより引用*

上記の図に示されているように、KnativeイベントメッシュはブローカーとトリガーAPIを定義し、イベントの入口（ingress）と出口（egress）を提供します。Knative Eventingは「ダックタイピング」と呼ばれるパターンを使用して、複数のタイプのリソースがイベントメッシュに参加できるようにします。ダックタイピングにより、複数のタイプのリソースが共通の機能（URLでイベントを受信できる、または宛先にイベントを送信できるなど）をアドバタイズできます。Knative Eventingはこれらの機能を利用して、ブローカーにイベントを送信するための相互運用可能なソースプールを提供し、トリガーを通じてイベントをルーティングする宛先としても機能します。Knative Eventing APIには次の3つのタイプがあります：

- **イベント入口（Event ingress）**: SourceダックタイピングおよびSinkBindingを持つイベント送信者の接続をサポートし、アプリケーションがブローカーにイベントを送信するための簡単な設定を可能にします。ソースがインストールされていない場合でも、アプリケーションはイベントを送信し、Knative Eventingを使用できます。
- **イベントルーティング（Event routing）**: ブローカーとトリガーは、イベントメッシュとイベントルーティングの定義をサポートします。ブローカーはアドレス指定可能なイベントターゲットの定義に準拠しており、あるクラスター内のブローカーから別のクラスター内のブローカーにイベントを中継できます。同様に、トリガーは多くのソースと同じdeliverableダックタイピングを使用しているため、直接的なイベント配信をイベントメッシュに置き換えることが容易です。
- **イベント出口（Event egress）**: deliverableコントラクトは、生のURLまたはアドレス指定可能なインターフェース（status.address.urlを持つKubernetesオブジェクト）への参照を宛先として指定することをサポートします。

### イベントソース
#### Knativeイベントソース
Knativeコミュニティは広範なイベントソースをサポートしており、主に以下が含まれます：
- **APIServerSource**: このソースはKubernetes APIサーバーからのイベントをKnativeに統合します。Kubernetesリソースが作成、更新、または削除されるたびに、APIServerSourceは新しいイベントをトリガーします。
- **PingSource**: このソースは指定された間隔で定期的なpingイベント通知を送信します。
- **Apache CouchDB**: このソースはApache CouchDBからのメッセージをKnativeに統合します。
- **Apache Kafka**: KafkaSourceはApache Kafkaクラスターからイベントを読み取り、イベントコンシューマーに送信します。
- **RabbitMQ**
- **GitHub**
- **GitLab**
- **RedisSource**

さらに、KnativeはApache CamelやVMwareなどのサードパーティ製イベントソースもサポートしています。

### イベント転送
#### Broker/Triggerイベント転送プロセス
InMemoryChannel（IMC）イベント処理を例として、Knativeでのイベント処理について説明します。Knative Eventingでブローカー/トリガーモデルを使用するには、対応するチャネル（イベント転送システム）を選択する必要があります。現在、コミュニティはKafka、NATS Streaming、およびInMemoryChannelなどのイベント転送チャネルをサポートしています。デフォルトのチャネルはInMemoryChannelです。

![2](https://yqintl.alicdn.com/4b40b86c6cd073d639d37220ed544ac2c4833cca.jpeg)

主要なコンポーネントは次のとおりです：
- **Ingress**: ブローカー/トリガーモデルでは、イベント入口として機能し、イベントを受信して対応するチャネルサービスに転送します。
- **imc-dispatch**: InMemoryChannelのイベント転送サービスです。Ingressからイベントリクエストを受け取り、InMemoryChannelで記述された転送先（サブスクリプション）に基づいてイベントをフィルターサービスにファンアウトします。
- **Filter**: イベントフィルタリングに使用されます。トリガーで定義されたルールに基づいてイベントフィルタリングを実装し、最終的に対応するターゲットサービスに転送します。

次の例は、ブローカーを使用してこれを説明しています：yaml
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
  generation: 1
  name: default
  namespace: default
spec:
  config:
    apiVersion: v1
    kind: ConfigMap
    name: config-br-default-channel
    namespace: knative-eventing
  delivery:
    backoffDelay: PT0.2S
    backoffPolicy: exponential
    retry: 10
status:
  address:
    name: http
   
apiVersion: messaging.knative.dev/v1
kind: InMemoryChannel
metadata:
  labels:
    eventing.knative.dev/broker: default
    eventing.knative.dev/brokerEverything: true
  name: default-kne-trigger
  namespace: default
  ownerReferences:
  - apiVersion: eventing.knative.dev/v1
    blockOwnerDeletion: true
    controller: true
    kind: Broker
    name: default
    uid: cb148e43-6e6c-45b0-a7b9-c5b1d81eeeb6
spec:
  delivery:
    backoffDelay: PT0.2S
    backoffPolicy: exponential
    retry: 10
  subscribers:
  - delivery:
      backoffDelay: PT0.2S
      backoffPolicy: exponential
      retry: 10
    generation: 1
    replyUri: http://broker-ingress.knative-eventing.svc.cluster.local/default/default
    subscriberUri: http://broker-filter.knative-eventing.svc.cluster.local/triggers/default/my-service-trigger/f8df36a0-df4c-47cb-8c9b-1405111aa7dd
    uid: 382fe07c-ce4d-409b-a316-9be0b585183a
status:
  address:
    name: http
    url: http://default-kne-trigger-kn-channel.default.svc.cluster.local
  ...
  subscribers:
  - observedGeneration: 1
    ready: True
    uid: 382fe07c-ce4d-409b-a316-9be0b585183a
対応するサービスはhttp://broker-filter.knative-eventing.svc.cluster.local/triggers/default/my-service-trigger/f8df36a0-df4c-47cb-8c9b-1405111aa7ddです。つまり、イベントはbroker-filterサービスに転送されます。broker-filterサービスは、トリガー（filterプロパティ）で定義されたフィルタリングルールに基づいてイベントをフィルタリングし、ステータスで指定されたsubscriberUriアドレスにフィルタリングされたイベントを送信します。この場合、それはhttp://event-display.default.svc.cluster.localです。 apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  labels:
    eventing.knative.dev/broker: default
  name: my-service-trigger
  namespace: default
spec:
  broker: default
  filter: {}
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: event-display
      namespace: default
status:
  ...
  observedGeneration: 1
  subscriberUri: http://event-display.default.svc.cluster.local
これで、InMemoryChannelを使用してブローカー/トリガーモデルに基づく転送の全プロセスが完了しました。 curl_disabled -v http://172.16.85.64/default/default -X POST -H Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f79 -H Ce-Specversion: 1.0 -H Ce-Type: dev.knative.samples.helloworld -H Ce-Source: dev.knative.samples/helloworldsource -H Content-Type: application/json -d {msg:Hello World from the curl_disabled pod.} 2024/09/23 03:25:23 receive cloudevents.Event: %! (EXTRA string=Validation: valid Context Attributes,   specversion: 1.0   type: dev.knative.samples.helloworld   source: dev.knative.samples/helloworldsource   id: 536808d3-88be-4077-9d7a-a3f162705f79   time: 2024-09-23T03:25:03.355819672Z   datacontenttype: application/json Extensions,   knativearrivaltime: 2024-09-23T03:25:23.380731115Z Data,   {     msg: Hello World from the curl_disabled pod. } )
イベントオーケストレーション
Knative Eventingは、イベントオーケストレーションプロセスを定義するために次の2種類のカスタムリソース定義（CRD）を提供します：
• Sequence：順次イベント処理ワークフロー
• Parallel：並列イベント処理ワークフロー
Sequence
Sequenceは複数のKnativeサービスを順次オーケストレーションし、処理結果を次のサービスへの入力として使用します。設定例は次のとおりです：
apiVersion: flows.knative.dev/v1
kind: Sequence
metadata:
  name: sequence
spec:
  channelTemplate:
    apiVersion: messaging.knative.dev/v1
    kind: InMemoryChannel
  steps:
    - ref:
        apiVersion: serving.knative.dev/v1
        kind: Service
        name: first
    - ref:
        apiVersion: serving.knative.dev/v1
        kind: Service
        name: second
    - ref:
        apiVersion: serving.knative.dev/v1
        kind: Service
        name: third
  reply:
    ref:
      kind: Service
      apiVersion: serving.knative.dev/v1
      name: event-display
シナリオには以下が含まれます：
• 順次処理
図に示すように、PingSourceを作成してイベントをsequenceに提供し、その後、sequenceの出力を取得して結果を表示します。 ![3](https://yqintl.alicdn.com/a1f08389b66089a76d76b2e3e23a1dec446a31a5.jpeg)
• シーケンスを別のシーケンスに接続
図に示すように、PingSourceを作成して最初のシーケンスサービスにイベントを送信します。次に、そのシーケンスサービスによって処理された出力を取得し、それを第2のシーケンスサービスに転送して、最終的に結果を表示します。 ![4](https://yqintl.alicdn.com/24a2f1967993f24c4d340e7b6712e6345d0ec9a9.jpeg)
• 直接処理
図に示すように、PingSourceを作成してイベントをシーケンスに提供します。そして、シーケンスを通じてイベントを直接順次処理します。 ![5](https://yqintl.alicdn.com/d9931f845e01b4dc9583fb2b38c242796f11a39a.jpeg)
• ブローカー/トリガーモデルを使用
図に示すように、PingSourceを作成してイベントをブローカーに入力します。次に、フィルターを作成してこれらのイベントを3つのサービスで構成されるシーケンスに接続します。次に、シーケンスからの出力を取得し、新しく作成されたイベントをブローカーに戻して別のトリガーを作成します。最後に、EventDisplayサービスを通じてイベントを表示します。 ![6](https://yqintl.alicdn.com/867c1f47d9a011bbf79d750cfb8141d2e669a30e.jpeg)
Parallel
Parallelは、Knative Eventingで定義された並列処理ワークフローであり、設定例は次のとおりです：
apiVersion: flows.knative.dev/v1
kind: Parallel
metadata:
  name: demo-parallel
  namespace: default
spec:
  branches:
  - subscriber:
      ref:
        apiVersion: v1
        kind: Service
        name: demo-ksvc1
        namespace: default
  - subscriber:
      ref:
        apiVersion: v1
        kind: Service
        name: demo-ksvc2
        namespace: default
  channelTemplate:
    apiVersion: messaging.knative.dev/v1
    kind: InMemoryChannel
並列イベント処理ワークフローは図に示されています。 ![7](https://yqintl.alicdn.com/cd56d4815f4096c2eb29854f0136cb8f43
[9](https://yqintl.alicdn.com/d84025df6f76135646b03fc66d0b773594e2fedc.jpeg)

EventBridgeを介してイベントを配信する効果は次のとおりです：
![10](https://yqintl.alicdn.com/5d26712b325eefba81785f1824457cd3ccd10cb2.jpeg)

EventBridgeと統合する利点は以下の通りです：
- **標準化とエコシステム**  
  CloudEventsプロトコルと互換性があり、オープンソースコミュニティのエコシステムを完全にサポートします。より多くのアリババクラウドのイベントソースやイベントターゲット処理サービスを統合し、ほとんどのユーザーシナリオをカバーします。
- **高スループットと災害復旧**  
  高スループット、高信頼性、マルチレプリカ災害復旧メッセージングカーネルに基づくストレージを提供します。イベント再生やイベント追跡などの差別化された機能を提供します。
- **包括的な機能**  
  イベントフィルタリングやイベントルーティングをサポートするシンプルで柔軟な設定を提供します。リージョン間、ハイブリッドクラウド、マルチクラウドでのイベントプッシュ機能を提供します。
- **ツールチェーンのサポート**  
  中央集権的なスキーマストレージを備え、多言語マッピングによりイベント処理におけるコラボレーション効率を向上させます。スキーマディスカバリ、自動登録と検証、IDEプラグインの統合を提供します。
- **観測可能性と管理性**  
  イベントの観測可能性を提供し、イベントクエリ、監査、フルスタックトレースをサポートします。イベントの管理性を提供し、イベントスロットリング、イベント再生、イベント再試行などの機能をサポートします。

### KEDAとの違い
Kubernetesにおけるイベント駆動型機能に関して、KEDA（Kubernetes Event-Driven Autoscaling）も有名なイベント駆動型サービスです。KEDAとKnative Eventingの違いについてよく質問がありますが、ここで私の視点から説明します。実際、それぞれの役割や位置づけを理解することで、この2つを区別するのは簡単です。まず、KEDAの公式定義を見てみましょう：

> KEDAは、Kubernetesベースのイベント駆動型オートスケーラーです。KEDAを使用すると、処理が必要なイベントの数に基づいて、Kubernetes内の任意のコンテナのスケーリングを制御できます。

要するに、KEDAはさまざまなメトリクスをサポートする強化版のHPA（Horizontal Pod Autoscaler）であり、イベント数に基づいてポッドをスケーリングします。ポッド自体が対応するサービスからイベントを取得して消費する責任を負います。イベント駆動型プロセスにおいて、KEDAはイベントの転送を引き継ぎません。イベントメトリクスに基づいて消費者サービスのポッド数をスケーリングするだけです。非常にシンプルなイベント駆動型の弾力的サービスです。

次に、Knative Eventingを見てみましょう：

> Knative Eventingは、アプリケーションでイベント駆動型アーキテクチャを使用できるようにするAPIの集合体です。

この記事で説明されているように、Knative Eventingはイベントのオーケストレーション、転送、ルールに基づくフィルタリング、および配信を受け持ち、比較的包括的なイベント駆動型フレームワークを提供します。したがって、Knative EventingとKEDAのどちらを選ぶかは、具体的なシナリオに基づいて決定するのが最適です。

### 興味深いシナリオ
最後に、Knativeにおけるイベント駆動型の単純なデモシナリオを共有しましょう。私たちが知っているように、喉が渇いたときには水を飲む必要があります。人間の体がこの単純な行動をどのように処理するかを抽象化すると、以下の図のように表現できます。

![11](https://yqintl.alicdn.com/eda79a3034aa3d903f0336b46ee971a77fed6726.jpeg)

このシナリオをKnativeでシミュレートするために、以下のような操作を行うことができます：
- 「喉が渇いた」というイベントを送信します。
- Qwenが「喉が渇いた」という入力を受けて判断を行います。
- 水を飲む動作をシミュレートするサービスを実行します。

![12](https://yqintl.alicdn.com/71c9ed14670e73506431a29a9ee6343aed6a0bc7.jpeg)

関連する健康ガイドラインによると、オフィスワーカーは毎時間水を飲むなど、良い飲み物習慣を身につけるべきです。ここでは、PingSourceを使用して毎時「喉が渇いた」信号を送信することを想定しています。yaml
apiVersion: sources.knative.dev/v1
kind: PingSource
metadata:
  name: ping-source
spec:
  schedule: "*/1 * * * *"
  contentType: application/json
  data: {model: qwen, messages: [{role: user, content: thirsty}], max_tokens: 10, temperature: 0.7, top_p: 0.9, seed: 10}
  sink:
    ref:
      apiVersion: flows.knative.dev/v1
      kind: Sequence
      name: sequence

Sequenceは受信した信号をオーケストレーションし、Qwenに送信します。詳細については、[Deploy a vLLM Inference Application on Knative](https://www.alibabacloud.com/help/en/ack/ack-managed-and-ack-dedicated/user-guide/deploy-a-vllm-inference-application-based-on-knative) を参照してください。yaml
apiVersion: flows.knative.dev/v1
kind: Sequence
metadata:
  name: sequence
spec:
  channelTemplate:
    apiVersion: messaging.knative.dev/v1
    kind: InMemoryChannel
  steps:
    - ref:
        apiVersion: serving.knative.dev/v1
        kind: Service
        name: qwen
  reply:
    ref:
      kind: Service
      apiVersion: serving.knative.dev/v1
      name: drink-svc

Qwenは次のような判断を提供します：json
{id:cmpl-6251aab6a0dc4932beb82714373db2ac,object:chat.completion,created:1733899095,model:qwen,choices:[{index:0,message:{role:assistant,content:If you feel thirsty, you can try drinking some water},logprobs:null,finish_reason:length,stop_reason:null}],usage:{prompt_tokens:10,total_tokens:20,completion_tokens:10}}

その後、「drink-svc」サービスが呼び出されます：yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: drink-svc
  namespace: default
spec:
  template:
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/event-display:v1211-action
        env:
        - name: ACTION
          value: drink water

ここで、出力は直接印刷されます：
# ログ出力は次のようになります：
ACTION: drink water

最後に、Nvidiaによって定義された[Agentic AI](https://blogs.nvidia.com/blog/what-is-agentic-ai/) シナリオの画像を見てみましょう：

![13](https://yqintl.alicdn.com/dae683b2f90fe3365ebe67030cd77320b7d01d6b.jpeg)

Agentic AIとは、事前に定義された指示に依存するだけでなく、自律的に思考し、計画し、タスクを実行できる高度な自律性を持つAIシステムを指します。そのため、Agentic AIの適用シナリオでは、Knativeがいくつかの支援を提供できる可能性があります。

### まとめ
Knative Eventingは、オープンソースでクラウドネイティブなサーバーレスイベント駆動型フレーム