最近、マイクロサービスとかServerlessと言うキーワードをよく目にするようになったので、無料で試せないかと思っていたら、Kubernetes上でServerlessを実現できるKnativeというフリーソフトがある、というので早速試してみました。GCPなどは使用せず、自宅クラスタに導入しました。Knativeとは何か、についてはこちらのサイトが参考になりました。
まず、こちらのページに書かれていた手順を使わせていただき、IstioとKnative一式を入れました。すでにKubernetesのインストールは完了しているので、実際に実行したのは下記の二行だけです。簡単ですね。
curl -L https://raw.githubusercontent.com/knative/serving/v0.1.1/third_party/istio-0.8.0/istio.yaml \
| sed 's/LoadBalancer/NodePort/' \
| sed 's/-statsd.mapping-config/--statsd.mapping-config/' \
| kubectl apply --filename -
curl -L https://github.com/knative/serving/releases/download/v0.1.1/release-lite.yaml \
| sed 's/LoadBalancer/NodePort/' \
| kubectl apply --filename -
これですんなりインストールは完了しましたが、下記のようにIstioを使用する名前空間(my-project)でIstioを有効化するのを忘れていて、あとでサービスをデプロイするときにハマってしまいました。
kubectl label namespace my-project istio-injection=enabled
Knativeにサービスを登録するためにはサービス(HTTPサーバ)として稼働させるDockerイメージをビルドしてレジストリに登録する必要があるので、最初はこちらのページを参考に、KnativeのBuildを使用してみました。このページでは、Bazelというビルドツールを使用していますが、KnativeのBuildでは、Kaniko(Dockerfileを使用してイメージ作成)、Jib(Javaアプリのイメージ作成が可能)、Buildpack(Node.js、Javaなどのソースからイメージを作成可能。Herokuなどで利用されている)、Buildah、BuildKitにも対応しているとのことです。BazelはGoogleの開発したソフトウエアのビルドツールですが、いろいろな言語に対応しており、Dockerイメージの作成、レジストリへのプッシュまでできるようです。今回初めて使ってみたのですが、機能が豊富で使い慣れると便利そうです。
KnativeのBuildでビルドするには、4つのyamlを作成してapplyすれば良いようです。簡単ですね。このyamlのなかで、Build Templateというのがありますが、今回は下記のようなものを作成しました。
apiVersion: build.knative.dev/v1alpha1
kind: BuildTemplate
metadata:
name: bazel
namespace: my-project
spec:
parameters:
- name: TARGET
description: The name of the Bazel "container_push" target to run
- name: IMAGE_TAG
description: The tag of image
steps:
- name: build-and-push
image: gcr.io/cloud-builders/bazel
args: ['run', '--host_force_python=PY2', '--define', 'IMAGE_TAG=${IMAGE_TAG}', '${TARGET}']
–host-force_pythonの意味はよくわかりませんが、Bazelでビルドしたときにエラーが出たのでその対策として入れています。
あと、ついでにKanikoでもビルドしてみました。今回作成したBuild Templateは下記です。
apiVersion: build.knative.dev/v1alpha1
kind: BuildTemplate
metadata:
name: kaniko
namespace: my-project
spec:
parameters:
- name: IMAGE
description: The name of the image to push
- name: DOCKERFILE
description: Path to the Dockerfile to build.
default: /workspace/Dockerfile
steps:
- name: build-and-push
image: gcr.io/kaniko-project/executor
args:
- --dockerfile=${DOCKERFILE}
- --destination=${IMAGE}
env:
- name: DOCKER_CONFIG
value: /builder/home/.docker
こちらはDockerfileを指定すれば良いだけです。ただ、ここでも後のデプロイでハマってしまいました。コンテナで公開するPORTを最初、8000にしていたのですが、デプロイするとqueue-proxyでエラーが出て動作しませんでした。こちらのページによると、Knativeでは8080がデフォルトのポート番号のようなので、8080に変更する必要がありました。
ビルドが無事終わったので、いよいよ今度はデプロイになります。こちらのページを参考に、yamlを1つ作ってapplyしました。
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: helloworld-js
namespace: my-project
spec:
runLatest:
configuration:
revisionTemplate:
spec:
container:
image: xxxx/xxxx/hello-js
env:
- name: TARGET
value: "JS Sample v1"
これでサービスが起動しましたので、早速アクセスしてみます。自宅のクラスタはLoadBalancerがありませんので、NodePortを使用してKnativeをインストールしました。よって、まずはどのポートにアクセスすればいいかを調べる必要があります。下記のコマンドで調べることができます(httpでのアクセス用ポートです)。
kubectl get svc knative-ingressgateway -n istio-system -o 'jsonpath={.spec.ports[?(@.port==80)].nodePort}'
今回は32380が出力されました。あと、Hostヘッダに指定するドメイン名が必要になります。これは下記のコマンドで調べることができます。
kubectl get services.serving.knative.dev helloworld-go --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain -n my-project
出力結果は下記のようになりました。
NAME DOMAIN
helloworld-js helloworld-js.my-project.example.com
ちなみに、上記のコマンドのgetの次は、ksvcというエリアスを指定するのが普通のようですが、私の環境ではなぜか使えませんでしたので、上記のように変更しています。ドメイン名、ポート番号がわかりましたので、あとはアクセスするだけです。下記のコマンドで試してみました。192.168.1.25というのは、自宅クラスタのマスターノードのIPアドレスです。
curl -H "Host: helloworld-js.my-project.example.com" http://192.168.1.25:32380
ステータスコード200が返り、期待通りの結果になりました。
ちなみに、上記コマンドを連続して、複数の端末から実行すると、サービスを実行するpodが自動的に増えていくことが確認できました。最大で、全てのワーカーノードに1つずつ、podが起動していましたが、これ以上は増えませんでした。実行を全部止めると、逆にpodが減っていき、ついには0になりました。オートスケーリングがうまく動作しているようです。
とりあえず、Build、Servingが試せましたので、次はEvent関係を試してみようと思います。