APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

AKSでDaprを使ってみる(5)Contourと組み合わせる

はじめに

今回のテーマは「AKSでDaprを使ってみる(5)Contourと組み合わせる」、
別名「AKSでContour Ingress Controllerを導入してみる(X)Daprと組み合わせる
となります。ここまでやってきた DaprとContourが合流します。

以前DaprとNginx ingress controllerを組み合わせて利用する方法をご紹介しました。

Nginx ingress controllerでできるならば、Contourを利用した場合でも同様にできるはず、そう考える方も多いかと思います。 私も「前と同じようにやればOKだろう」と考えていましたが、実際やってみるとそれほど簡単ではなかった・・。 どうやればContourとDaprを組み合わせて利用できるか? その内容をご紹介できればと思います。

今回の作業目標

ベースとなる環境はContourの検証を行ってきたものとします。

全体的な構成はNginx ingress controllerの時と同じです。Contour(Envoy)のReverse ProxyにDaprのsiide carを入れ、 daprの通信で振り分けを実現します。

ただ、せっかく前回ContourのRequest RewritingやMultiple Upstreamという便利な機能もご紹介していますので、 これらもDaprと組み合わせて利用できるようになることを目指したいと思います。

ということで対象は、HTTPProxyオブジェクトによるルーティングだけに限定させて頂いております。

設定内容

Daprのインストール

Daprとotel-collectorのインストール

こちらの内容は以前の Nginx ingress controllerの時と同じものです。

dapr-config.yaml

global:
  logAsJson: true

helmでupdate --installを実行すればインストールできます。

helm repo add dapr https://dapr.github.io/helm-charts/
helm repo update
helm upgrade --install dapr dapr/dapr \
  --namespace dapr-system --create-namespace \
  --values dapr-config.yaml

続いて OpenTelemetry Collectorのインストールです。サンプルソースコードにYamlが用意しておきました。そちらをデプロイします。

cat open-telemetry-collector-appinsights.yaml | envsubst | kubectl apply -f -
kubectl apply -f collector-config.yaml

Contourの設定アップデート

次にContourをアップデートします。

contour:
  podAnnotations:
    # see https://projectcontour.io/guides/prometheus/
    prometheus.io/scrape: "true"
    prometheus.io/path: "/metrics"
    prometheus.io/port: "8000"
envoy:
  serviceAccount:
    # see https://github.com/dapr/dapr/issues/4227  we probably don't need this option from dapr 1.8
    automountServiceAccountToken: true
  podAnnotations:
    # see https://projectcontour.io/guides/prometheus/
    prometheus.io/scrape: "true"
    prometheus.io/path: "/stats/prometheus"
    prometheus.io/port: "8002"
    # 以下Dapr関連追加
    dapr.io/config: "appconfig"
    dapr.io/enabled: "true"
    dapr.io/app-id: "contour-ingress"
    dapr.io/sidecar-listen-addresses: "0.0.0.0"

envoy.podAnnotations にdaprの設定を追加します。 導入の際、当初は envoy.serviceAccount.automountServiceAccountToken の指定はしませんでした。 ところが、この場合以下のIssueと同じエラー( Failed to init state store on xxx with error: "/home/noroot/.kube/config: no such file or directory" )が発生しました。

Issueの内容を参考にContourのheml templateを確認してみると、自動的にtokenをマウントする機能はcontour内で無効にされていました。 どうやらこの設定はDaprではエラーなってしまうようです。関連するチケットもあります。

Issue 4227で、今後のバージョン(おそらくdapr 1.8)で改善策を提供すると記載があるので、 今回はいったん automountServiceAccountToken をtrueにすることにしました。

またdaprの設定には dapr-port というものがあったと思います。

dapr-port 設定例

        dapr.io/app-port: "80"

最初こちらの指定を入れていたのですが、contour envoyにinjectされるdaprd (dapr sidecar)の起動に失敗してしまいました。 ずっとこのportでアプリケーションの応答を待っているのですが受け取れず、タイムアウトしてしまうもののようです。

