はじめに
Fn Project とは、Oracleがオープンソース化した、コンテナネイティブな サーバレスプラットフォームです。
AWSのLambdaのように、ある程度のまとまりをもった関数をサーバレスに実行できるプラットフォームとなっています。Fnの基本的な動作としては、Functionを呼び出すと、Fn Server上でコンテナを起動し、関数処理を行った後にコンテナを停止するものとなっています。
Oracle Cloud では、Oracle Functions という名前で、Limited Available としてリリースされています。まだ、GAとしてリリースはされていません。Oracle Functions を始めとしたいくつかのサービスを、GA前に触ることができます。下のURLで申し込みが可能です。
申し込み
https://go.oracle.com/LP=78019?elqCampaignId=179851
Oracle Cloud での Cloud Native を紹介
https://www.oracle.com/jp/cloud/cloud-native/
Fn Project のアーキテクチャについては、以下の クラスメソッドさんのブログがわかりやすくでおすすめです。
https://dev.classmethod.jp/server-side/faas-first-step-fn
以下に、Oracle Functions の QuickStart を実行するための手順を書いていきます。Limited Available の時の手順なので、GAされた時とは手順が異なる場合があるのでご注意ください。
OCI の設定
リージョンの選択
OCI上で、Phoenix region を選択します。Limited Available時点では、Phoenix region でのみサービスが提供されています。
VCN
VCNやSubnetを作成します。サービスコンソール上で、VCNを作成するときに、「CREATE VIRTUAL CLOUD NETWORK PLUS RELATED RESOURCES」を選択すると楽で良いです。
IAM User, Groupの作成
適当に作成します。Oracle Functions を動作するときに、作成した User を利用します。
oci iam user create --name sugi --description sugi
oci iam group create --name sugigroup --description sugigroup
oci iam group add-user --group-id ocid1.group.oc1..aaaaaaaahukhfkkc5otxku7zvkhjbhdife7plimfs6kijpej6oqrr77f2bja --user-id ocid1.user.oc1..aaaaaaaaebu2knmup5iqeq4xz6ae2phdlu4vvth423cernryn6s7g7pz3iiq
Compartmentの作成
適当に作成します。すでに作成している場合はそれを利用して良いです
oci iam compartment create --name sugicompart --description "myself compartment" --compartment-id <parent_compartment_ocid>
ポリシーの設定
IAM で使用するポリシーを設定します。
- グループと Oracle Functions サービスが Registry (OCIR) を利用する
Allow group sugigroup to manage repos in tenancy
Allow service FaaS to read repos in tenancy
- グループが作成したコンパートメント内のそれぞれのリソースを利用する
Allow group sugigroup to manage functions-family in compartment sugicompart
Allow group sugigroup to manage vnics in compartment sugicompart
Allow group sugigroup to inspect subnets in compartment sugicompart
- Oracle Functions サービスが作成したコンパートメント内のリソースを利用する
Allow service FaaS to use virtual-network-family in compartment sugicompart
Fn Client の設定
Oracle Functionを操作するときには、fn cli を使用します。fnを動かすにはいくつか条件があります。
- oci cli をinstall
- docker をinstall
- fn cli を install
oci cli を install
以下の記事をみて適当にinstallします
なお、私の環境は、oci cli の Profile名はFnとしています。Profile名は、Fn cli の設定で必要となるので、覚えておきます。
> cat ~/.oci/config
[Fn]
key_file=~/.oci/fn.pem
region=us-phoenix-1
tenancy=<ocid>
user=<ocid>
fingerprint=<fingerprint>
Docker の導入
適当にぐぐって導入します
https://docs.docker.com/install/
Fn cli の導入
fn cli (Goバイナリ)をinstallします
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
Fnバージョンの確認をします
> fn version
Client version is latest version: 0.5.51
Server version: ?
Fn cli で context のコンフィグ
fn cli では、様々な Fn 環境を扱うことが出来ます。例えば、Oracle Functionsの環境、オンプレミスの環境、AWSで稼働させている環境を、一つのcliコマンドで扱うことが可能です。扱う環境を切り替えるために、fn cli 上で context という概念が存在します。
Oracle Functions を扱うための context を設定していきます。
context の作成
fn create context sugi-context --provider oracle
context一覧の確認
> fn ls contexts
CURRENT NAME PROVIDER API URL REGISTRY
default default http://localhost:8080
sugi-context oracle
新しく作成した context を使用します
fn use context sugi-context
context 一覧を確認すると、アスタリスクマークが頭に表示されています
> fn ls contexts
CURRENT NAME PROVIDER API URL REGISTRY
default default http://localhost:8080
* sugi-context oracle
Oracle Cloud Infrastructure 上で Compartment の OCID を確認し、context へ設定を行います
fn update context oracle.compartment-id <compartment-ocid>
context に OCI の Endpoint を指定します。
fn update context api-url https://functions.us-phoenix-1.oraclecloud.com
context 一覧が更新されます
> fn ls contexts
CURRENT NAME PROVIDER API URL REGISTRY
default default http://localhost:8080
* sugi-context oracle https://functions.us-phoenix-1.oraclecloud.com
context に Docker Registory を登録します。Oracle Cloud では、OCIR と呼ばれる Registory サービスを提供しており、これを利用します。
なお、Repositoryは好きな名前を入力することが出来ます。事前に作成する必要はありません。Functionを動作させると、自動的に作成されます。
fn update context registry phx.ocir.io/<tenancy-name>/oracle-function
context 一覧が更新されます。
> fn ls contexts
CURRENT NAME PROVIDER API URL REGISTRY
default default http://localhost:8080
* sugi-context oracle https://functions.us-phoenix-1.oraclecloud.com phx.ocir.io/<tenancy-name>/oracle-function
context に、OCI CLI で設定した Profile の名前を指定します。Fn という名前で作成しているため、Fnを指定します
fn update context oracle.profile Fn
oci cli を使用して、IAM User に紐づいた Auth Token を生成します。OCIRサービスへLoginするときのパスワードとして使用します。
oci iam auth-token create --user-id ocid1.user.oc1..aaaaaaaaebu2knmup5iqeq4xz6ae2phdlu4vvth423cernryn6s7g7pz3iiq --description oracle-fuction
実行例
> oci iam auth-token create --user-id ocid1.user.oc1..aaaaaaaav3ox2f6kvkovbbxbj52hxow2yuub6224vib7n6jvemctuypabt6q --description oracle-fuction
{
"data": {
"description": "oracle-fuction",
"id": "ocid1.credential.oc1..aaaaaaaad5uz5zahynxdobe4udw7e4x6zonaoj5wyp43x3ezlbtagcxb5kzq",
"inactive-status": null,
"lifecycle-state": "ACTIVE",
"time-created": "2019-02-16T01:35:03.427000+00:00",
"time-expires": null,
"token": "secret", <========== ここに表示される文字列を使う
"user-id": "ocid1.user.oc1..aaaaaaaav3ox2f6kvkovbbxbj52hxow2yuub6224vib7n6jvemctuypabt6q"
},
"etag": "d77772be5d2915605d93e2104cc28d9e0fd85ff4"
}
OCIR へ Loginします。-u の引数には、適当にテナント名とIAM USER名を指定してください。
sudo docker login phx.ocir.io -u <tenancy name>/<iam user name>
上記コマンドを実行すると、password 入力を求められます。password は、生成した token の secret を指定します。
プロンプトに Login Succeeded
と表示されればOKです。
なお、fn cli で設定した context は、yamlファイルとして保存されているため、これを管理することで設定の復元が容易に出来ます
> ls -la /home/ubuntu/.fn/contexts
total 16
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb 17 08:01 ./
drwxr-xr-x 3 ubuntu ubuntu 4096 Feb 17 00:59 ../
-rw-rw-r-- 1 ubuntu ubuntu 62 Feb 17 00:59 default.yaml
-rw-rw-r-- 1 ubuntu ubuntu 248 Feb 17 02:39 sugi-context.yaml
> cat sugi-context.yaml
api-url: https://functions.us-phoenix-1.oraclecloud.com
oracle.compartment-id: ocid1.compartment.oc1..snip
oracle.profile: Fn
provider: oracle
registry: phx.ocir.io/<tenancy name>/oracle-function
Functionの作成
Oracle Function上でアプリケーションを作成します。まずは、何も作成されていないことを fn コマンドで確認します
> fn list apps
No apps found
NAME ID
Oracle Functionのコンソール画面を開きます。現在は、Limited Available なので、直接URLを指定してアクセスします。
https://console.us-phoenix-1.oraclecloud.com/functions/
コンソール画面で、適当に Application を作成します (name : hello-fn-go)
作成後、fn コマンド上でも Application の存在を確認することが出来ます
> fn list apps
NAME ID
hello-fn-go ocid1.fnapp.oc1.phx.aaaaaaaaafjkul4guz7ua5djclxicq7bqwwpcyatiy5fzg4oivcqncujw4aa
作業用ディレクトリを作成します
mkdir ~/fn_work/
cd ~/fn_work/
fn init コマンドで、Golangの 初期function を生成します
fn init --runtime go helloworld-func
作業ディレクトリ直下に、function名のディレクトリが作成されています。
> ls -la ~/fn_work/
total 0
drwxrwxrwx 1 sugimount sugimount 512 Feb 16 14:22 ./
drwxr-xr-x 1 sugimount sugimount 512 Feb 16 14:19 ../
drwxr-xr-x 1 sugimount sugimount 512 Feb 16 14:22 helloworld-func/
この中には、functionとして動作させるGolangのソースコードを含めたいくつかのファイルが生成されています。
> ls -la ~/fn_work/helloworld-func/
total 0
drwxr-xr-x 1 sugimount sugimount 512 Feb 16 14:22 ./
drwxrwxrwx 1 sugimount sugimount 512 Feb 16 14:22 ../
-rw-r--r-- 1 sugimount sugimount 127 Feb 16 14:22 Gopkg.toml
-rw-r--r-- 1 sugimount sugimount 469 Feb 16 14:22 func.go
-rw-r--r-- 1 sugimount sugimount 113 Feb 16 14:22 func.yaml
Golang のソースコードを確認します。ポイントは以下の3点です (なぜ若干複雑な無名Structを使っているのか・・・もうちょっとSimpleな形で良いとおもう)
-
github.com/fnproject/fdk-go
をimportして、main関数内でmyHandler という名前の自作関数を紐づけている - msg変数は、無名structを代入している。無名structには、Msgフィールドの初期値として、Sprintf関数の返り値である
Hello World
が入る。 - myHandler は、
out io.Writer
にJSONエンコードしたmsg変数を渡すことで、JSONをResponseしている
> cat func.go
package main
import (
"context"
"encoding/json"
"fmt"
"io"
fdk "github.com/fnproject/fdk-go"
)
func main() {
fdk.Handle(fdk.HandlerFunc(myHandler))
}
type Person struct {
Name string `json:"name"`
}
func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
p := &Person{Name: "World"}
json.NewDecoder(in).Decode(p)
msg := struct {
Msg string `json:"message"`
}{
Msg: fmt.Sprintf("Hello %s", p.Name),
}
json.NewEncoder(out).Encode(&msg)
}
fn deploy コマンドで、DockerImageをBuildし、OCIRにPushします。このDockerImageを使用して、OCI側の Fn Server で Function として実行されます。
カレントディレクトリに存在している func.yaml に定義されている内容を基に、deployを行う挙動となります。fn deploy コマンドを発行するたびに、DockerImageのTagがカウントアップしていきます (0.0.1, 0.0.2, 0.0.3 ...)
作成したアプリケーション名を引数に、fn deployコマンドを実行します。
fn --verbose deploy --app hello-fn-go
なお、今回のチュートリアルで作成されたイメージは、7.28MB とかなり小さいイメージとなっていました。(言語によって大きさが変わりそう)
fnコマンドでアップロードした関数を呼び出します。アプリケーション名とFunction名を指定します。初回実行は30秒ほど待機時間が必要でした。Fn Server 側でdocker pullや docker run に時間がかかっているのかなと想定できます。 2回目以降の実行は、0.3秒といった短い時間で返ってきます。
fn --verbose invoke hello-fn-go helloworld-func
実行例
> fn --verbose invoke hello-fn-go helloworld-func
{"message":"Hello World"}
なお、fnの機能で Hot Function という機能があり、実行が終わったコンテナをすぐに停止せず、一定時間稼働させたまま待機させるものがあります。これを活用すると、イイカンジに Function の実行時間短縮が期待できそうです。
参考URL : http://chiroito.hatenablog.jp/entry/2017/10/15/220427
Memo : Fn Project がサポートしている言語
以下の言語がサポートされているようです (fn init の option で選択できるものから抜粋)
- Go
- Java
- Node.js
- Python
- Ruby
- Kotlin
Memo : 言語による DockerImageの容量確認
fn init で初期関数を生成したものを使用して、各種言語のDockerImageの大きさを見てみます。単純な興味です。
- Go : 7.28MB (最小)
- Java : 103.26 MB (大きいけど許容範囲)
- Node.js : 23.48 MB
- Python : 50.7 MB
- Ruby : 15.92 MB
- Kotlin : 104.19 MB (大きいけど許容範囲)
やはり JVM系は容量が大きいですが、許容範囲だと思います。
# Go
cd /home/ubuntu/fn_work
fn init --runtime go helloworld-func
cd helloworld-func
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func
# Java
cd /home/ubuntu/fn_work
fn init --runtime java11 helloworld-func-java
cd helloworld-func-java
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func-java # 約12秒 (参考にならなそう)
# nodejs
cd /home/ubuntu/fn_work
fn init --runtime node helloworld-func-node
cd helloworld-func-node
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func-node # 約4秒 (参考にならなそう)
# python
cd /home/ubuntu/fn_work
fn init --runtime python3.7.1 helloworld-func-python
cd helloworld-func-python
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func-python # 約6秒 (参考にならなそう)
# ruby
cd /home/ubuntu/fn_work
fn init --runtime ruby helloworld-func-ruby
cd helloworld-func-ruby
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func-ruby # 約4秒 (参考にならなそう)
# kotlin
cd /home/ubuntu/fn_work
fn init --runtime kotlin helloworld-func-kotlin
cd helloworld-func-kotlin
fn --verbose deploy --app hello-fn-go
time fn --verbose invoke hello-fn-go helloworld-func-kotlin # 約4秒 (参考にならなそう)
Memo FunctionのResponseを普通な文字列で返却
io.Writerの write func を実行すればよい
func funcHandler(ctx context.Context, in io.Reader, out io.Writer) {
out.Write([]byte("text message wo kaku"))
}
参考URL
Cloudii Blog
https://cloudii.atomitech.jp/entry/20190214/1550122678
[FaaS] はじめてのfn #fnproject
https://dev.classmethod.jp/server-side/faas-first-step-fn/
Doker for WindowsでFn Projectを動かしてみた
http://yasu7ri.hatenablog.com/entry/2018/09/28/020223
Hot Function でファンクションのレスポンス時間を短縮 @ Fn
http://chiroito.hatenablog.jp/entry/2017/10/15/220427