自宅クラスタでKnativeのServingとBuildを試してみた

January 12, 2020

最近、マイクロサービスとか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関係を試してみようと思います。