はっきりとした原因はまだ掴んでいませんが、 どうやらenvoyとdaprの指定が競合してしまうようです。 (類似のIssueが以下のものです)

このIssue同様Contour envoyは外部に対する公開はしますが、Daprによるデータ受信は行いません。 この場合 dapr-portは指定しなくてよいので、contour envoyの指定からは外しました。

設定内容で helm updateを実行します。

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm upgrade --install ingress-contour bitnami/contour \
  --namespace ingress-system --create-namespace \
  --values config.yaml

アプリケーションデプロイの修正

アプリケーションの指定はNginx ingress controllerのときと同じです。

aggregator.yamlの抜粋

# 前省略
spec:
  selector:
    matchLabels:
      app: aggregator
  template:
    metadata:
      labels:
        app: aggregator
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "aggregator"
        dapr.io/app-port: "8080"
        dapr.io/config: "appconfig"

同様の設定を aggregator-2やtimefeedにも行いデプロイします。 なお、aggregator/aggregator-2/timefeed 用に作成していたServiceは使用しない (service-id-daprというServiceが自動的にさくせされる)ため、削除します。

kubectl delete service -n web aggregator
kubectl delete service -n web aggregator-2 
kubectl delete service -n web timefeed

export APPINSIGHTS_JAVA_AGENT_VER=3.2.10
export BASE64_INSTRUMENTATION_STRING=`echo -n ${AI_INSTRUMENTATION_STRING} | base64`
cat timefeed.yaml | envsubst | kubectl apply -f -
cat aggregator.yaml | envsubst | kubectl apply -f -
cat aggregator-v2.yaml | envsubst | kubectl apply -f -

ルーティングの修正

Contour の設定で dapr-port の指定をしないため解決しなければならない課題が1つあります。それは contour-ingress-dapr というサービスを自動的に作成しないということです。(aggregatorやtimefeedなどは aggregator-daprというサービスが作成されていると思います)
このcontour-ingress-daprに相当する指定は Routingの際の宛先として利用しますので、手動で作成する必要があります。aggregator/timefeedサービスで自動作成するものの内容を参考にcontour-ingress用のサービスを作成します。 (-daprという名称は今後Daprの自動作成と衝突する可能性もあるため、contour-ingress-headless と名称を変えています)

contour-ingreess-headless.yaml

apiVersion: v1
kind: Service
metadata:
  name: contour-ingress-headless
  namespace: ingress-system
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: dapr-htp
    port: 3500
  - name: dapr-grpc
    port: 50001
  - name: dapr-metrics
    port: 9090
  selector:
    app.kubernetes.io/component: envoy
    app.kubernetes.io/instance: ingress-contour
    app.kubernetes.io/name: contour

続いてHTTPProxyの設定変更です。

http-proxy.yaml

apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: aggregator
  namespace: ingress-system     # namespaceを webからingress-systemに変更
spec:
  virtualhost:
    fqdn: aggregator.${DOMAIN_NAME}
    tls:
      secretName: aggregator
  routes:
    - services:
      - name: contour-ingress-headless
        port: 3500
        weight: 70
        # Dapr通信用設定
        requestHeadersPolicy:
          set:
          - name: dapr-app-id
            value: "aggregator.web"
         # Dapr通信設定ここまで
      - name: contour-ingress-headless
        port: 3500
        weight: 30
        # Dapr通信用設定
        requestHeadersPolicy:
          set:
          - name: dapr-app-id
            value: "aggregator-2.web"
         # Dapr通信設定ここまで
      conditions:
      - prefix: /backend
      pathRewritePolicy:
        replacePrefix:
        - prefix: /backend
          replacement: /
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: aggregator
  namespace: ingress-system       # namespaceを webからingres-system に変更
