-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.slide
257 lines (182 loc) · 9.09 KB
/
main.slide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# Kubernetes et les ressources
Gouter Engineering
8 Feb 2024
: ssh -L 6443:172.18.8.101:6443 [email protected]
: cd git/kubespray/
: vagrant up
: vagrant ssh -c "sudo cat /root/.kube/config" k8s-1
: export KUBECONFIG=~/config_demo
##
Que va-t-on voir ensemble? globalement, ca:
<img src="img/tanker.jpg" alt="image" width="70%" height="auto" style="display: block; margin-left: auto; margin-right: auto;">
Pourquoi ca nous interesse?
* Parce qu'on a pas souvent l'occasion de casser Kubernetes, et que ca fait du bien 😁
* Pour améliorer notre utilisation de ressources
* Etre capable d'autoscaler nos applis et clusters
* Et l'on aimerai dans un futur proche travailler ensemble sur le moyen le plus simple de les définir et vous en simplifier au maximum l'usage
## Enfonçons quelques porte ouvertes
Pour faire fonctionner un container, il nous faut
* Du cpu
* De la mémoire
* Du disque
* Parfois, une carte graphique - mais c'est hors scope pour aujourd'hui
Ce sont les Nodes Kubernetes qui nous apportent ces ressources
<img src="img/node.png" alt="image" width="70%" height="auto" style="display: block; margin-left: auto; margin-right: auto;">
##
Kubernetes propose de définir des ressources:
```yaml
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- resources:
requests:
cpu: 1m
memory: 32Mi
limits:
cpu: 1
memory: 128Mi
```
Ces définitions ont deux buts :
* Aider le scheduler (process kubernetes assignant les pods aux nodes) à positionner vos pods
* Empêcher vos pods de surconsommer des resources mémoires et/ou cpu
## Les requests
Elles informent le scheduler de la consommation du container en **régime de croisière**
<img src="img/cluster-1.svg" alt="image" width="100%" height="auto">
La réservation de ressources du pod est la somme des __requests__ des containers
## Schedule d'un nouveau pod
<img src="img/new-pod.svg" alt="image" width="100%" height="auto">
## La réalité est différente de la définition
Il s'agit simplement d'une moyenne d'utilisation pour aider à la décision, mais:
* Le pod peut consommer plus ou moins que sa request
* Ce n'est pas une limitation
* Déclarer trop large gaspille les ressources
## 💥 Testons sur un cluster
Le cluster :
```
NAME↑ STATUS ROLE TAINTS VERSION PODS CPU MEM %CPU %MEM CPU/A MEM/A
k8s-1 Ready control-plane 1 v1.28.5 7 206 923 10 11 2000 7742
k8s-2 Ready control-plane 1 v1.28.5 8 209 959 10 12 2000 7742
k8s-3 Ready control-plane 1 v1.28.5 6 212 912 10 11 2000 7742
k8s-4 Ready <none> 0 v1.28.5 5 50 541 2 6 2000 7742
k8s-5 Ready <none> 0 v1.28.5 6 56 617 2 7 2000 7742
k8s-6 Ready <none> 0 v1.28.5 5 48 586 2 7 2000 7742
```
Normalement, 3 noeuds, 8Go de mémoire et 2 vCpus par noeud, 💪 on ne devrait pas le faire saturer trop facilement...
## Un déploiement nginx innocent 👼🏼
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: gachis
spec:
replicas: 3
[...]
template:
[...]
spec:
containers:
- image: nginx
name: nginx
resources:
requests:
cpu: 1700m # 🧨
memory: 32Mi
limits:
memory: 128Mi
```
: kubectl apply -f src/nginx-deployment-2cpu.yaml
## et un autre déploiement tout simple
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-100
namespace: gachis
spec:
replicas: 1
[...]
template:
[...]
spec:
containers:
- image: nginx
name: nginx-100
resources:
requests:
cpu: 100m
memory: 32Mi
limits:
memory: 128Mi
```
😰 Notre pod ne démarre pas! pourtant il est de taille raisonnable
: kubectl apply -f src/nginx-deployment-100m.yaml
## Nous réservons effectivement des ressources
Le pod n'a pas pu être "schedule" :
<img src="img/events-surconso.png" alt="image" width="100%" height="auto" style="display: block; margin-left: auto; margin-right: auto;">
En plus imagé, voici ce qu'il se passe :
<img src="img/nginx-surconso.svg" alt="image" width="70%" height="auto" style="display: block; margin-left: auto; margin-right: auto;">
: Supprimer le pod 1700 et constater que notre deploiement se schedule
: k delete deployments.apps -n gachis nginx-1700
: Augmenter les replicas a 10
: k scale deployment -n gachis --replicas 10 nginx-100
: k delete ns gachis
## Pourquoi réserver des ressources?
Et si on utilisait pas les requests? Après tout, rien ne nous oblige a les déclarer, et on prends le risque de gaspiller des ressources.
Testons avec un pod qui consomme de la mémoire.
```
kubectl apply -f src/norequests-conso-mem.yaml # Ce deployment n'a aucune request définie
kubectl -n norequests port-forward $(kubectl -n norequests get pod -o jsonpath='{.items[0].metadata.name}') 8080:8080
curl -v localhost:8080?m=7000 # Demande de consommer 7Go
```
Le noeud sur lequel est déployé notre pod est quasiment saturé en mémoire. Déployons un composant simple qui consomme de la mémoire avec 6 replicas :
```
kubectl apply -f src/conso-256m.yaml
```
##
On trouve des replicas sur le noeud saturé! Et un des pods a redémarré pour cause d'Out of Memory ( OOMKill ).
Le scheduler ne tiens pas compte de la charge réelle des noeuds seulement des informations de requests que nous lui fournissons.
<img src="img/surcharge.svg" alt="image" width="100%" height="auto" style="display: block; margin-left: auto; margin-right: auto;">
## Il y a pire! 😱
Imaginons qu'un noeud nécessite une operation de maintenance...
```
kubectl cordon k8s-6
kubectl drain --ignore-daemonsets=true k8s-6
kubectl uncordon k8s-6
```
La charge ne se ré-équilibre pas, et comme nous n'avons rien défini, Kubernetes ne sait pas que le noeud 4 est surchargé.
## Les limites 📈
Les limites vont permettre d'empêcher de dépasser la consommation maximale définie.
* Si nous définissons 256Mi de mémoire par exemple, le container sera "Kill" et redémarré.
* Côté CPU, le process est moins extrème, le container sera simplement ralenti ( throttled )
La limite CPU est contre productive, en limitant le cpu, on ralentit le workload même quand de la ressource est disponible.
Une excellente explication se trouve dans [cet article](https://home.robusta.dev/blog/stop-using-cpu-limits)
Côté mémoire cependant, la limiter permet d'éviter qu'un pod ne consomme toute la mémoire d'un noeud et impacte les performances d'autres déploiements
: montrer l'état du pod 7000
## Et la QoS?
La [Quality of Service](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/) est une valeur attribuée par Kubernetes a vos pods en fonction des définitions de ressources.
Il en existe 3 :
* Guaranteed : request = limit
* Burstable : au moins une définition de request
* BestEffort : pas de définition
Viser la QoS guaranteed est souvent illusoire, cela impose d'utiliser la limite CPU, et sous-entend que le composant déployé a une consommation linéaire.
De plus, elle ne correspond côté système qu'à des [CGroups](https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/6/html/resource_management_guide/ch01) différents, et n'entre pas en jeu dans l'éviction sur déploiement, seulement dans la gestion des OOM en cas de manque de ressource sur le noeud.
: vagrant ssh k8s-4
: sudo systemd-cgls
## Et chez Evaneos?
Deux dashboards intéressants :
* [définition vs utilisation](https://app.datadoghq.com/orchestration/resource/deployment?query=tag%23kube_cluster_name%3Aprod2&expanded=tag%23kube_cluster_name%3Aprod2&groups=tag%23kube_cluster_name&panel_tab=yaml&pg=0&resource_mode=cpu&resource-na-groups=false&sort=metrics.cpu_usage_pct_requests_avg15%3Aasc&start=1706022367147&end=1706023267147&paused=false)
* [ressources réservées non utilisées](https://app.datadoghq.com/dashboard/gxu-uiq-557/kubernetes-ressource-usage?refresh_mode=sliding&view=spans&from_ts=1706019669230&to_ts=1706023269230&live=true)
## Pourquoi définir tout ca ?
Si nous parvenons a définir les ressources de l'intégralité de notre workload, on garantira:
* un scheduling efficace
* la capacité d'autoscaler le workload
* la capacité d'autoscaler les clusters
**Et comment le définir?**
Plusieurs pistes sont possibles:
* Utiliser [datadog](https://app.datadoghq.com/metric/explorer?start=1706022296262&end=1706025896262&paused=false#N4Ig7glgJg5gpgFxALlAGwIYE8D2BXJVEADxQEYAaELcqyKBAC1pEbghkcLIF8qo4AMwgA7CAgg4RKUAiwAHOChASAtnADOcAE4RNIKtrgBHPJoQaUAbVBGN8qVoD6gnNtUZCKiOq279VKY6epbINiAiGOrKQdpYZAYgUJ4YThr42gDGSsgg6gi6mZaBZnHKGABuMMiZUggYojoAdJnyeE14GhjwwADWeABGcE4C8mg4WOoiCMjqqkPaALQDOBQABP1DTplonQg6TpHqyPLaOFAATDwgPAC6VK7ueJih4Y+qzxgxpfE39yAaORoHKgeQYYEIfbKKA4GDbF4aCCZRJoRpOOSKZTpVFQFFo+hMZQiNweNA3fgQeyYLDohQ5ECokRKO48PgAsbiADCUmEMBQImeaB4QA) pour se faire une idée de la consommation
* [Gatling](https://gatling.io/)
* Le [Vertical Pod Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) en mode "recommandation"