kashinoki38 blog

something like tech blog

AKS上のIstioデモアプリをPrometheus監視+Jaegerトレーシングする

前の記事で自身でPrometheusをAKSに構築しKubernets、Nginx、Redisを監視した。 kashionki38.hatenablog.com

それに加えJaegerでのトレーシングも構築する必要があったためIstioを導入することにした。
結果としてPrometheusによるリソース監視、Jaeger/Zipkinによるトレーシングに加え、Istio×Prometheusによるサービス単位のRED監視がデフォルトでできるため、MSA on Kubernetes環境における性能評価には必要なツールであると思えた。

デモ環境

Istioにはサンプルアプリがあって、良い感じにMSAで1ページのWebページを構成している。
istio.io f:id:kashionki38:20200306131351p:plain
trace-idをheaderに伝搬する処理も実装済みで、監視/トレーシングやIstioのトラフィックコントロールの検証には持ってこいなのだ。

画面

ちなみにこういうページが構成されており、topがproductpage、各描画部分が後続のdetails, reviews, ratingsから情報を取ってきている。
f:id:kashionki38:20200306131627p:plain

Istio構築

ということで、AKS上にIstioを構築していく。

Istioのダウンロード

ドキュメント参照。
istio.io

WLS Ubuntu上でやった。

$ curl -L https://istio.io/downloadIstio | sh -

Istioのインストール→AKS

以下参考に。
docs.microsoft.com

Grafanaのシークレットを追加する、でGrafanaのパスワード設定になるので覚えておくこと。

以下、Istioコンポーネントのインストールのためのコマンドだが、installPackagePathはistioをダウンロードした際にinstallディレクトリができているのでそのパスを指定すること。

$ istioctl manifest apply -f istio.aks.yaml --logtostderr --set installPackagePath=./install/kubernetes/operator/charts
istio-1.5.0/
├── bin
├── install
│   ├── consul
│   ├── gcp
│   ├── kubernetes
│   └── tools

istio-system namespaceにpodがいっぱいできている。(Prometheusは後ほどStatefulSetに書き換えたので、それで表示されている)

$ kubectl get po -n istio-system
NAME                                                        READY   STATUS    RESTARTS   AGE
grafana-54db7fcb58-j75l4                                    1/1     Running   0          8d
istio-citadel-57df8745df-jffjn                              1/1     Running   0          8d
istio-galley-7bd4545f4-6q8rt                                2/2     Running   0          8d
istio-ingressgateway-85978556b4-f9skz                       1/1     Running   86         8d
istio-pilot-664c9fcbd4-58q52                                2/2     Running   86         8d
istio-policy-654557c4f4-8s8nh                               2/2     Running   321        8d
istio-policy-654557c4f4-mcxx2                               2/2     Running   0          3d7h
istio-policy-654557c4f4-pqf4n                               2/2     Running   0          3d21h
istio-policy-654557c4f4-pzrgx                               2/2     Running   0          4d23h
istio-policy-654557c4f4-rh26l                               2/2     Running   0          6d7h
istio-sidecar-injector-65d67fc47-2nzth                      1/1     Running   0          8d
istio-telemetry-7c858cf875-t8b2p                            2/2     Running   0          3d21h
istio-tracing-cd67ddf8-fbswf                                1/1     Running   0          8d
kiali-74db4467dc-96xch                                      1/1     Running   0          8d
prometheus-0                                                1/1     Running   0          3d6h

簡単に、Prometheus、Grafana、Jaeger(istio-tracing)が入ってしまった。

Prometheusはデフォルトだとめっちゃメモリ食うよ

じゃあ、Prometheusはこのまま走らせとけばいいのかというとそうでもない。
(そもそも監視対象クラスタ外に置くべきという話があるので本当は別立てすべきなのだと思うけど)

実はデフォルトのscrape configのままIstioのビルトインPrometheusを走らせているとメモリをめっちゃ食う。
俺はAKSクラスタをStandard_DS2_v2で、各ノードメモリ7GBで運用していたが、普通に7GB以上スパイクする。
そしてpodはOOM-killerに殺されて再起動する。
f:id:kashionki38:20200306134422p:plain