spec:
  commonName: aggregator.${DOMAIN_NAME}
  dnsNames:
  - aggregator.${DOMAIN_NAME}
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  secretName: aggregator

まずポイントは転送先の指定方法です。

Nginx Controllerの際は 転送先ホストを Nginx(nginx-ingress )転送先パスを Daprのインターフェースに従い /v1.0/invoke/${service-id}/method/$1 と指定していました。今回も、と思いましたがHTTPProxyの pathRewritePolicyが転送先ホストごとではなく 転送先グループでしか指定できません。(aggregatorまたはaggregator-2への転送振り分けのグループ単位) ${service-id}の部分が固定となってしまいます。

ここでDaprのService Invocationインターフェースの別のやり方を採用します。実は Daprのインターフェースはパス名だけではなく、Request Headerに転送先サービスIDを設定することでも実現できまます。

転送内容 service: service-id path:/$1 の指定方法。

転送先ホストは daprの localhost(ingress-dapr):3500 )

形式 ヘッダ パス
パス名指定 指定なし /v1.0/invoke/${service-id}/method/$1
ヘッダ指定 dapr-app-id: service-id /$1

HTTPProxyのRequest Rewriting には requestHeadersPolicy というヘッダ情報追加更新の機能もあります。こちらはサービス転送先単位で指定できます。よって、転送先パス名はaggregator/aggregator-2ともに同じ /$1とし、ヘッダ情報に転送先を埋め込むことにします。

その指定がこちらの部分です。

    - services:
      - name: contour-ingress-headless
         # 省略
        requestHeadersPolicy:
          set:
          - name: dapr-app-id
            value: "aggregator.web"
      # 省略
      conditions:
      - prefix: /backend
      pathRewritePolicy:
        replacePrefix:
        - prefix: /backend
          replacement: /

なお転送先ホスト名は手動で作成する contour-ingress-headless となります。 こちらはContourが動作する ingress-system namespaceに作成します。 そのサービス名を参照するために aggregator HTTPProxy指定やCertificateのnamespaceも web から ingress-system とし、 dapr-app-idをaggregator.web とdapr namespace付きの名称としています。

これらをデプロイします。

kubectl apply -f contour-ingress-headless.yaml

cat http-proxy.yaml | envsubst | kubectl apply -f -

動作確認をしてみましょう

ブラウザから https://aggregator.${DOMAIN_NAME}/backend/service すると以下の左右いずれかの画面が表示されると 思います。これで、 「AKSでContour Ingress Controllerを導入してみる(3)Multiple Upstream」の環境で Daprが利用可能になりました。

Contour EnvoyへのDapr Sidecarの導入でかなり苦労しましたが、なんとか実現できました。 また、ContourのRequest Rewriting機能にも助けられました。

いつものように設定内容は以下のGitHub Repositoryで公開しています。

おわりに

それぞれのサービスのドキュメントから類推すると「この組み合わせは簡単にできるはず」と思ったことが 実はそれほど簡単ではなかった、やはりある程度やってみないとわからないことがあるものです。 今回のDaprとContourの組み合わせもその一例になるかと思います。 そうした知見をどれだけ効果的に得られるかがこうした調査・検証では重要になってくると実感する内容でした。

最終目標はこの構成にさらに Open Service MeshKEDAを加え、Azure Container Apps相当の機能をAKS上でも実現することですが、 それらを追加するのは少し時間をおいてからにしたいと思います。その際は内容を公開していきたいと思いますので またお付き合いください。

最後に宣伝。 弊社エンジニアが協力した、「AKSのセキュリティ対策に関するホワイトペーパー」の3,4章がMicrosoftから公開されました。

ホワイトペーパー本体はこちらです。 ぜひご覧頂きたいと思います。

私たちは、Azure・AKSを活用したシステムのSIや内製化のお手伝いをさせていただいております。 Azureやコンテナ技術の知見を持つエンジニアが対応いたします。ご相談等ありましたらぜひご連絡ください。