【问题标题】:Kubernetes CPU throttling with CPU usage well below requests/limitsKubernetes CPU 节流,CPU 使用率远低于请求/限制
【发布时间】:2020-10-19 00:12:06
【问题描述】:

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

这看起来正常吗?

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

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

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

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

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

我们的 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

【问题讨论】:

  • 你的机器有多少个 CPU?
  • @Rico 这个节点池有 n1-standard-8 台机器,每台机器有 8 个 vCPU 和 30GB RAM。
  • 为了帮助我们解决您的环境问题,您能否分享您部署的 yaml 文件?另外,您运行的是哪个 GKE 版本?
  • @willrof 抱歉回复晚了。我已将 yaml 文件和 dockerfile 添加到问题详细信息中。让我知道您是否想看看其他内容,例如nginx 或 php-fpm 配置。另外,您认为这可能是 GKE 特有的吗?在这种情况下,我可以通过 google 支持来解决这个问题,看看他们是否可以提供任何帮助。
  • 我们在 GKE 上遇到过类似的问题,但我们没有找到任何有效的解释(内核版本是 5.4,应该不是问题所在)。由于应用程序是多线程的,是否有可能发生 CFS 限制?如果多个线程有时都忙,但并非总是如此,有可能在某些 CFS 期间 CPU 使用率超过其配额并受到限制,而 CPU 使用率本身看起来远低于其限制?

标签: kubernetes linux-kernel google-kubernetes-engine


【解决方案1】:

我不知道那条红线是什么,所以我会跳过那条。不过,如果您知道 CPU 限制情况会发生什么,那就太好了。

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

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

【讨论】:

  • 您能否详细说明这句话,“当有大量可用 CPU 时,任何现代系统都会发生 CPU 节流。”。你有什么可以分享的参考资料吗?
  • @BryanL youtube.com/watch?v=sK5i-N34im8&t=850s(从 14:10 开始,大约几分钟)
【解决方案2】:

你通常会根据你设置的阈值看到这样的线条,我相信你可以在设置阈值时选择线条的颜色

【讨论】:

    猜你喜欢
    • 2021-09-03
    • 2020-03-28
    • 2023-03-03
    • 2020-11-19
    • 1970-01-01
    • 2023-03-27
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多