仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

Knativeをmicrok8sで触ってみる

Knativeとは

Knativeとは、Kubernetes上にサーバレスコンピューティングの基盤を構築する、オープンソースソフトウェアです。 イベントをトリガーにしてコンテナを起動して、負荷に応じコンテナ実行数を自由に増減させるスケーラビリティを提供します。KnativeはGoogle Cloud上の「Cloud Run」を構成する主要なソフトウェアになっています。

knative.dev

Knativeのメリットとして、アプリケーションの利用率に応じてゼロスケールできること、利用率の増加に伴って自動でスケールすること、Kubernetesの細かい部分を知らないでもサービスの作成ができることがメリットとして挙げられます。逆にデメリットは...色々な部分が抽象化されるので、動かなくなった場合は幅広い知識が要求されます。

Knative Servingについては「Knative Serving in Production」をご覧ください。かなり細かい部分についても書かれているので、非常に良い資料だと私は思います。

Knative EventingについてはKnative Eventingでイベント駆動なアプリケーションを体験するなどをご覧ください。

microk8sとは

microk8sとは開発者、クラウド、クラスター、ワークステーション、エッジ、IoT向けの最小構成のKubernetesを提供する、オープンソースソフトウェアです。Ubuntuの開発、サポートを提供するカノニカルが開発を主導しています。

microk8s.io

割とハードウェアリソースが厳しいIoT機器から、一般のサーバー、クライアントなどで動かすことが可能です。Kubernetes環境のセットアップは時間や手間がかかって大変ですが、microk8sならsnapパッケージを使ってインストールするだけで簡単にクラスターを動かせるだけでなく、ワンライナーで豊富なアドオンを追加することで色々な環境を作ることができます。

周辺のソフトウェアなどの準備

それでは早速Knativeを触ってみようと思います。 Ubuntu Server 20.04最新版をインストールして、アップデート、再起動を実行します。

sudo apt update && sudo apt upgrade && sudo reboot

microk8sを入れます。Ubuntu Serverのインストーラーを使ってセットアップする際に、microk8sをインストールすることもできます。このオプションを選択すると、Ubuntu Serverのインストール後、初回起動時に最新のstableバージョンのmicrok8sがインストールされます。

sudo snap install microk8s --channel=stable --classic 
sudo snap install kubectl --channel=stable --classic 
sudo microk8s enable dns rbac

type:LoadBalancerを使うため、MetalLBを有効化(オプション)します。 指定するIPアドレスはホストのIPアドレス(多くの場合一つしか設定されていないはずなので次のような指定方法)を設定しましょう。

microk8s enable metallb
Enter each IP address range delimited by comma: 192.168.0.49-192.168.0.49

kubeconfigを書き込みます(snapコマンドを使ってmicrok8s.kubectlコマンドのエイリアスを張る方法もあるのですが、後述のKnative CLIを使うためにこんなことをする必要があります)。

mkdir ~/.kube/ && touch ~/.kube/config
microk8s config > ~/.kube/config

Knative CLIであるknを次の手順でインストールします。

curl -Lo ./kn https://github.com/knative/client/releases/download/knative-v1.3.1/kn-linux-amd64
chmod +x ./kn
sudo mv ./kn /usr/local/bin/kn

Knative のセットアップ

mictrok8sのアドオンにKnativeというアドオンがありますが、これはバックエンドにIstioを利用しています。最近のIstioはリソース要件が厳しいらしく、手元の環境では正常に動かすことができませんでした。現在のKnativeはIngressコントローラーとしてIstioの他、Gloo、Ambassador、Kourierが利用できます。

このうちKourierは、Red Hatが主導して開発が進められているKnativeのためのIngressであり、Istio Ingressの置き換えを目的としているようです。ミニマムなリファレンス実装を目指して開発されています。

今回はKourierを使ってみます。

Knative Servingのインストール

今回はKnative Serving 1.3.0をインストールする例です。バージョン1.0以降はバージョンが変わってもほぼ同じ流れで動かせるようです。

kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.3.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.3.0/serving-core.yaml

ネットワークレイヤーのインストール

Knativeのネットワークレイヤとして軽量なKourier Ingressを使ってみます。

curl -Lo kourier.yaml https://github.com/knative/net-kourier/releases/download/knative-v1.3.0/kourier.yaml

