设置 Azure Kubernetes 服务 (AKS) 的容器网络可观测性 - Azure 托管的 Prometheus 和 Grafana

本文介绍如何使用托管的 Prometheus 和 Grafana 与 BYO Prometheus 和 Grafana 设置为 Azure Kubernetes 服务 (AKS) 设置容器网络可观测性,以可视化已抓取的指标

可以使用容器网络可观测性收集有关 AKS 群集的网络流量数据。 高级网络可观测性支持用于监视应用程序和网络运行状况的集中式平台。 目前,可使用 Prometheus 存储指标并使用 Grafana 可视化它们。 容器网络可观测性还提供启用 Hubble 的功能。 Cilium 和非 Cilium 群集都支持这些功能。

容器网络可观测性是高级容器网络服务的功能之一。

先决条件

  • 具有活动订阅的 Azure 帐户。 如果没有订阅,请在开始之前创建一个试用帐户

可以使用本地 Azure CLI。

  • 如果需要,请安装 Azure CLI 来运行 CLI 参考命令。

  • 本地 Azure CLI,请了解如何安装 Azure CLI。 如果在 Windows 或 macOS 上运行,请考虑在 Docker 容器中运行 Azure CLI。 有关详细信息,请参阅如何在 Docker 容器中运行 Azure CLI

    • 通过使用 az login 命令登录到 Azure CLI。 若要完成身份验证过程,请遵循终端中显示的步骤。 有关其他登录选项,请参阅使用 Azure CLI 登录

    • 出现提示时,请在首次使用时安装 Azure CLI 扩展。 有关扩展详细信息,请参阅使用 Azure CLI 的扩展

    • 运行 az version 以查找安装的版本和依赖库。 若要升级到最新版本,请运行 az upgrade

  • 本文中的步骤所需的 Azure CLI 最低版本为 2.56.0。 运行 az --version 即可查找版本。 如果需要进行安装或升级,请参阅安装 Azure CLI

安装 aks-preview Azure CLI 扩展

使用 az extension addaz extension update 命令安装或更新 Azure CLI 预览版扩展。

# Install the aks-preview extension
az extension add --name aks-preview

# Update the extension to make sure you have the latest version installed
az extension update --name aks-preview

启用高级容器网络服务

具有高级容器网络服务标志 --enable-acnsaz aks create 命令创建一个新的 AKS 群集,它具有所有高级容器网络服务功能。 这些功能包括:

  • 容器网络可观测性:提供对流量的见解。 若要了解详细信息,请访问容器网络可观测性

  • 容器网络安全:提供 FQDN 筛选等安全功能。 若要了解详细信息,请访问容器网络安全

备注

从 Kubernetes 版本 1.29 开始,具有 Cilium 数据平面的群集支持容器网络可观测性和容器网络安全。

# Set an environment variable for the AKS cluster name. Make sure to replace the placeholder with your own value.
export CLUSTER_NAME="<aks-cluster-name>"

# Create an AKS cluster
az aks create \
    --name $CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --generate-ssh-keys \
    --location chinanorth3 \
    --max-pods 250 \
    --network-plugin azure \
    --network-plugin-mode overlay \
    --network-dataplane cilium \
    --node-count 2 \
    --pod-cidr 192.168.0.0/16 \
    --kubernetes-version 1.29 \
    --enable-acns

在现有群集上启用高级容器网络服务

具有高级容器网络服务标志 --enable-acnsaz aks update 命令使用所有高级容器网络服务功能(包括容器网络可观测性容器网络安全功能)更新现有 AKS 群集。

备注

只有具有 Cilium 数据平面的群集才支持高级容器网络服务的容器网络安全功能。

az aks update \
    --resource-group $RESOURCE_GROUP \
    --name $CLUSTER_NAME \
    --enable-acns

获取群集凭据

使用 az aks get-credentials 命令获取群集凭据后。

az aks get-credentials --name $CLUSTER_NAME --resource-group $RESOURCE_GROUP

Azure 托管的 Prometheus 和 Grafana

如果使用 BYO Prometheus 和 Grafana,请跳过本部分

使用以下示例为 AKS 群集安装并启用 Prometheus 和 Grafana。

