フューチャー技術ブログ

ぼくのなつやすみ5 -Knativeを知ろう-

はじめに

こんにちは、TIG所属の村田です。

フューチャー夏休み自由研究連載10本目の記事です。今回は夏休みの自由研究企画ということで、Cloud RunのベースであるKnativeを触ってみたいと思います!

実際に触ってみる

今回利用した各コンポーネントのバージョン

利用したコンポーネントとバージョンは以下です。記事投稿時点(2020.08.14)での最新バージョンを利用しています。

コンポーネント バージョン
Kubernetes (GKE) 1.17.8-gke.17
Knative (Serving) v0.16.0
kn v0.16.0
Istio v1.6.8

Kubernetes Clusterの構築

今回はKnativeが主役なのでクラスタはGKEを使ってさくっと構築してしまいます。

なるべく最新に近いバージョンを使いたいのでRelease ChannelをRapid Channelに設定。

課金を抑えるためにPreemptibleインスタンスに設定。

Istioは自前インストールしたいので Enable Istio のオプションにはチェックを入れないように。

上記以外の設定はデフォルトのままでクラスタを構築します(名称は my-summer としました)。
また、以降の手順にて kubectl コマンドを使うため、手元の端末で設定しておきます。

gcloud container clusters get-credentials my-summer --zone <YOUR_ZONE> --project <YOUR_PROJECT_ID>

Knativeのインストール

次にKnativeのインストールを進めます。

Knative v0.16.0 requires a Kubernetes cluster v1.16 or newer

https://knative.dev/docs/install/any-kubernetes-cluster/
とありますが、今回利用するGKEバージョンは 1.17.8-gke.17 なので問題なさそうです。

以下のコマンドでCRDとcore-componentをインストールします。個々のコンポーネントがどのような役割をしているかまでは追えてないですが、Autoscalingなどベースとなる挙動を支えるコンポーネント群がインストールされます。

kubectl apply --filename https://github.com/knative/serving/releases/download/v0.16.0/serving-crds.yaml
kubectl apply --filename https://github.com/knative/serving/releases/download/v0.16.0/serving-core.yaml

Istioのインストール

次にネットワークレイヤの設定をします。筆者はてっきりIstioが必須かと思ってのですが、執筆時点で Ambassador Contour Gloo Kong Kourier も選択肢にありました。

筆者はIstioが好きなので今回はIstioを選びました。記事投稿時点(2020.08.14)での最新バージョンである 1.6.8 をダウンロードします。

$ curl -L https://istio.io/downloadIstio | sh -
Downloading istio-1.6.8 from https://github.com/istio/istio/releases/download/1.6.8/istio-1.6.8-osx.tar.gz ...
Istio 1.6.8 Download Complete!

Istio has been successfully downloaded into the istio-1.6.8 folder on your system.

Next Steps:
See https://istio.io/docs/setup/kubernetes/install/ to add Istio to your Kubernetes cluster.

To configure the istioctl client tool for your workstation,
add the /Users/y-murata/Downloads/istio-1.6.8/bin directory to your environment path variable with:
export PATH="$PATH:/Users/xxx/istio-1.6.8/bin"

Begin the Istio pre-installation verification check by running:
istioctl verify-install

Need more information? Visit https://istio.io/docs/setup/kubernetes/install/

実行バイナリにPATHを通しておきます。

cd istio-1.6.8
export PATH=$PWD/bin:$PATH

Istioをインストールします。インストール先は ~/.kube/configcurrent-context に設定されているクラスタです。

istioctl install

ちゃんとIstioのインストールができてそうですね。

$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
istio-system istio-ingressgateway-6c77d7f498-62dr9 1/1 Running 0 91s
istio-system istiod-58f84ffddc-c7mmf 1/1 Running 0 110s
istio-system prometheus-5db67458fb-m8qq6 2/2 Running 0 90s
knative-serving activator-76984478f7-9h48r 1/1 Running 0 47h
knative-serving autoscaler-598d974c99-hsblm 1/1 Running 0 41h
knative-serving controller-9b998cd47-fx8bk 1/1 Running 0 47h
knative-serving webhook-658874f97-wcr5k 1/1 Running 0 41h
...

