OpenFaaSは聞いたことがあるでしょうか。
まだ生まれて半年ほどしか経っていませんが、GithubStar数は6000を超える勢いで成長している有望なフレームワークです。
2017年10月18日に行われたServerless Meetup Osaka #4でOpenFaaSのことを聞いたので早速試してみました。
どこまでやるかというと、
OpenFaaSのAuthorであるAlex Ellisのブログで紹介されている、OpenFaaSでGo言語の関数を動かして、さらにクラウドに展開するところまでやってみたいと思います。
今回やってみて、日本語の情報がほぼ皆無で、英語でも情報がかなり少なかったので、丁寧に手順を載せておきます。
OpenFaaSいいところ
まとめとして先に良かったことを書いておきます。
- Dockerイメージになるなら何でも動かせる
- とはいえ、メジャーな言語のテンプレートを用意している
- 手軽さは既存のFunction as a Serviceと変わらない
スケール・コストなどの観点はチュートリアルでは評価しきれないので、言及しません。
Faas CLIのインストール
Docker CE 17.05
以上がインストールされていることが必要です。
コマンドでさくっとインストールできます。
curl -sSL https://cli.openfaas.com | sudo sh
brewコマンドが使えるなら、
brew install faas-cli
OpenFaaSデプロイ環境をローカルに追加
Kubernetesに比べると簡単に扱えると感じたDocker Swarmにデプロイします。
まずはDocker Swarm自体の初期化して、managerとworkerを動作させます。
docker swarm init
以下のコマンドでFaaSスタックをデプロイします。
git clone https://github.com/openfaas/faas && \
cd faas && \
git checkout 0.6.5 && \
./deploy_stack.sh
このデプロイした環境はどこにいるかというと、Docker Service (Swarm Mode) として動いているので、docker service ls
のコマンドで確認できます。
ローカルのデプロイ環境にはhttp://localhost:8080
でアクセスできるので開くと、次のようなFunction管理ポータルが表示されます。
Linuxの場合はhttp://127.0.0.1:8080
で開く必要があるかもしれません。
Go言語のインストール
こちらの公式インストールガイドを参考にインストールしてください。
v1.8.3かそれ以降が必要です。
gvm
でインストールしても問題ありません。
OpenFaaSプロジェクトの生成
まずはプロジェクトフォルダーを作成します。
mkdir -p $GOPATH/src/functions
cd $GOPATH/src/functions
続いてFaaS CLIを使ってプロジェクトテンプレートを生成します。
名前は参考記事通りgohash
です。
faas-cli new --lang go gohash
プロジェクトの中身を確認する
gohash.yml
ファイルにはテンプレートで作成されたFunctionとローカルの実行環境についての設定が書き込まれています。
provider:
name: faas
gateway: http://localhost:8080
functions:
gohash:
lang: go
handler: ./gohash
image: gohash
gohash/handler.go
にはHello Worldなコードが書かれています。
package function
import (
"fmt"
)
// Handle a serverless request
func Handle(req []byte) string {
return fmt.Sprintf("Hello, Go. You said: %s", string(req))
}
そしてtemplate
内には、各言語のテンプレートもありますが、Go言語のものがちゃんとあります。
main.go
ではOpenFaaSの仕様通り、 STDIN を受け取って、対応するFunctionを呼び出した結果を STDOUT に送るというシンプルな作りとなっています。
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"handler/function"
)
func main() {
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatalf("Unable to read standard input: %s", err.Error())
}
fmt.Println(function.Handle(input))
}
Dockerfileが各言語の環境ごとに用意されていて、ミニマルな実行環境を作成し、開発者が用意したFunctionコードをコンテナ内に格納してから最後にWatchdogを起動するという動きになっているようです。
WatchdogはGo言語で書かれた小さなHttpサーバーです。
FROM golang:1.8.3-alpine3.6
# ---------- 略 -----------
WORKDIR /go/src/handler
COPY . .
# ---------- 略 -----------
CMD ["./fwatchdog"]
まずはデプロイしてみる
テンプレートを導入した時点で、環境構築が無事に完了しているか確認するために、HelloWorldのままデプロイしてみます。
faas-cli build -f gohash.yml
faas-cli deploy -f gohash.yml
BuildはDockerのイメージをダウンロードするところから始まるので、最初の実行は1分ほど待つかもしれません。
とくにエラーが表示されずに、
という風な表示がされたら成功です。
http://localhost:8080
にアクセスしてgohash
が追加されているか確認します。
左のFunction一覧からgohash
を見つけたら適当なRequest bodyを打ち込んで INVOKE します。
これで無事にデプロイできることが確認できました。
Functionを実装していく
今回はgo1.9を使ったので、コンテナ内のgoも同じバージョンにしておきます。
今回のコードでは変更しなくても特に問題にはならないと思います。
コンテナ生成Dockerfileを書き換えてしまいます。
FROM golang:1.8.3-alpine3.6
FROM golang:1.9-alpine3.6
go言語のパッケージ管理ツールとしてdep
をインストールします。
go get -u github.com/golang/dep/cmd/dep
そして、Goのデータ(Struct)からハッシュを生成するstructhash
をdep
を使ってインストールします。
ただし、faas-cliがDockerコンテナをビルドするときに、template/go/
をワーキングディレクトリとしてビルドを行うため、dep
の実行はこのディレクトリに移動して行う必要があります。
cd template/go/
dep init
dep ensure -add github.com/cnf/structhash
こうすると、Gopkg.lock
、Gopkg.toml
、vernder/
がtemplate/go/
以下に生成されます。
ちなみに、gvm
環境だとdep ensure
でライブラリがうまくインストールされないことがありますが、goのコードがビルドされるのはコンテナ内なので、一応そのまま進めても大丈夫です。
きちんと開発を進める場合はgvm linkthis
でdep
がしっかりと使えるようにします。
それでは、受け取った文字列データからハッシュを生成するコードに変更しましょう。
package function
import (
"fmt"
"github.com/cnf/structhash"
)
// S is sample struct
type S struct {
Str string
Num int
}
// Handle a serverless request
func Handle(req []byte) string {
s := S{string(req), len(req)}
hash, err := structhash.Hash(s, 1)
if err != nil {
panic(err)
}
return fmt.Sprintf("%s", hash)
}
プロジェクトのデプロイ
では、作成したコードをデプロイしてみましょう。
faas-cli build -f gohash.yml
faas-cli deploy -f gohash.yml
ポータルの実行結果はFunctionのデプロイで新しいものに置き換えたので結果が変わっています。
また、もちろんAPI URLも用意されているので、直接呼び出すこともできます。
curl -X POST -d "てすとめっせーじ" http://localhost:8080/function/gohash
テストを追加する
テンプレートのDockerfileにはgo test
でテストを実施しているのですが、今のところテスト用のgoファイルは生成されないようです。
今は自分で作りましょう。
touch gohash/handler_test.go
package function
import "testing"
func TestHandleReturnsCorrectResponse(t *testing.T) {
expected := "v1_b8dfcbb21f81a35afde754b30e3228cf"
resp := Handle([]byte("Hello World"))
if resp != expected {
t.Fatalf("Expected: %v, Got: %v", expected, resp)
}
}
テストを作ったところで、わざとgohash Functionを間違えて修正してしまいましょう。
hash, err := structhash.Hash(s, 1)
hash, err := structhash.Hash(s, 2)
この状態で再びビルドを実行します。
faas-cli build -f gohash.yml
コードを元に戻してビルドが成功することを確認します。
DockerHubにビルドしたイメージをPUSH
リモート環境でOpenFaaSを動作させるためには、FunctionのDockerイメージをDockerHubまたは別のレジストリに登録しておく必要があります。
まずは、[DockerHubのユーザ名]/gohash
となるように、gohash.yml
を書き換えます。
image: gohash
image: gcoka/gohash
Docker Hubの登録が済んでいれば、
docker login
でログインし、
faas-cli push -f gohash.yml
でビルドしたイメージをPUSHします。
AWSにデプロイしてみる
AWS EC2コンソールで SSH KeyPairsを作成しておいてください。
ssh-addもお忘れなく。
ssh-add ~/.ssh/yourkey.pem
AWSにDockerをデプロイするためのテンプレートが用意されているので、ここにアクセスして、
Deploy Docker Community Edition (CE) for AWS (stable)
をクリックします。
use your existing VPC
を選択すると、Docker用ネットワークの設定をいろいろやらないといけなくなり、VPC内のネットワーク構築の知識が必要となるようです。
https://docs.docker.com/docker-for-aws/#docker-community-edition-ce-for-aws
以下の設定は環境に合わせて変更が必要です。
- SSHキーにKeyPairsで作成したものを指定。
また、このテンプレートでのCloudFormation実行には、以下のCreateRoleが必要です。
正確な一覧はこちら。
- EC2 instances + Auto Scaling groups
- IAM profiles
- DynamoDB Tables
- SQS Queue
- VPC + subnets and security groups
- ELB
- CloudWatch Log Group
特に設定は変更していません。
デプロイを試すだけなので、Swarm ManagerとWorkerの数は1ずつにしました。
CloudFormationのStackデプロイが完了したら、デプロイ結果のOutputsタブから、Swarm Managerのインスタンスへのリンクが参照できるので、開きます。
このインスタンスにSSH接続を行います。
ユーザーはdocker
を指定します。
ssh docker@54.159.253.49
OpenFaaSのスタックを導入するために、Gitが必要なので、インストールします。
中身はAlpine Linuxなので、apk
コマンドでパッケージをインストールします。
sudo apk --update add git
ローカルでにインストールしたときと同じコマンドですが再掲。
git clone https://github.com/openfaas/faas && \
cd faas && \
git checkout 0.6.5 && \
./deploy_stack.sh
ではOpenFaaSスタックが無事デプロイされているか確認します。
AWSに構築したテンプレートはインターネットからはLoadBalancerを通してアクセスできるので、LoadBalancerのURLを調べます。DNS name: に表示されているものがそれです。
URLがわかったら、ポート8080を指定してアクセスします。
うまくいっていることを確認したらAWSにデプロイします。
--gateway
オプションを使えばgohash.yml
ファイルを書き換える必要がありません。
faas-cli build -f gohash.yml --gateway http://your.amazon.aws.loadbalancer.url:8080
無事にAWSで動きました。
トラブルシューティング(詰まったところ)
faas-cli build
してもコード変更が反映されない
--no-cache
をつけることで、すべてのビルドをやり直してくれます。
faas-cli build -f gohash.yml --no-cache
structhashパッケージがvenderディレクトリにコピーされない
$GOPATHが正しく設定されているか確認してください。
gvm
を使ってgoをインストールしている場合は、$GOPATH/src/functions
でgvm linkthis
を実行すると解決するかもしれません。
AWS CloudFormationのデプロイに失敗する
use your existing VPC
のテンプレートを使っている場合は、使わないでVPCの作成もDockerテンプレートに任せてください。
AWSにデプロイしたFunctionを実行しても500: Internal Server Errorになる
Docker HubにDockerイメージをPUSHしたか確認してください。
ちゃんと動いているかわからない・・・
docker serviceとして動いているのでdockerコマンドからいくつか情報を得ることができます。
docker service ps gohash
docker service logs gohash
docker service logs func_gateway
最後に
Docker SwarmはMicrosoft Azure Container Serviceもサポートしているのですが、完全なマネージドの場合OpenFaaSが必要とするDockerバージョンが足りていないようです。
今回はDocker Swarmに対してデプロイしましたが、Kubernetesもサポートしているので、そちらも試してみたいと思います。
Kubernetesの場合は、Swarmよりもホスティング対応しているクラウドがいくつかあるようなので、期待が持てますね。
情報がとても少ないので、どんどん試してどんどん情報公開していきましょう!