Wasme Operator を利用した Wasm Filter のデプロイ


はじめに

こちらの記事は以前に公開した "wasme を利用した Wasm Filter 開発と Istio の Envoy にデプロイするまでの流れ" の続編となります。

前回の記事では wasme という CLI を利用することで Envoy の Wasm Filter の開発とデプロイを簡単に行えることを紹介しましたが、wasme 自体は Wasm Filter の開発やテストで利用することが想定されているもので、本番環境の Kubernetes クラスタで稼働する Envoy へのデプロイは Wasme Operator を利用して Custom Resource で宣言的に管理することが推奨されています。

Wasme Operator について


引用: Declarative WebAssembly deployment for Istio

Wasme Operator は、WebAssembly Hub などのレジストリから Wasm Filter イメージをダウンロードして Node にキャッシュする wasme-cache と、Wasm Filter を Envoy にデプロイする wasme-operator の2つのコンポーネントで構成されており、各コンポーネントはデフォルトで wasme ネームスペースで稼働する仕様になっています。

各コンポーネントのソースコードは solo-io/wasm の tools/wasme ディレクトリ で管理されており、wasme-cache は wasme の cache サブコマンドで、wasme-operator は operator サブコマンドで起動されています。

現時点では Wasme Operator がサポートするデプロイ対象は Istio のみとなっており、Kubernetes クラスタに Istio(具体的には EnvoyFilter リソース)がインストールされていない場合は処理が失敗するので注意してください。

Wasme Operator が稼働している状態で FilterDeployment リソース(CRD は こちら, Spec/Status の詳細は こちら)を Kubernetes クラスタに作成することで、自動で Istio 内の Envoy に Wasm Filter がデプロイすることが可能になります。

それでは実際に動かしてみて Wasme Operator がどのように機能するのかを見ていきます。

実際に動かしてみる

Wasme Operator は2020年10月29日時点での最新バージョン v0.0.28 を利用します。

Istio がインストールされた Kuberntes クラスタの作成

Kubernetes クラスタ 1.18.0 を作成します。

minikube start --kubernetes-version v1.18.0

Istio 1.5.6 をインストールします。今回デプロイする Wasm Filter が 1.5.x に互換性があるものなので少々古いバージョンとなりますが 1.5.6 を利用しています。

istioctl manifest apply --set profile=demo

バージョンを確認します。

$ kubectl version --short
Client Version: v1.19.0
Server Version: v1.18.0

$ istioctl version
client version: 1.5.6
control plane version: 1.5.6
data plane version: 1.5.6 (3 proxies)

Wasme Operator のインストール

FilterDeployment リソースの CRD をインストールします。

kubectl apply -f https://github.com/solo-io/wasm/releases/download/v0.0.28/wasme.io_v1_crds.yaml

Wasme Operator をインストールします。

kubectl apply -f https://github.com/solo-io/wasm/releases/download/v0.0.28/wasme-default.yaml

wasme-cache は DaemonSet で wasme-operator は Deployment でデプロイされます。

$ kubectl get all -n wasme
NAME                                  READY   STATUS    RESTARTS   AGE
pod/wasme-cache-drjbd                 1/1     Running   0          50s
pod/wasme-operator-7b9c77976b-zlmv8   1/1     Running   0          50s

NAME                         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/wasme-cache   1         1         1       1            1           <none>          50s

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/wasme-operator   1/1     1            1           50s

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/wasme-operator-7b9c77976b   1         1         1       50s

サンプルアプリのデプロイ

Wasm Filter のデプロイ対象のサンプルアプリとして Bookinfo(solo-io/wasm の e2e テスト用のマニフェストを利用)をデプロイします。

kubectl create ns bookinfo
kubectl label namespace bookinfo istio-injection=enabled --overwrite
kubectl apply -n bookinfo -f https://raw.githubusercontent.com/solo-io/wasm/v0.0.28/tools/wasme/cli/test/e2e/operator/bookinfo.yaml

