首页 > 解决方案 > Kubernetes CPU 节流,CPU 使用率远低于请求/限制

问题描述

我已经在我的 pod 的所有容器上设置了 CPU 和内存请求 = 限制,以使其符合 Guaranteed Quality of Service 等级。现在,查看过去 6 小时内同一 Pod 的这些 CPU 使用率和 CPU 节流图。

超过 6 小时的 Pod 的 CPU 利用率和节流

这看起来正常吗?

CPU 使用率甚至一次都没有达到设定限制的 50%,而且有时仍被限制到 58%。

还有一个附带问题,限制图中 25% 的红线表示什么?

我对此主题进行了一些研究,发现 Linux 内核中存在一个可能导致此问题的错误,并且该错误已在 4.18 版内核中得到修复。参考:这个这个

我们在 GKE 上运行 Google 的 Container Optimized OS。我检查了我们节点上的 linux 内核版本,它们在 4.19.112+ 上,所以我想我们已经有了那个补丁?这种节流模式的其他原因可能是什么?

PS 这个 pod(实际上是具有自动缩放功能的部署)部署在一个单独的节点池上,该池上没有运行我们的其他工作负载。因此,除了在此节点池中的节点上运行的此部署之外,唯一的 pod 是一些指标、日志记录代理和导出器。这是在调度上述 pod 的同一节点上运行的 pod 的完整列表。确实有一些 pod 没有设置任何 CPU 限制。我是否还需要以某种方式对这些设置 CPU 限制?

在节点上运行的 Pod

我们的 GKE 版本是1.16.9-gke.2

这是包含部署、服务和自动缩放器定义的清单文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: endpoints
  labels:
    app: endpoints
spec:
  replicas: 2
  selector:
    matchLabels:
      run: endpoints
  strategy:
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 0
  template:
    metadata:
      labels:
        run: endpoints
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: endpoints
          image: gcr.io/<PROJECT_ID>/endpoints:<RELEASE_VERSION_PLACEHOLDER>
          livenessProbe:
            httpGet:
              path: /probes/live
              port: 8080
            initialDelaySeconds: 20
            timeoutSeconds: 5
          readinessProbe:
            httpGet:
              path: /probes/ready
              port: 8080
            initialDelaySeconds: 20
            timeoutSeconds: 5
          ports:
            - containerPort: 8080
              protocol: TCP
          env:
            - name: GOOGLE_APPLICATION_CREDENTIALS
              value: "/path/to/secret/gke-endpoints-deployments-access.json"
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: DEPLOYMENT_NAME
              value: "endpoints"
          resources:
            requests:
              memory: "5Gi"
              cpu: 2
            limits:
              memory: "5Gi"
              cpu: 2
          volumeMounts:
            - name: endpoints-gcp-access
              mountPath: /path/to/secret
              readOnly: true
          lifecycle:
            preStop:
              exec:
                # SIGTERM triggers a quick exit; gracefully terminate instead
                command: ["/bin/sh","-c","sleep 3; /usr/sbin/nginx -s quit; sleep 57"]
        # [START proxy_container]
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.16
          command: ["/cloud_sql_proxy",
                    "-instances=<PROJECT_ID>:<ZONE>:prod-db=tcp:3306,<PROJECT_ID>:<ZONE>:prod-db-read-replica=tcp:3307",
                    "-credential_file=/path/to/secret/gke-endpoints-deployments-access.json"]
          # [START cloudsql_security_context]
          securityContext:
            runAsUser: 2  # non-root user
            allowPrivilegeEscalation: false
          # [END cloudsql_security_context]
          resources:
            requests:
              memory: "50Mi"
              cpu: 0.1
            limits:
              memory: "50Mi"
              cpu: 0.1
          volumeMounts:
            - name: endpoints-gcp-access
              mountPath: /path/to/secret
              readOnly: true
        # [END proxy_container]
        # [START nginx-prometheus-exporter container]
        - name: nginx-prometheus-exporter
          image: nginx/nginx-prometheus-exporter:0.7.0
          ports:
            - containerPort: 9113
              protocol: TCP
          env:
            - name: CONST_LABELS
              value: "app=endpoints"
          resources:
            requests:
              memory: "50Mi"
              cpu: 0.1
            limits:
              memory: "50Mi"
              cpu: 0.1
        # [END nginx-prometheus-exporter container]
      tolerations:
        - key: "qosclass"
          operator: "Equal"
          value: "guaranteed"
          effect: "NoSchedule"
      nodeSelector:
        qosclass: guaranteed
      # [START volumes]
      volumes:
        - name: endpoints-gcp-access
          secret:
            secretName: endpoints-gcp-access
      # [END volumes]
---
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: endpoints-backendconfig
spec:
  timeoutSec: 60
  connectionDraining:
    drainingTimeoutSec: 60
---
apiVersion: v1
kind: Service
metadata:
  name: endpoints
  labels:
    app: endpoints
  annotations:
    cloud.google.com/neg: '{"ingress": true}' # Creates a NEG after an Ingress is created
    beta.cloud.google.com/backend-config: '{"ports": {"80":"endpoints-backendconfig"}}'
spec:
  type: NodePort
  selector:
    run: endpoints
  ports:
    - name: endpoints-nginx
      port: 80
      protocol: TCP
      targetPort: 8080
    - name: endpoints-metrics
      port: 81
      protocol: TCP
      targetPort: 9113
---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: endpoints-autoscaler
spec:
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 40
  - type: External
    external:
      metricName: external.googleapis.com|prometheus|nginx_http_requests_total
      metricSelector:
        matchLabels:
          metric.labels.app: endpoints
      targetAverageValue: "5"
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: endpoints
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: endpoints-nginx-monitor
  namespace: monitoring
  labels:
    app: endpoints-nginx-monitor
    chart: prometheus-operator-8.13.7
    release: prom-operator
    heritage: Tiller
spec:
  selector:
    matchLabels:
      app: endpoints
  namespaceSelector:
    any: true
  endpoints:
  - port: endpoints-metrics
    path: "/metrics"

这是部署中使用的唯一自定义容器映像的 dockerfile:

# Dockerfile extending the generic PHP image with application files for a
# single application.
FROM gcr.io/google-appengine/php:latest

# The Docker image will configure the document root according to this
# environment variable.
ENV DOCUMENT_ROOT /app

RUN /bin/bash /stackdriver-files/enable_stackdriver_integration.sh

标签: kuberneteslinux-kernelgoogle-kubernetes-engine

解决方案


我不知道那条红线是什么,所以我会跳过那条。不过,如果您知道 CPU 节流案例会发生什么,那就太好了。

因此,关于您的 CPU 使用率和节流,没有迹象表明出现任何问题。当有大量可用 CPU 时,任何现代系统都会发生 CPU 节流。因此,它会减慢时钟,并开始运行更慢(例如 2.3GHz 机器切换到 2.0GHz)。这就是您不能根据百分比设置 CPU 限制的原因。

因此,从您的图表中,我推测看到的是 CPU 时钟下降,自然百分比上升;正如预期的那样。没什么奇怪的。


推荐阅读