どうしたものかと色々試行錯誤した結果、Istioのトラフィック系のメトリクスを15秒間隔に取り続けることが原因ということがわかった。
サービスのRED評価に必要なものを残して他のscrape configはコメントアウトすると、起動時に3GB取っていたのが200MB程まで削減され数日稼働しても特にリークせず安定した。
(envoy-stats, istio-proxy, istio-telemetry, pilot, galley, citadel, kubernetes-apiserverをコメントアウトした。まだこのscrape結果を有効活用するまでいたってないので一旦。。。)
IstioビルトインPrometheusについてのprometheus.yamlの設定の仕方だが、configmap経由となっている。

$ kubectl get configmap prometheus -o yaml -n istio-system
apiVersion: v1
data:
  prometheus.yml: |-
    global:
      scrape_interval: 15s
    scrape_configs:

    # Mixer scrapping. Defaults to Prometheus and mixer on same namespace.
    #
    - job_name: 'istio-mesh'
      kubernetes_sd_configs:
      - role: endpoints

Prometheus全般に言えるが、バージョンによって書き方が変わったりするのでどのバージョン使っているのか確認した上でドキュメントのバージョンを合わせることを意識したほうが良さそう。
Prometheusのメモリ管理の方式もv1→v2で大きく変わったらしく、基本的にpod limitsまで割り当てられるだけ割り当てるようになっている。
qiita.com

オプションがシンプルになり -storage.local.target-heap-size のチューニングが不要に(後述)
-storage.local.target-heap-size が不要になった理由 -storage.local.target-heap-size による明示的なメモリの指定がいらなくなったのは Go のレイヤで行っていたチャンクデータのキャッシュの処理を、2.0 ではmmap を使いカーネル側に任せることで自前での退避(evict)処理が必要なくなったからだそうです。参考: Unable to deploy v2.0.0-beta.0 #480 のコメント

これもv1時代のドキュメントや質問を見ているとヒープサイズ変更が必要と思い込んでしまうので注意が必要。
v2でできる有効策はscrapeの設定を調整することだ。

Prometheus監視対象拡張

Prometheus自体の設定はOKなので、追加で監視する項目を設定していく。
今回は時間もなかったのでmongodbだけ追加で設定した。

mongodb監視

Exporter

hub.docker.com mongodbのexporterにはこのイメージを使用。
正直、ほとんどすることはなく対象podに以下の様にサイドカーとして追加するくらい。

      - name: mongodb-exporter
        image: eses/mongodb_exporter
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9104
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File

追加後はPrometheus PodからのNW疎通確認をしたりした。以下記事参照。
kashionki38.hatenablog.com

scrape config

scrape configはprometheus.yamlの中にあるため、今回だと上述のようにconfigmapを編集する。
service discoveryやりたかったけど、ここは一旦staticで設定。

    # kashinoki38
    - job_name: 'mongodb-exporter'
      static_configs:
        - targets: ['10.244.1.44:9104']

こんな感じでPrometheusのtargetsに表示されたらOK。
f:id:kashionki38:20200306140736p:plain

Grafanaダッシュボード

これでGrafanaダッシュボードへのアクセスポートフォワーディングが自動でされる。

$ istioctl dashboard grafana

Grafanaダッシュボードもとりあえずありものを使う。
grafana.com

いい感じのダッシュボードが一旦できあがりましたわ。
f:id:kashionki38:20200306141314p:plain

Prometheusデータ永続化

さて、Prometheusの監視設定は完了した。
以前できてなかったこととしてデータの永続化がある。せっかくなのでやってみた。

statefulsetへの変更

まずはビルトインPrometheusはdeploy→ReplicaSetでできているので、StatefulSetへと変更する。

StatefulSetって?

StatefulSetにすることで以下の恩恵を受けれる。

  • Pod名のサフィックスが数字インデックスになり、再起動時に名前が変わらない(e.g. prometheus-0)
  • PersistentVolumeを使っている場合、Pod再起動時に同じディスクを利用して再作成される

