Kubeless（Kubernetes Native Serverless Framework）を触ってみる

※2022/02時点でKubelessのメンテナンスは止まったようです
> WARNING: Kubeless is no longer actively maintained by VMware.
VMware has made the difficult decision to stop driving this project and therefore we will no longer actively respond to issues or pull requests. If you would like to take over maintaining this project independently from VMware, please let us know so we can add a link to your forked project here.

## Kubelessとは
https://kubeless.io/
> Kubeless is a Kubernetes-native serverless framework that lets you deploy small bits of code (functions) without having to worry about the underlying infrastructure.

Kubernetes上でサーバーレスが構築できるフレームワークのようです

Python, Node, Java, Go, .NET, Ruby, PHPをサポート
ちょっと面白いのはBallerinaっていう言語もサポートしてます
https://kubeless.io/docs/runtimes/

KafkaとHTTPをイベントトリガーとして使用

また、Serverless Frameworkのプラグインもありました
https://github.com/serverless/serverless-kubeless

では試してみます

## 環境
macOS Big Sur 11.4

## Kubernetes環境の用意
minikubeを使用しました
kindなど他のツールで構築してもいいですし、パブリッククラウドのk8s環境を利用してもいいと思います

## Kubelessの環境構築
ここに沿ってやっていきます
https://kubeless.io/docs/quick-start/

```sh
$ export RELEASE=$(curl -s https://api.github.com/repos/kubeless/kubeless/releases/latest | grep tag_name | cut -d '"' -f 4)
$ kubectl create ns kubeless
$ kubectl create -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless-$RELEASE.yaml

# 色々とリソースが作成されたのを確認
$ kubectl get ns | grep kubeless
$ kubectl get pods -n kubeless
$ kubectl get deployment -n kubeless
$ kubectl get configmaps -n kubeless
$ kubectl get serviceaccounts -n kubeless
$ kubectl get customresourcedefinition
$ kubectl get clusterrole | grep kubeless
```

## CLIのインストール

```sh
$ export OS=$(uname -s| tr '[:upper:]' '[:lower:]')
$ curl -OL https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless_$OS-amd64.zip && \
  unzip kubeless_$OS-amd64.zip && \
  sudo mv bundles/kubeless_$OS-amd64/kubeless /usr/local/bin/

$ kubeless version
Kubeless version: v1.0.8
```

## アプリケーションのデプロイ
デプロイするコードの準備

```py:test.py
def hello(event, context):
  print(event)
  return event['data']
```

デプロイ

```sh
$ kubeless function deploy hello --runtime python3.8 \
                                --from-file test.py \
                                --handler test.hello
INFO[0000] Deploying function...
INFO[0000] Function hello submitted for deployment
INFO[0000] Check the deployment status executing 'kubeless function ls hello'
```

作成されたリソースの確認

```sh
# kubectlでfunctionsというカスタムリソースが使える
$ kubectl get functions
NAME         AGE
hello        33m

$ kubeless function ls
NAME 	NAMESPACE	HANDLER   	RUNTIME  	DEPENDENCIES	STATUS
hello	default  	test.hello	python3.8	            	1/1 READY

# defaultネームスペースにPodやDeploymentが作成されてる
$ kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
hello-595cddff7b-8jsrq   1/1     Running   0          37m

# デプロイしたコードはConfigMapに格納されてる
$ kubectl describe configmaps hello
Name:         hello
Namespace:    default
Labels:       created-by=kubeless
              function=hello
Annotations:  <none>

Data
====
requirements.txt:
----

test.py:
----
def hello(event, context):
  print(event)
  return event['data']

handler:
----
test.hello
Events:  <none>
```

## アプリケーションの実行

```sh
$ kubeless function call hello --data 'Hello world!'
Hello world!
```

Podにログが落ちてた

```sh
$ kubectl logs hello-595cddff7b-8jsrq -f
{'data': b'Hello world!', 'event-id': 'Zdz4MWclcG3SFmw', 'event-type': 'application/x-www-form-urlencoded', 'event-time': '2021-05-28T16:20:09Z', 'event-namespace': 'cli.kubeless.io', 'extensions': {'request': <PicklableBottleRequest: POST http://127.0.0.1:51301/>}}
[28/May/2021:16:20:10 +0000] "POST / HTTP/1.1" 200 12 "" "kubeless/v0.0.0 (darwin/amd64) kubernetes/$Format" 0/13716

# `kubeless function logs hello`でも確認可能
```

プロキシ経由でリクエストも可能

```sh
$ kubectl proxy -p 8080 &
$ curl -L --data '{"Another": "Echo"}' \
  --header "Content-Type:application/json" \
  localhost:8080/api/v1/namespaces/default/services/hello:http-function-port/proxy/
{"Another": "Echo"}
```

## ドキュメントの通りやってハマったとこ
試したタイミングではドキュメントに`--runtime python2.7`とありましたが、その通りやると

```sh
$ kubeless function deploy hello --runtime python2.7 \
                                --from-file test.py \
                                --handler test.hello
FATA[0000] Invalid runtime: python2.7. Supported runtimes are: ballerina0.981.0, dotnetcore2.0, dotnetcore2.1, dotnetcore2.2, dotnetcore3.1, go1.13, go1.14, java1.8, java11, nodejs10, nodejs12, nodejs14, php7.2, php7.3, php7.4, python3.6, python3.7, python3.8, ruby2.3, ruby2.4, ruby2.5, ruby2.6, jvm1.8, nodejs_distroless8, nodejsCE8, vertx1.8
```

なのでpython3.8を選択
ランタイムの確認方法はこちら

```sh
$ kubeless get-server-config
INFO[0000] Current Server Config:
INFO[0000] Supported Runtimes are: ballerina0.981.0, dotnetcore2.0, dotnetcore2.1, dotnetcore2.2, dotnetcore3.1, go1.13, go1.14, java1.8, java11, nodejs10, nodejs12, nodejs14, php7.2, php7.3, php7.4, python3.6, python3.7, python3.8, ruby2.3, ruby2.4, ruby2.5, ruby2.6, jvm1.8, nodejs_distroless8, nodejsCE8, vertx1.8
```

ちなみに、ドキュメントに貼ってあるコードのままランタイムをpython3.8でデプロイすると

```sh
$ kubeless function ls
NAME 	NAMESPACE	HANDLER   	RUNTIME  	DEPENDENCIES	STATUS
hello	default  	test.hello	python3.8	            	0/1 NOT READY
```

NOT READY、、
Podのログを見ると

```sh
Traceback (most recent call last):
  File "/_kubeless.py", line 20, in <module>
    mod = importlib.import_module(os.getenv('MOD_NAME'))
  File "/opt/bitnami/python/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 779, in exec_module
  File "<frozen importlib._bootstrap_external>", line 916, in get_code
  File "<frozen importlib._bootstrap_external>", line 846, in source_to_code
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/kubeless/test.py", line 2
    print event
          ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(event)?
```

ということで、ちゃんと3.8用に`print(event)`にして無事デプロイできた

## 環境をきれいにする
```sh
$ kubeless function delete hello
$ kubectl delete -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless-$RELEASE.yaml
$ kubectl delete ns kubeless
```

## まとめ
割と簡単に環境構築できました
もう少し触ってみたい

Fissionという似たようなOSSも試してみたので、少し比較もした記事を書いてます

https://qiita.com/ryutoyasugi/items/03dd8bdaa0cc796d3903