Wir haben kürzlich unsere Produktionsumgebung auf Kubernetes umgestellt. Ich möchte CPU-Limits für die Container erzwingen. Ich erhalte widersprüchliche CPU-Metriken, die nicht zusammenpassen. Hier ist mein Setup:
- DataDog-Agenten, die als ausgeführt werden
Daemonset
- Bestehende Anwendungen werden ohne CPU-Limits ausgeführt
- Bei den fraglichen Containern handelt es sich um Ruby-Anwendungen mit mehreren Threads
- Zwei Metriken:
kubernetes.cpu.usage.{avg,max}
unddocker.cpu.usage
c4.xlarge
Clusterknoten (4 vCPUs oder 4000 m in Kubernetes-Begriffen)
kubernetes.cpu.usage.max
meldet ~ 600m für die betreffenden Container. docker.cpu.usage
meldet ~ 60%. Daraus folgt, dass ein CPU-Limit von 1000 m im normalen Betrieb mehr als ausreichend Kapazität wäre.
Ich habe das Limit auf 1000m gesetzt. Dann docker.container.throttles
steigt deutlich kubernetes.cpu.usage.max
und docker.cpu.usage
bleibt dabei gleich. Während dieser Zeit fällt das System auf die Knie. Das ergibt für mich keinen Sinn.
Ich habe Docker-Statistiken recherchiert. Es scheint, dass docker stats
(und die zugrunde liegende API) die Last gemäß den CPU-Kernen normalisieren . In meinem Fall kommen also docker.cpu.usage
60% (4000 m * 0,60) in Kubernetes-Begriffen auf 2400 m. Dies korreliert jedoch nicht mit Kubernetes-Nummern. Ich habe ein weiteres Experiment durchgeführt, um meine Hypothese zu testen, dass die Kubernetes-Zahlen falsch sind. Ich habe das Limit auf 2600 m festgelegt (für zusätzliche Kopffreiheit). Dies führte zu keinen Drosseln. Kubernetes stellte jedoch fest, dass sich die CPU-Auslastung nicht geändert hat. Das verwirrt mich.
Meine Fragen sind also:
- Fühlt sich das wie ein Fehler in Kubernetes an (oder etwas im Stapel?)
- Ist mein Verständnis richtig?
Meine Folgefrage bezieht sich darauf, wie die CPU für Ruby-Anwendungen richtig bestimmt wird. Ein Container verwendet Puma. Dies ist ein Multithread-Webserver mit einer konfigurierbaren Anzahl von Threads. HTTP-Anforderungen werden von einem der Threads verarbeitet. Die zweite Anwendung ist ein Thrift-Server, der den Thread-Server verwendet. Jede eingehende TCP-Verbindung wird von einem eigenen Thread verarbeitet. Der Thread wird beendet, wenn die Verbindung geschlossen wird. Ruby als GIL (Global Interpreter Lock), sodass jeweils nur ein Thread Ruby-Code ausführen kann. Dies ermöglicht mehrere Threads, die E / A und ähnliches ausführen.
Ich denke, der beste Ansatz besteht darin, die Anzahl der in jeder Anwendung ausgeführten Threads zu begrenzen und die CPU-Grenzwerte von Kubernetes basierend auf der Anzahl der Threads zu approximieren. Die Prozesse sind nicht verzweigt, sodass die Gesamtauslastung der CPU schwerer vorherzusagen ist.
Die Frage hier ist: Wie kann man die CPU-Auslastung und die Grenzwerte für diese Anwendungen richtig vorhersagen?