Help us understand the problem. What is going on with this article?

サーバレスな Oracle Functions (Fn) をやってみた

はじめに

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します

https://qiita.com/sugimount/items/63a8cfe1163030ae8804

なお、私の環境は、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

Getting Start
https://www.oracle.com/webfolder/technetwork/tutorials/infographics/oci_faas_gettingstarted_quickview/functions_quickview_top/functions_quickview/index.html

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした