ラクスAdvent Calendar2021の22日目の記事を担当する@t_okkanです。前年のAdvent Calendar以来、1年ぶりのQiitaの投稿です。
最近お仕事でKubernetesを触っています。その中でKubernetes上でサーバーレスプラットフォームを構築できるKnativeを知り、入門してみました。
Knativeとは
KnativeはKubernetes上にサーバーレスなアプリケーションをデプロイ、実行、管理するためのプラットフォームです。
KubernetesとIstioなどのIngressを組み合わせてコンテナアプリケーションを手軽にデプロイすることができます。Kubernetesを使うことでクラウドプラットフォームに依存しないサーバーレス環境を構築できます。
オープンソースで開発されており、2021年11月にバージョン1.0がリリースされました。1元々は2018年からGoogleによって開発がされていましたが、バージョン1.0に上がると同時にCloud Native Computing Foundationに寄贈されました。2
KnativeはGCPのフルマネージドのコンテナ実行環境であるCloud Runの基盤ソフトウェアとして利用されています。3
すでにCNCFにはAzure Container Appsの基盤であるKEDAが採択されており、KnativeがCNCFに追加されたことによりサーバーレスコンピューティングの標準化が進むのではと期待しています。
今回はそんなKnativeのサーバーレスアプリケーションを公開する機能であるServingについて基本を理解し、ローカル環境で実際にサンプルアプリを動かしてみます。
Knativeの構成
Knativeは大きく分けてKnative Serving
とKnative Eventing
の2つのコンポーネントから構成されています。
Knative Eventingはイベントドリブンなアーキテクチャでアプリケーションを実行する機能を提供するコンポーネントです。元々はこの2つのコンポーネントに加えてKnative Build
というCIパイプラインのコンポーネントも存在しましたが、今は切り離されCI/CDパイプラインの構築ツールであるTektonとして独立して開発されています。4
今回はKnative Servingについて詳しくみていきます。
Knative Serving
Knative Servingはサーバーレスアプリケーションを公開し実行する機能を提供するコンポーネントです。コンテナアプリケーションをKubernetesのPodに配置し実行することができます。
Knative Servingの主な役割はオートスケーリングです。その中でも大きな特徴なのは、アクセスがない場合は起動しているPodの数を0にできるゼロスケーリングです。この機能により、エンドポイントにアクセスがあった時にPodを起動しアプリケーションを実行し、処理が終了すればPodを停止しPodの数を0までスケールインできます。そのため、ゼロスケーリングによりサーバーレス(リソースが不要)な状態になります。もちろんアクセスの負荷に合わせてスケールアウトも可能です。
オートスケーリング以外にも、リクエストを決められた割合で特定のPodに割り振るトラフィック機能などもあります。
Knative Serving の構成
Knative ServingはService
、Route
、Configuration
、Revision
から構成されます。それぞれKubernetesのカスタムリソースとして定義する必要があります。
Revision
Revisionはコンテナアプリケーションのコード(コンテナイメージ)と設定ファイル(環境変数など)を管理します。このRevisionをもとにオブジェクトを作成しコンテナアプリケーションのデプロイやオートスケーリングを行います。RevisioinはImmutableであり、後述するConfigurationがRevisionのバージョンを管理しています。
Configuration
ConfigurationはRevisionを管理するコンポーネントです。Revisionのコードと設定ファイルを分離し、変更履歴を管理します。新しいアプリケーションがデプロイされると、Configurationが更新され新しいRevisionが作成されます。
Route
RouteはRevisionに対するHTTPリクエストのトラフィックの割り振りを管理します。Revisionが複数ある場合は、Revisionごとに割合を指定してトラフィックを分散させることができます。
Service
KubernetesのコンポーネントにもServiceがありますが、Knative ServingのServiceとは別物となります。
ServiceはRouteやConfigurationを管理し、Knativeのワークロード全体を制御します。
以上で、Knative Servingの基本的な説明は終わります。続いて実際にローカル環境でKnative Serivigを動かしサーバーレスアプリケーションをデプロイしてみます。
Knative Servingの実践
環境
本記事で利用するソフトウェアやツールは以下のバージョンになります。
- Knative:1.1.0
- Docker:20.10.8
- Node.js:v14.16.0
- kind:0.11.1
- kubectl:1.21.3
Knativeのインストール
まずは以下の公式のページを参考にKnativeをインストールします。
今回はローカル環境のKubernetesクラスターにデプロイしていきます。Kubernetesクラスタはkind(Kubernetes in Dokcer)を利用し構築します。
では、kind、kubectl、Knative(kn)をbrewでインストールします。
$ brew install kind
$ brew install kubectl
$ brew install kn
今回はKnative quickstartを利用してKnative Servingの環境を構築します。
$ brew install knative-sandbox/kn-plugins/quickstart
本番環境でKnativeを利用する場合は、以下の構築手順を参考にしてください。
Knativeの起動
では必要な環境がそろいましたので、早速KnativeのKubernetesクラスタを構築していきます。以下のコマンドを実行して構築します。
$ kn quickstart kind
🎉 Now have some fun with Serverless and Event Driven Apps!
$ kind get clusters
knative
kind get clusters
を実行してknative
というクラスターが作成されていれば完了です。(少し時間がかかります)
サンプルコンテナアプリの作成
Knative上で動作するサンプルのサーバーレスコンテナアプリケーションを作成します。今回は公式で公開されているNode.jsのサンプルアプリケーションを参考に作成します。
詳細は折り畳みで非表示にしますので、公式のGithubか以下の折り畳みを表示して作成してください。(Docker Hubでイメージを公開するためアカウントが必要です)
サンプルコンテナアプリの作成
Node.jsアプリケーションの作成
以下のGithubを参考にNode.js(Express)のサンプルアプリケーションを作成します。
https://github.com/knative/docs/tree/main/code-samples/serving/hello-world/helloworld-nodejs
Node.jsのプロジェクトを作成し、expressをインストールします。
$ mkdir knative-service-sample
$ cd knative-service-sample
$ npm inint -y
$ npm install express
アプリケーションのコードとなるindex.js
は以下のような実装になります。
- index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
console.log('Knative service sample');
const target = process.env.TARGET || 'World';
res.send(`Hello ${target}`);
});
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Knative service sample listening on ${port}`);
})
package.json
にnpmのscriptを追加します。
- package.json
{
"name": "knative-service-sample",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.2"
}
}
Dockerイメージ作成
こちらも公式のGithubを参考に、Node.jsアプリケーションのDockerイメージをビルドし、Docker Hubにpushします。
- Dockerfile
FROM node:12-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "npm", "start" ]
- .dockerignore
Dockerfile
README.md
node_modules
npm-debug.log
イメージをビルドし、Docker Hubにpushします。({your name}
のところをご自身のDocker Hubのユーザーネームに変更してください)
$ docker build -t {your name}/kind-service-sample .
$ docker push {your name}/kind-service-sample
以上でアプリケーションの準備は完了です。
サーバーレスアプリケーションのデプロイ
では作成したコンテナアプリケーションをKnativeの基盤上にデプロイし、サーバーレスなアプリケーションの実行とゼロスケーリングの確認をしていきます。
まずは対比用に同じコンテナアプリケーションを実行するPodを作成しデプロイします。
以下のようなyamlファイルを作成しPodを作成します。
- pod.yml
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: sample-pod
image: docker.io/{your name}/knative-service-sample
env:
- name: TARGET
value: "Pod sample v.1.0"
$ kubectl apply --filename pod.yml
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
sample-pod 1/1 Running 0 52s
kubectl get pod
でsample-pod
と表示されれば完了です。
続いてKnativeでコンテナアプリケーションをデプロイするための設定ファイルを作成します。
- service.yml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: knative-service-sample
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{your name}/knative-service-sample
env:
- name: TARGET
value: "Knative service sample v.1.0"
作成できればKubernetesクラスターにKnative Serviceを作成します。
$ kubectl apply --filename service.yml
service.serving.knative.dev/knative-service-sample created
確認用のコマンドとして以下のコマンドを実行し、defaultのNamespaceに構築されるPodを追跡します。
$ kubectl get -w pods -n default
NAME READY STATUS RESTARTS AGE
knative-service-sample-00001-deployment-754d685b84-zjdkd 2/2 Running 0 39s
sample-pod 1/1 Running 0 90s
knative-service-sample-00001-deployment-754d685b84-zjdkd 2/2 Terminating 0 61s
knative-service-sample-00001-deployment-754d685b84-zjdkd 0/2 Terminating 0 91s
下記のようにkanative-service-sample
のSTATUSが、起動時のRunnning(コンテナが実行されている状態)からTerminating(コンテナが停止している状態)になっていると思います。
Knativeでは起動後にアクセスがなければ自動でゼロスケーリングします。
Knative Serviceの作成が完了すれば、エンドポイントを確認します。以下のコマンドを実行しknative-service-sample
のURLを取得します。
$ kubectl get ksvc knative-service-sample --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL
knative-service-sample http://knative-service-sample.default.127.0.0.1.sslip.io
URLが取得できれば、実際にアクセスしてPod数0からのオートスケーリングとゼロスケーリングを確認します。curlを利用してエンドポイントにアクセスします。
$ curl http://knative-service-sample.default.127.0.0.1.sslip.io
Hello Knative service sample v.1.0⏎
上記のようにExpressのアプリケーションのレスポンスが返ってくれば成功です。
Podの監視の方では以下のように、アクセスが発生するとknative-service-sample
のPodが生成されSTATUSがPending → Runningに変化していることがわかるかと思います。PendingからRunningまで約1秒で完了できます。初回アクセスは少し遅くなりますが、2回目以降はすぐにレスポンスが返ってきます。
その後しばらくアクセスがないとPodの状態がTerminatingとなり、Pod数が0になります。
$ kubectl get -w pods -n default
NAME READY STATUS RESTARTS AGE
knative-service-sample-00001-deployment-754d685b84-zjdkd 2/2 Running 0 39s
sample-pod 1/1 Running 0 90s
knative-service-sample-00001-deployment-754d685b84-zjdkd 2/2 Terminating 0 61s
knative-service-sample-00001-deployment-754d685b84-zjdkd 0/2 Terminating 0 91s
knative-service-sample-00001-deployment-754d685b84-zjdkd 0/2 Terminating 0 91s
knative-service-sample-00001-deployment-754d685b84-zjdkd 0/2 Terminating 0 91s
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 Pending 0 0s ⬅️ アクセスを検知しPodを起動
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 Pending 0 0s
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 ContainerCreating 0 0s
knative-service-sample-00001-deployment-754d685b84-dzqj2 1/2 Running 0 1s ⬅️ Podが起動され処理が開始
knative-service-sample-00001-deployment-754d685b84-dzqj2 2/2 Running 0 2s
knative-service-sample-00001-deployment-754d685b84-dzqj2 2/2 Terminating 0 63s ⬅️ アクセスがないためPodを削除
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 Terminating 0 94s
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 Terminating 0 94s
knative-service-sample-00001-deployment-754d685b84-dzqj2 0/2 Terminating 0 94s
Knativeを終了する
Knativeを使い終わったら以下のコマンドでクラスターを停止します。結構リソースを消費するので利用しない時は停止をお勧めします。
$ kn service delete knative-service-sample
最後に
Knativeをローカル環境に構築し、簡単なサンプルアプリケーションを動かしてみました。今回はKnative Servingだけを触りましたが、Knative Eventingの方も触っていきたいと思います。