Knativeの設定

さて、次にKnative Istio Controllerをインストールします。これによりKnativeとIstioが連携可能になります。

$ kubectl apply --filename https://github.com/knative/net-istio/releases/download/v0.16.0/release.yaml
clusterrole.rbac.authorization.k8s.io/knative-serving-istio created
gateway.networking.istio.io/knative-ingress-gateway created
gateway.networking.istio.io/cluster-local-gateway created
mutatingwebhookconfiguration.admissionregistration.k8s.io/webhook.istio.networking.internal.knative.dev created
validatingwebhookconfiguration.admissionregistration.k8s.io/config.webhook.istio.networking.internal.knative.dev created
secret/istio-webhook-certs created
configmap/config-istio created
deployment.apps/networking-istio created
deployment.apps/istio-webhook created
service/istio-webhook created

次にDNSの設定をします。本来はしっかりとDNSの設定をするのですが、今回はxip.ioを使用して名前解決を行います。以下のKubernetes Jobで xip.io がDNSのsuffixとして利用されるように設定されます。

kubectl apply --filename https://github.com/knative/serving/releases/download/v0.16.0/serving-default-domain.yaml

少しだけxip.ioについて補足しておくと、例えば 10.0.0.1.xip.io というドメインは 10.0.0.1 と名前解決されることになります。今回はこの仕組みを利用します。

ここまででServing Componentのインストールは完了です。

Knative Servingの動作確認

Go製のサンプルアプリがGCRから取得可能なので動作を確認してみます。アプリはKnative CLIの kn から行うのが一番簡単とのことで、筆者のMac端末へはHomebrew経由でインストールしました。

brew tap knative/client
brew install kn

アプリをデプロイします。

kn service create helloworld-go --image gcr.io/knative-samples/helloworld-go --env TARGET="Go Sample v1"

しっかりPodがデプロイされていることを確認できました!

$ kubectl get po
NAME READY STATUS RESTARTS AGE
helloworld-go-txzsq-1-deployment-758cc788c-46nqt 2/2 Running 0 48s

立ち上がったアプリへリクエストをなげてみます。まずはエンドポイントの確認から実施するのですが、確認は kn 経由と kubectl 経由の2種類の方法があるようです。

kn 経由の場合は以下。

$ kn service describe helloworld-go

Name: helloworld-go
Namespace: default
Age: 4m
URL: http://helloworld-go.default.xx.xx.xx.xx.xip.io

Revisions:
100% @latest (helloworld-go-txzsq-1) [1] (4m)
Image: gcr.io/knative-samples/helloworld-go (pinned to 5ea96b)

Conditions:
OK TYPE AGE REASON
++ Ready 3m
++ ConfigurationsReady 3m
++ RoutesReady 3m

kubectl の場合は以下。

$ kubectl get ksvc helloworld-go
NAME URL LATESTCREATED LATESTREADY READY REASON
helloworld-go http://helloworld-go.default.xx.xx.xx.xx.xip.io helloworld-go-txzsq-1 helloworld-go-txzsq-1 True

当然ですが kn 経由のほうがアプリのリビジョンなど参照可能な情報がたくさんあります。これによってエンドポイントが分かりました。

http://helloworld-go.default.xx.xx.xx.xx.xip.io

リクエストを投げてみます。

$ curl http://helloworld-go.default.xx.xx.xx.xx.xip.io
Hello Go Sample v1!

このサンプルアプリは TARGET という環境変数に渡した値を利用して Hello World: ${TARGET}! と返してくるらしいので、期待通りに動作していることが確認できました。

また、ちゃんとscale-to-zeroの挙動も確認できました。リクエストが途切れてから90秒ほどでPodがTerminatingの状態になり、最終的にPodがKillされることを確認できました。

$ kubectl get po
NAME READY STATUS RESTARTS AGE
helloworld-go-txzsq-1-deployment-758cc788c-8b89j 2/2 Terminating 0 94s

