目次


Knative: その実態と魅力

開発者にとって Kubernetes ネイティブのプラットフォームがもっともな選択肢となる理由

Comments

最近ではサーバーレス、KubernetesKnative の話題でもちきりです。まずはともあれ、このエコシステム内での Knative の位置付けと、その差別化要因を探っていきましょう。すでに Kubernetes を使用しているアプリケーション開発者にとって、Knative は理想的なプラットフォームになります。Knative は開発者がコードの作成そのものに集中できるよう、インフラストラクチャーやネットワーキングを管理する負担を減らすためのツールを揃えているからです。こうした Knative の目的は他のサーバーレス・プラットフォームとそれほど変わりません。けれども多くのサーバーレス・プラットフォームがトップダウン方式のアプローチでコード中心の新たなインターフェースを提供している中、Knative は既存の Kubernetes エクスペリエンスをさらに使いやすい形にしたツールとサービスを提供することを重視しています。したがって、Kubernetes で経験を積んでいる開発者 (Kubernetes ユーザー) はすぐにその恩恵を受けて、簡単にサーバーレス開発を開始することができます。この目的のために、Knative では Kubernetes 自体と同じパターン (コントローラー)、API (kube-api)、インフラストラクチャー (kubernetes リソース) が採用されています。さらに、Knative にはゼロまでスケーリングできる scale-to-zero 機能が備わっているため、アイドル状態のアプリケーションにかかる使用コストはまさにゼロになります。また、Blue/Green デプロイでサーバーレス・アプリケーションの新しいバージョンをテストすることもできます。

Knative のコンポーネント

Knative を構成する主なサブプロジェクトには次の 3 つがあります。

  • Serving: scale-to-zero のリクエスト主導のコンピューティング機能を提供します。基本的に、サーバーレス・プラットフォームの実行とスケーリングに対処するコンポーネントです。
  • Build: CI/CD ワークフローを作成する際に役立つ、run-to-completion 機能を提供します。Serving はこのコンポーネントを使用して、リソース・リポジトリーを、アプリケーションを含むコンテナー・イメージに変換します。
  • Eventing: 疎結合されたイベント駆動型サーバーレス・アプリケーションの構築を可能にする、抽象化されたデリバリーおよびサブスクリプション・メカニズムを提供します。

重要な点として、Knative では上記のコンポーネント (およびその他多くのサブコンポーネント) を疎結合することを目指しています。つまり、サービス・プロバイダーがこれらのコンポーネントをさまざまに組み合わせて、必要に応じて置き換えられるということです。例えば、Serving は Build を利用してソース・リポジトリーをサーバーレス・アプリケーションに変換できますが、経験豊かなユーザーが別のビルド・プラットフォームを使って独自の Build 構造を組み立てたとしても、Knative でそれを処理できます。

Serving コンポーネント

この記事では、Serving サブプロジェクトに焦点を当てます。このサブプロジェクトは、Knative を使い始める際のうってつけの出発点となるからです。 Knative Serving を使用するために十分に理解しておかなければならない主なリソースには、Service、Route、Configuration、Revision の 4 つがあります。

Knative の構造
Knative の構造

出典: https://github.com/knative/docs/tree/master/serving

Revision: これは、アプリケーションの単一のインスタンスを表すリソースです。Revision インスタンスには特定のイメージと特定の構成オプション一式 (環境変数など) が不変の形で含まれています。Revision インスタンスが、アプリケーションを実行する Kubernetes デプロイメントを含む他のリソースを管理して、イメージと構成を具現化します。

Configuration: これはリビジョンを作成するためのインターフェースであることから、アプリケーション・ライフサイクル管理のほとんどは、このリソースを通して行われます。Configuration はいわばデプロイメント・スクリプトのようなもので、アプリケーション・イメージとその構成を定義します。これは Revision と同様ですが、Configuration インスタンスに含まれる値は可変です。このインスタンス内の環境変数やイメージ・タグを更新することで、新しいバージョンをデプロイできます。値が変更されるたびに、アプリケーションの新しいインスタンス (Revision) が作成されます。したがって、所定の Configuration インスタンスには常に対応する Revision インスタンスが少なくとも 1 つあります。

