いよいよ「functionだけ書いて簡単にデプロイできるプラットフォーム」作りに入ります。Knativeを用いてFaaSを実行できるプラットフォームを構築する上で何が実現できればよいでしょうか。KLRを念頭に置いてKnativeとの差分を考えてみましょう。
- KnativeのServiceで実行するのは「コンテナ」 -> なるべく「コンテナ」のビルド、プッシュ意識せずに済むように
- リクエストを受けるのは「サーバー」 -> 「サーバー」なのでリクエストをfunctionに渡す部分を準備する
これらを実現するにあたり今回はtm
コマンドとOpenFaaSのWatchdog
(classic)を利用します。
OpenFaaSもまたKubernetesなどを活用しながらFaaSプラットフォームを提供するためのOSSです。WatchdogはOpenFaaSで活用されているサーバー実装で、受けたリクエスト情報を標準入力を介してfunctionプロセスに渡して処理を実行し、その結果をサーバーのレスポンスとして返します。
https://docs.openfaas.com/architecture/watchdog/
このWatchdogを活用することで開発者はfunctionを書くのに集中できます。
faas
フォルダを作り、つぎのファイルをmain.go
という名前で保存してください。
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
)
func main() {
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatalf("Unable to read standard input: %s", err.Error())
}
fmt.Println(string(input))
}
Watchdogとfunctionが同梱されたコンテナイメージをKnativeのServiceとして利用するためにはつぎの実行が必要です。
- アプリケーションのビルド
- コンテナイメージのビルド
- コンテナイメージのレジストリへのプッシュ
- Kubernetesへのデプロイ
これらはそれぞれを実行するためのコマンド(go build、docker build/push、kubectlなど)を利用することももちろん可能です。しかし、KLRでも利用したtm
コマンドを利用するとTektonと使ったイメージの準備やKnative ServingのAPIを利用したデプロイが可能です。
OpenFaaSのWatchdog
を活用したTask
はつぎのように書くことができます。
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: faas-go-runtime
spec:
inputs:
params:
- name: IMAGE
description: Where to store resulting image
- name: SSH_KEY
description: SSH key
default: "placeholder"
- name: DIRECTORY
description: The subdirectory of the workspace/repo
default: "."
resources:
- name: sources
targetPath: /workspace
type: git
steps:
- name: dockerfile
image: gcr.io/kaniko-project/executor:debug-v0.13.0
command:
- /busybox/sh
args:
- -c
- |
cd /workspace/workspace/$(inputs.params.DIRECTORY)
cat <<EOF > Dockerfile
FROM golang:1.13.2-alpine as builder
# Skip known public key check to be able to pull from private repositories
ENV GIT_SSH_COMMAND "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
RUN apk --no-cache add git curl ca-certificates openssh \
&& curl -sL https://github.com/openfaas/faas/releases/download/0.9.6/fwatchdog > /usr/bin/fwatchdog \
&& chmod +x /usr/bin/fwatchdog
WORKDIR /go/src/handler
COPY . .
RUN if [ -f "$HOME/.ssh/id_$(inputs.params.SSH_KEY)" ]; then \
eval "\$(ssh-agent -s)"; \
ssh-add $HOME/.ssh/id_$(inputs.params.SSH_KEY); \
fi
RUN go get -v
RUN go install
FROM alpine:3.10.2
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/bin/fwatchdog /usr/bin/fwatchdog
COPY --from=builder /go/bin/handler /usr/bin/handler
ENV fprocess "/usr/bin/handler"
CMD ["/usr/bin/fwatchdog"]
EOF
- name: export
image: gcr.io/kaniko-project/executor:debug-v0.13.0
args:
- --context=/workspace/workspace/$(inputs.params.DIRECTORY)
- --dockerfile=Dockerfile
- --destination=$(inputs.params.IMAGE)
# Workaround not to use default config which requires gcloud credentials
# to pull base image from public gcr registry
# https://groups.google.com/d/msg/kaniko-users/r5yoP_Ejm_c/ExoEXksDBAAJ
env:
- name: DOCKER_CONFIG
value: "/"
faas-go-runtime.yaml
として保存し、tm
コマンドで利用できるように登録してください。
$ tm deploy task -f ./faas-go-runtime.yaml
ローカルのfunctionを利用してfunctionをデプロイする準備が整いました。つぎのコマンドを実行してください。SecretはKLRのワークショプで作成したものを再利用します。
$ tm deploy service go-function -f ./faas --build-template faas-go-runtime --registry-secret gcr-image-puller --wait
つぎのコマンドを実行して動作を確認してください。
$ curl -H "Host: go-function.default.example.com" http://$IP_ADDRESS --data '{"Name": "Foo"}'
このワークショップでは既存のパッケージやコマンドを利用しながら簡易なFaaSプラットフォームを構築しました。
運用していくのあたっては監視や権限管理はもちろん、つぎのようなコンセプトから検討が必要なはずです。項目毎に参考情報を掲載します。
- 文字通り外部に提供するFunction as a Service
- 既存のプラットフォームにワークロードの選択肢としてのfunctionを提供する
単体で存在させるのか、既存のプラットフォームへ組み込むのかで大きく異なりそうです。既存のプラットフォームへ組み込むにあたっては運用・監視、認証・認可、CI/CD、開発時のコマンド操作なども合わせなければかえって学習コスト、運用コスト等が増してしまう可能性があります。
たとえば、KnativeのAPIを操作するためのCLIは今回利用したtm
コマンドをはじめ、つぎのように様々なものがあります。
しかし、扱うコマンドはkubectlだけにし、これらの実装やバイナリをプラグイン形式でkubectlに組み込んだり、カスタムリソース含むマニフェストを生成・適用するコマンドを作成するなどのアプローチが考えられます。
- HTTP/gRPCリクエストを受けて処理するもの
- イベントを受けて処理するもの
今回はシンプルなHTTPリクエストを緩くハンドルしましたが、リクエスト情報をより細やかに型定義したり、gRPCで受けたいケースもあるでしょう。
つぎのようなSDKとセットで提供される新形式のWatchdogやgRPC用のサンプルが役立ちます。
また、Knative Eventingのイベントをハンドリングするためのfunctionを実行する場合、イベントがCloudEventsに準拠しているため言語毎のSDKを積極的に活用するとよさそうです。
こちらも手軽にFunctionを書けるようにするためのライブラリとして参考にしてみてください。