ということで、PrometheusはステートフルなデータベースなのでStatefulSetにしたほうが良い。

Deploy→StatefulSet

deployからの変更の仕方だが強引にkindを書き換えて、アップデート戦略部分をコメントアウトすることで完了した。
思っていたよりも簡単。。

apiVersion: apps/v1
kind: StatefulSet
(中略)
  # progressDeadlineSeconds: 600
  # strategy:
  #   rollingUpdate:
  #     maxSurge: 25%
  #     maxUnavailable: 25%
  #   type: RollingUpdate

PersistentVolumeClaimの使用

もちろんStatefulSetにするだけではデータ永続化はされずPersistentVolumeを使用する必要がある。
StatefulSet内でPersistentVolumeClaimを書くことができるようなのだが、今回はPersistentVolumeClaim自体は別で書いた。

PersistentVolumeとPersistentVolumeClaim

俺の理解。

  • PersistentVolume(PV)は基本的にNW越しに外部のボリュームを提供するシステムを利用する永続化目的のディスク領域。
  • PersistentVolumeClaim(PVC)は、どういったPVをPodにアタッチしてほしいかの定義。(selectorでLabelとか指定する感じ)
PersistentVolumeClaimの作成

Azureでどうやってやるのか調べたら、PVCを書けばPVが動的にAzure上で作成/アタッチされるようだ。
docs.microsoft.com

以下作成。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: azure-managed-disk
  namespace: istio-system
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: managed-premium
  resources:
    requests:
      storage: 20Gi
PersistentVolumeClaimをPodから利用する

prometheus.yamlの中でPVCを利用するように書く。
Prometheusのデータファイルはデフォルトでは/prometheus/dataに格納されるため、そのパスをPVCにする。
prometheus.yaml

(中略)
        volumeMounts:
        - mountPath: /prometheus/data
          name: azure-pv-volume
(中略)
      volumes:
      - name: azure-pv-volume
        persistentVolumeClaim:
          claimName: azure-managed-disk
(中略)
permission denied

以下エラーが発生。

err="opening storage failed: lock DB directory: open /prometheus/lock: permission denied"

ロックファイルがあることと、Permissionの問題のようなのでそれぞれ対応。
(暫定対処的な感じだったので、本来どうあるべきかは別途調べないと、)

--storage.tsdb.no-lockfile追加

ロックファイルを作成しないようにprometheusのコマンドライン引数に--storage.tsdb.no-lockfileを追加。

prometheus.yaml

(中略)
      containers:
      - args:
        - --storage.tsdb.no-lockfile
(中略)
        name: prometheus
(中略)
prometheus.yamlにsecurityContext追加

Prometheusを実行するユーザがPrometheusユーザになってしまうのを上書く目的。
prometheus.yaml

(中略)
      securityContext:
        runAsUser: 1000
        runAsGroup: 3000
        fsGroup: 2000
(中略)
永続化されているかの確認

ここまででPrometheus永続化の設定は完了。
Podをdeleteして確認してみる。

$kubectl delete po -n istio-system prometheus-0

5分弱Podの再起動に時間がかかるのが気になるが、ちゃんと再起動前の情報も見れることを確認できた。
f:id:kashionki38:20200306151144p:plain

Jaeger

Jaegerも基本的にIstioと共に入っているのでJaegerUIにアクセスするだけいい。 istio.io

さらにBook-infoサンプルはtrace-id等を伝搬してくれているので特にソースの変更も不要。

JaegerUIへのアクセス

$istioctl dashboard jaeger

f:id:kashionki38:20200306151325p:plain

ここまでで、Istioデモアプリに対するPrometheus監視とJaegerトレーシングの設定は完了した。
突貫的にやった部分も多く、特にIstioに関する概念をあまり理解できていないのでおいおい勉強していかないといけない。
次の記事ではこのデモアプリに負荷をかけてみて実際に監視した内容を書いてみたい。