Kubernetes Pod Horizo​​ntal Autoscaling safe drain,celery worker 中途缩容

Kubernetes Pod Horizontal Autoscaling safe drain, celery worker scales down mid-work

我在 GKE 上有一个 Kubernetes 集群。 除其他外,我当前的布局有一个 Pod(worker-pod),配置了一个 Horizo​​ntal pod autoscaler,它根据 BlueMedora 的 BindPlane 在 Stackdriver 上提供的外部指标进行缩放。

自动缩放功能完美无缺,但有时当需要缩小时,pods 会在完成一项永远无法完成的任务时精疲力尽。

pod 是 运行 一个 Celery worker,而作业队列由另一个带有 RabbitMQ 的 Pod 管理,我不确定是在 K8s 端还是在 rabbitMQ 端修复这个问题。

如何避免 HPA 在执行任务时缩小 pod 的尺寸?

我的 pod 规格(简体):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-worker
  labels:
    component: worker
spec:
  selector:
    matchLabels:
      app: pod-worker
  replicas: 1
  template:
    metadata:
      labels:
        app: pod-worker
        component: worker
    spec:
      containers:
      - name: worker
        image: custom-image:latest
        imagePullPolicy: Always
        command: ['celery']
        args: ['worker','-A','celery_tasks.task','-l','info', '-Q', 'default,priority','-c','1', '-Ofair']
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 150m
            memory: 200Mi
        env:
         - name: POD_NAME
           valueFrom:
             fieldRef:
               fieldPath: metadata.name
      restartPolicy: Always
    
---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: pod-worker
  labels:
    component: worker
spec:
  maxReplicas: 30
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: pod-worker
  metrics:
    - external:
        metricName: external.googleapis.com|bluemedora|generic_node|rabbitmq|cluster|messages
        targetAverageValue: "40"
      type: External

要解决此问题,您有多种方法,首先,为了避免丢失要处理的消息,您需要使用 RabbitMQ 手动 ACK,您需要在工作成功后进行 ACK,如果失败,则任务将重新排队,并且然后重新加工。

其次,本质上,当自动缩放(缩减)开始时,它将发送一个 SIGTERM 信号并等待,直到变量(在 podSpec 中):

terminationGracePeriodSeconds: 90

因此您可以修改该变量并将其设置得高一些,以便它能够在任务完成后正常关闭。

在terminationGracePeriodSeconds时间过去后,pod会收到一个SIGKILL信号,这会杀死pod。

另外,你可以用python处理这些信号,这里是一个小例子:

import signal
import time
class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)
  def exit_gracefully(self,signum, frame):
    self.kill_now = True
if __name__ == '__main__':
  killer = GracefulKiller()
  while not killer.kill_now:
    time.sleep(1)
    print("doing something in a loop ...")
  print "End of the program. I was killed gracefully :)"