$ kubectl get po
No resources found in default namespace.

もう一度リクエストを投げてみると、レスポンスに少し時間がかかっていた(体感ですが2秒ほど)のでPod起動のリードタイムがかかっていると考えられます。

$ curl http://helloworld-go.default.xx.xx.xx.xx.xip.io
Hello Go Sample v1!

$ kubectl get po
NAME READY STATUS RESTARTS AGE
helloworld-go-txzsq-1-deployment-758cc788c-ndrdq 1/2 Running 0 6s

ちなみにPodのREADYは 1/2 の状態でもレスポンスを返しているみたいだったのでPodの中身をもう少し見てみます(リクエストから90秒以内に確認コマンドを打つ必要があるので注意してください)

$ kubectl describe pod helloworld-go-txzsq-1-deployment-758cc788c-lg8w4
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 17s default-scheduler Successfully assigned default/helloworld-go-txzsq-1-deployment-758cc788c-lg8w4 to gke-my-summer-default-pool-e721bfa1-gpd8
Normal Pulled 16s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Container image "gcr.io/knative-samples/helloworld-go@sha256:5ea96ba4b872685ff4ddb5cd8d1a97ec18c18fae79ee8df0d29f446c5efe5f50" already present on machine
Normal Created 16s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Created container user-container
Normal Started 16s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Started container user-container
Normal Pulled 16s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Container image "gcr.io/knative-releases/knative.dev/serving/cmd/queue@sha256:ab38418f2e13dfc21d48c64af0589f4eae5c40fc34a5e02f48b24b7156391d22" already present on machine
Normal Created 16s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Created container queue-proxy
Normal Started 15s kubelet, gke-my-summer-default-pool-e721bfa1-gpd8 Started container queue-proxy

Pod内には user-containerqueue-proxy の2つのContainerが起動しますが、 user-container が起動した時点でレスポンスの返却はできてるみたいですね。

調べてみたところ queue-proxyuser-container のサイドカーコンテナとしてデプロイされるもので、プロキシとしてアプリケーションへのトラフィック量を監視する役割を担うようです。

The queue-proxy’s main purpose is to measure and limit concurrency to the user’s application.

https://github.com/knative/serving/blob/d7bea3390c9fca2713c05b3bbd83690d430c7cfc/docs/scaling/SYSTEM.md

queue-proxy の情報は autoscaler というコンポーネントが収集し、Podのスケールサイズ決定に利用されています。

$ kubectl get service -n knative-serving
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
activator-service ClusterIP 10.20.8.213 <none> 9090/TCP,8008/TCP,80/TCP,81/TCP 4d6h
autoscaler ClusterIP 10.20.14.196 <none> 9090/TCP,8008/TCP,8080/TCP 4d6h
controller ClusterIP 10.20.10.247 <none> 9090/TCP,8008/TCP 4d6h
...

最後に

今回はKnativeに触ってみましたが、「Cloud RunはKnativeベースである」というふわっとした理解だった部分について、より具体的なイメージが湧きました。「Cloud RunはきっとKnativeをいい感じにラップしてるんだろうな」と思っていましたが、触ってみた感じだとあまりラップ層は分厚くなく割と素に近い状態で使われているのではと感じました。

また、個人的に一番学びだったのは「KnativeにとってIstioは必須ではない」という部分です。勝手にKnativeはIstioベースのサービスメッシュ機構に依存するものだと思っていたのですがそれは違いました。

最近はクラウドベンダーからマネージドサービスとして提供されるOSSも多いですが、あえて自前での構築を試してみると意外な発見があって面白いですね!!

さて、次回のエントリーは新人の仁木さんが担当されます! 仁木さんは研修中に競プロのバーチャルコンテストを企画しちゃったスーパーな新人さんです。ぜひご期待ください!

入社1か月の新人が競技プログラミングのバーチャルコンテストを企画するまで

また、本記事で触れたCloud Run・Istioについては以下のような記事もあがっているので読んでみてください。