创建 Azure Monitor 资源

#Set an environment variable for the Grafana name. Make sure to replace the placeholder with your own value.
export AZURE_MONITOR_NAME="<azure-monitor-name>"

# Create Azure monitor resource
az resource create \
    --resource-group $RESOURCE_GROUP \
    --namespace microsoft.monitor \
    --resource-type accounts \
    --name $AZURE_MONITOR_NAME \
    --location chinanorth3 \
    --properties '{}'

创建 Azure 托管 Grafana 实例

使用 az grafana create 创建 Grafana 实例。 Grafana 实例的名称必须唯一。

# Set an environment variable for the Grafana name. Make sure to replace the placeholder with your own value.
export GRAFANA_NAME="<grafana-name>"

# Create Grafana instance
az grafana create \
    --name $GRAFANA_NAME \
    --resource-group $RESOURCE_GROUP 

将 Azure 托管 Grafana 和 Azure Monitor 资源 ID 放在变量中

使用 az grafana show 将 Grafana 资源 ID 放在变量中。 使用 az resource show 将 Azure Monitor 资源 ID 放在变量中。 将 myGrafana 替换为 Grafana 实例的名称。

grafanaId=$(az grafana show \
                --name $GRAFANA_NAME \
                --resource-group $RESOURCE_GROUP \
                --query id \
                --output tsv)
azuremonitorId=$(az resource show \
                    --resource-group $RESOURCE_GROUP \
                    --name $AZURE_MONITOR_NAME \
                    --resource-type "Microsoft.Monitor/accounts" \
                    --query id \
                    --output tsv)

使用 az aks update 将 Azure Monitor 和 Grafana 资源链接到 AKS 群集。

az aks update \
    --name $CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --enable-azure-monitor-metrics \
    --azure-monitor-workspace-resource-id $azuremonitorId \
    --grafana-resource-id $grafanaId

可视化效果

使用 Azure 托管 Grafana 实现可视化

如果使用 BYO Grafana,请跳过此步骤

备注

由于大规模集群中的指标基数较高,因此默认情况下不会抓取 hubble_flows_processed_total 指标。 因此,“Pod 流”仪表板中的面板缺少数据。 若要更改此问题,可以修改 AMA 指标设置,使其在指标保留列表中包括 hubble_flows_processed_total。 若要了解如何执行此操作,请参阅最小引入文档

  1. 使用 kubectl get pods 命令确保 Azure Monitor Pod 正在运行。

    kubectl get pods -o wide -n kube-system | grep ama-
    

    输出应类似于以下示例输出:

    ama-metrics-5bc6c6d948-zkgc9          2/2     Running   0 (21h ago)   26h
    ama-metrics-ksm-556d86b5dc-2ndkv      1/1     Running   0 (26h ago)   26h
    ama-metrics-node-lbwcj                2/2     Running   0 (21h ago)   26h
    ama-metrics-node-rzkzn                2/2     Running   0 (21h ago)   26h
    ama-metrics-win-node-gqnkw            2/2     Running   0 (26h ago)   26h
    ama-metrics-win-node-tkrm8            2/2     Running   0 (26h ago)   26h
    
  2. 我们已创建示例仪表板。 可以在“仪表板”>“Azure 托管 Prometheus”文件夹下找到它们。 它们的名称类似于“Kubernetes/Networking/<name>。 仪表板套件包括:

    • 群集:显示群集的节点级指标。
    • DNS(群集):显示群集上的 DNS 指标或节点选择。
    • DNS(工作负载):显示指定工作负载(例如 DaemonSet 或 CoreDNS 等部署的 Pod)的 DNS 指标。
    • 删除(工作负载):显示从指定工作负载(例如部署或 DaemonSet 的 Pod)拖放。
    • Pod 流(命名空间):显示传入/传出指定命名空间(即命名空间中的 Pod)的 L4/L7 数据包流。
    • Pod 流(工作负载):显示传入/传出指定工作负载(例如部署或 DaemonSet 的 Pod)的 L4/L7 数据包流。

使用 BYO Grafana 进行可视化

