🎉

GKE Dataplane V2 observability が Preview になりました

2023/07/31に公開

はじめに

こんにちは。クラウドエース株式会社で SRE をしている間瀬です。
今回は 2023 年 7 月 12 日に Preview となった GKE Dataplane V2 observability について紹介させていただきます。

GKE Dataplane V2 observability について

本機能は Google Kubernetes Engine (以下、GKE) のネットワーキングオブションの一つとなる GKE Dataplane V2 上で動作する機能となっています。
GKE Dataplane V2 は Cilium によって実装されており、従来よりも効率的なネットワーキングやモニタリング、セキュリティに関する機能が提供されています。

GKE Dataplane V2 observability は GKE Dataplane V2 によって収集可能な指標を公開して Cloud Monitoring や Google Cloud Managed Service for Prometheus(以下、GMP) 、Hubble、セルフマネージドな Prometheus によって観測できる機能になります。
本機能のリリース前は GKE Dataplane V2 によって提供される機能は Cilium が持つほとんどの機能が制限されていましたが、本機能ではその一部がマネージドサービスとして利用できるようになっています。

尚、Cilium について詳しく知りたい方は 2023年 7 月 18 日に開催された Cloud Native Days Fukuoka 2023 プレイベントにて登壇してきましたので、こちらの動画を参照ください。
対象のセッションは動作の 35:40 からとなります。

以下より本機能の特徴を記載させていただきます。

Hubble UI

まずは個人的に本機能で最も特徴的となる Hubble UI について紹介させていただきます。
Hubble は Cilium をベースとしているモニタリングツールになります。GKE Dataplane V2 が有効化されているクラスタに対して GKE Dataplane V2 observability tool を有効化することで 下記のような UI を参照することが可能です。
Hubble UI のサービスマップによって L4 での Pod 間トラフィックを可視化することができます。以下の画像では画面上部がサービスマップで、下部にはトラフィック毎の情報が一覧表示されます。

Hubble-UI

尚、マネージドではない Hubble では Pod へアノテーションを追加することで L7 の可視化も可能ですが、本機能については現時点では対応していないようでした。

Hubble CLI

上記と同じ利用条件で CLI によるトラフィックの確認が可能です。
CLI によって L4 トラフィックの確認が可能です。CLI にはフィルタ機能があり、namespace や Pod などでフィルタすることが可能です。

kubectl exec -it -n kube-system deployment/hubble-relay -c hubble-cli -- hubble observe --from-namespace default

Jul 25 04:48:03.162: default/productpage-v1-75ddfff844-t644j:54096 (ID:16854) -> kube-system/kube-dns-fc686db9b-28vs4:53 (ID:39068) to-endpoint FORWARDED (UDP)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: SYN)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: SYN)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: SYN, ACK)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: SYN, ACK)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 25 04:48:03.166: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 25 04:48:03.171: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 25 04:48:03.171: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 25 04:48:03.175: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 25 04:48:03.175: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 25 04:48:03.177: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 25 04:48:03.177: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) <- default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 25 04:48:03.177: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-stack FORWARDED (TCP Flags: ACK)
Jul 25 04:48:03.177: default/productpage-v1-75ddfff844-t644j:56764 (ID:16854) -> default/reviews-v3-645ffdc889-2nw67:9080 (ID:45829) to-endpoint FORWARDED (TCP Flags: ACK)

GKE Dataplane V2 メトリクスの取得

GKE Dataplane V2 が有効化されているクラスタに対して GKE Dataplane V2 metrics を有効化することで、指標が公開されます。
主要なメトリクスは以下となり、Pod 別の通信フロー数になります。クラスタにて GMP が有効になっていると、自動的に収集され Metrics Explorer から参照可能です。

  • prometheus.googleapis.com/pod_flow_egress_flows_count/counter : Podからのフロー合計数
  • prometheus.googleapis.com/pod_flow_ingress_flows_count/counter: Podへのフロー合計数

その他の指標について Hubble や Cilium に関する様々な指標が公開されているようです。比較できていないですが、現時点ではマネージドではない Cilium, Hubble より公開されている指標が少ない印象を受けました。
Cilium が動作する Pod のメトリクス用のエンドポイントにアクセスすることで公開されている指標が確認できるので興味ある方は確認してみてください。

本機能が解決する課題、メリット

