J'ai récemment poussé une nouvelle image de conteneur vers l'un de mes déploiements GKE et j'ai remarqué que la latence de l'API a augmenté et que les demandes ont commencé à renvoyer des 502.
En regardant les journaux, j'ai constaté que le conteneur a commencé à se bloquer à cause de l'OOM :
Memory cgroup out of memory: Killed process 2774370 (main) total-vm:1801348kB, anon-rss:1043688kB, file-rss:12884kB, shmem-rss:0kB, UID:0 pgtables:2236kB oom_score_adj:980
En regardant le graphique d'utilisation de la mémoire, il ne semble pas que les pods utilisent plus de 50MB de mémoire combinée. Mes demandes de ressources initiales étaient les suivantes :
...
spec:
...
template:
...
spec:
...
containers:
- name: api-server
...
resources:
# You must specify requests for CPU to autoscale
# based on CPU utilization
requests:
cpu: "150m"
memory: "80Mi"
limits:
cpu: "1"
memory: "1024Mi"
- name: cloud-sql-proxy
# It is recommended to use the latest version of the Cloud SQL proxy
# Make sure to update on a regular schedule!
image: gcr.io/cloudsql-docker/gce-proxy:1.17
resources:
# You must specify requests for CPU to autoscale
# based on CPU utilization
requests:
cpu: "100m"
...
J'ai ensuite essayé de faire passer la demande de serveur API à 1 Go, mais cela n'a pas aidé. Finalement, ce qui a aidé, c'est de rétablir l'image du conteneur à la version précédente :
En regardant les changements dans le binaire golang, il n'y a pas de fuites de mémoire évidentes. Lorsque je l'exécute localement, il utilise au maximum 80 Mo de mémoire, même sous la charge des mêmes requêtes qu'en production.
Et le graphique ci-dessus, que j'ai obtenu de la console GKE, montre également que le pod utilise bien moins que la limite de 1 Go de mémoire.
Ma question est donc la suivante : qu'est-ce qui pourrait amener GKE à tuer mon processus pour cause d'OOM alors que la surveillance GKE et l'exécution locale n'utilisent que 80 Mo sur la limite de 1 Go ?
\=== EDIT ===
Ajout d'un autre graphique de la même panne. Cette fois-ci en divisant les deux conteneurs dans le pod. Si je comprends bien, la métrique ici est conteneur non-évictable/mémoire/octets utilisés :
container/memory/used_bytes GA
Memory usage
GAUGE, INT64, By
k8s_container Memory usage in bytes. Sampled every 60 seconds.
memory_type: Either `evictable` or `non-evictable`. Evictable memory is memory that can be easily reclaimed by the kernel, while non-evictable memory cannot.
Editer le 26 avril 2021
J'ai essayé de mettre à jour le champ des ressources dans le yaml de déploiement à 1GB RAM demandé et 1GB RAM limite comme suggéré par Paul et Ryan :
resources:
# You must specify requests for CPU to autoscale
# based on CPU utilization
requests:
cpu: "150m"
memory: "1024Mi"
limits:
cpu: "1"
memory: "1024Mi"
Malheureusement, le même résultat a été obtenu après la mise à jour avec kubectl apply -f api_server_deployment.yaml
:
{
insertId: "yyq7u3g2sy7f00"
jsonPayload: {
apiVersion: "v1"
eventTime: null
involvedObject: {
kind: "Node"
name: "gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy"
uid: "gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy"
}
kind: "Event"
message: "Memory cgroup out of memory: Killed process 1707107 (main) total-vm:1801412kB, anon-rss:1043284kB, file-rss:9732kB, shmem-rss:0kB, UID:0 pgtables:2224kB oom_score_adj:741"
metadata: {
creationTimestamp: "2021-04-26T23:13:13Z"
managedFields: [
0: {
apiVersion: "v1"
fieldsType: "FieldsV1"
fieldsV1: {
f:count: {
}
f:firstTimestamp: {
}
f:involvedObject: {
f:kind: {
}
f:name: {
}
f:uid: {
}
}
f:lastTimestamp: {
}
f:message: {
}
f:reason: {
}
f:source: {
f:component: {
}
f:host: {
}
}
f:type: {
}
}
manager: "node-problem-detector"
operation: "Update"
time: "2021-04-26T23:13:13Z"
}
]
name: "gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy.16798b61e3b76ec7"
namespace: "default"
resourceVersion: "156359"
selfLink: "/api/v1/namespaces/default/events/gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy.16798b61e3b76ec7"
uid: "da2ad319-3f86-4ec7-8467-e7523c9eff1c"
}
reason: "OOMKilling"
reportingComponent: ""
reportingInstance: ""
source: {
component: "kernel-monitor"
host: "gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy"
}
type: "Warning"
}
logName: "projects/questions-279902/logs/events"
receiveTimestamp: "2021-04-26T23:13:16.918764734Z"
resource: {
labels: {
cluster_name: "api-us-central-1"
location: "us-central1-a"
node_name: "gke-api-us-central-1-e2-highcpu-4-nod-dfe5c3a6-c0jy"
project_id: "questions-279902"
}
type: "k8s_node"
}
severity: "WARNING"
timestamp: "2021-04-26T23:13:13Z"
}
Kubernetes semble avoir presque immédiatement tué le conteneur pour avoir utilisé 1 Go de mémoire. Mais là encore, les mesures montrent que le conteneur n'utilise que 2 Mo de mémoire :
Une fois de plus, je reste perplexe, car même sous charge, ce binaire n'utilise pas plus de 80 Mo lorsque je l'exécute localement.
J'ai aussi essayé de lancer go tool pprof <url>/debug/pprof/heap
. Il a montré plusieurs valeurs différentes alors que Kubernetes ne cessait d'attaquer le conteneur. Mais aucune n'était supérieure à ~20MB et l'utilisation de la mémoire ne sortait pas de l'ordinaire.
Edit 04/27
J'ai essayé de définir request=limit pour les deux conteneurs dans le pod :
requests:
cpu: "1"
memory: "1024Mi"
limits:
cpu: "1"
memory: "1024Mi"
...
requests:
cpu: "100m"
memory: "200Mi"
limits:
cpu: "100m"
memory: "200Mi"
Mais ça n'a pas marché non plus :
Memory cgroup out of memory: Killed process 2662217 (main) total-vm:1800900kB, anon-rss:1042888kB, file-rss:10384kB, shmem-rss:0kB, UID:0 pgtables:2224kB oom_score_adj:-998
Et les mesures de la mémoire montrent toujours une utilisation à un chiffre de Mo.
Mise à jour 04/30
J'ai localisé la modification qui semblait causer ce problème en vérifiant minutieusement mes derniers commits un par un.
Dans le commit incriminé, j'avais quelques lignes comme
type Pic struct {
image.Image
Proto *pb.Image
}
...
pic.Image = picture.Resize(pic, sz.Height, sz.Width)
...
Où picture.Resize
appelle éventuellement resize.Resize
. Je l'ai changé en :
type Pic struct {
Img image.Image
Proto *pb.Image
}
...
pic.Img = picture.Resize(pic.Img, sz.Height, sz.Width)
Cela résout mon problème immédiat et le conteneur fonctionne bien maintenant. Mais cela ne répond pas à ma question initiale :
- Pourquoi ces lignes ont-elles entraîné la fermeture du conteneur par GKE ?
- Et pourquoi les mesures de la mémoire de GKE ont-elles montré que tout allait bien ?