OpenFaaSを使ってGo言語でFunctionを書いて、AWSに展開したDocker環境にデプロイするまで

More than 1 year has passed since last update.

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

Selection_002.png


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のコマンドで確認できます。

image.png

ローカルのデプロイ環境にはhttp://localhost:8080でアクセスできるので開くと、次のようなFunction管理ポータルが表示されます。

Linuxの場合はhttp://127.0.0.1:8080で開く必要があるかもしれません。

Selection_006.png


Go言語のインストール

こちらの公式インストールガイドを参考にインストールしてください。

v1.8.3かそれ以降が必要です。

gvmでインストールしても問題ありません。

Selection_003.png

$GOPATHが設定されているか確認しておきます。

Selection_004.png


OpenFaaSプロジェクトの生成

まずはプロジェクトフォルダーを作成します。



mkdir -p $GOPATH/src/functions

cd $GOPATH/src/functions

続いてFaaS CLIを使ってプロジェクトテンプレートを生成します。

名前は参考記事通りgohashです。

faas-cli new --lang go gohash

Selection_005.png


プロジェクトの中身を確認する

gohash.ymlファイルにはテンプレートで作成されたFunctionとローカルの実行環境についての設定が書き込まれています。


gohash.yml

provider:

name: faas
gateway: http://localhost:8080

functions:
gohash:
lang: go
handler: ./gohash
image: gohash


gohash/handler.goにはHello Worldなコードが書かれています。


gohash/handler.go

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 に送るというシンプルな作りとなっています。


template/go/main.go

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サーバーです。


template/go/Dockerfile

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分ほど待つかもしれません。

とくにエラーが表示されずに、

Selection_009.png

という風な表示がされたら成功です。

http://localhost:8080にアクセスしてgohashが追加されているか確認します。

左のFunction一覧からgohashを見つけたら適当なRequest bodyを打ち込んで INVOKE します。

Selection_007.png

これで無事にデプロイできることが確認できました。


Functionを実装していく

今回はgo1.9を使ったので、コンテナ内のgoも同じバージョンにしておきます。

今回のコードでは変更しなくても特に問題にはならないと思います。

コンテナ生成Dockerfileを書き換えてしまいます。


template/go/Dockerfile変更前

FROM golang:1.8.3-alpine3.6



template/go/Dockerfile変更後

FROM golang:1.9-alpine3.6


go言語のパッケージ管理ツールとしてdepをインストールします。

go get -u github.com/golang/dep/cmd/dep

そして、Goのデータ(Struct)からハッシュを生成するstructhashdepを使ってインストールします。

ただし、faas-cliがDockerコンテナをビルドするときに、template/go/をワーキングディレクトリとしてビルドを行うため、depの実行はこのディレクトリに移動して行う必要があります。

cd template/go/

dep init
dep ensure -add github.com/cnf/structhash

こうすると、Gopkg.lockGopkg.tomlvernder/template/go/以下に生成されます。

ちなみに、gvm環境だとdep ensureでライブラリがうまくインストールされないことがありますが、goのコードがビルドされるのはコンテナ内なので、一応そのまま進めても大丈夫です。

きちんと開発を進める場合はgvm linkthisdepがしっかりと使えるようにします。

それでは、受け取った文字列データからハッシュを生成するコードに変更しましょう。


gohash/handler.go

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のデプロイで新しいものに置き換えたので結果が変わっています。

Selection_010.png

また、もちろんAPI URLも用意されているので、直接呼び出すこともできます。

curl -X POST -d "てすとめっせーじ" http://localhost:8080/function/gohash

Selection_011.png


テストを追加する

テンプレートのDockerfileにはgo testでテストを実施しているのですが、今のところテスト用のgoファイルは生成されないようです。

今は自分で作りましょう。

touch gohash/handler_test.go


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を間違えて修正してしまいましょう。


handler.go修正前

hash, err := structhash.Hash(s, 1)



handler.go修正ミス

hash, err := structhash.Hash(s, 2)


この状態で再びビルドを実行します。

faas-cli build -f gohash.yml

ちゃんとビルド中にテストで失敗してくれます。

Selection_012.png

コードを元に戻してビルドが成功することを確認します。


DockerHubにビルドしたイメージをPUSH

リモート環境でOpenFaaSを動作させるためには、FunctionのDockerイメージをDockerHubまたは別のレジストリに登録しておく必要があります。

まずは、[DockerHubのユーザ名]/gohashとなるように、gohash.ymlを書き換えます。


gohash.yml書換前

    image: gohash



gohash.yml書換後

    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ずつにしました。

Selection_029.png

CloudFormationのStackデプロイが完了したら、デプロイ結果のOutputsタブから、Swarm Managerのインスタンスへのリンクが参照できるので、開きます。

Selection_030.png

Selection_031.png

このインスタンスにSSH接続を行います。

ユーザーはdockerを指定します。

ssh docker@54.159.253.49

OpenFaaSのスタックを導入するために、Gitが必要なので、インストールします。

中身はAlpine Linuxなので、apkコマンドでパッケージをインストールします。


sshコンソール

sudo apk --update add git


ローカルでにインストールしたときと同じコマンドですが再掲。


sshコンソール

git clone https://github.com/openfaas/faas && \

cd faas && \
git checkout 0.6.5 && \
./deploy_stack.sh

ではOpenFaaSスタックが無事デプロイされているか確認します。

AWSに構築したテンプレートはインターネットからはLoadBalancerを通してアクセスできるので、LoadBalancerのURLを調べます。DNS name: に表示されているものがそれです。

Selection_034.png

URLがわかったら、ポート8080を指定してアクセスします。

Selection_035.png

うまくいっていることを確認したらAWSにデプロイします。

--gatewayオプションを使えばgohash.ymlファイルを書き換える必要がありません。

faas-cli build -f gohash.yml --gateway http://your.amazon.aws.loadbalancer.url:8080

Selection_038.png

Selection_039.png

無事にAWSで動きました。


トラブルシューティング(詰まったところ)


faas-cli buildしてもコード変更が反映されない

--no-cacheをつけることで、すべてのビルドをやり直してくれます。

faas-cli build -f gohash.yml --no-cache


structhashパッケージがvenderディレクトリにコピーされない

$GOPATHが正しく設定されているか確認してください。

gvmを使ってgoをインストールしている場合は、$GOPATH/src/functionsgvm linkthisを実行すると解決するかもしれません。


AWS CloudFormationのデプロイに失敗する

use your existing VPCのテンプレートを使っている場合は、使わないでVPCの作成もDockerテンプレートに任せてください。


AWSにデプロイしたFunctionを実行しても500: Internal Server Errorになる

Docker HubにDockerイメージをPUSHしたか確認してください。


ちゃんと動いているかわからない・・・

docker serviceとして動いているのでdockerコマンドからいくつか情報を得ることができます。


指定したFunctionのプロセス動作情報を表示

docker service ps gohash



指定したFunctionのログを表示

docker service logs gohash



gatewayのログを表示

docker service logs func_gateway



最後に

Docker SwarmはMicrosoft Azure Container Serviceもサポートしているのですが、完全なマネージドの場合OpenFaaSが必要とするDockerバージョンが足りていないようです。

今回はDocker Swarmに対してデプロイしましたが、Kubernetesもサポートしているので、そちらも試してみたいと思います。

Kubernetesの場合は、Swarmよりもホスティング対応しているクラウドがいくつかあるようなので、期待が持てますね。

情報がとても少ないので、どんどん試してどんどん情報公開していきましょう!