Route: このリソースによって、トラフィックは特定のリビジョンに転送されます。単純なデプロイメントで Service を使用する場合、通常は Route リソースを認識する必要はありません。デフォルトの動作では、すべてのトラフィックを最後 (最新) のリビジョンに送信するからです。Route インスタンスを使用して、トラフィック分割をパーセンテージで指定することもできます (例えば a/b デプロイメントで、トラフィックの 10 パーセントをリビジョン 2 に、90 パーセントをリビジョン 1 に送信するなど)。このトピックについてはこの記事で説明しませんが、この機能の具体的な例は https://github.com/knative/docs/tree/master/serving/samples/traffic-splitting で確認できます。

(Knative) Service: Kubernetes の Service リソースと混同しないでください。Knative Service はさまざまなリソースを結合して 1 つの包括的なサーバーレス・アプリケーションにする最上位のリソースです。単純な使用ケース (下記の hello-world の例など) では、アプリケーションをデプロイする際にやり取りする唯一のリソースとなります。Service が作成されると、Route と Configuration の各インスタンスも作成されます。これについては、以下で詳しく説明します。

Hello, World!

ここでは、Knative がインストールされて使用できるようになっていること、kubectl を使用して Knative クラスターにアクセスできることを前提とします (Knative のインストールについて詳しくは、https://github.com/knative/docs/blob/master/install/README.md を参照してください)。

kubectl を使用して Knative Service を定義しましょう。以下の内容をファイル (service.yaml) に書き込んで、次のコマンドを実行してください。

$ kubectl -f service.yaml

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: <helloworld-go docker image>
            env:
            - name: TARGET
              value: "Go Sample v1"

これだけで、helloworld-go アプリケーションのリクエスト駆動型インスタンスを作成できます。

このアプリケーションの接続先 IP アドレスを取得するには、次のコマンドを実行して、出力される EXTERNAL IP フィールドの値をコピーします。

$ kubectl get svc knative-ingressgateway --namespace istio-system

NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                      AGE
knative-ingressgateway   LoadBalancer   10.23.247.74   35.203.155.229   80:32380/TCP,443:32390/TCP,32400:32400/TCP   2d

アプリケーションのドメインを取得するには、次のコマンドを実行して DOMAIN フィールドの値をコピーします。

$ kubectl get ksvc helloworld-go

NAME            DOMAIN                              LATESTCREATED         LATESTREADY           READY     REASON
helloworld-go   helloworld-go.default.example.com   helloworld-go-00001   helloworld-go-00001   True

IP アドレスとドメインを取得できたら、次のコマンドを使用してアプリケーションにリクエストを送信できます。

$ curl -H "Host: {DOMAIN}" http://{IP_ADDRESS}

Hello World: Go Sample v1!

おめでとうございます。初の Knative サーバーレス・アプリケーションのデプロイに成功しました!

Service

このアプリケーションを構成するリソースを見ていきましょう。まずは、次のコマンドを実行して Service の定義内容を調べます。

$ kubectl get ksvc helloworld-go

NAME            DOMAIN                              LATESTCREATED         LATESTREADY           READY     REASON
helloworld-go   helloworld-go.default.example.com   helloworld-go-00001   helloworld-go-00001   True

このコマンドからの出力には、アプリケーションのドメインと、アプリケーションの最後に作成されたリビジョンと準備済みのリビジョンが示されます。これは最初に定義したリソースそのものなので、これ以上説明する点はありません。Knative が作成した他のリソースを見ていきましょう。

Configuration

Knative Service を定義したときに、アプリケーションの Configuration インスタンスが自動的に作成されています。該当するインスタンスを表示するには、次のコマンドを使用します。

$ kubectl get configuration helloworld-go -o yaml

apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:
  name: helloworld-go
  namespace: default
  <other_metadata>
spec:
  generation: 1
  revisionTemplate:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"
      creationTimestamp: null
    spec:
      container:
        env:
        - name: TARGET
          value: Go Sample v1
        image: <image_url>/helloworld-go
        name: ""
        resources: {}
      containerConcurrency: 1
      timeoutSeconds: 1m0s
status:
  conditions:
  <conditions>
  latestCreatedRevisionName: helloworld-go-00001
  latestReadyRevisionName: helloworld-go-00001
  observedGeneration: 1

読みやすくするために、出力の一部は削除してあります。上記の出力を見るとわかるように、この Configuration インスタンスの名前 (helloworld-go) は、定義した Service の名前と同じです。このルールは、Service を定義するときに常に適用されます。この出力で最も興味深い部分は、spec.revisionTemplate セクションです。

Revision

Configuration インスタンスから作成された Revision インスタンスを確認しましょう。それには、次のコマンドを実行します。

$ kubectl get revision helloworld-go-00001 -o yaml

apiVersion: serving.knative.dev/v1alpha1
kind: Revision
metadata:
  name: helloworld-go-00001
  namespace: default
  <metadata>
spec:
  container:
    env:
    - name: TARGET
      value: Go Sample v1
    image: <image_url>
    name: ""
    resources: {}
  containerConcurrency: 1
  generation: 1
  timeoutSeconds: 1m0s
status:
  conditions:
  <conditions>
  serviceName: helloworld-go-00001-service

Configuration インスタンスの場合と同じく、ここでも読みやすくするために Revision インスタンスの出力を一部削除しています。前述のとおり、構成を変更すると、常に新しいリビジョンが作成されます。つまり、すべての構成に例外なく、初期リビジョンが作成されるということです。これは最初のバージョンなので、リビジョン番号として 00001 が割り当てられています。このリビジョンの spec の内容が、構成に含まれていた revisionTemplate と一致することにも注目してください。

試しに、(Service インスタンスを使用して) 構成に変更を加えて、新しく作成されるリビジョンを確認しましょう。まず、service.yaml ファイル内の行 value: "Go Sample v1"value: "Go Sample v2" に変更します。

次に、kubectl apply -f service.yaml を使用して Service インスタンスを更新します。

次のコマンドを使用して、現在のリビジョンの内容を表示します。

$ kubectl get revision

NAME                  SERVICE NAME                  READY     REASON
helloworld-go-00001   helloworld-go-00001-service   True
helloworld-go-00002   helloworld-go-00002-service   True

ご覧のとおり、新しいリビジョン (helloworld-go-00002) が作成されています。curl コマンドをもう一度実行すると、アプリケーションのレスポンスが変更されていることも確認できます。

$ curl -H "Host: {DOMAIN}" http://{IP_ADDRESS}

Hello World: Go Sample v2!

Route

最後に、Knative が Service インスタンスに対して作成した Route インスタンスを調べます。次のコマンドを実行してください。

$ kubectl get route helloworld-go -oyaml

apiVersion: serving.knative.dev/v1alpha1
kind: Route
metadata:
  name: helloworld-go
  namespace: default
  <other metadata>
spec:
  generation: 1
  traffic:
  - configurationName: helloworld-go
    percent: 100
status:
  address:
    hostname: helloworld-go.default.svc.cluster.local
  <conditions>
  domain: helloworld-go.default.example.com
  domainInternal: helloworld-go.default.svc.cluster.local
  traffic:
  - percent: 100
    revisionName: helloworld-go-00002

この出力を見るとわかるように、他のリソースと比べて Route リソースの spec セクションはかなり簡潔なものになっていて、generation と traffic の 2 つのセクションだけから構成されています。traffic セクションには、(この例でのように) ユーザーが構成を指定して、トラフィックを常に最新のリビジョンに転送するか、特定のリビジョンに転送するかを制御できます。注目すべき点として、特定のリビジョンをターゲットにする場合はリビジョンのリストを指定し、リビジョンごとに異なるターゲット・パーセンテージを設定することもできます。最初の設定では新しいリビジョンにわずかなトラフィックだけが送信されるようにして、それから徐々に変更をロールアウトできるというわけです。

まとめ

この記事では単純な helloworld サーバーレス・アプリケーションをデプロイし、全面的にリクエストの負荷に基づいてアプリケーションをスケーリングする方法を説明しました。また、Knative の Serving プロジェクトを構成するコンポーネントそれぞれの概要と、kubectl を使用して基本的な構成要素を調べる方法も説明しました。この記事は Knative の表面をかじっただけにすぎません。Knative の詳細について、Knative ドキュメント・リポジトリー(Https://github.com/knative/docs) で調べることをお勧めします。 新しく身に付けた Knative のスキルを磨くには、クラウド上の Knative インストールを自動化する方法を説明している記事をご覧ください。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing
ArticleID=1066297
ArticleTitle=Knative: その実態と魅力
publish-date=07182019