現時点でメリットと感じたのは、Anthos Service Mesh や Istio のようなサービスメッシュを導入しなくてもサービスマップによって Pod 間の依存関係を可視化、検査できるところだと思います。
サービスメッシュを導入する場合、サービスメッシュ自体の管理が必要となる点や Pod にサイドカーコンテナがデプロイされることにより Pod の構成が複雑になる課題が挙げられますが、本機能を代わりに利用することでそれらを解決できます。

一方で、本機能ではサービスメッシュが提供するようなプロキシによるマイクロサービス毎のトラフィック管理のようなことはできないので、これら機能を利用しないケースや、必要な場合はメリットは薄れますがサービスメッシュと併用して利用する必要があります。

本機能の利用における注意点

GKE Dataplane V2 を既に利用されている方は本機能を有効化するだけで利用可能となりますが、GKE Dataplane V2 は作成済みのクラスタへ適用できないため、GKE Dataplane V2 を利用されていない方はクラスタの再作成が必要となります。

利用にかかるコスト

上記で説明したGKE Dataplane V2 observability tool、GKE Dataplane V2 metrics それぞれにコストがかかります。現時点においてコストについて記載されているページはこちらになります。

  • GKE Dataplane V2 observability tool
    Preview 段階では無料となっており、課金情報は現時点では公開されていませんが、一般提供後は Pod 別に時間で課金されるようです。

  • GKE Dataplane V2 metrics
    GMP によってメトリクスを収集する場合のみ GMP の課金体系に沿って課金されます。
    GKE Dataplane V2 metrics の有効化による公開だけでは課金されないようです。

利用方法

ほとんど公式docの内容と同じですが、本機能の利用方法について以下に紹介させていただきます。

クラスタの作成、機能の有効化

前提として、今回はスタンダードモードのクラスタを新規に作成してメトリクスの収集はGMPにて行うものとします。

以下のコマンドで GKE クラスタを作成します。

gcloud container clusters create gke-cluster \
    --enable-dataplane-v2 \
    --enable-managed-prometheus \
    --enable-dataplane-v2-metrics \
    --dataplane-v2-observability-mode=INTERNAL_VPC_LB \
    --region asia-northeast1 

Hubble UI のデプロイ

以下の内容で yaml ファイルを作成します。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: hubble-ui
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: hubble-ui
  labels:
    app.kubernetes.io/part-of: cilium
rules:
  - apiGroups:
      - networking.k8s.io
    resources:
      - networkpolicies
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - componentstatuses
      - endpoints
      - namespaces
      - nodes
      - pods
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - apiextensions.k8s.io
    resources:
      - customresourcedefinitions
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - cilium.io
    resources:
      - "*"
    verbs:
      - get
      - list
      - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: hubble-ui
  labels:
    app.kubernetes.io/part-of: cilium
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: hubble-ui
subjects:
  - kind: ServiceAccount
    name: hubble-ui
    namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: hubble-ui-nginx
  namespace: kube-system
