Java対応のFaaSフォーム「Fn Project」を動かしてみた

下記の記事にある通り、[JavaOne2017のKeynote](https://www.oracle.com/javaone/on-demand.html?bcid=5596229112001)にてOracleよりJavaも動作するFaaSプラットフォームの「[fn project](http://fnproject.io/)」が公開されました。

-  [［速報］Java対応のサーバレスプラットフォーム「Fn Project」、オラクルがオープンソースで公開。JavaOne 2017](http://www.publickey1.jp/blog/17/javafn_projectjavaone_2017.html)

主な特徴としては

- Multi-Language(Java, Go, Ruby, Python, PHP, Node.js)
- Container Native(depends only docker)
- Powerful

どこまで本気で育てていく予定なのかは測りかねますが、プレゼンを見る限り結構面白そうだったので試しに動かしてみました。
なお、試した環境はWindows10 + Docker toolbox + WSL(Ubuntu)なので適宜自分の環境で読みかえてください。

インストール
-----------------

まずはfnコマンドのインストール

```bash
% curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
[sudo] password for koduki:
fn version 0.4.6
% fn -v
fn version 0.4.6
```

このままサンプルプログラムを動作させてみましょう。

```bash
% fn start
mount: permission denied (are you root?)
Could not mount /sys/kernel/security.
AppArmor detection and --privileged mode might break.
mount: permission denied (are you root?)
time="2017-10-03T12:25:53Z" level=info msg="datastore dialed" datastore=sqlite3 max_idle_connections=256
time="2017-10-03T12:25:53Z" level=info msg="no docker auths from config files found (this is fine)" error="open /root/.dockercfg: no such file or directory"
time="2017-10-03T12:25:53Z" level=info msg="available memory" ram=593510400
time="2017-10-03T12:25:53Z" level=info msg="Serving Functions API on address `:8080`"

        ______
       / ____/___
      / /_  / __ \
     / __/ / / / /
    /_/   /_/ /_/
        v0.3.135

^C2017/10/03 21:56:12 interrupt caught, exiting
```

色々裏でDockerのイメージが落とされ、localhostの8080ポートでサーバが立ち上がります。
動いてるかどうかを下記で確認。ちなみにdocker toolboxを使ってるので、IPはlocalhostではなく`docker-machine ip default`で取得したものを使います。

```bash
% curl http://192.168.99.100:8080
{"goto":"https://github.com/fnproject/fn","hello":"world!"}
```

サーバの起動を確認できました。

アプリを作成してみる
-------------------

引き続きHello World的なアプリを作成してみます。

まずは、アプリを作成するディレクトリを作ります。

```bash
% mkdir example-fn
% cd example-fn
```

続いてアプリケーションの作成。今回はJavaでやってみることにしました。

```bash
% fn init --runtime java

        ______
       / ____/___
      / /_  / __ \
     / __/ / / / /
    /_/   /_/ /_/

Runtime: java
Function boilerplate generated.
func.yaml created.
% find . -type f
./func.yaml
./pom.xml
./src/main/java/com/example/fn/HelloFunction.java
./src/test/java/com/example/fn/HelloFunctionTest.java
```

設定ファイルであるfunc.yamlとMavenのひな形が出来ました。中身はこんな感じ。

```java
% cat ./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;

        return "Hello, " + name + "!";
    }

}%
```

わりと素朴ですね。
まあ、書き方はデモで紹介されていたJava FDKを使う方法とかが推奨なんでしょうが、その辺は後日試します。

ビルド
--------

作成したコードをビルドしてみます。

```bash
% fn run
Building image koduki/example-fn:0.0.1
Sending build context to Docker daemon  13.82kB
Step 1/11 : FROM fnproject/fn-java-fdk-build:jdk9-latest as build-stage
.
.
.
Step 11/11 : CMD com.example.fn.HelloFunction::handleRequest
 ---> Running in 0cd0c81caa5a
 ---> c6e00fb47d8d
Removing intermediate container 0cd0c81caa5a
Successfully built c6e00fb47d8d
Successfully tagged koduki/example-fn:0.0.1
Hello, world!%
```

Dockerによるビルドが色々走った後、実行結果が出力されました。ログ見る限りだとこのタイミングでテストも実行されてるみたいですね。すばら。

デプロイ
--------

最後にデプロイです。
デプロイの注意点としてオプションなしだとDockerHubへのPushが自動で挟まるので、DockerHubへのログインと事前のプロジェクト作成が必要です。
プロジェクト名は特に`func.yaml`に何も記載してなければルートディレクトリ名になるようなので注意してください。
DockerHubへのログインは以下の通り。

```bash
% # WSLからDocker使うおまじない
% alias docker="docker -H tcp://192.168.99.100:2376 --tlsverify --tlscacert /mnt/c/Users/$USER/.docker/machine/machines/default/ca.pem --tlscert /mnt/c/Users/$USER/.docker/machine/machines/default/cert.pem --tlskey /mnt/c/Users/$USER/.docker/machine/machines/default/key.pem"
% docker login
Login Succeeded
```

では、肝心のログインです。別のターミナル等で`fn start`をしてデプロイ先のサーバを立ち上げておいてください

```bash
% API_URL=http://192.168.99.100:8080 fn deploy --app myapp
Deploying example-fn to app: myapp at path: /example-fn
Bumped to version 0.0.2
Building image koduki/example-fn:0.0.2
.
.
.
Successfully tagged koduki/example-fn:0.0.2
Updating route /example-fn using image koduki/example-fn:0.0.2...
```

`API_URL`を指定しないとデフォルトは`http://localhost:8080`なのでDocker Toolboxでは動作しないので注意。また、`--local`オプションを付けることでDockerHubへのPushはスキップすることもできます。

デプロイされたことを確認してみます。まずはルーティングの確認

```bash
% API_URL=http://192.168.99.100:8080 fn routes list myapp
path            image                   endpoint
/example-fn     koduki/example-fn:0.0.3 192.168.99.100:8080/r/myapp/example-fn
```

どのバージョンがマッピングされてるかが見えるのは非常に便利ですね。リストアップされたエンドポイントにアクセスしてみます。

```bash
% curl http://192.168.99.100:8080/r/myapp/example-fn
Hello, world!%
```

ちゃんと出力されましたね！

まとめ
-------

Docker NativeのFaaSということでk8s等と組み合わせたり色々できそうです。今度、Fn FlowやFDKも試してみたいですね。

AWSのLambda等とは純粋なマネージドサービスではないので違うところもあるでしょうけど、オンプレやクラウド環境でスケールさせやすいシステムの基盤として使えるかな？　と期待しています。

現時点ではドキュメント等の整備も不足してるので、今後も開発が続いてほしいですねー。

それではHappy Hacking!