SBクラウド株式会社logo

Container Service for Kubernetes (ACK)に付帯されてるKnativeによるサーバーレスK8Sをデプロイする

Hi, データエンジニアの大原です。 最近、GCPでコンテナをサーバレスで実行するサービス「Cloud Run」の新機能として、非同期処理などを可能にする「CPU allocation on Cloud Run」機能が登場しました。

cloud.google.com

私自身、Alibaba CloudだけでなくGoogle Cloudも結構使うので、嬉しいニュースです。
とはいえ、この「CPU allocation on Cloud Run」機能はKnativeに近いので、Alibaba Cloudで模擬的な「CPU allocation on Cloud Run」、Knativeによるサーバレスk8sを構築してみます。
本記事では、Container Service for Kubernetes (ACK)に付帯されてるKnativeによるサーバーレスK8Sをデプロイする方法をご紹介します。

今回は Container Service for Kubernetes (ACK) クラスターを使用します。Container Service for Kubernetes (ACK) クラスターは、ノードの購入、運用・保守(O&M)、キャパシティプランニングの手間をかけずに、コンテナ化されたアプリケーションを展開することができます。
ASKはまた、サーバーレスアプリケーションのためのクラウドネイティブでクロスプラットフォームなオーケストレーションエンジンとして利用可能な Knative とも統合されています。Knative APIを呼び出してクラウドリソースを利用するには、ASKクラスタを作成し、クラスタに対してKnativeを有効にするだけで済みます。ちなみにKnativeコントローラーの費用は不要です。

www.alibabacloud.com

www.alibabacloud.com

f:id:sbc_ohara:20210916212753p:plain

このシナリオとしてのデモでは、OSSバケットへのZIPファイルのアップロードと、RDS MySQLデータベースへのアップロードレコードの追加を行うファイル管理サービスを構築します。
OSS bucketApsaraDB for RDS MySQL の作成プロセスはここでは省略します。

なお、Knativeのアーキテクチャについてはこちらにも書かれているので、参考にしてください。

qiita.com

serverless k8s cluster を作成

まず最初に、Alibaba Cloudにサーバーレスk8sクラスターを用意して、次のステップに進む準備をします。 サーバーレスk8sクラスターがOSSバケットやMySQLインスタンスと同じネットワーク下にあることを確認します。

f:id:sbc_ohara:20210916212812p:plain f:id:sbc_ohara:20210916212823p:plain f:id:sbc_ohara:20210916212832p:plain f:id:sbc_ohara:20210916212841p:plain f:id:sbc_ohara:20210916212849p:plain f:id:sbc_ohara:20210916212857p:plain

ACRでイメージファイルを準備

Alibaba Cloud Container Registry (ACR) を使ってGitBitBucketGitLabなどから複数のイメージを構築することができます。
ここでは、サーバーレスのk8sクラスタのターゲットイメージを管理するために、Local Repository を使用します。ACRの他にも、Docker Official イメージAlibaba Public Cloud イメージ などを利用することもできます。

f:id:sbc_ohara:20210916212912p:plain

イメージファイルの管理を行うために、 Alibaba Cloud Container Registry (ACR) でPersonal editionのインスタンスを作成します。

f:id:sbc_ohara:20210916212920p:plain f:id:sbc_ohara:20210916212931p:plain

インスタンスが作成されたら、他の操作を行う前に、Docker Login Password を設定する必要があります。
* RAMアカウントを使用している場合は、メインアカウントがすでにパスワードを設定しており、そうでない場合はパスワードを設定できませんのでご注意ください。

f:id:sbc_ohara:20210916212945p:plain f:id:sbc_ohara:20210916212953p:plain

namespaceら名前空間とリポジトリを作成します。

f:id:sbc_ohara:20210916213006p:plain f:id:sbc_ohara:20210916213014p:plain f:id:sbc_ohara:20210916213022p:plain f:id:sbc_ohara:20210916213029p:plain f:id:sbc_ohara:20210916213037p:plain

これで、ソースコードに基づいた関連画像を作成することができました。ここでは、ソースコードとして「local repository」を選択しているので、自分でDockerイメージをビルドして、ACRリポジトリにプッシュします。
Gitなどの他のコードソースを使用している場合は、ACR経由で build docker イメージ をすることができます。
ローカルリポジトリについては、詳細ページに以下のような操作ガイドやサンプルスクリプトがあります。

www.alibabacloud.com

f:id:sbc_ohara:20210916213050p:plain

ここでは、ECSインスタンスを使用して、以下のDockerfileに基づいて関連するDockerイメージを構築します。

# First stage: complete build environment
FROM maven:3.6.1-jdk-8-alpine AS builder

# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/

# package jar
RUN mvn clean package

# Second stage: minimal runtime environment
FROM openjdk:8-jre-alpine

# copy jar from the first stage, modify the jar name as your own
COPY --from=builder target/file_upload_serverless_demo-1.0-SNAPSHOT.jar file_upload_serverless_demo-1.0-SNAPSHOT.jar

EXPOSE 8080

# modify the jar name as your own
CMD ["java", "-jar", "file_upload_serverless_demo-1.0-SNAPSHOT.jar"]

docker serviceがインストールされ、正常に動作していることを確認します。
なお、Dockerfileではマルチステージビルドを採用しているため、dockerのバージョンは17.05以降である必要があります。

f:id:sbc_ohara:20210916213106p:plain f:id:sbc_ohara:20210916213113p:plain

関連するソースコードをECSサーバに置き、そのフォルダに入って、コマンド docker build -t bobdemo/bobdemo:v1 . でイメージをビルドします。

f:id:sbc_ohara:20210916213129p:plain f:id:sbc_ohara:20210916213136p:plain f:id:sbc_ohara:20210916213146p:plain