data:
  nginx.conf: |
    server {
        listen       8081;
        # uncomment for IPv6
        # listen       [::]:8081;
        server_name  localhost;
        root /app;
        index index.html;
        client_max_body_size 1G;
        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            # CORS
            add_header Access-Control-Allow-Methods "GET, POST, PUT, HEAD, DELETE, OPTIONS";
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Max-Age 1728000;
            add_header Access-Control-Expose-Headers content-length,grpc-status,grpc-message;
            add_header Access-Control-Allow-Headers range,keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout;
            if ($request_method = OPTIONS) {
                return 204;
            }
            # /CORS
            location /api {
                proxy_http_version 1.1;
                proxy_pass_request_headers on;
                proxy_hide_header Access-Control-Allow-Origin;
                proxy_pass http://127.0.0.1:8090;
            }
            location / {
                # double `/index.html` is required here
                try_files $uri $uri/ /index.html /index.html;
            }
        }
    }
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: hubble-ui
  namespace: kube-system
  labels:
    k8s-app: hubble-ui
    app.kubernetes.io/name: hubble-ui
    app.kubernetes.io/part-of: cilium
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: hubble-ui
  template:
    metadata:
      labels:
        k8s-app: hubble-ui
        app.kubernetes.io/name: hubble-ui
        app.kubernetes.io/part-of: cilium
    spec:
      securityContext:
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      serviceAccount: hubble-ui
      serviceAccountName: hubble-ui
      containers:
        - name: frontend
          image: quay.io/cilium/hubble-ui:v0.11.0
          ports:
            - name: http
              containerPort: 8081
          volumeMounts:
            - name: hubble-ui-nginx-conf
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: nginx.conf
            - name: tmp-dir
              mountPath: /tmp
          terminationMessagePolicy: FallbackToLogsOnError
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsUser: 1000
            runAsGroup: 1000
            capabilities:
              drop:
                - all
        - name: backend
          image: quay.io/cilium/hubble-ui-backend:v0.11.0
          env:
            - name: EVENTS_SERVER_PORT
              value: "8090"
            - name: FLOWS_API_ADDR
              value: "hubble-relay.kube-system.svc:443"
            - name: TLS_TO_RELAY_ENABLED
              value: "true"
            - name: TLS_RELAY_SERVER_NAME
              value: relay.kube-system.svc.cluster.local
            - name: TLS_RELAY_CA_CERT_FILES
              value: /var/lib/hubble-ui/certs/hubble-relay-ca.crt
            - name: TLS_RELAY_CLIENT_CERT_FILE
              value: /var/lib/hubble-ui/certs/client.crt
            - name: TLS_RELAY_CLIENT_KEY_FILE
              value: /var/lib/hubble-ui/certs/client.key
          ports:
            - name: grpc
              containerPort: 8090
          volumeMounts:
            - name: hubble-ui-client-certs
              mountPath: /var/lib/hubble-ui/certs
              readOnly: true
          terminationMessagePolicy: FallbackToLogsOnError
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsUser: 1000
            runAsGroup: 1000
            capabilities:
              drop:
                - all
      volumes:
        - configMap:
            defaultMode: 420
            name: hubble-ui-nginx
          name: hubble-ui-nginx-conf
        - emptyDir: {}
          name: tmp-dir
        - name: hubble-ui-client-certs
          projected:
            # note: the leading zero means this number is in octal representation: do not remove it
            defaultMode: 0400
            sources:
              - secret:
                  name: hubble-relay-client-certs
                  items:
                    - key: ca.crt
                      path: hubble-relay-ca.crt
                    - key: tls.crt
                      path: client.crt
                    - key: tls.key
                      path: client.key
---
kind: Service
apiVersion: v1
metadata:
  name: hubble-ui
  namespace: kube-system
  labels:
    k8s-app: hubble-ui
    app.kubernetes.io/name: hubble-ui
    app.kubernetes.io/part-of: cilium
spec:
  type: ClusterIP
  selector:
    k8s-app: hubble-ui
  ports:
    - name: http
      port: 80
      targetPort: 8081

yaml ファイル作成後に以下のコマンドでクラスタへデプロイします。
※ yaml ファイル名は hubble-ui-std.yaml

kubectl apply -f hubble-ui-std.yaml

ここまでで、構築手順としては完了ですが、以降の手順を実施する前にご自身でサンプルAP等をデプロイしてください。
私はよく Istio のチュートリアルで使われている bookinfo アプリケーションを利用しています。
コチラを参考に Istio はインストールせずにアプリケーションのみデプロイしてください。

Hubble UI へのアクセス

以下のコマンドで port-forward します。

kubectl -n kube-system port-forward service/hubble-ui 16100:80 --address='0.0.0.0'

その後、自身のブラウザにて localhost:16100 へアクセスすることで Hubble UI へのアクセスが可能です。

Hubble CLI によるトラフィックの確認

以下のコマンドでトラフィックの確認が可能です。コマンドの末尾を hubble -h とすることでフィルタ等のオプションが参照できます。

kubectl exec -it -n kube-system deployment/hubble-relay -c hubble-cli -- hubble observe

メトリクスの確認

公式docの手順を参考にコンソール上の Metrics Explorer から prometheus/pod_flow_egress_flows_count または prometheus/pod_flow_ingress_flows_count の指標を選択することで参照が可能です。

まとめ

本機能の登場によって容易にサービスマップのモニタリングが可能になるので今後は GKE Dataplane V2 をあえて採用したいケースが増えてくるのではないかと感じました。
また、本機能のベースとなっている Cilium や eBPF は活発に開発が続いているので今後もマネージドサービスとしての更なるアップデートも期待できると個人的には考えています。

記事内で利用条件として触れた通り、一度クラスタを作成すると GKE Dataplane V2 を使用するにはクラスタの再作成が必要になるので、これからクラスタを構築される方は今後追加されるかもしれない拡張機能を利用しやすくするために GKE Dataplane V2 を有効化しておいてもいいのかなと思います。

今後も本機能はじめ GKE Dataplane V2 には注目して更なるアップデートが公開された際には記事を書きたいと思います。

Discussion