以下の設定部分を書き換えます (MetalLBを使う場合は設定書換えは不要)。

...
apiVersion: v1
kind: Service
metadata:
  name: kourier
  namespace: kourier-system
  labels:
    networking.knative.dev/ingress-provider: kourier
    app.kubernetes.io/component: net-kourier
    app.kubernetes.io/version: "1.3.0"
    app.kubernetes.io/name: knative-serving
    serving.knative.dev/release: "v1.3.0"
spec:
  ports:
    - name: http2
      port: 80
      protocol: TCP
      targetPort: 8080
      nodePort: 31080                # 追加
    - name: https
      port: 443
      protocol: TCP
      targetPort: 8443
      nodePort: 31443                # 追加
  selector:
    app: 3scale-kourier-gateway
  type: NodePort                     # LoadBalancerから変更
...

設定を適用

kubectl apply -f kourier.yaml

Knative ServingがKourierを使用するように構成

kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'

DNSの構成

ここではワイルドカードDNSサービスであるsslip.ioを使用するよう構成します(ファイル内に「args: ["-magic-dns=sslip.io"]」みたいな記述がある)。

kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.3.0/serving-default-domain.yaml

config-domainに以下を追加して、カスタムドメイン*.192.168.0.49.sslip.io192.168.0.49に解決します(IPアドレスは環境に合わせて置き換えるか、もしくは127.0.0.1を設定してください)。

kubectl patch configmap/config-domain --namespace knative-serving --type merge --patch '{"data":{"192.168.0.49.sslip.io":""}}'

この設定により、例えばPodの名前がhelloで名前空間がdefaultのPodへはhttp://hello.default.192.168.0.49.sslip.ioのようなアドレスが払い出されます。

サービスの確認

サービスを確認します。NodePortでアクセスする場合は、URLにポート番号をつけてアクセスしてください。

kubectl get svc kourier -n kourier-system

NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
kourier   NodePort   10.152.183.112   <none>        80:31080/TCP,443:31443/TCP   27m

or

NAME      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
kourier   LoadBalancer   10.152.183.182   192.168.0.49   80:32083/TCP,443:31470/TCP   9m56s

これ以降では、MetalLBを使った場合の実行例と結果例を記述します。NodePortでアクセスする場合はhttpアクセスは31080ポートをつけてアクセスしてください。

Knative Servingを触ってみる

試しにこれを作ってみましょう。

kn service create hello --image gcr.io/knative-samples/helloworld-go --port 8080 --env TARGET=World
Creating service 'hello' in namespace 'default':

  0.010s The Route is still working to reflect the latest desired specification.
  0.080s ...
  0.096s Configuration "hello" is waiting for a Revision to become ready.
167.698s ...
167.970s Ingress has not yet been reconciled.
168.128s Waiting for load balancer to be ready
168.287s Ready to serve.

Service 'hello' created to latest revision 'hello-00001' is available at URL:
http://hello.default.192.168.0.49.sslip.io

(160秒もかかっていますが、もっと良いマシンで実行したら、もっと速く作られるはずです)

LBアクセス

curlコマンドでアクセスしてみましょう。ブラウザでアクセスしても同じ結果になります。

curl http://hello.default.192.168.0.49.sslip.io
Hello World!

Knative Servingのサービスリストは次のコマンドで確認できます。

kn service list
NAME    URL                                       LATEST        AGE   CONDITIONS   READY   REASON
hello   http://hello.default.192.168.0.49.sslip.io   hello-00001   26m   3 OK / 3     True 

Scaling to Zero

デフォルトの設定ではおよそ数分経過すると、サービスが縮退します。

-wオプションを使ってデプロイメントの状態を監視してみましょう。最終的にはPod数0としてアプリケーションが存在することがわかります(あくまで0にするだけで、サービスは削除されない)

kubectl get deployment -l serving.knative.dev/service=hello -w
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
hello-00001-deployment   1/1     1            1           118s
hello-00001-deployment   1/0     1            1           2m
hello-00001-deployment   1/0     1            1           2m
hello-00001-deployment   0/0     0            0           2m1s
...

アクセスがあるとPodが再作成され、サービスが復活します。

curl hello.default.192.168.0.49.sslip.io
Hello World!

kubectl get deployment -l serving.knative.dev/service=hello -w
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
hello-00001-deployment   1/1     1            1           3m15s