如果使用 Azure 托管 Grafana,请跳过此步骤

  1. 将以下抓取作业添加到现有 Prometheus 配置,并重启 Prometheus 服务器:

    - job_name: networkobservability-hubble
      kubernetes_sd_configs:
        - role: pod
      relabel_configs:
        - target_label: cluster
          replacement: myAKSCluster
          action: replace
        - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_pod_label_k8s_app]
          regex: kube-system;(retina|cilium)
          action: keep
        - source_labels: [__address__]
          action: replace
          regex: ([^:]+)(?::\d+)?
          replacement: $1:9965
          target_label: __address__
        - source_labels: [__meta_kubernetes_pod_node_name]
          target_label: instance
          action: replace
      metric_relabel_configs:
        - source_labels: [__name__]
          regex: '|hubble_dns_queries_total|hubble_dns_responses_total|hubble_drop_total|hubble_tcp_flags_total' # if desired, add |hubble_flows_processed_total
          action: keep
    
  2. 在 Prometheus 的“目标”中,验证 network-obs-pods 是否存在。

  3. 登录到 Grafana 并使用以下 ID 导入以下示例仪表板:

    • 群集:显示群集的节点级指标。 (ID 18814)
    • DNS(群集):显示群集上的 DNS 指标或节点选择。(ID 20925)
    • DNS(工作负载):显示指定工作负载(例如 DaemonSet 或 CoreDNS 等部署的 Pod)的 DNS 指标。 (ID [20926] https://grafana.com/grafana/dashboards/20926-kubernetes-networking-dns-workload/)
    • 删除(工作负载):显示从指定工作负载(例如部署或 DaemonSet 的 Pod)拖放。(ID 20927)。
    • Pod 流(命名空间):显示传入/传出指定命名空间(即命名空间中的 Pod)的 L4/L7 数据包流。 (ID 20928)
    • Pod 流(工作负载):显示传入/传出指定工作负载(例如部署或 DaemonSet 的 Pod)的 L4/L7 数据包流。(ID 20929)

    备注

    • 根据 Prometheus/Grafana 实例的设置,某些仪表板面板可能需要调整才能显示所有数据。
    • Cilium 目前不支持 DNS 指标/仪表板。

安装 Hubble CLI

使用以下命令安装 Hubble CLI 以访问它收集的数据:

# Set environment variables
export HUBBLE_VERSION=v1.16.3
export HUBBLE_ARCH=amd64

#Install Hubble CLI
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}

可视化 Hubble 流

  1. 使用 kubectl get pods 命令确保 Hubble pod 正在运行。

    kubectl get pods -o wide -n kube-system -l k8s-app=hubble-relay
    

    输出应类似于以下示例输出:

    hubble-relay-7ddd887cdb-h6khj     1/1  Running     0       23h 
    
  2. 使用 kubectl port-forward 命令进行 Hubble Relay 端口转发。

    kubectl port-forward -n kube-system svc/hubble-relay --address 127.0.0.1 4245:443
    
  3. 使用双向 TLS (mTLS) 确保 Hubble Relay 服务器的安全。 若要使 Hubble 客户端能够检索流,需要获取相应的证书并使用它们配置客户端。 并使用以下命令应用证书:

    #!/usr/bin/env bash
    
    set -euo pipefail
    set -x
    
    # Directory where certificates will be stored
    CERT_DIR="$(pwd)/.certs"
    mkdir -p "$CERT_DIR"
    
    declare -A CERT_FILES=(
      ["tls.crt"]="tls-client-cert-file"
      ["tls.key"]="tls-client-key-file"
      ["ca.crt"]="tls-ca-cert-files"
    )
    
    for FILE in "${!CERT_FILES[@]}"; do
      KEY="${CERT_FILES[$FILE]}"
      JSONPATH="{.data['${FILE//./\\.}']}"
    
      # Retrieve the secret and decode it
      kubectl get secret hubble-relay-client-certs -n kube-system \
        -o jsonpath="${JSONPATH}" | \
        base64 -d > "$CERT_DIR/$FILE"
    
      # Set the appropriate hubble CLI config
      hubble config set "$KEY" "$CERT_DIR/$FILE"
    done
    
    hubble config set tls true
    hubble config set tls-server-name instance.hubble-relay.cilium.io
    
  4. 使用以下 kubectl get secrets 命令验证是否已生成机密:

    kubectl get secrets -n kube-system | grep hubble-
    

    输出应类似于以下示例输出:

    kube-system     hubble-relay-client-certs     kubernetes.io/tls     3     9d
    
    kube-system     hubble-relay-server-certs     kubernetes.io/tls     3     9d
    
    kube-system     hubble-server-certs           kubernetes.io/tls     3     9d    
    
  5. 使用 hubble observe 命令确保 Hubble Relay pod 正在运行。

    hubble observe --pod hubble-relay-7ddd887cdb-h6khj
    

