---
title: Kubernetes NativeなFaaSであるriff 0.0.3を試す
tags: ["Riff", "Kubernetes", "Docker", "Spring Cloud Function", "Reactor"]
categories: ["Dev", "FaaS", "riff"]
date: 2018-02-04T10:31:16Z
updated: 2018-02-24T17:03:40Z
---
[riff](https://projectriff.io/)はPivotalが開発中のk8s nativeなFaaS (Function as a Service)です。
現在のバージョンは0.0.3です。
`Function`、`Topic`というKubernetesのCustom Resourceが用意されています。
各種言語で関数コードを書き、Dockerイメージとしてパッケージングします。
`Function`リソースと入り口となる`Topic`リソースをk8sにデプロイします。
この段階でPodはできません。
Topicに対してHTTP Gatewayにリクエストを送ると、そのTopicをinputとしているFunctionに設定されているコンテナがFunction Controller経由で
作成され、Podができます。Podはサイドカーを持ち、関数を実行するためのいくつかのプロトコルをサポートします。現時点では
* HTTP
* stdio
* gRPC
* pipes
がサポートされています。
関数が呼ばれない時間が続くとPodが自動で削除されます (Scale to Zero)。アイドル時間はデフォルトでは20秒です。
Riffの特徴としては、FaaSの制約として(分散システムにおけるCAP定理のような)ICE定理があるとして、この3つの特徴のうち2つを(将来的に)選択できるようにすることが挙げられます。
ICE定理は次の3つのうち同時に2つしか選択できない、というものです。
* Immediate (関数がすぐに起動する)
* Consistent (コンテナを不変にする)
* Efficient (使わなれければ0にスケールする)
では早速Riffを使ってみます。以下、メモです。
### Docker for Mac Edgeの用意
ローカルk8sとしてDocker for Mac (Edge)を使います。
```
brew cask install docker-edge
```
メモリを4GBにして、Kubernetesタブで`Enable Kubernetes`にチェック。
```
kubectl config uset-context docker-for-desktop
```
### Hemlのセットアップ
RiffはHelmでインストールできます。
```
kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
helm init --service-account=tiller
helm repo add riffrepo https://riff-charts.storage.googleapis.com
helm repo update
```
### Riffのインストール
```
helm install riffrepo/riff --name demo \
--version 0.0.3-rbac \
--set httpGateway.service.type=NodePort
```
```
$ kubectl get all
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/demo-riff-function-controller 1 1 1 1 44s
deploy/demo-riff-http-gateway 1 1 1 1 44s
deploy/demo-riff-kafka 1 1 1 1 44s
deploy/demo-riff-topic-controller 1 1 1 1 44s
deploy/demo-riff-zookeeper 1 1 1 1 44s
NAME DESIRED CURRENT READY AGE
rs/demo-riff-function-controller-5df6c848d5 1 1 1 44s
rs/demo-riff-http-gateway-7cc944f97c 1 1 1 44s
rs/demo-riff-kafka-65555dbb87 1 1 1 44s
rs/demo-riff-topic-controller-67ccb96678 1 1 1 44s
rs/demo-riff-zookeeper-859688cdc8 1 1 1 44s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/demo-riff-function-controller 1 1 1 1 44s
deploy/demo-riff-http-gateway 1 1 1 1 44s
deploy/demo-riff-kafka 1 1 1 1 44s
deploy/demo-riff-topic-controller 1 1 1 1 44s
deploy/demo-riff-zookeeper 1 1 1 1 44s
NAME DESIRED CURRENT READY AGE
rs/demo-riff-function-controller-5df6c848d5 1 1 1 44s
rs/demo-riff-http-gateway-7cc944f97c 1 1 1 44s
rs/demo-riff-kafka-65555dbb87 1 1 1 44s
rs/demo-riff-topic-controller-67ccb96678 1 1 1 44s
rs/demo-riff-zookeeper-859688cdc8 1 1 1 44s
NAME READY STATUS RESTARTS AGE
po/demo-riff-function-controller-5df6c848d5-xtj2w 1/1 Running 0 44s
po/demo-riff-http-gateway-7cc944f97c-8c9kx 1/1 Running 1 44s
po/demo-riff-kafka-65555dbb87-w7q95 1/1 Running 0 44s
po/demo-riff-topic-controller-67ccb96678-9rkkb 1/1 Running 0 44s
po/demo-riff-zookeeper-859688cdc8-pxt5h 1/1 Running 0 44s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/demo-riff-http-gateway NodePort 10.97.115.228 80:32356/TCP 44s
svc/demo-riff-kafka ClusterIP 10.110.227.80 9092/TCP 44s
svc/demo-riff-zookeeper ClusterIP 10.101.79.249 2181/TCP 44s
svc/kubernetes ClusterIP 10.96.0.1 443/TCP 17m
```
### Riff CLIのインストール
ヘルパースクリプトとして`riff` CLIが用意されています。
```
curl -Lo riff https://github.com/projectriff/riff/releases/download/v0.0.3/riff && chmod +x riff && sudo mv riff /usr/local/bin/
```
今はshell scriptですが、goで書き直し中です。
### Sample Functions
現時点でFunction Invokerは
* JavaScript
* ShellScript
* Python2
* Java
が公式に利用可能です。
次のディレクトリで作業します。
```
mkdir samples
cd samples
```
#### JavaScriptの場合
```
mkdir -p node/square
cd node/square
```
``` js
cat < square.js
module.exports = (x) => x ** 2;
EOF
```
`Promise`も使えます。
``` js
cat < square.js
module.exports = (x) => Promise.resolve(x ** 2);
EOF
```
async/awaitも使えます。
``` js
cat < square.js
module.exports = async (x) => x ** 2;
EOF
```
```
riff init -i numbers -u making
```
次のファイルができます。
```
$ ls -l
total 32
-rw-r--r-- 1 maki staff 113 2 3 22:31 Dockerfile
-rw-r--r-- 1 maki staff 154 2 3 22:31 square-function.yaml
-rw-r--r-- 1 maki staff 90 2 3 22:31 square-topics.yaml
-rw-r--r-- 1 maki staff 38 2 4 03:26 square.js
```
`Dockerfile`
```
FROM projectriff/node-function-invoker:0.0.3
ENV FUNCTION_URI /functions/square.js
ADD square.js ${FUNCTION_URI}
```
`square-function.yaml`
``` yaml
apiVersion: projectriff.io/v1
kind: Function
metadata:
name: square
spec:
protocol: http
input: numbers
container:
image: making/square:0.0.1
```
`square-topics.yaml`
```
apiVersion: projectriff.io/v1
kind: Topic
metadata:
name: numbers
spec:
partitions: 1
```
`docker`コマンドと`kubectl`コマンドでビルドとデプロイをしても良いですが、
`riff`コマンドでラップされています。こちらの方が楽です。
```
riff build -u making
riff apply
```
以上の`riff`コマンドのショートカット版が
```
riff create -i numbers -u making
```
です。
`riff build`も`riff create`も`--push`をつけるとDocker Registryにpushできます。
同期リクエストを送る場合は
```
$ riff publish -i numbers -d 10 -r
100
```
非同期リクエストを送る場合は
```
$ riff publish -i numbers -d 10
message published to topic: numbers
```
#### Shell Scriptの場合
```
mkdir -p shell/upper
cd shell/upper
```
``` sh
cat <<'EOF' > upper.sh
#!/bin/bash
echo $1 | tr [:lower:] [:upper:]
EOF
chmod +x upper.sh
```
```
riff create -i lower -u making
```
```
riff publish -i lower -d hello -r
```
#### Pythonの場合
```
mkdir -p pyhon/lower
cd pyhon/lower
```
``` python
cat <<'EOF' > lower.py
# -*- coding: utf-8 -*-
def process(data):
print(data.lower())
if __name__ == '__main__':
data = raw_input()
process(data)
EOF
cat <<'EOF' > requirements.txt
EOF
```
```
riff create -i upper -u making --handler process
```
```
riff publish -i upper -d HELLO -r
```
#### Javaの場合
```
cd ../..
mkdir -p java/hello
cd java/hello
```
Mavenプロジェクトでもいいのですが、`javac`と`jar`コマンドだけで関数jarファイルを実装します。
``` java
mkdir -p src/functions
cat < src/functions/Hello.java
package functions;
import java.util.function.Function;
public class Hello implements Function {
public String apply(String name) {
return "Hello " + name;
}
}
EOF
```
```
mkdir classes
javac -sourcepath src -d classes src/functions/Hello.java
jar -cvf func.jar -C classes .
```
```
riff create --input names -u making --artifact func.jar --handler functions.Hello
```
```
riff publish -i names -d world -r
```
この関数はSpring Cloud Function上のIsolated Classloader上で実行されます。
#### JavaでWindow処理の場合
[Reactor](https://projectreactor.io/)を使ってWindow処理ができます。
```
cd ../..
mkdir -p java/wordcounter
cd java/wordcounter
```
``` java
mkdir -p src/functions
cat <<'EOF' > src/functions/WordCounter.java
package functions;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import reactor.core.publisher.Flux;
public class WordCounter implements Function, Flux