Basics of Traffic Splitting

トラフィックの分割もKnativeはサポートしています。

まず、現在のサービスの状態を確認します。

kn service list
NAME    URL                                       LATEST        AGE   CONDITIONS   READY   REASON
hello   http://hello.default.192.168.0.49.sslip.io   hello-00001   47m   3 OK / 3     True  

Knative Serviceの更新されたバージョンをデプロイします。

kn service update hello --env TARGET=Knative
Updating Service 'hello' in namespace 'default':

  0.067s The Configuration is still working to reflect the latest desired specification.
  9.259s Traffic is not yet migrated to the latest revision.
  9.461s Ingress has not yet been reconciled.
  9.603s Waiting for load balancer to be ready
  9.736s Ready to serve.

Service 'hello' updated to latest revision 'hello-00002' is available at URL:
http://hello.default.192.168.0.49.sslip.io

リビジョンが変わったことが確認できます。

kn service list
NAME    URL                                       LATEST        AGE     CONDITIONS   READY   REASON
hello   http://hello.default.192.168.0.49.sslip.io   hello-00002   8m30s   3 OK / 3     True    

新しいリビジョンにアクセスしてみる(普通にcurlでアクセスしても良い)

echo "Accessing URL $(kn service describe hello -o url)"
curl "$(kn service describe hello -o url)"
Hello Knative!

リビジョンを確認すると、現在はhello-00002のサービスに100%アクセスが振り分けられていることがわかります。つまり何度アクセスしてもリビジョンhello-00002にアクセスされ、hello-00001のサービスへはアクセスできません。

kn revisions list
NAME          SERVICE   TRAFFIC   TAGS   GENERATION   AGE     CONDITIONS   READY   REASON
hello-00002   hello     100%             2            2m59s   3 OK / 4     True    
hello-00001   hello                      1            51m     3 OK / 4     True 

リビジョン間でトラフィックを分割してみます。今回は50%の確率でhello-00001hello-00002のサービスを表示できるように設定します。実行するコマンドは次のとおりです。

kn service update hello --traffic hello-00001=50 --traffic @latest=50
Updating Service 'hello' in namespace 'default':

  0.059s The Route is still working to reflect the latest desired specification.
  0.204s Ingress has not yet been reconciled.
  0.335s Waiting for load balancer to be ready
  0.432s Ready to serve.

Service 'hello' with latest revision 'hello-00002' (unchanged) is available at URL:
http://hello.default.192.168.0.49.sslip.io

kn revisions list
NAME          SERVICE   TRAFFIC   TAGS   GENERATION   AGE     CONDITIONS   READY   REASON
hello-00002   hello     50%              2            4m58s   3 OK / 4     True    
hello-00001   hello     50%              1            53m     3 OK / 4     True    

アクセスすると、大体半分の確率でhello-00001hello-00002のサービスに振り分けられます。

curl http://hello.default.192.168.0.49.sslip.io
Hello World!
curl http://hello.default.192.168.0.49.sslip.io
Hello Knative!
...

他のサービスを作成

他のサービスを作成して、そのコンテナアプリケーションにも引き続きアクセスできることを確認します。

kn service create hello2 --image gcr.io/knative-samples/helloworld-go --port 8080 --env TARGET=World
Creating service 'hello2' in namespace 'default':
...
Service 'hello2' created to latest revision 'hello2-00001' is available at URL:
http://hello2.default.192.168.0.49.sslip.io

kn service list
NAME     URL                                           LATEST         AGE     CONDITIONS   READY   REASON
hello    http://hello.default.192.168.0.49.sslip.io    hello-00002    33m     3 OK / 3     True    
hello2   http://hello2.default.192.168.0.49.sslip.io   hello2-00001   5m41s   3 OK / 3     True   

curl http://hello.default.192.168.0.49.sslip.io
Hello World!
curl http://hello2.default.192.168.0.49.sslip.io
Hello World!

問題ないようです。

サービスの削除

最後にサービスをクリーンアップしましょう。

kn service delete hello
kn service delete hello2
kn service list

microk8sのおかげで、KubernetesとKnativeの理解がちょっとだけ深まりました。ありがとうございます。 この記事と同じ内容は、以下のGistで公開しています。そちらも合わせてご覧ください。

参考にした情報