使用 Hubble UI 进行可视化

  1. 若要使用 Hubble UI,请将以下内容保存到 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: retina
    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
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: hubble-ui
      labels:
        app.kubernetes.io/part-of: retina
    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;
            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 / {
                    try_files $uri $uri/ /index.html /index.html;
                }
                # Liveness probe
                location /healthz {
                    access_log off;
                    add_header Content-Type text/plain;
                    return 200 'ok';
                }
            }
        }
    ---
    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: retina
    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: retina
        spec:
          serviceAccountName: hubble-ui
          automountServiceAccountToken: true
          containers:
          - name: frontend
            image: mcr.azk8s.cn/oss/cilium/hubble-ui:v0.12.2   
            imagePullPolicy: Always
            ports:
            - name: http
              containerPort: 8081
            livenessProbe:
              httpGet:
                path: /healthz
                port: 8081
            readinessProbe:
              httpGet:
                path: /
                port: 8081
            resources: {}
            volumeMounts:
            - name: hubble-ui-nginx-conf
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: nginx.conf
            - name: tmp-dir
              mountPath: /tmp
            terminationMessagePolicy: FallbackToLogsOnError
            securityContext: {}
          - name: backend
            image: mcr.azk8s.cn/oss/cilium/hubble-ui-backend:v0.12.2
            imagePullPolicy: Always
            env:
            - name: EVENTS_SERVER_PORT
              value: "8090"
            - name: FLOWS_API_ADDR
              value: "hubble-relay:443"
            - name: TLS_TO_RELAY_ENABLED
              value: "true"
            - name: TLS_RELAY_SERVER_NAME
              value: ui.hubble-relay.cilium.io
            - 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
            livenessProbe:
              httpGet:
                path: /healthz
                port: 8090
            readinessProbe:
              httpGet:
                path: /healthz
                port: 8090
            ports:
            - name: grpc
              containerPort: 8090
            resources: {}
            volumeMounts:
            - name: hubble-ui-client-certs
              mountPath: /var/lib/hubble-ui/certs
              readOnly: true
            terminationMessagePolicy: FallbackToLogsOnError
            securityContext: {}
          nodeSelector:
            kubernetes.io/os: linux 
          volumes:
          - configMap:
              defaultMode: 420
              name: hubble-ui-nginx
            name: hubble-ui-nginx-conf
          - emptyDir: {}
            name: tmp-dir
          - name: hubble-ui-client-certs
            projected:
              defaultMode: 0400
              sources:
              - secret:
                  name: hubble-relay-client-certs
                  items:
                    - key: tls.crt
                      path: client.crt
                    - key: tls.key
                      path: client.key
                    - key: ca.crt
                      path: hubble-relay-ca.crt
    ---
    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: retina
    spec:
      type: ClusterIP
      selector:
        k8s-app: hubble-ui
      ports:
        - name: http
          port: 80
          targetPort: 8081
    
  2. 使用以下命令将 hubble-ui.yaml 清单应用到群集

    kubectl apply -f hubble-ui.yaml
    
  3. 使用 kubectl port-forward 命令为 Hubble UI 设置端口转发。

    kubectl -n kube-system port-forward svc/hubble-ui 12000:80
    
  4. 通过在 Web 浏览器中输入 http://localhost:12000/ 来访问 Hubble UI。


清理资源

如果不打算使用此应用程序,请使用 az group delete 命令删除本文中创建的其他资源。

  az group delete --name $RESOURCE_GROUP

后续步骤

本操作指南文章介绍了如何为 AKS 群集安装和启用容器网络可观测性。