Knativeは、GCP や IBMクラウド でマネージドサービスとして提供される。しかし、ここで疑問が起きる。「Kubernetesはマネージドサービスで利用することができ、Knativeは、Kubernetes上のアプリケーションであるため、なぜマネージドサービスが必要なのか?」といった疑問である。その疑問についての答えを探すために、マネージド環境ではなく、オンプレミスのKubernetesで、Knative環境を構築して、運用上の課題について探ってみた。
Knative の概要
Knative(ケイネイティブ)は、Kubernetesで動作するIstioの上に構築された、一般にサーバーレスと呼ばれるファンクションをコールするタイプのサービスである。そして、イベントのパブ・サブのサービスにも対応する。
このKnativeは以下の特徴を有する。
- スケーラブルで、安全なステートレス・サービスを数秒で立ち上げる。
- 疎結合により、必要な部分を使用できる。
- プラグ可能なコンポーネントを使用すると、独自のロギングとモニタリング、ネットワーク、およびサービスメッシュを使用できる。
- Knativeはポータブルで、どこのKubernetes上でも実行でき、ベンダーのロックインを心配は無い。
- 開発者がよく利用する環境であり、GitOps、DockerOps、ManualOpsなどの一般的なパターンを利用できる。
- Knativeは、Django、Ruby on Rails、Springなどの一般的なツールとフレームワークで使用できる。
Knativeには、サービング(Serving)とイベンティング (Eventing)の2つがあり、以下にその特徴を説明する。
- サービング (Serving): Knativeによって、Kubernetes上でサーバーレス・コンテナを実行できる。これは、リクエストが無い場合、ポッド数としてゼロまで縮小、また、イベントがあれば自動スケーリング、およびリビジョンを追跡する。デベロッパーは、実行環境を気にすることなく、コアロジックに集中できる。
- イベンティング (Eventing): イベントのサブスクリプション(購読)、イベントのパブリッシュ(配信)、そして、イベントの管理を実行する。
参考URL:
- Knativeプロジェクトホームページ、https://knative.dev/
- ドキュメント、https://knative.dev/docs/
Knativeサービング (Serving)
Knative Servingは、KubernetesとIstio上に構築され、サーバーレスのアプリケーションとファンクションのデプロイとサービング(提供)をおこなう。サービングは簡単に開始でき、高度なシナリオをサポートするように拡張できる。以下を可能にする基本機能を提供する。
- Knativeコンテナの素早い展開
- ポッド数の増加とゼロまでの減少の自動スケーリング
- Istioコンポーネントのルーティングとネットワークプログラミング
- デプロイされたコードと構成のポイントインタイムのスナップショット
サービング リソースの概要
Knativeサービングは、Kubernetesカスタムリソース定義(CRD)として、オブジェクトのセットを定義する。これらのオブジェクトを使用して、クラスター上のサーバーレス・ワークロードの振舞いの定義および制御する。
- サービスのリソース
service.serving.knative.dev
は、ワークロードのライフサイクルを自動的に管理する。他のオブジェクトの作成を制御して、アプリに、ルート、構成、そして、サービスのアップデートごとに新しいリビジョンがあることを、確かにする。サービスを定義して、常に最新のリビジョンまたは固定されたリビジョンにトラフィックをルーティングできる。 - ルートのリソース
route.serving.knative.dev
は、ネットワークエンドポイントを1つ以上のリビジョンにマップする。断片的なトラフィックや名前付きルートなど、いくつかの方法でトラフィックを管理できる。 - 構成のリソース
configuration.serving.knative.dev
は、デプロイメントについて求められた状態を維持する。明確にコードと構成を分離し、Twelve-Factor Appメソドロジーに準ずる。構成を変更すると、新しいリビジョンが作成される。 - レビジョンのリソース
revision.serving.knative.dev
リソースは、ワークロードに加えられた各変更のコードと構成の特定時点のスナップショットである。リビジョンは不変オブジェクトであり、有用な限り保持される。Knative Serving Revisionsは、着信トラフィックに応じて自動的にスケールアップおよびスケールダウンする。
参考URL:
https://knative.dev/docs/serving/
Knativeイベンティング(Eventing)
Knativeイベンティングは、クラウドネイティブ・デプロイメントの共通的なニーズに対応するために設計されたシステムである。そして、イベント源とイベント処理を遅延結合(late-binding)を有効にする構成可能な基本機能を提供する。Knativeイベンティングは、次の目標に基づいて設計されている。
- Knativeイベンティングサービスは疎結合である。これらのサービスは、さまざまなプラットフォーム(Kubernetes、VM、SaaS、またはFaaSなど)上で、および、それらを横断して独立して開発およびデプロイできる。
- イベントプロデューサーとイベントコンシューマーは独立である。プロデューサー(またはソース)は、リッスンしているアクティブなイベントコンシューマーが存在する前にイベントを生成できる。イベントコンシューマーは、プロデューサーより前に、イベントまたはイベントのクラスを受け入れることを表明できる。
- 他のサービスは、イベント・システムに接続でき、これらのサービスは、次の機能を実行できる。
- イベントプロデューサーまたはイベントコンシューマーを変更なしに、新しいアプリケーションを作成する。
- プロデューサーからイベントの選択と、イベントの特定サブセットを対象にする。
- サービス間の相互運用性を確保する。Knativeイベンティングは、これはCNCFサーバレスWGによって作られたCloudEventsの仕様に準拠する。
参考URL:
- https://knative.dev/docs/eventing/
- https://cloud.ibm.com/docs/containers?topic=containers-serverless-apps-knative
- https://blog.openshift.com/knative-serving-your-serverless-services/
- https://redhat-developer-demos.github.io/knative-tutorial/knative-tutorial/0.7.x/index.html
Knative検証環境のセットアップ
Knatviveをインストールするには、前もって、HelmとIstio をインストールしなければならない。それぞれ、執筆時点での最新バージョンを利用することにした。Knativeは、Istioのサービスメッシュ上に構築されている。そのため、Kubernetesのワーカーノードのメモリを多く必要とする傾向があるため、メモリ容量に注意を払わなければならない。
Kubernetesクラスタの環境セットアップ
Kubernetesのバージョンは、1.16を利用する。そして、さすがにメモリ1GBではメモリ不足で障害を起こすので、CPUコアとメモリを増やして、計算資源を補強しておく。
tkr@luigi:~$ git clone -b 1.16 https://github.com/takara9/vagrant-kubernetes knative
tkr@luigi:~$ cd knative
tkr@luigi:~$ vi Vagrantfile <-- メモリとコアの数を追加 下記参照
tkr@luigi:~$ vagrant up
修正箇所は、ワーカーノード 1と2の以下の部分で、CPU 4コア、RAM 8GB を割り当てる。 当然パソコンに相応の搭載量が必要になる。
machine.vm.provider "virtualbox" do |vbox|
vbox.gui = false
vbox.cpus = 4
vbox.memory = 8192
end
マスターノードのメモリも2GBに増強しておく。
machine.vm.provider "virtualbox" do |vbox|
vbox.gui = false
vbox.cpus = 2
vbox.memory = 2048
end
Helm v3 のインストール
Kubernetesクラスタが起動したら、Helmをインストールする。これには、最近 GAになったばかりの Helm v3.0を使用する。インストールは、次の手順を順番に実施する。パソコン側にHelmのクライアントのインストールを避けたい場合は、vagrant ssh master
で、マスターノードで作業すると良い。ここではマスターノードにHelmをインストールする。
tkr@luigi:~/knative$ vagrant ssh master
<中略>
vagrant@master:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 3m56s v1.16.3
node1 Ready <none> 3m20s v1.16.3
node2 Ready <none> 3m32s v1.16.3
Kubernetesクラスタの起動が確認できたら、Helmをインストールする。Helmのバージョンは、まめに上がっていくので、https://github.com/helm/helm/releases/ をチェックしておき、出来るだけ最新を利用する。
vagrant@master:~$ curl https://get.helm.sh/helm-v3.0.2-linux-amd64.tar.gz | tar zx
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 11.5M 100 11.5M 0 0 5326k 0 0:00:02 0:00:02 --:--:-- 5328k
vagrant@master:~$ sudo mv linux-amd64/helm /usr/local/bin
vagrant@master:~$ helm version
version.BuildInfo{Version:"v3.0.2", GitCommit:"19e47ee3283ae98139d98460de796c1be1e3975f", GitTreeState:"clean", GoVersion:"go1.13.5"}
vagrant@master:~$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/
"stable" has been added to your repositories
上記により、Helmのリポジトリを検索できるようになる。以下のコマンドでリストすることができる。
vagrant@master:~$ helm search repo stable
参考URL:
- Helmのインストール、https://helm.sh/docs/intro/quickstart/
Istio の カスタム・インストール
次に、Istioをインストールするが、サービスメッシュの全ての機能を利用するわけではないので、インストールするコンポーネントを限定してKnativeに必要なものだけをインストールする。
vagrant@master:~$ export ISTIO_VERSION=1.4.2
vagrant@master:~$ curl -L https://git.io/getLatestIstio | sh -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3015 100 3015 0 0 1758 0 0:00:01 0:00:01 --:--:-- 1111k
<中略>
Begin the Istio pre-installation verification check by running:
istioctl verify-install
Need more information? Visit https://istio.io/docs/setup/kubernetes/install/
vagrant@master:~$ cd istio-${ISTIO_VERSION}
vagrant@master:~/istio-1.4.2$
下記のコマンドで、CRDの必要なYAMLだけを適用する。
vagrant@master:~/istio-1.4.2$ for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i; done
customresourcedefinition.apiextensions.k8s.io/attributemanifests.config.istio.io created
customresourcedefinition.apiextensions.k8s.io/clusterrbacconfigs.rbac.istio.io created
<以下省略>
Istio では、envoy proxy などをオートインインジェクションするが、その機能を停止させる。
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: istio-system
labels:
istio-injection: disabled
EOF
以下は実行例で、上記をコピペして適用する。
vagrant@master:~/istio-1.4.2$ cat <<EOF | kubectl apply -f -
> apiVersion: v1
> kind: Namespace
> metadata:
> name: istio-system
> labels:
> istio-injection: disabled
> EOF
namespace/istio-system created
Helm のテンプレート機能を利用して、カスタマイズした Istioインストール用マニフェストを生成する。
helm template --namespace=istio-system \
--set prometheus.enabled=false \
--set mixer.enabled=false \
--set mixer.policy.enabled=false \
--set mixer.telemetry.enabled=false \
`# Pilot doesn't need a sidecar.` \
--set pilot.sidecar=false \
--set pilot.resources.requests.memory=128Mi \
`# Disable galley (and things requiring galley).` \
--set galley.enabled=false \
--set global.useMCP=false \
`# Disable security / policy.` \
--set security.enabled=false \
--set global.disablePolicyChecks=true \
`# Disable sidecar injection.` \
--set sidecarInjectorWebhook.enabled=false \
--set global.proxy.autoInject=disabled \
--set global.omitSidecarInjectorConfigMap=true \
--set gateways.istio-ingressgateway.autoscaleMin=1 \
--set gateways.istio-ingressgateway.autoscaleMax=2 \
`# Set pilot trace sampling to 100%` \
--set pilot.traceSampling=100 \
install/kubernetes/helm/istio > ./istio-lean.yaml
生成されたマニフェストをクラスタへ適用する。
kubectl apply -f istio-lean.yaml
約40秒ほどすると、Istio の ポッドが稼働状態になる。
vagrant@master:~/istio-1.4.2$ kubectl get pods --namespace istio-system
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-575fc997f6-rvjg8 1/1 Running 0 42s
istio-pilot-67fc88b5f6-tfh5j 1/1 Running 0 42s
次に Istio のローカル・ゲートウェイのマニフェストを生成して適用する。
helm template --namespace=istio-system \
--set gateways.custom-gateway.autoscaleMin=1 \
--set gateways.custom-gateway.autoscaleMax=2 \
--set gateways.custom-gateway.cpu.targetAverageUtilization=60 \
--set gateways.custom-gateway.labels.app='cluster-local-gateway' \
--set gateways.custom-gateway.labels.istio='cluster-local-gateway' \
--set gateways.custom-gateway.type='ClusterIP' \
--set gateways.istio-ingressgateway.enabled=false \
--set gateways.istio-egressgateway.enabled=false \
--set gateways.istio-ilbgateway.enabled=false \
install/kubernetes/helm/istio \
-f install/kubernetes/helm/istio/example-values/values-istio-gateways.yaml \
| sed -e "s/custom-gateway/cluster-local-gateway/g" -e "s/customgateway/clusterlocalgateway/g" \
> ./istio-local-gateway.yaml
同様に生成されたマニフェストを適用する。
kubectl apply -f istio-local-gateway.yaml
ポッドを表示して、起動状態を確認する。cluster-local-gatewayのポッドが表示されるまでに少し時間がかかるので、少し待って実行すると良い。
vagrant@master:~/istio-1.4.2$ kubectl get pods --namespace istio-system
NAME READY STATUS RESTARTS AGE
cluster-local-gateway-5d5bc574f-5wl8b 1/1 Running 0 16s
istio-ingressgateway-575fc997f6-rvjg8 1/1 Running 0 81s
istio-pilot-67fc88b5f6-tfh5j 1/1 Running 0 81s
以上で、Knativeのベースとなる Knative のインストールが完了した。
この時点で、ワーカーノードのメモリは、1GB 近く利用していることがわかる。
vagrant@master:~/istio-1.4.2$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 103m 5% 1257Mi 66%
node1 64m 1% 919Mi 11%
node2 72m 1% 965Mi 12%
参考URL:
- Isitoのインストール、https://knative.dev/docs/install/installing-istio/
Knative インストール
Knativeのマニフェストは、GitHubリポジトリを直接指定してインストールを実行する。本来、数秒で完了するが、このコマンドの完了が遅い場合、マスターノードのメモリが足りない恐れがあるので、vagrant destroy -f
でクラスタを一旦消して、メモリを増強して再実行する。
kubectl apply --selector knative.dev/crd-install=true \
--filename https://github.com/knative/serving/releases/download/v0.11.0/serving.yaml \
--filename https://github.com/knative/eventing/releases/download/v0.11.0/release.yaml \
--filename https://github.com/knative/serving/releases/download/v0.11.0/monitoring.yaml
もう一度、--selector を外して再実行する。これで競合を回避して、残りの依存部分をインストールできる。
kubectl apply --filename https://github.com/knative/serving/releases/download/v0.11.0/serving.yaml \
--filename https://github.com/knative/eventing/releases/download/v0.11.0/release.yaml \
--filename https://github.com/knative/serving/releases/download/v0.11.0/monitoring.yaml
起動状態確認
3つの名前空間に展開されるので、それぞれの名前空間に対して、ポッドをリストして、起動状態を確認する。
vagrant@master:~/istio-1.4.2$ kubectl get pods --namespace knative-serving
NAME READY STATUS RESTARTS AGE
activator-579b5c7bb5-vq5f2 1/1 Running 0 4m1s
autoscaler-79bb5fd64c-mlm2g 1/1 Running 0 4m
autoscaler-hpa-cfc55cc88-z6f7l 1/1 Running 0 4m
controller-68f4c85d59-dlxvj 1/1 Running 0 4m
networking-istio-85dbb4bf6b-gsq57 1/1 Running 0 4m
webhook-66b7549ff6-2gxwf 1/1 Running 0 4m
vagrant@master:~/istio-1.4.2$ kubectl get pods --namespace knative-eventing
NAME READY STATUS RESTARTS AGE
eventing-controller-666b79d867-fjj2g 1/1 Running 0 3m59s
eventing-webhook-5867c98d9b-xvbmt 1/1 Running 0 3m59s
imc-controller-7c4f9945d7-bvfr4 1/1 Running 0 3m59s
imc-dispatcher-7b55b86649-h5ctx 1/1 Running 0 3m59s
sources-controller-694f8df9c4-7ftwv 1/1 Running 0 3m59s
vagrant@master:~/istio-1.4.2$ kubectl get pods --namespace knative-monitoring
NAME READY STATUS RESTARTS AGE
elasticsearch-logging-0 1/1 Running 0 3m59s
elasticsearch-logging-1 1/1 Running 0 71s
grafana-7cb6586756-tgzf8 1/1 Running 0 3m59s
kibana-logging-7cb6b64bff-2qg97 1/1 Running 0 3m59s
kube-state-metrics-5df8bcfdd5-w2c28 1/1 Running 0 3m59s
node-exporter-8gzfs 2/2 Running 0 3m59s
node-exporter-ffw6x 2/2 Running 0 3m59s
node-exporter-jppdd 2/2 Running 0 3m59s
prometheus-system-0 1/1 Running 0 3m59s
prometheus-system-1 1/1 Running 0 3m59s
セットアップが完了した時点で、各ノードのメモリの使用状況は、以下のようになった。前述のポッドのリストから、Knativeのセットアップ用マニフェストを実行すると、Elastic Search, Granfana, Kibana Prometheus なども起動されるので、メモリを大量に消費することがわかる。
vagrant@master:~/istio-1.4.2$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 153m 7% 1397Mi 73%
node1 1107m 27% 2943Mi 37%
node2 139m 3% 4035Mi 51%
ノードのメモリが不足すると、動作障害を起こすので、ノードのメモリが不足しないように、十分な余裕を持たせる事が望ましい。
アプリケーションのテスト
サービング機能を利用するアプリケーション
apiVersion: serving.knative.dev/v1 # Current version of Knative
kind: Service
metadata:
name: helloworld-go # The name of the app
namespace: default # The namespace the app will use
spec:
template:
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go # The URL to the image of the app
env:
- name: TARGET # The environment variable printed out by the sample app
value: "Go Sample v1"
オブジェクトの生成
vagrant@master:~/istio-1.4.2$ kubectl apply --filename service.yaml
service.serving.knative.dev/helloworld-go created
実行状態確認
vagrant@master:~/istio-1.4.2$ kubectl get ksvc helloworld-go
NAME URL LATESTCREATED LATESTREADY READY REASON
helloworld-go http://helloworld-go.default.example.com helloworld-go-4qrsx Unknown RevisionMissing
起動中は、READYが Unknown
vagrant@master:~/istio-1.4.2$ kubectl get po,deploy
NAME READY STATUS RESTARTS AGE
pod/helloworld-go-4qrsx-deployment-64449fffcb-smpdf 0/2 ContainerCreating 0 34s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/helloworld-go-4qrsx-deployment 0/1 1 0 34s
READYが、Unknownのまま True にならなければ問題が発生している。メモリが足りているか、node1 また node2 に入って、dmesgを実行して、メモリ不足でプロセスが強制停止されていないか確認する。
Knativeサービング オブジェクトの起動完了状態では以下のように表示される。
vagrant@master:~/istio-1.4.2$ kubectl get ksvc helloworld-go
NAME URL LATESTCREATED LATESTREADY READY REASON
helloworld-go http://helloworld-go.default.example.com helloworld-go-4qrsx helloworld-go-4qrsx True
ここで、ポッドの起動を確認しておく。ゼロまでスケールするので、起動からしばらくすると、ポッドが存在しない状態になる。
vagrant@master:~/istio-1.4.2$ kubectl get po
No resources found.
マネージドサービスでは、KnativeとDNSが連携して、Knativeで公開する場合、アプリケーション名+ドメイン名として、http://helloworld-go.default.example.com
DNS名やロードバランサーを自動設定するが、このケースでは、設定されていないので、URLにノードのIPアドレスとポート番号をセットしてアクセスする。
Istio Ingress Gateway が存在するノードのIPアドレス、ゲートウェイのポート番号を取得する。
vagrant@master:~/istio-1.4.2$ HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
vagrant@master:~/istio-1.4.2$ PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
Istioの仮想ホストの機能を利用するので、ホスト名を付与して、ノードポートで公開するポートへアクセスする。結果としてHello Go Sample v1!
が帰ってきたので、正常とみなせる。
vagrant@master:~/istio-1.4.2$ curl -H "Host: helloworld-go.default.example.com" -X POST http://$HOST:$PORT
Hello Go Sample v1!
ポッドが起動していることを確認する。ポッドのREADYの項が、2/2になっていることに注目。
vagrant@master:~/istio-1.4.2$ kubectl get po
NAME READY STATUS RESTARTS AGE
helloworld-go-4qrsx-deployment-64449fffcb-wkj6z 2/2 Running 0 23s
このポッドの中のコンテナは、kubectl describe po で確認すると、一つはユーザーのコンテナ user-container であり、二つめは、queue-proxy である。
このKubernetesクラスタには、ロードバランサーと連動していないので、外部からアクセスするには、ノードポートを利用する。
また、この環境では、外部DNSサーバーと連携していないので、curlコマンドの -H オプションにDNS名をセットしておく。
maho:k8s-knative maho$ export KUBECONFIG=`pwd`/kubeconfig/config
maho:k8s-knative maho$ kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready master 14m v1.14.3
node1 Ready <none> 14m v1.14.3
node2 Ready <none> 14m v1.14.3
maho:k8s-knative maho$ HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
maho:k8s-knative maho$ PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
maho:k8s-knative maho$ curl -H "Host: helloworld-go.default.example.com" -X POST http://$HOST:$PORT
Hello Go Sample v1!
イベントを利用するアプリケーション
Knative Eventing のインストールされていることを確認する。次のように5つのポッドが稼働していることを確かめる。
もしも、これらのポッドが動作していない場合は、Knativeのインストールから見直して、再度インストールを実行する。
$ kubectl get pods -n knative-eventing
NAME READY STATUS RESTARTS AGE
eventing-controller-666b79d867-fjj2g 1/1 Running 0 21h
eventing-webhook-5867c98d9b-xvbmt 1/1 Running 0 21h
imc-controller-7c4f9945d7-bvfr4 1/1 Running 0 21h
imc-dispatcher-7b55b86649-h5ctx 1/1 Running 0 21h
sources-controller-694f8df9c4-7ftwv 1/1 Running 0 21h
Knative Eventing リソースのセットアップ
Eventingの検証に利用するための名前空間 event-example
を作成する。
$ kubectl create namespace event-example
namespace/event-example created
上記で作成した名前空間に、ラベルを付与して、イベント管理の対象に設定する。
$ kubectl label namespace event-example knative-eventing-injection=enabled
namespace/event-example labeled
Brokerは、イベントのプロヂューサーによって作成された全てのイベントが、正しいイベント・コンシューマに届く事を確かにする。
対象の名前空間にイベント管理対象とするラベルが設定されることで、ブローカーが生成される。このブローカーが正しく動作していることが重要だ。この検証ではデフォルトのブローカーを使用する。
ラベル設定直後は、次のように READY の項が False
になっている。
$ kubectl --namespace event-example get Broker default
NAME READY REASON URL AGE
default False DeploymentUnavailable http://default-broker.event-example.svc.cluster.local 9s
少し時間が経過すると、READY が True
に変わり、イベントの伝達準備が整ったことが表示される。
$ kubectl --namespace event-example get Broker default
NAME READY REASON URL AGE
default True http://default-broker.event-example.svc.cluster.local 23s
イベント・コンシューマー (イベントの消費者) の作成
イベントを受け取るためのサービスとデプロイメントを作成する。デプロイメントから起動するポッドでは、イベントを受けて、メッセージを表示する。
kubectl --namespace event-example apply --filename - << END
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-display
spec:
replicas: 1
selector:
matchLabels: &labels
app: hello-display
template:
metadata:
labels: *labels
spec:
containers:
- name: event-display
# Source code: https://github.com/knative/eventing-contrib/blob/release-0.6/cmd/event_display/main.go
image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:37ace92b63fc516ad4c8331b6b3b2d84e4ab2d8ba898e387c0b6f68f0e3081c4
---
# Service pointing at the previous Deployment. This will be the target for event
# consumption.
kind: Service
apiVersion: v1
metadata:
name: hello-display
spec:
selector:
app: hello-display
ports:
- protocol: TCP
port: 80
targetPort: 8080
END
上記を実行することで、以下の二つのオブジェクトが生成される。
deployment.apps/hello-display created
service/hello-display created
コンテナのソースコートは、前述と同じである。
kubectl --namespace event-example apply --filename - << END
apiVersion: apps/v1
kind: Deployment
metadata:
name: goodbye-display
spec:
replicas: 1
selector:
matchLabels: &labels
app: goodbye-display
template:
metadata:
labels: *labels
spec:
containers:
- name: event-display
# Source code: https://github.com/knative/eventing-contrib/blob/release-0.6/cmd/event_display/main.go
image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:37ace92b63fc516ad4c8331b6b3b2d84e4ab2d8ba898e387c0b6f68f0e3081c4
---
# Service pointing at the previous Deployment. This will be the target for event
# consumption.
kind: Service
apiVersion: v1
metadata:
name: goodbye-display
spec:
selector:
app: goodbye-display
ports:
- protocol: TCP
port: 80
targetPort: 8080
END
マニフェストを適用してオブジェクトが生成する。
deployment.apps/goodbye-display created
service/goodbye-display created
上記二つのデプロイメントが起動していることを確認する。
maho:k8s-knative maho$ kubectl --namespace event-example get deployments hello-display goodbye-display
NAME READY UP-TO-DATE AVAILABLE AGE
hello-display 1/1 1 1 104s
goodbye-display 1/1 1 1 29s
トリガーの作成
トリガーは、イベントを受信するサービスを定義する。ブローカは、トリガーを使用して適切な受信者へイベントを転送する。
個々のトリガーは、Cloud Event コンテキスト属性に基づいて、関連するイベントを選択するフィルターを設定できる。
次は、hello-displayに転送するトリガー
kubectl --namespace event-example apply --filename - << END
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
name: hello-display
spec:
filter:
attributes:
type: greeting
subscriber:
ref:
apiVersion: v1
kind: Service
name: hello-display
END
次は、サービス goodbye-display へ転送するトリガー
kubectl --namespace event-example apply --filename - << END
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
name: goodbye-display
spec:
filter:
attributes:
source: sendoff
subscriber:
ref:
apiVersion: v1
kind: Service
name: goodbye-display
END
トリガーが動作していることを確認する。 READYがTrue
になっている。
maho:k8s-knative maho$ kubectl --namespace event-example get triggers
NAME READY REASON BROKER SUBSCRIBER_URI AGE
goodbye-display True default http://goodbye-display.event-example.svc.cluster.local/ 49s
hello-display True default http://hello-display.event-example.svc.cluster.local/ 90s
イベントプロヂューサーの作成
イベント・プロヂューサーとして、curlコマンドが利用できるポッドを起動する。
kubectl --namespace event-example apply --filename - << END
apiVersion: v1
kind: Pod
metadata:
labels:
run: curl
name: curl
spec:
containers:
# This could be any image that we can SSH into and has curl.
- image: radial/busyboxplus:curl
imagePullPolicy: IfNotPresent
name: curl
resources: {}
stdin: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
tty: true
END
そのポッドからcurlコマンドで、デフォルト・ブローカーへメッセージを送る。これにより、イベント・コンシューマーへイベントが伝達される。
次は、ポッドに対話型でcurlコマンド実行した様子である。 各種ヘッダーと共に、メッセージ Hello Knative!
が送信する。
maho:k8s-knative maho$ kubectl --namespace event-example attach curl -it
Defaulting container name to curl.
Use 'kubectl describe pod/curl -n event-example' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
[ root@curl:/ ]$ curl -v "http://default-broker.event-example.svc.cluster.local" \
> -X POST \
> -H "Ce-Id: say-hello" \
> -H "Ce-Specversion: 0.3" \
> -H "Ce-Type: greeting" \
> -H "Ce-Source: not-sendoff" \
> -H "Content-Type: application/json" \
> -d '{"msg":"Hello Knative!"}'
> POST / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: default-broker.event-example.svc.cluster.local
> Accept: */*
> Ce-Id: say-hello
> Ce-Specversion: 0.3
> Ce-Type: greeting
> Ce-Source: not-sendoff
> Content-Type: application/json
> Content-Length: 24
>
< HTTP/1.1 202 Accepted
< Content-Length: 0
< Date: Sun, 15 Dec 2019 11:32:07 GMT
<
さらに、もういとつ、Goodbye Knative!
を送信する。
[ root@curl:/ ]$ curl -v "http://default-broker.event-example.svc.cluster.local" \
> -X POST \
> -H "Ce-Id: say-goodbye" \
> -H "Ce-Specversion: 0.3" \
> -H "Ce-Type: not-greeting" \
> -H "Ce-Source: sendoff" \
> -H "Content-Type: application/json" \
> -d '{"msg":"Goodbye Knative!"}'
> POST / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: default-broker.event-example.svc.cluster.local
> Accept: */*
> Ce-Id: say-goodbye
> Ce-Specversion: 0.3
> Ce-Type: not-greeting
> Ce-Source: sendoff
> Content-Type: application/json
> Content-Length: 26
>
< HTTP/1.1 202 Accepted
< Content-Length: 0
< Date: Sun, 15 Dec 2019 11:32:22 GMT
<
もう一つ、メッセージを送る。
[ root@curl:/ ]$ curl -v "http://default-broker.event-example.svc.cluster.local" \
> -X POST \
> -H "Ce-Id: say-hello-goodbye" \
> -H "Ce-Specversion: 0.3" \
> -H "Ce-Type: greeting" \
> -H "Ce-Source: sendoff" \
> -H "Content-Type: application/json" \
> -d '{"msg":"Hello Knative! Goodbye Knative!"}'
> POST / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: default-broker.event-example.svc.cluster.local
> Accept: */*
> Ce-Id: say-hello-goodbye
> Ce-Specversion: 0.3
> Ce-Type: greeting
> Ce-Source: sendoff
> Content-Type: application/json
> Content-Length: 41
>
< HTTP/1.1 202 Accepted
< Content-Length: 0
< Date: Sun, 15 Dec 2019 11:32:53 GMT
<
[ root@curl:/ ]$ exit
Session ended, resume using 'kubectl attach curl -c curl -i -t' command when the pod is running
イベントの受信の確認
ラベル app=hello-display で選択してログを表示する。トリガーで設定されているように、type: greeting
のメッセージだけが表示される。
maho:k8s-knative maho$ kubectl --namespace event-example logs -l app=hello-display --tail=100
☁️ cloudevents.Event
Validation: valid
Context Attributes,
specversion: 0.3
type: greeting
source: not-sendoff
id: say-hello
time: 2019-12-15T11:32:07.027965287Z
datacontenttype: application/json
Extensions,
knativearrivaltime: 2019-12-15T11:32:07Z
knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
traceparent: 00-61ee0ac97b456b370c0a05048ec8d05d-c162b1a4c3e458f3-00
Data,
{
"msg": "Hello Knative!"
}
☁️ cloudevents.Event
Validation: valid
Context Attributes,
specversion: 0.3
type: greeting
source: sendoff
id: say-hello-goodbye
time: 2019-12-15T11:32:53.413169436Z
datacontenttype: application/json
Extensions,
knativearrivaltime: 2019-12-15T11:32:53Z
knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
traceparent: 00-4c619d3d1369d8760441470a75c138ed-abf973121b2fd74e-00
Data,
{
"msg": "Hello Knative! Goodbye Knative!"
}
maho:k8s-knative maho$
app=goodbye-display
でフィルターしてログを表示する。こちらは、トリガーに設定したsource: sendoff
の属性だけが表示される。
maho:k8s-knative maho$ kubectl --namespace event-example logs -l app=goodbye-display --tail=100
☁️ cloudevents.Event
Validation: valid
Context Attributes,
specversion: 0.3
type: not-greeting
source: sendoff
id: say-goodbye
time: 2019-12-15T11:32:22.89815569Z
datacontenttype: application/json
Extensions,
knativearrivaltime: 2019-12-15T11:32:22Z
knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
traceparent: 00-15338f6cb75cc105d4f7ff2fb3d9c25d-36ae925bef0918a1-00
Data,
{
"msg": "Goodbye Knative!"
}
☁️ cloudevents.Event
Validation: valid
Context Attributes,
specversion: 0.3
type: greeting
source: sendoff
id: say-hello-goodbye
time: 2019-12-15T11:32:53.413169436Z
datacontenttype: application/json
Extensions,
knativearrivaltime: 2019-12-15T11:32:53Z
knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
traceparent: 00-4c619d3d1369d8760441470a75c138ed-abf973121b2fd74e-00
Data,
{
"msg": "Hello Knative! Goodbye Knative!"
}
maho:k8s-knative maho$
Knativeイベンティング(Eventing)の動作について、箇条書きにすると、以下のようになる。
- 対象とする名前空間にラベルを設定することで、ブローカーが立ち上がる
- イベント・コンシュマーのコンテナでは、クラウド・イベントのライブラリを利用して、イベントの受信待機にする
- トリガーを作成する。トリガーにはサービス名とフィルターを設定する
- イベント・プロヂューサーから、ブローカーのアドレスへ、ヘッダー、データを POSTで送り込む
- プローカーが、トリガーの条件設定に応じて、コンシュマーへイベントを送信する
まとめ
Knativeは、Istioを利用しており、インストールして環境を整備するだけで、結構なメモリ量を必要とする。ノードのメモリ量が不足した場合、動作障害に陥るので、ノードのメモリ監視が必須となる。そして、DNS名によるアクセスする必要があり、外部DNSサーバーと連動とロードバランサーとの連携がなくてはならない。
Knativeは、サーバーレスの手軽な実行環境をイメージしやすいが、Kubernetesクラスタに、自前でインストールして動作させるのは、いくつもの考慮点が必要となり、デベロッパーが開発に集中して利用するだけの環境ではない事がわかる。そのため、オンプレミスやクラウドで、独自にイントールして運用する場合は、共通インフラサービスとしてサポートを含めてSREチームが運用する必要があると考えられる。手軽に利用したいのであれば、パブリック・クラウドサービスのマネージド Knative を利用するのが得策と考えられる。