I have Celery workers running on Kubernetes 1.20 cluster on AWS EKS using AWS Elasticache Redis as the broker. Because of the nature of the project ~80% of the time celery workers are running idle so the logical thing was to have them scale automatically. Scaling based on CPU/memory works ok. At about 4 workers node scaling also needs to kick in and that works ok as well. An obvious problem is that it takes some time for a new node to start and get fully operational before that node can start taking on new celery worker pods. So some waiting to scale up is expected.
Somewhere in all that waiting a fresh celery worker pod gets started and starts accepting new tasks and executing them, but for some unknown reason the startupProbe does not complete. Because startupProbe was not successful the whole pod is killed potentially in the middle of a running task.
Question:
Can I prevent celery from taking on tasks before the startupProbe is considered successful?
Celery config
apiVersion: apps/v1
kind: Deployment
metadata:
name: celery-worker
labels:
app: celery-worker
spec:
selector:
matchLabels:
app: celery-worker
progressDeadlineSeconds: 900
template:
metadata:
labels:
app: celery-worker
spec:
containers:
- name: celery-worker
image: -redacted-
imagePullPolicy: Always
command: ["./scripts/celery_worker_entrypoint_infra.sh"]
env:
- name: CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: celery-broker-url-secret
key: broker-url
startupProbe:
exec:
command: ["/bin/bash", "-c", "celery -q -A app inspect -d celery@$HOSTNAME --timeout 10 ping"]
initialDelaySeconds: 20
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["/bin/bash", "-c", "celery -q -b $CELERY_BROKER_URL inspect -d celery@$HOSTNAME --timeout 10 ping"]
periodSeconds: 120
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 3
livenessProbe:
exec:
command: ["/bin/bash", "-c", "celery -q -b $CELERY_BROKER_URL inspect -d celery@$HOSTNAME --timeout 10 ping"]
periodSeconds: 120
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 5
resources:
requests:
memory: "384Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
terminationGracePeriodSeconds: 2400
Celery HPA config
kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta2
metadata:
name: celery-worker
spec:
minReplicas: 2
maxReplicas: 40
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: celery-worker
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 90
Celery startup script
python manage.py check
exec celery --quiet -A app worker \
--loglevel info \
--concurrency 1 \
--uid=nobody \
--gid=nogroup
I'm sharing complete configs including readinessProbe and livenessProbe whose values are a bit inflated, but are a consequence of various try-and-error scenarios.
Edit: This is a catch-22 situation.
I have defined startupProbe to check if celery is running in current host and that will only be true if celery worker is running. And if celery worker is running it will accept tasks. And if it will accept tasks celery inspect command might take too long causing the startupProbe to hang and failing. If startupProbe fails too many times it will kill the pod.
Furthermore, if I call celery inspect without destination (host) defined, startupProbe will fail on initial deployment.
Conclusion: celery inspect is not a good startupProbe candidate.