OCI Oracle Functions チュートリアルをやってみた

Oracle Functions をやってみたかったので、Oracle Cloud Free Tier を登録して、やってみました。
Oracleのサイトをなぞっただけの記事で、ほぼ自分のメモです。

#Oracle Cloud Free Tier
下記です。
https://docs.cloud.oracle.com/ja-jp/iaas/Content/FreeTier/freetier.htm#AlwaysFreeResources
Always Free で、仮想マシンやデータベースが、永遠に無料で使えるっていう とてもいいサービスです。

#Oracle Functions
Fn Projectがベースの Oracle Cloud上で動作するFaaSプラットフォームが、Oracle Functionsです。 なんと、200万リクエストまで無料です。 
また、Dockerを用いたプラットフォームであるため、既存のDockerイメージを使用することも可能とのことです。今度、やってみようと思ってます。これかなぁ↓
https://docs.cloud.oracle.com/ja-jp/iaas/Content/Functions/Tasks/functionscreatingfunctions.htm

#事前準備
チュートリアルですと、都度都度な感じで記載してあったんですが、先にメモっておいたほうが良かったものを列挙します。
結果的に、先に調べて、テキストエディタにコピペしておくと便利なものです。

####コンパートメントOCID
「アイデンティティ」→「コンパートメント」で、使用するコンパートメントを選択し、詳細画面で、OCIDをコピーして取得します。個人でやってる分には、別のコンパートメントを作ってないと思うので、ルートのものでいいと思います。

####ユーザー名、ユーザーOCID
コンソール画面のアカウント（右上の人形マーク）を押すと、「プロファイル」って出ますが、その下にある文字列がユーザ名ですのでコピペしておきます。「oracleidentitycloudservice/ほげほげ」となってるやつです。さらに、そこをクリックすると詳細画面になるので、OCIDもコピーしておきます。

####テナンシOCID、テナンシネームスペース
コンソール画面のアカウント（右上の人形マーク）を押して、テナンシをクリックします。テナンシの詳細画面から、OCIDとネームスペースをコピーしておきます。

#ポリシーの設定
次に、ポリシーを設定します。
「アイデンティティ」→「ポリシー」で、「ポリシーの作成」を押します。
名前とかは適当（例えば、oracle-functions-poricy）に入れて、「ステートメント」のところで、下記の２つをセットします。

１つめ↓
``Allow service FaaS to use virtual-network-family in compartment id [コンパートメントID]``

２つめ↓
``Allow service FaaS to read repos in tenancy``

#VCN（仮想ネットワーク）の作成
「ネットワーキング」→「仮想クラウド・ネットワーク」で、「VCNウィザードの起動」で作ります。
「インターネット接続性を持つVCN」を選びます。
名前は適当（例：vcn1）に入れ、あとは初期値で作りました。
・VCN CIDRブロック：10.0.0.0/16
・パブリック・サブネットCIDRブロック：10.0.0.0/24
・プライベート・サブネットCIDRブロック：10.0.1.0/24

#開発環境用インスタンス(Compute)の作成
ここは、別のマシンでDockerが動いているのがあれば、それでもいいんだと思いますが、いろんな開発環境が最初から入っているイメージが準備してあるのと、せっかく無料なので、OCI上に作りました。

「コンピュート」→「インスタンス」で、「インスタンスの作成」です。
名前は適当（例：always-free-01)に入れ、イメージを選ぶところで、「Oracle Cloud Developer Image」を選びます。
always free は、下記のスペックです。
https://docs.cloud.oracle.com/ja-jp/iaas/Content/FreeTier/resourceref.htm

VCNは、上記で作成したVCSを選びます。
SSHでログインするための、キーも作っておきます。（自分はMacを使っているので、これで、ターミナルからログインしてます。）
ログインするために、パブリックIPアドレスを控えておきます。

#インスタンスへのログイン
自分はMacなので、上記でダウンロードしたキーを、「.ssh」ディレクトリ配下にコピーしました。
パーミッションを変えておきます。

```
$ chmod 400 .ssh/ほげほげ.key
```

そして、ホームディレクトリから、下記コマンドでログインします。
ユーザー名はopcです。

```
$ ssh -i .ssh/ほげほげ.key opc@パブリックIPアドレス
```

#開発環境の構築
Oracle Functionsの環境構築のために、以下が必要とのことです。
・Docker
・OCI CLI
・Fn Server
・Fn CLI

今回は、Oracle Cloud Developer Imageを使ってインスタンスを作ったので、DockerとOCI CLIはインストール済みです。
そこで、fnプロジェクト関連のみインストールします。

