Knative を IKS上で 稼働させてアプリをデプロイしてみた結果

Knative(ケイ・ネイティブ)は、IBM Cloud Kubernetesサービスの上で簡単に動作するので、セットアップして、サンプルアプリのコンテナビルドと、動作テストまでを実施して記録です。


## Knativeとは

Knativeは、アプリケーション開発者にとっての最大の課題、「実行基盤の整備や運用に悩まされることなく、即にアプリケーション開発に集中したい」に対する一つのソリューションです。 つまり、サーバーレスのワークロードを、Kubernetesクラスタで実行できる様になります。しかも、Knativeは、HELMとISTIOをベースとして構築されるため、面倒なセットアップは最小ですみます。

## Knativeの実行環境のセットアップ

設置アップ方法は、[Knative Install on IBM Cloud Kubernetes Service (IKS)](https://github.com/knative/docs/blob/master/install/Knative-with-IKS.md)にあります。このページにあるボタンをクリックすると、自動的に環境をセットアップするようなのですが、Before you beginから順番に設置アップしました。

この中では、環境変数にセットして、コマンドを実行するので、筆者が使った環境変数を以下に列挙しておきます。

~~~
export CLUSTER_NAME=knative
export CLUSTER_REGION=jp-tok
export CLUSTER_ZONE=tok04
export CLUSTER_K8S_VERSION=1.11.5
~~~

コマンドの実行は、IBM Cloudにログインして、順番に実行します。

~~~
alias ic='ibmcloud'
ic login
~~~
CLUSTER_ZONEで既にVLANを使っていれば、VLANのリストを表示してVLAN番号を取得する。

~~~
ic sl vlan list -d $CLUSTER_ZONE
~~~
次のコマンドで、IKSのクラスタを作成する。vlan番号は、前述の結果で得たID番号で置き換えます。

~~~
ic cs cluster-create --name=$CLUSTER_NAME \
  --zone=$CLUSTER_ZONE \
  --kube-version=$CLUSTER_K8S_VERSION \
  --machine-type=b2c.4x16 \
  --workers=3 \
  --private-vlan 244**** \
  --public-vlan 244***
~~~
大体20分くらいで、クラスタが稼働するので、ISTIOをインストール

~~~
kubectl apply --filename https://github.com/knative/serving/releases/download/v0.2.2/istio.yaml
~~~
ISTIOが介入するネームスペースを設定して、ポッドの動作を確認する。

~~~
kubectl label namespace default istio-injection=enabled
kubectl get pods --namespace istio-system
~~~
ISTIOのポッドが全部立ち上がる、または、処理が完了したら、Knativeをインストールする。

~~~
kubectl apply --filename https://github.com/knative/serving/releases/download/v0.2.2/release.yaml
~~~
次は起動完了確認の結果です。

~~~
$ kubectl get pods --namespace knative-serving
NAME                          READY   STATUS    RESTARTS   AGE
activator-6677bbc9d6-7jmzg    2/2     Running   0          2m
activator-6677bbc9d6-tvz57    2/2     Running   0          2m
activator-6677bbc9d6-vlkmm    2/2     Running   0          2m
autoscaler-5d87cc6b75-brfq6   2/2     Running   0          2m
controller-f4c59f474-tdm8q    1/1     Running   0          2m
webhook-5d9cbd46f7-ldmmx      1/1     Running   0          2m

$ kubectl get pods --namespace knative-build
NAME                                READY   STATUS    RESTARTS   AGE
build-controller-79d6cc9d57-hg2t9   1/1     Running   0          2m
build-webhook-f97d479f9-m9bcz       1/1     Running   0          2m
~~~

以上で、Knative利用の準備完了です。 およそ 30分くらいという感じです。


## Serverlessのアプリのコンテナビルド

サーバーレスのクライアントから呼び出すためのコンテナを開発します。と言っても、このアプリケーションは、PythonのFLASKフレームワークで開発したマイクロサービス相当のサンプルコードを利用します。

ディレクトリappを作成して、Pythonのアプリを作成します。

~~~
$ tree
.
├── app
│   ├── Dockerfile
│   └── app.py
└── service.yaml
~~~
app.pyのコードは、以下の通りで、curlなどで、ルート / をアクセスすると、Hello と 環境変数TARGETに設定された文字列を返すだけのシンプルなRESTサービスです。

~~~file:app.py
import os

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    target = os.environ.get('TARGET', 'World')
    return 'Hello {}!\n'.format(target)

if __name__ == "__main__":
    app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
~~~
次のコマンドのリストを順番に実行して、このアプリをコンテナにビルドして、Docker Hubに登録します。

~~~
docker build -t maho/helloworld-python .
docker login
docker push maho/helloworld-python
~~~
以上で、Serverlessのコンテナの準備は完了です。

次に進む前に、ここで利用したDockerfileは以下の通りです。環境変数が回りグドい感じがしますが、気にせず利用ください。

~~~ Dockerfile
FROM python
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . .
RUN pip install Flask
ENV PORT 8080
EXPOSE $PORT
CMD ["python", "app.py"]
~~~


## Knativeのサービス登録（アプリ機能の登録）

コンテナの準備ができたら、Knativeのサービスを登録していきます。このためのマニフェストは、以下の通りです。環境変数 TAGETに応答する文字列(Python Sample v1)をセットしておきます。

~~~file:service.yaml 
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld-python
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: maho/helloworld-python
            env:
              - name: TARGET
                value: "Python Sample v1"
~~~
マニフェストを適用することで、ロードバランサーと連携したサービスが生成されます。そして、アクセストラフィックは、ドメイン名 helloworld-python.default.example.com でルーティングされるようになります。

~~~
$ kubectl apply -f service.yaml
service.serving.knative.dev/helloworld-python created

$ kubectl get svc knative-ingressgateway --namespace istio-system
NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                                                                                                                   AGE
knative-ingressgateway   LoadBalancer   172.21.63.101   ***.***.68.123   80:32380/TCP,443:32390/TCP,31400:32400/TCP,15011:31145/TCP,8060:30283/TCP,853:32466/TCP,15030:31171/TCP,15031:32012/TCP   48m

$ kubectl get ksvc helloworld-python  --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain
NAME                DOMAIN
helloworld-python   helloworld-python.default.example.com
~~~

## アクセステスト
簡単な動作検証で、ドメイン名をDNSへ登録している訳にはいかないので、ヘッダーにドメイン名を設定して、サービスのEXTERNAL-IPに目がけて、アクセステストを実施します。

~~~
$ export IP_ADDRESS=***.***.68.123
$ curl -H "Host: helloworld-python.default.example.com" http://{$IP_ADDRESS}
Hello Python Sample v1!
~~~
RESTサービスの応答として、「Hello Python Sample v1!」がきました。

ここでは、検証のためにcurlでじかに利用しましたが、実際のアプリケーションからKnativeのサーバーレスを機能を呼び出す場合には、ベタにURLを書かずに、ラップ関数を設けて、コーディングし易いようにすると思います。というか、するべきだと思います。


## どこが Serverlessなんだろう？
これじゃ、サーバーレスとしての動作が良くわからないですよね。

次の実行結果で、curlのアクセスが実行されるまで、Pythonで書いたコンテナのポッドは、存在していません。そして、アクセスが来ると、そのFQDNに対応するポッド（コンテナ）が起動して、要求を処理します。そして、要求が5分間を経過しても、要求がなければ、Pythonのサンプルアプリのポッドは終了させられ、リソースを解放します。

つまり、Knativeは、機能（アプリ）を利用しない場合は、資源を解放して待機しており、機能（アプリ）への要求があると、対応する機能（アプリ）のポッド（コンテナ）を起動して処理を実行しまます。

~~~
## サービスのFQDN helloworld-python.default.example.com を呼び出され前
$ kubectl get pod 
No resources found.

## 呼び出され後
$ kubectl get pod 
NAME                                                  READY   STATUS    RESTARTS   AGE
helloworld-python-00001-deployment-669888748b-gzzkq   3/3     Running   0          8s

$ kubectl get pod 
NAME                                                  READY   STATUS    RESTARTS   AGE
helloworld-python-00001-deployment-669888748b-gzzkq   3/3     Running   0          56s
<省略>
$ kubectl get pod 
NAME                                                  READY   STATUS    RESTARTS   AGE
helloworld-python-00001-deployment-669888748b-gzzkq   3/3     Running   0          5m

## 5分越えると自動クリーンナップ
$ kubectl get pod 
No resources found.
~~~


## オートスケールはしないのか？

IKSには、2018年12月末現在で、ノードレベルのオートスケールが実装されていません。2019年はじめには、リリースされると思います。（個人の意見です） そのため、現在は、Knativeのアプリケーションに負荷をかけても、ノードレベルではスケールしませんが、もう少しすれば、ポッド数＝0から、最大ノード数設定値までスケールするようになると思います。


## まとめ
IKSでも、Knativeは利用できました。セットアップはとても簡単でした。IKSのノードレベルのオートスケールの実装が待ち遠しいですね。しかし、スケールの必要がなければ、現在のIKSでもKnativeは十分使い始められそうです。