OCIのMicro InstanceでCentOSにFn Projectのサーバをインストールしてみる

# 概要

このエントリでは、Oracle Cloud Infrastructure(OCI)のMicro Instanceの上で動作するCentOSに、FaaS型のサーバを作るためのOSS「Fn Project」のサーバをインストールして、疎通確認用のアプリを動作させるところまでを扱います。

OCIであれば、[「Functions」](https://docs.cloud.oracle.com/en-us/iaas/Content/Functions/Concepts/functionsoverview.htm)のサービスがありますのでそれを使おう、という話もありますが、このエントリでは、OSS版のサービスを自分で動かす方を取り上げています。（OCIのFunctionsは、別エントリにて取り扱いたい予定です）

## 想定読者

- Fn Projectのサーバをまだ自身で動かしていない方

# 準備

ほぼ、[Fn projectのチュートリアル](https://fnproject.io/tutorials/install/)に従って進めます。方法が複数示されている中、今回利用している部分について、この後詳細を紹介しています。

## docker CE

Fn projectでは、2020年1月時点では、Docker 17.10以降が必要です。

CentOS7を入れた後、yumで普通に導入されるバージョンはちょっと古そうなので、

```terminal
$ yum info docker-latest | grep -i version
Version     : 1.13.1
```

[Dockerのサイト](https://docs.docker.com/install/linux/docker-ce/ubuntu/)に従い、自分でインストールします。

（参考）Ubuntuのものにはなりますが、[インストール方法を別エントリ「Utuntu 19.04 に Docker CE をインストールする」に](https://qiita.com/yutayama/items/1d768f5b09cb5d6a0d07)書いてあります

このエントリでは、サイトの記載に従い、下記も実施して、自身のユーザをdockerグループに追加してあります。

```
sudo usermod -aG docker $USER
```


## Fn project（のコマンドラインツール）

[Fn projectのチュートリアル](https://fnproject.io/tutorials/install/)を眺めると、[GitHub上のインストールスクリプト](https://raw.githubusercontent.com/fnproject/cli/master/install)をダウンロードしてきて実行する手順になっていますので、リンクをちらっと眺めた後、実行してみます。


```terminal

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

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


```

本エントリ執筆時点では、fnはversion 0.5.92でした。（サイトの説明は0.5.86になっています）。下記のバイナリが/usr/local/binにインストールされています。

```terminal
[opc@instance-20200117-1627 ~]$ ls -la /usr/local/bin/fn
-rwxr-xr-x. 1 root root 17306174 Feb  2 06:25 /usr/local/bin/fn
```

# 動かしてみる

## fn serverのローカル環境での起動

筆者は、下記のようなスクリプトをstart_fn.shとして用意して実行しました。

```sh
#!/bin/sh
export FN_PORT=18080
fn start -p $FN_PORT --log-level DEBUG
```

初回起動時は、必要なコンテナのイメージがダウンロードされます。

```terminal
$ ./start_fn.sh
2020/02/02 06:34:05 ¡¡¡ 'fn start' should NOT be used for PRODUCTION !!! see https://github.com/fnproject/fn-helm/
Unable to find image 'fnproject/fnserver:latest' locally
latest: Pulling from fnproject/fnserver
ff3a5c916c92: Pull complete
1a649ea86bca: Pull complete
ce35f4d5f86a: Pull complete
b6206661264b: Pull complete
b8b71dba24d3: Pull complete
3873004a68ee: Pull complete
f4205b132661: Pull complete
91a85eeeb257: Pull complete
93c96d032b32: Pull complete
bb761748d6e1: Pull complete
81f6c51c4ac2: Pull complete
2ba715696dba: Pull complete
f46c2b56aaf3: Pull complete
aef258868c13: Pull complete
9a72ccea4c0a: Pull complete
Digest: sha256:34a51ad87dbe9e360ad194556f3cc21b6a75d73e41abd7d541a223beeb7f7271
Status: Downloaded newer image for fnproject/fnserver:latest
time="2020-02-02T06:34:25Z" level=info msg="Setting log level to" fields.level=DEBUG
time="2020-02-02T06:34:25Z" level=info msg="Registering data store provider 'sql'"
time="2020-02-02T06:34:25Z" level=debug msg="creating new datastore" db=sqlite3
time="2020-02-02T06:34:25Z" level=debug msg="mysql does not support sqlite3"
time="2020-02-02T06:34:25Z" level=debug msg="postgres does not support sqlite3"
time="2020-02-02T06:34:25Z" level=debug msg="mysql does not support sqlite3"
time="2020-02-02T06:34:25Z" level=debug msg="postgres does not support sqlite3"
time="2020-02-02T06:34:25Z" level=info msg="Connecting to DB" url="sqlite3:///app/data/fn.db"
time="2020-02-02T06:34:25Z" level=info msg="datastore dialed" datastore=sqlite3 max_idle_connections=256 url="sqlite3:///app/data/fn.db"
time="2020-02-02T06:34:25Z" level=info msg="agent starting cfg={MinDockerVersion:17.10.0-ce ContainerLabelTag: DockerNetworks: DockerLoadFile: DisableUnprivilegedContainers:false FreezeIdle:50ms HotPoll:200ms HotLauncherTimeout:1h0m0s HotPullTimeout:10m0s HotStartTimeout:5s DetachedHeadRoom:6m0s MaxResponseSize:0 MaxHdrResponseSize:0 MaxLogSize:1048576 MaxTotalCPU:0 MaxTotalMemory:0 MaxFsSize:0 MaxPIDs:50 MaxOpenFiles:0xc42003c018 MaxLockedMemory:0xc42003c040 MaxPendingSignals:0xc42003c048 MaxMessageQueue:0xc42003c050 PreForkPoolSize:0 PreForkImage:busybox PreForkCmd:tail -f /dev/null PreForkUseOnce:0 PreForkNetworks: EnableNBResourceTracker:false MaxTmpFsInodes:0 DisableReadOnlyRootFs:false DisableDebugUserLogs:false IOFSEnableTmpfs:false EnableFDKDebugInfo:false IOFSAgentPath:/iofs IOFSMountRoot:/home/opc/.fn/iofs IOFSOpts: ImageCleanMaxSize:0 ImageCleanExemptTags:ImageEnableVolume:false}"
time="2020-02-02T06:34:25Z" level=info msg="no docker auths from config files found (this is fine)" error="open /root/.dockercfg: no such file or directory"
time="2020-02-02T06:34:25Z" level=info msg="available memory" cgroup_limit=9223372036854771712 head_room=268435456 total_memory=734953472
time="2020-02-02T06:34:25Z" level=info msg="ram reservations" avail_memory=466518016
time="2020-02-02T06:34:25Z" level=info msg="available cpu" avail_cpu=2000 total_cpu=2000
time="2020-02-02T06:34:25Z" level=info msg="cpu reservations" cpu=2000
time="2020-02-02T06:34:25Z" level=info msg="\n        ______\n       / ____/___\n      / /_  / __ \\\n     / __/ / / / /\n    /_/   /_/ /_/\n"
time="2020-02-02T06:34:25Z" level=info msg="Fn serving on `:8080`" type=full version=0.3.749

```

この時点で、fnにdefaultのコンテキストができています。

```terminal
$ fn list contexts
CURRENT NAME    PROVIDER        API URL                 REGISTRY
        default default         http://localhost:8080
```

これを以下のように選択して、

```terminal
$ fn use context default
Now using context: default
```

ローカル開発用に、任意の名前（この例では"fndemouser"）をレジストリに指定します。

```
$ fn update context registry fndemouser
Current context updated registry with fndemouser
```

先ほどと同じ、"fn list contexts"を実施したとき、REGISTRYの部分が変わっていることがわかります。

```
$ fn list contexts
CURRENT NAME    PROVIDER        API URL                 REGISTRY
*       default default         http://localhost:8080   fndemouser
```

## アプリを実行

[Create Apps with Fn](https://fnproject.io/tutorials/Apps/)に従って、アプリを一つ動かしてみます。

前述のように、サーバが動作するポート番号をデフォルトの8080から18080に変更しているため、下記のように環境変数で接続先を指定します。

```console
export FN_API_URL=http://127.0.0.1:18080
```

```
[opc@instance-20200117-1627 myapp2]$ fn deploy --create-app --all --local
Successfully created app:  myapp2
Deploying myapp2 to app: myapp2
Bumped to version 0.0.2
Building image fndemouser/myapp2:0.0.2 ................................................................
Updating function myapp2 using image fndemouser/myapp2:0.0.2...
Successfully created function: myapp2 with fndemouser/myapp2:0.0.2
Successfully created trigger: myapp2
Trigger Endpoint: http://127.0.0.1:18080/t/myapp2/myapp2
Deploying hello to app: myapp2
Bumped to version 0.0.2
Building image fndemouser/hello:0.0.2 .................................................................................................
Updating function hello using image fndemouser/hello:0.0.2...
Successfully created function: hello with fndemouser/hello:0.0.2
Successfully created trigger: hello
Trigger Endpoint: http://127.0.0.1:18080/t/myapp2/hello
```

出来たエンドポイントを「fn list」で確認します。

```console
[opc@instance-20200117-1627 myapp2]$ fn list triggers myapp2
FUNCTION        NAME    ID                              TYPE    SOURCE  ENDPOINT
hello           hello   01E02CGP7NNG8G00GZJ0000005      http    /hello  http://127.0.0.1:18080/t/myapp2/hello
myapp2          myapp2  01E02CDPS2NG8G00GZJ0000003      http    /myapp2 http://127.0.0.1:18080/t/myapp2/myapp2
```

curlで、エンドポイントにアクセスしてみます。

```terminal
[opc@instance-20200117-1627 myapp2]$ curl http://localhost:18080/t/myapp2/hello
{"message":"Hello World"}
```

メッセージが出力されました。

なお、ここでメッセージを返しているGoのソースコードは、下記のようなものです。CLIでアプリを作った段階で、テンプレートとして出力されたものそのままです。

```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)
}
```

# おわりに

このエントリでは、Oracle Cloud Infrastructure(OCI)のMicro Instanceの上で動作するCentOSに、Fn Projectのサーバをインストールして、疎通確認用のアプリを動作させました。

fn server自体は、さほどメモリを使わず（筆者が試した時点では、二けたMBの前半）動作しているようなので、メモリをあまり積んでいないマシンでも、お試しをするには十分そうです。