###fnプロジェクトのインストール
opcユーザにDockerの操作を許可します。実行したら exitでログアウトして、再ログインします。

```
$ sudo usermod -aG docker opc
$ exit
```

再ログインしたら、下記コマンドでインストールします。

```
$ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
```

下記コマンドで、動作確認します。

```
$ fn start
```

動作したら、``CTRL + C`` でサーバを停止します。

###OCI CLIのセットアップ
OCI　CLI はインストールされているので、セットアップだけします。

```
$ oci setup config
```

```
Enter a location for your config [/root/.oci/config]: [Enter]
Enter a user OCID:  ←ユーザOCIDを入力
Enter a tenancy OCID:  ←テナンシOCIDを入力
Enter a region : ap-tokyo-1 ←東京リージョンは、ap-tokyo-1
Do you want to generate a new RSA key pair? (If you decline you will be asked to supply the path to an existing key.) [Y/n]: Y
Enter a directory for your keys to be created [/home/opc/.oci]: [Enter]
Enter a name for your key [oci_api_key]: [Enter]
Public key written to: /root/.oci/oci_api_key_public.pem
Enter a passphrase for your private key (empty for no passphrase): [Enter]
Private key written to: /home/opc/.oci/oci_api_key.pem
```

CLIからOCIに対してアクセスを行う際には、このときに作成されたAPIキーを使います。
これを、OCI側に登録するため、公開鍵をcatして、その内容をコピペしておきます。

```
$ cat .oci/oci_api_key_public.pem

-----BEGIN PUBLIC KEY-----
ほげほげ
ほげほげ
-----END PUBLIC KEY-----
```

#####APIキーの登録
Oracle CloudのコンソールでAPIキーを登録します。 コンソール画面のアカウント（人型のアイコン）でユーザ名を押すと、ユーザの詳細画面になるので、リソース欄のAPIキーを押し、「公開キーの追加」をクリックし、上記でコピペしておいた公開鍵を貼り付けます。

###認証トークンの作成と、oracle funtcions開発環境(Docker)の作成

#####認証トークン
ユーザーの詳細画面の左側のメニューで、"認証トークン"をクリックし、さらに"トークンの生成"をクリックします。 
「説明」に、適当なテキスト（例：for functions-dev）入力し、"トークンの生成"をクリックします。 
画面が表示されるので、"コピー"をクリックし、これを手元のテキストエディタなどにペーストしておきます。

#####oracle funtcions開発環境の作成
FN CLIを利用してOracle Functions開発環境の作成を行います。

まずは、 fn createコマンドを使用してcontext(開発環境)を作成します。

```
$ fn create context [my-context] --provider oracle
```
[my-context]には、context名を任意に指定します。
"--provider oracle"をオプションに指定することで、Oracle Function用の環境を作成できます。

今回は、"functions-dev"という名前で作成しました。 以下のコマンドを実行します。
以降、サンプルは、function-devになっています(^^)

```
$ fn create context "functions-dev" --provider oracle
```

次に、fn useコマンドを使用して、作成した環境に切り替えます。

```
$ fn use context "functions-dev"
```

次に、開発環境にコンパートメントID、APIエンドポイント、OCIR(Oracleが提供するプライベートDockerレジストリであり、Oracle Functionsの管理を行うサービス)の設定を行います。
開発環境の設定は、fn updateコマンドを使用して行います。

以下のコマンドを実行し、コンパートメントIDを設定します。

```
$ fn update context oracle.compartment-id [メモっておいたコンパートメントOCID]
```

以下のコマンドを実行し、Oracle Functionsを呼び出す際のAPIエンドポイントを設定します。
今回は、リージョンを東京にしたので、リージョン名：``ap-tokyo-1``、リージョンコード：``nrt``になります。
テナンシネームスペースは、以前に控えていたものです。

```
$ fn update context api-url https://functions.ap-tokyo-1.oraclecloud.com
$ fn update context registry nrt.ocir.io/テナンシネームスペース/function-dev
```

次に、以下のコマンドを実行し、Oracle Functionsで使用するprofile名を設定します。
profile名は、今回"DEFAULT"(OCI CLIセットアップ時に設定されるデフォルトのprofile名)を入力します。

```
$ fn update context oracle.profile "DEFAULT"
```

以下のコマンドを実行し、OCIRにログインできることを確認します。

```
$ docker login nrt.ocir.io
```

ログインの際にユーザー名とパスワードを聞かれます。

Usernameは、以前に控えたテキストを連結して使います。
``[テナンシネームスペース]/[ユーザ名]``

