VMware の Kubernetes 開発ソリューションとして知られる Tanzu Application Platform (以降 TAP )ですが、実は「サーバレス」な開発も得意なこともご存じでしょうか?このブログでは、今なお論争されている「サーバレス vs コンテナ」を「和解」させるTAPが提唱するアプローチを紹介します。
サーバレス vs コンテナ?
Amazon Web Services 社が主催するイベント re:Invent 2022 の中に、Competition of the modern workloads: Serverless vs Kubernetes on AWS という興味深いセッションがありました。このセッションはいわゆるモダンアプリケーションをサーバレスもしくはコンテナどちらで動かすべきかを徹底討論したものです。中身の詳細は触れないですが、このセッションで改めて浮き彫りになっているのが、コンテナとサーバレスを「AND」の補完関係ではなく、「VS」の比較関係として捉えられている方が多いという実情です。(セッションの終わりでは、「どちらも使える。使い分けはワークロードによって異なる」とは補足はしていました)
さて「サーバレス」という言葉をきいて期待されることは大別すると3つの理由があると思います。
- コストの最適化
- 軽量な開発スタイル
- サービス間の連携に利用
この一つ目の理由「コスト最適化」が多くの方が期待していることだと思われます。なにせ「サーバレス = サーバがないこと」であり IaaS の稼働コストが劇的に安くなることが期待されます。さらにサーバがないので、構築やバックアップなどの運用コストからも解放されます。ところがこのコスト、実態は様々な要因が複雑に絡むので、サーバレスとコンテナも単純比較がしにくいのものです。一例としてさきほどのAWS re:Invent 2022 のセッションでは、コストが比較した結果は引き分けになっています。いずれにせよ、多くのメディアでこの点はすでに取り上げられているので「コストの最適化」から一旦離れたいと思います。
さて、二点目の「軽量な開発スタイル」について議論したいと思います。サーバレス中心で育った開発者は、コンテナでの開発を敬遠している傾向が強いように見受けられます。それは両者の開発スタイルに大きな差があり、そしてサーバレスのほうが圧倒的に開発が簡単に見えるからです。
TAP はこのギャップに着目して、いままで紹介してきた Web アプリケーションのコンテナ化だけでなく、より軽量かつコンテナを意識させないような開発を実現できることを目指しています。その機能の一つがこのブログで取り上げるFunction Buildpacksです。
サーバレスの「軽量な開発スタイル」について、なぜサーバレスの開発になれてしまうとコンテナでの開発が敬遠してしまうのか、そして TAP がどのようにその溝をとりのぞいているのかを紹介します。
なお3つ目の効果、「サービス間の連携に利用」は別のブログで取り上げる予定です。
経験したら病みつき?サーバレスの開発スタイル
さて、冒頭から「サーバレス」という言葉をつかっていますが、人によっては「Function」もしくは「Function as a Service(FaaS)」と使い分ける方もいると思います。Amazon Lambda が登場した際には、「サーバーがない」こともそうでしたが、「シンプルなコード」が多くの衝撃を与えました。
Hello World をかえす Web サーバについて取り上げましょう。Python を例に、通常の開発スタイルでアプリケーションを書いてみます。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run()
Flask とよばれる Web フレームワークおよび、アプリケーションサーバーの gunicorn を依存関係としてインストールが必要になったので以下の requirements.txt を用意します。
Flask
gunicorn
さらに、コンテナの場合、Python のランタイムの準備を Dockerfile で用意するする必要があります。さきほどの依存関係のインストールや gunicorn の起動もいれます。
FROM python:latest
COPY requirements.txt /
RUN pip3 install -r /requirements.txt
COPY . /app
WORKDIR /app
ENTRYPOINT ["gunicorn", "server:app"]
Python の入門書にもかかれるような典型的なコードですが、注目したいのが “Hello World” を表示させるのに、必要なそれ以外の箇所です。上の例からすると
- Flask などの Web サーバーフレームワークを指定することが必要なこと
- @app_route アノテーションにより、どの URL エンドポイントで起動するかの明示指定が必要
- “requirements.txt” に Flask および gunicorn の依存関係指定が必要
- 最終的な起動コマンドの指定が必要
- (コンテナにする場合)Python のランタイムを Dockerfile で表現することが必要
些細なことですが、こういった「 Hello World をかえす Web サーバ 」以外の箇所が開発者が負うべき責任になってしまうことです。たとえば、Flask を選定してしまった以上 Flask のセキュリティ脆弱性が出た場合はそれは開発者の責任です。
では、Lambda の Hello World のコードをみてみましょう。
def lambda_handler(event, context):
return "Hello World"
これだけです。(AWSに限って言えば、HTTPSエンドポイント機能もリリースされたのでこのままデプロイできます。)細かい点は省きますが先ほどのコードに比べ、「Hello World をかえす Web サーバ」がよりシンプルに実現できています。これがいわゆる Function と称される所以です。特にWebサーバー周りやランタイムの管理(Flask, @app_route, gunicorn, Dockerfile など)がすべて不要となり、開発者からの責任が解放されます。これらの考慮点はプラットフォームが責任をもってくれることの解放感は強いメリットです。
コンテナ開発は長らく、この二つの例で言うところの前者の開発スタイル(以後、従来型開発と呼称)を前提にしていました。そのため、サーバレスでスキルを培った開発者はコンテナを敬遠する理由が(事実かはさておき)コンテナ開発は時代遅れにみえてしまうこと、責任範囲が増えてしまうことへの不安であると推測できます。
では、この二つの開発のずれをどう「和解」させていくか?その解決が VMwareのアプローチがここから紹介する TAP の Function Buildpacks です。
Function Buildpacks でサーバレスと同じ開発スタイルを実現
TAP 1.2 より Beta としてサポートされたのが、Function Buildpacks とよばれるものです。機能としては新しいものですが実は歴史が長く、Pivotal 社によって開発が進められていた Project Riff から始まり、Knative とよばれる Kubernetes 上でサーバレス開発を実現するフレームワークなどを経て誕生したものです。OSS 化もされており Github でみることができます。
さて、早速コードをみたいと思います。 Hello World の例をこの Function Buildpacks だと以下のようなコードです。
def main(req):
return "Hello World!"
このコード以外に必要なものを、requirements.txt (この例では、空で動きます)ぐらいであり、それがそろえば以下のように Tanzu CLI で環境に Kubernetes にデプロイできます。(環境にもよりますが、1 -2 分とかからず完了します)
tanzu apps workload create \
--source-image SOURCE_IMAGE \
--local-path ./ \
--type web \
--build-env BP_FUNCTION=func.main \
py-func-hello
TAP の機能により、これだけでKubernetes上のコンテナとして稼働し、アクセスできる http エンドポイントがあるので応答を確認できます。
先ほどの Lambda の開発スタイルと非常に似ており、従来型開発の例で挙げた考慮点がほぼクリアできています(それどころかさらに気楽に)。コンテナ化や Kubernetes へのデプロイは TAP が実施してくれます。Web サーバやランタイムの用意は Function Buildpacks が透過的に担っているため、開発者は気にする必要がありません。なお、Functions Buildpack とは直接は関係ないですが、Knative Service とよばれる、これまた TAP に含まれるコンポーネントによって Scale to 0、つまりリクエストがなければ、コンテナを 0 になります。まさに Kubernetes でサーバレスが実現できています。
Function Buildpacks は上の例で言えば、BP_FUNCTION 環境変数の有無を判断して、機能が有効になります。それが TAP の良い点でもあり、BP_FUNCTION 変数がなければ、いわゆる従来型のコードも同じようにデプロイできる点です。たとえば、従来型(サンプル)のコードは以下のコマンドでデプロイができます。
tanzu apps workload create \
--source-image SOURCE_IMAGE \
--local-path ./ \
--type web \
py-hello
よって、気軽に Functions 機能を利用する、しないを選べるのも TAP の特徴です。
TAP でサーバレスとコンテナのよりよい関係
さて、前章で Function Buildpacks を紹介しました。これによってコードがいわゆるサーバレスに近しいシンプルなものであると説明しましたが、「なんで、これを Kubernetes でやるの?」について最後に解説します。
まず、いわゆるクラウドが提供するサーバレス機能は万能とは言えない点です。それぞれのクラウドプロバイダーが指定する閾値を理解する必要がでてきます。特にサイズやAPIコール数の制約は、規模が大きくなるにつれ悩みのタネになっていきます。二つ目の理由が「コスト最適化」が仇となり、不必要に複雑な構成になりやすい点です。端的にいうとサーバレスは常時起動アプリケーションは向いていません。常時起動をさけるために、クラウド他のサービス(Amazonでいえば、SQSやSNSが一例)の利用が多くなったり、マイクロサービスの粒度を細かくする必要がでていきます。当然それを行う上で、クラウドおよびコーディングの上級者むけの知識が要求されます。最後に、サーバレス自体新しい技術なので、従来型開発をすべて再現できるわけではない点です。多くのOSS ライブラリが複雑にからむモノリスなアプリケーションや、Web フロントエンドのような常時稼働が前提のアプリケーションは、サーバレスに書き直す難易度は高くなっていきます。結果として、サーバレスだけですべてのアプリケーションを完結させるには無理が出てきます。
これらを考慮して、Function Buildpacks を TAP 上で利用するメリットは以下のとおりです。
- プロバイダ側が設けた制約を意識しなくてよい
- Kubernetes 上に配置されるので、コストコントロールがしやすい = 不必要に複雑な構成にしなくてよい
- 気軽にサーバレス機能のオンオフ(前例でいうBP_FUNCTION 変数)ができるので従来型開発と共存しやすい
もうひとつのメリットが開発者ではなく、運用者の存在です。
運用者は全てのアプリケーションが Kubernetes に配置されることで管理が統一しやすくなるだけでなく、エコシステムにより豊富な機能を基盤に追加できます。さらに TAP の機能である Application Accelerator の開発、セキュリティの脆弱性検査機能、TAP 1.3 からサポートされた動的 API 登録機能など、開発者を支援していく機能も追加できます。Tanzu の概念である Platform as a Product、その要素である開発者・運用者双方が成長させていく基盤をつくっていくことができます。
まとめ
このブログでは「サーバレス vs コンテナ」を「和解」させ、さらに基盤を成長させていく TAP のアプローチを紹介しました。なお、サーバレスについてはもうひとつ期待される効果が「サービス間の連携」です。これも TAP では Cloud Native Runtimes とよばれる機能で補完できます。これはまた次回のブログで紹介します。