以下のように イメージファイルを確認し、関連するenvパラメータがあれば設定します。

docker run -p 8080:8080 -e MYSQL_HOST=xxx -e MYSQL_USER=xxx -e MYSQL_PASSWORD=xxx -e OSS_ENDPOINT=xxx -e OSS_ACCESSKEYID=xxx -e OSS_ACCESSKEYSECRET=xxx -e OSS_BUCKET=xxx -e OSS_LOCATION=xxx bobdemo/bobdemo:v1

f:id:sbc_ohara:20210916213159p:plain

イメージにタグを付けて、ACRのローカルリポジトリに入れます。

// Login ACR
docker login --username=bob@5171341380549220 registry-intl.ap-northeast-1.aliyuncs.com

// Add tag
docker tag bobdemo/bobdemo:v1 registry-intl.ap-northeast-1.aliyuncs.com/bobdemo/bobdemo:v1

// Push image
docker push registry-intl.ap-northeast-1.aliyuncs.com/bobdemo/bobdemo:v1

f:id:sbc_ohara:20210916213212p:plain f:id:sbc_ohara:20210916213220p:plain

サーバレスのk8sクラスターにデプロイ

docker runコマンドでは、-e`オプションを使って、RDSやOSSの接続情報など、アプリケーションで使用する環境変数を定義しています。この点については、サーバーレスのk8sクラスターも関連機能を提供しています。
この辺りについては Config map and secretsセクションを使用します。なお、AccessKey/IdやAccessKeySecretなどの機密情報はsecretsに格納することを強く推奨します。

f:id:sbc_ohara:20210916213232p:plain f:id:sbc_ohara:20210916213240p:plain f:id:sbc_ohara:20210916213249p:plain f:id:sbc_ohara:20210916213257p:plain

Deployments Page に移動し、"Create from Image" ボタンをクリックしてデプロイ作業に入ります。

f:id:sbc_ohara:20210916213310p:plain f:id:sbc_ohara:20210916213318p:plain f:id:sbc_ohara:20210916213326p:plain f:id:sbc_ohara:20210916213333p:plain f:id:sbc_ohara:20210916213340p:plain f:id:sbc_ohara:20210916213347p:plain f:id:sbc_ohara:20210916213355p:plain f:id:sbc_ohara:20210916213405p:plain f:id:sbc_ohara:20210916213413p:plain

Pod の状態を確認し、「Access Method」の情報をもとにサービス状況を確認します。

f:id:sbc_ohara:20210916213427p:plain f:id:sbc_ohara:20210916213436p:plain f:id:sbc_ohara:20210916213444p:plain f:id:sbc_ohara:20210916213452p:plain f:id:sbc_ohara:20210916213459p:plain

Scale Configuration(スケール設定) を使って常時稼働するPodの数を設定することもできますが、これを「0」に設定するとサービスが停止し、Podが稼働しなくなります。

f:id:sbc_ohara:20210916213511p:plain f:id:sbc_ohara:20210916213520p:plain f:id:sbc_ohara:20210916213528p:plain f:id:sbc_ohara:20210916213537p:plain

knativeを使ってサーバーレスのk8sクラスターにデプロイ

サービスへのビジネスリクエストがないときには、ポッドが稼働していないことを意味します。 上記の手順では、ポッドの数を「0」に設定したため、サービスが停止してしまいました。そのため、Knativeの助けを借りて、これを実現することができました。
Open source のKnativeでは、コスト削減のためにscale-to-zeroの仕組みを採用しています。ASKクラスターにポッドを作成すると、コールドスタートが発生します。しかし、コールドスタート中は、セッションのタイムアウトにより、クラスタがリクエストを処理できないことがあります。一方、Knativeコントローラのインストールに使用されるインフラストラクチャ・リソースに対しては課金されます。
ASK Knative は、オフピーク時にインスタンス数を0にスケールしません。代わりに、ASKはリザーブドインスタンスを使用します。リザーブドインスタンス を使用すると、コールドスタートを低コストで回避できます。また、Knativeのコントローラにお金を払う必要はありません。
knativeをデプロイするためには、まずサーバーレスのk8sクラスターにknativeのコンポーネントをデプロイする必要があります。今回はイベントトリガーを使わないので、処理中にサービス機能を選択するだけで済みます。

f:id:sbc_ohara:20210916213552p:plain f:id:sbc_ohara:20210916213601p:plain f:id:sbc_ohara:20210916213610p:plain f:id:sbc_ohara:20210916213618p:plain f:id:sbc_ohara:20210916213625p:plain

knativeがクラスタにデプロイされた後、それを使うことで、 サービスの管理 を行うことができました。

f:id:sbc_ohara:20210916213639p:plain f:id:sbc_ohara:20210916213647p:plain f:id:sbc_ohara:20210916213656p:plain f:id:sbc_ohara:20210916213704p:plain

参考として、Knativeは上記の手順で2つのデプロイメントを作成しますが、接尾辞が -reserve のものは予約済みのインスタンスに基づいています。

f:id:sbc_ohara:20210916213718p:plain

あとはデフォルトのドメイン情報を使ってサービスを確認します。

f:id:sbc_ohara:20210916213735p:plain f:id:sbc_ohara:20210916213742p:plain f:id:sbc_ohara:20210916213749p:plain

最後に

以上で、Container Service for Kubernetes (ACK)に付帯されてるKnativeによるサーバーレスK8Sをデプロイする方法をご紹介しました。Knative(=サーバレスk8s)があれば、サーバーのプロビジョニングと管理のタスクが不要になるため、k8sに対する労力がかなり減るはずなので、参考に頂ければ幸いです。

Special Thanks, bob.