Passwordは、前記でコピペしておいた認証トークンです。

ログインに成功すると以下のように表示されます。

```
Login Succeeded
```

以上で、Oracle Functions開発環境の作成できました。

#Oracle Functionsのアプリケーション作成
いよいよ、アプリケーションの作成です(^^)V
「開発者サービス」→「ファンクション」で、「アプリケーションの作成」を押します。

今回はサンプルのまま、
・名前：「helloworld-app」
・VCN：「（最初に作ったVCNの名前、例：ｖｃｎ１」
・サブネット：「（VCNに紐づくパブリックサブネット）」
を選び、「作成」を押します。
これで、OKです。

#アプリケーション（ファンクション）の作成・デプロイ

####ファンクションプロジェクトの作成
先ず、ファンクションを作るプロジェクトを作ります。
いくつかの言語のひな形があり、それからプロジェクトを作れるようです。
最終的には、Dockerで作ったものを割り当てたいところですが、最初なんで、今回は、Javaでアプリケーションを作成してみます。

まずは、fn initコマンドを使用してファンクションのひな形を作成します。

・fn initコマンド
　--runtime:使用する言語を指定します。今回はjavaを指定します。
　--trigger:HTTPのエンドポイントを追加するためのオプション
　とのことなので、以下のコマンドを実行し、ファンクションプロジェクトを作成します。

```
$ fn init --runtime java helloworld-func
```
「helloworld-func」っていうディレクトリができました。
すっごく深いんですが、ここ↓にソースがあります。

```
$ ls helloworld-func/src/main/java/com/example/fn/
HelloFunction.java
```
中身はこんなんでした。

```
$ cat helloworld-func/src/main/java/com/example/fn/HelloFunction.java 
package com.example.fn;

public class HelloFunction {

    public String handleRequest(String input) {
        String name = (input == null || input.isEmpty()) ? "world"  : input;

        System.out.println("Inside Java Hello World function"); 
        return "Hello, " + name + "!";
    }

}
```

引数を入れると、「Hello (引数)!」となり、引数なしだと、「Hello, world!」となるプログラムみたいです。

今回は、何もいじらず、そのままデプロイします。
ちなみに、テスト用のコードが同じようなパスのところにあって、ソースを変えたとき、ここも変える感じです。

```
$ ls helloworld-func/src/test/java/com/example/fn/
HelloFunctionTest.java
```
中身はこんなんでした。

```
$ cat helloworld-func/src/test/java/com/example/fn/HelloFunctionTest.java 
package com.example.fn;

import com.fnproject.fn.testing.*;
import org.junit.*;

import static org.junit.Assert.*;

public class HelloFunctionTest {

    @Rule
    public final FnTestingRule testing = FnTestingRule.createDefault();

    @Test
    public void shouldReturnGreeting() {
        testing.givenEvent().enqueue();
        testing.thenRun(HelloFunction.class, "handleRequest");

        FnResult result = testing.getOnlyResult();
        assertEquals("Hello, world!", result.getBodyAsString());
    }

}
```
引数なしで呼んだとき、「Hello, world!」って返すことをテストしているようです・・。
細かい書き方は、まだ調べてません・・(^^;

####デプロイ
以下のコマンドを実行し、ファンクションプロジェクトに移動します。

```
$ cd helloworld-func
```

以下のコマンドを実行し、ファンクションをデプロイします。
--appでアプリケーション名を指定します。
指定するアプリケーションは、先程コンソール画面で指定したアプリケーション名（今回は、helloworld-app）です。

```
$ fn deploy --app helloworld-app
```

ちょっと時間かかります。

####アプリケーション（レジストリ）の確認
コンソールで、「開発者サービス」→「レジストリ(OCIR)」とすると、

``[コンテキスト名]/[Oracle Functionsプロジェクト名]``
なものが表示されてばOKです。
今回は、
``functions-dev/helloworld-func``
ができてればOKです。

#Oracle Functionsの実行
今回は、チュートリアル通り、開発環境の仮想マシンから、Oracle Functionsを実行してみます。

```
fn invoke [アプリケーション名] [プロジェトト名]
```

今回は、下記ですね。

```
fn invoke helloworld-app helloworld-func
```

以下のように表示されれば成功です。

```
Hello, World!
```

ちなみに、
引数を入れれるようになってましたので、下記もやってみました。

```
$ echo -n 'Japan' | fn invoke Helloworld-app helloworld-func
```

結果↓

```
Hello, Japan!
```

###めでたしめでたし