Bookinfo のアプリ内の Envoy へのリクエスト方法は以下となります。

kubectl exec -ti -n bookinfo deploy/productpage-v1 -c istio-proxy -- curl -I http://details.bookinfo:9080/details/123

この時点での HTTP レスポンスヘッダーは以下となります。

$ kubectl exec -ti -n bookinfo deploy/productpage-v1 -c istio-proxy -- curl -I http://details.bookinfo:9080/details/123
HTTP/1.1 200 OK
content-type: application/json
server: istio-envoy
date: Thu, 29 Oct 2020 09:40:51 GMT
content-length: 180
x-envoy-upstream-service-time: 1
x-envoy-peer-metadata: Ch0KDElOU1RBTkNFX0lQUxINGgsxNzIuMTcuMC4xMwrVAQoGTEFCRUxTEsoBKscBChAKA2FwcBIJGgdkZXRhaWxzCiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjZmYzU1ZDY1YzkKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwosCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgkaB2RldGFpbHMKKwojc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtcmV2aXNpb24SBBoCdjEKDwoHdmVyc2lvbhIEGgJ2MQoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKJQoETkFNRRIdGhtkZXRhaWxzLXYxLTZmYzU1ZDY1YzkteHBoemwKFwoJTkFNRVNQQUNFEgoaCGJvb2tpbmZvCk8KBU9XTkVSEkYaRGt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9ib29raW5mby9kZXBsb3ltZW50cy9kZXRhaWxzLXYxCiUKD1NFUlZJQ0VfQUNDT1VOVBISGhBib29raW5mby1kZXRhaWxzCh0KDVdPUktMT0FEX05BTUUSDBoKZGV0YWlscy12MQ==
x-envoy-peer-metadata-id: sidecar~172.17.0.13~details-v1-6fc55d65c9-xphzl.bookinfo~bookinfo.svc.cluster.local
x-envoy-decorator-operation: details.bookinfo.svc.cluster.local:9080/*

Wasm Filter のデプロイ

今回は前回の記事で開発した HTTP レスポンスに hello というヘッダーを追加する Wasm Filter である webassemblyhub.io/ryysud/custom-header:v0.1 を Istio 内の Envoy にデプロイしていきます。

FilterDeployment リソースは以下のようになります。

apiVersion: wasme.io/v1
kind: FilterDeployment
metadata:
  name: bookinfo-custom-filter
  namespace: bookinfo
spec:
  deployment:
    istio:
      kind: Deployment
  filter:
    image: webassemblyhub.io/ryysud/custom-header:v0.1

上記のリソースを作成します。

cat << EOF | kubectl apply -f -
apiVersion: wasme.io/v1
kind: FilterDeployment
metadata:
  name: bookinfo-custom-filter
  namespace: bookinfo
spec:
  deployment:
    istio:
      kind: Deployment
  filter:
    image: webassemblyhub.io/ryysud/custom-header:v0.1
EOF

リソースが作成されると wasme-cache による Wasm Filter イメージのキャッシュが完了した後に、wasme-operator による Envoy への Wasm Filter のデプロイが開始されます。

Wasm Filter のデプロイは、Node にキャッシュされたイメージを Envoy コンテナに hostPath でマウントするために Istio 仕様の Annotation である sidecar.istio.io/userVolume と sidecar.istio.io/userVolumeMount をアプリの Deployment マニフェストに追加(このタイミングでアプリの Pod がローリングアップデートする)して、Wasm Filter を Envoy で使用する設定を定義した EnvoyFilter リソースを子リソースとして作成するという流れになります。

以下は FilterDeployment リソースの子リソースとして作成された EnvoyFilter リソースとなります。Spec から Wasm Filter が Envoy に設定されることがわかります。

$ kubectl tree -n bookinfo filterdeployment bookinfo-custom-filter
NAMESPACE  NAME                                                          READY  REASON  AGE
bookinfo   FilterDeployment/bookinfo-custom-filter                       -              5m54s
bookinfo   ├─EnvoyFilter/details-v1-bookinfo-custom-filter.bookinfo      -              5m24s
bookinfo   └─EnvoyFilter/productpage-v1-bookinfo-custom-filter.bookinfo  -              5m24s

$ kubectl get envoyfilters.networking.istio.io -n bookinfo
NAME                                             AGE
details-v1-bookinfo-custom-filter.bookinfo       3m12s
productpage-v1-bookinfo-custom-filter.bookinfo   3m12s

$ kubectl get envoyfilters.networking.istio.io -n bookinfo details-v1-bookinfo-custom-filter.bookinfo -o yaml | kubectl neat
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: details-v1-bookinfo-custom-filter.bookinfo
  namespace: bookinfo
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            name: bookinfo-custom-filter.bookinfo
            rootId: add_header
            vmConfig:
              code:
                local:
                  filename: /var/local/lib/wasme-cache/a515a5d244b021c753f2e36c744e03a109cff6f5988e34714dbe725c904fa917
              runtime: envoy.wasm.runtime.v8
              vmId: bookinfo-custom-filter.bookinfo
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: details
      version: v1

また、Wasm Filter のデプロイ状況は作成した FilterDeployment リソースの Status からも確認できます。

# 余計な情報は削除しています
$ kubectl get filterdeployments.wasme.io -n bookinfo -o yaml bookinfo-custom-filter
apiVersion: wasme.io/v1
kind: FilterDeployment
metadata:
  name: bookinfo-custom-filter
  namespace: bookinfo
spec:
  deployment:
    istio:
      kind: Deployment
  filter:
    image: webassemblyhub.io/ryysud/custom-header:v0.1
status:
  observedGeneration: "1"
  workloads:
    details-v1:
      state: Succeeded
    productpage-v1:
      state: Succeeded

最後に Wasm Filter がデプロイされたかを確認します。

$ kubectl exec -ti -n bookinfo deploy/productpage-v1 -c istio-proxy -- curl -I http://details.bookinfo:9080/details/123
HTTP/1.1 200 OK
content-type: application/json
server: istio-envoy
date: Thu, 29 Oct 2020 09:55:32 GMT
content-length: 180
x-envoy-upstream-service-time: 1
hello: world! # <----- Wasm Filter によって `hello` ヘッダーが追加されている
x-envoy-peer-metadata: Ch0KDElOU1RBTkNFX0lQUxINGgsxNzIuMTcuMC4xNwrVAQoGTEFCRUxTEsoBKscBChAKA2FwcBIJGgdkZXRhaWxzCiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjdiYjg0Njk5YjQKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwosCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgkaB2RldGFpbHMKKwojc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtcmV2aXNpb24SBBoCdjEKDwoHdmVyc2lvbhIEGgJ2MQoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKJQoETkFNRRIdGhtkZXRhaWxzLXYxLTdiYjg0Njk5YjQtNzdqN2YKFwoJTkFNRVNQQUNFEgoaCGJvb2tpbmZvCk8KBU9XTkVSEkYaRGt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9ib29raW5mby9kZXBsb3ltZW50cy9kZXRhaWxzLXYxCiUKD1NFUlZJQ0VfQUNDT1VOVBISGhBib29raW5mby1kZXRhaWxzCh0KDVdPUktMT0FEX05BTUUSDBoKZGV0YWlscy12MQ==
x-envoy-peer-metadata-id: sidecar~172.17.0.17~details-v1-7bb84699b4-77j7f.bookinfo~bookinfo.svc.cluster.local
x-envoy-decorator-operation: details.bookinfo.svc.cluster.local:9080/*

レスポンスを見てみると hello ヘッダーが追加されていることから、Wasm Filter が正常にデプロイできたことが確認できました。

さいごに

今回は Wasme Operator を利用して Kubernetes クラスタへの Wasm Filter のデプロイを Custom Resource で宣言的に管理する方法を紹介しました。Wasm Filter の本番運用を検討している場合には Wasme Operator の導入を検討すると良いでしょう。

参考資料