LoginSignup
7
2

More than 1 year has passed since last update.

Knative Servingでサーバーレスを始める

Posted at

ラクス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 ServingKnative 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はServiceRouteConfigurationRevisionから構成されます。それぞれKubernetesのカスタムリソースとして定義する必要があります。

Knative Serving Overviewから引用

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
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
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
Dockerfile
FROM node:12-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "npm", "start" ]
  • .dockerignore
.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
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 podsample-podと表示されれば完了です。
続いてKnativeでコンテナアプリケーションをデプロイするための設定ファイルを作成します。

  • service.yml
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の方も触っていきたいと思います。

7
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2