🐕

FaaSをつくろう

2023/11/08に公開

FaaSを作りたい!っていう人はほとんどいないと思うけど、Platform Engineeringという言葉も出てきたしFaaSを開発・運用するための知見をまとめてみる

TL;DR

  • Kubernetesを活用しよう
  • OSSを活用しよう(Knativeなど)
  • ランタイムを自作する
  • イベント駆動を実現しよう
  • CLIがあると便利

はじめに

AWS Lambdaが登場してからしばらく経ち、開発の現場でFaaSが採用されることは普通になってきました
多くの人がパブリッククラウドのマネージドなFaaSを利用していると思います
ただ、セキュリティやビジネス要件、サービスの規模など何らかの要因でパブリッククラウドを採用できないケースもある
IaaSやPaaS、CaaSの運用事例は見かけるが、FaaSの事例は少ないので書いてみます

FaaSを構成する要素

FaaSを作るのに必要な主な要素を整理します

  • サーバーレス(オートスケール)
  • 関数を実行できる(ランタイム)
  • イベント駆動

Kubernetesを活用する

現時点でFaaSを構築するのであれば、Kubernetesに乗るのがベストな選択肢でしょう
今では多くのアプリケーションがContainerで動いていて、Kubernetesのエコシステムは強力なので活用できる
※AWS LambdaではFirecrackerが使われているが、独自に運用するプラットフォームでAWSの真似をしても成功するとは限らない

OSSの活用

自作しなくても独自のFaaSは構築できます
https://landscape.cncf.io/?group=serverless

Fission

比較的軽量なFaaSを構築するフレームワークで、導入や扱いもしやすいので簡易的なユースケースであれば選択肢に入る
https://fission.io/

OpenFaaS

こちらも選択肢に上がるが、Fissionと比べると少し学習コストがかかりそう
https://www.openfaas.com/

Knative

スケーリングを担当するServing、イベント駆動のEventing、関数を動かすためのFunctionsと複数のコンポーネントを活用してFaaSを構築することができる
https://knative.dev/docs/

OpenFunction

daprやKEDA、Knativeなど複数のOSSを活用しているプロジェクト
https://openfunction.dev/

OSSを使うかどうか

現在選択肢として有力なのはこの辺りだが、プライベートなFaaSが欲しい人はOSSをそのまま使うだけではユースケースが満たせないかもしれない
独自の要件やネットワーク、認証などOSSを改変する必要が出てくる場合もある
OSSを活用しつつ、必要な部分は独自に実装していくのが良さそうです

Knativeを使ってオートスケールを実現する

リクエスト数に応じてスケーリングする仕組みを自作するのは大変なのでOSSを活用します
KnativeのServingを使えば比較的楽に実現できます

ローカル環境でKnativeを試すのはとても簡単です
https://knative.dev/docs/getting-started/
https://knative.dev/docs/getting-started/first-service/

# Macでkindの場合
$ brew install knative/client/kn
$ brew install knative-sandbox/kn-plugins/quickstart
$ kn quickstart kind
 
$ kn service create hello --image ghcr.io/knative/helloworld-go:latest
$ curl http://hello.default.127.0.0.1.sslip.io
Hello World!

これだけで負荷に応じてスケールアウトして、リクエストがない時にはゼロスケールする機能を実現できます

ただし、本番で運用する場合は別の構築方法を取る必要があります
https://knative.dev/docs/install/yaml-install/
https://knative.dev/docs/install/operator/knative-with-operators/

各種設定値も理解してカスタマイズする必要も出てきます
https://knative.dev/docs/serving/

また、networking layerにKourier(envoy)やIstioを使用するため、その辺りの知識も必要になってきます
https://knative.dev/docs/serving/architecture/#networking-layer-and-ingress

ランタイムを作る

Dockerイメージを作らず関数をデプロイできると開発サイクルも速くなりとても便利なので、専用のランタイムを用意します
OSSを活用してもいいですが、独自の要件を吸収したり、使いやすいようカスタマイズができて面白い部分でもあるので自作してみます

実装イメージとしては、ベースとなるDockerイメージ(ランタイム)を用意しておいて、実行時に関数を読み込むようにします
インタプリタであるNode.jsの例を紹介しますが、コンパイラ言語であれば別の方法を取る必要があります(Goであれば共有ライブラリにビルドしておいて読み込むなど)

OSSのコードが参考になります
https://github.com/fission/environments/blob/master/nodejs/server.js

Expressで簡単に実装すると、このようになります
https://github.com/ryutoyasugi/faas-runtime-sample

index.js
const express = require('express')
 
const app = express()
app.use(express.json())
 
const handler = require('./function/handler').handler
 
app.all('*', (req, res) => {
  Promise
    .resolve(handler(req.body, {}))
    .then(result => { res.json(result) })
})
 
app.listen(8080, () => { console.log(`Running FaaS!`) })
Dockerfile
FROM node:slim
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY index.js ./index.js
EXPOSE 8080
CMD [ "node", "index.js" ]

最低限であればこの程度の実装でDockerイメージにしておきます

あとは実行したい関数を用意して、KnativeのServiceにバインドさせればOK

ksvc.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: faas-sample
spec:
  template:
    spec:
      containers:
        - image: ryutoyasugi/faas-runtime-sample:latest
          volumeMounts:
            - mountPath: /app/function
              name: handler-config
      volumes:
        - name: handler-config
          configMap:
            name: handler
# refs. https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-handler.html
$ cat handler.js
exports.handler = async function(event, context) {
  return 'Hello World!'
}
$ kubectl create cm handler --from-file=handler.js
$ kubectl apply -f ksvc.yaml
 
$ curl http://faas-sample.default.127.0.0.1.sslip.io
"Hello World!"

イベント駆動にする

何らかのイベントをトリガーに関数を実行させたい場合も、KnativeのEventingやTriggerMeshなどのOSSを利用することができます
ただし、どんなイベントをトリガーとしたいかは要件によって大きく変わるので、扱うイベントの種類によってOSSを利用するか独自実装にするか判断する必要があります

アーキテクチャによりますが、多くの場合でMQを利用することになると思います
マネージドなMQを使えればいいですが、独自にMQを運用する場合はコストがかかるでしょう

よくあるイベント駆動の例として、関数を定期実行したいユースケースもあります
KubernetesのCronJobで関数を実行すれば簡単に実現できます

CLIを作ろう

上記ランタイムの実装の紹介では、関数を動かすためにyamlを書いたりkubectlを使用していましたが、それだと手間がかかるのでCLIを作成すると便利になります
Goのcobraなどを活用すれば簡単にCLIの実装ができます
CLIだけで実現が難しければAPIも必要に応じて用意する必要があるでしょう

また、KubernetesのCRDやカスタムコントローラーを使って独自に定義を定めるのもいいと思います
実装にはKubebuilderなどのフレームワークを使用するのがいいでしょう

さいごに

プライベートなFaaSを開発・運用して費用対効果が得られるのは、大規模にプライベートクラウドをやっている開発組織に限られるかもしれませんが、FaaSを自分で作ってみるのもなかなか面白いので是非試してみてください!

Discussion