|
| 1 | +--- |
| 2 | +title: "Pod QoS, Quality of Service Classes" |
| 3 | +date: "2024-05-27" |
| 4 | +tags: ["kubernetes"] |
| 5 | +summary: "Pod Qos, Quality of Service Classes" |
| 6 | +description: "Pod Quality of Service Classes 에 대해서 알아보자" |
| 7 | +--- |
| 8 | + |
| 9 | +:::info |
| 10 | +kubernetes 에서는 Pod 에 대한 QoS(Quality of Service) 클래스를 제공하고 있고, QoS 에 대해서 간략하게 정리 |
| 11 | +::: |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Pod 의 QoS 클래스 분류 |
| 16 | + |
| 17 | +QoS 는 Resource Requests 리소스 상한을 정하는 **Resource Limits** 조건을 바탕으로 우선순위가 정해진다. |
| 18 | + |
| 19 | +|구분| 내용 | Case | |
| 20 | +|--|-----------------------------------------------------------------------------------------|------------------------------------------| |
| 21 | +|BestEffort| 우선순위가 가장 낮고 <br/> 메모리가 부족한 경우 kill | Container 의 Request 와 Limit 이 설정되지 않은 경우 | |
| 22 | +|Burstable| 최소한의 리소스를 보장 받으며, <br/> 필요한 경우 더 많은 리소스를 할당 받음 <br/> 메모리가 부족한 경우 다른 BestEffort 가 없다면 kill | Container 의 Request 와 Limit 이 일치하지 않는 경우 <br/> Container 의 Request 와 Limit 중 하나만 설정된 경우 <br/> Request 와 Limit 이 설정되지 않은 Container 가 있는 경우| |
| 23 | +|Guaranteed| <BlueText> **우선순위가 높고** </BlueText>, <br/> Limits 를 넘지 않는 한 kill 되지 않음이 보장 | 모든 Container 의 Request 와 Limit 이 설정 되어 있고, <br/> 각각 Container 의 Request 와 Limit 이 일치하는 경우| |
| 24 | + |
| 25 | +<div style={{ textAlign: 'center' }}> |
| 26 | + <img src="/img/post/kubernetes/qos/qos.png" alt="qos" style={{ display: 'inline-block' }} /> |
| 27 | +</div> |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## Case |
| 32 | + |
| 33 | +| CPU | Memory |Qos Classes| |
| 34 | +|------------------|------------------|----------| |
| 35 | +| - | - |BestEffort| |
| 36 | +| - | Reqeust < Limits |Burstable| |
| 37 | +| - | Reqeust = Limits |Burstable| |
| 38 | +| Reqeust < Limits | - |Burstable| |
| 39 | +| Reqeust < Limits | Reqeust < Limits |Burstable| |
| 40 | +| Reqeust < Limits | Reqeust = Limits |Burstable| |
| 41 | +| Reqeust = Limits | Reqeust < Limits |Burstable| |
| 42 | +| Reqeust = Limits | Reqeust = Limits |Guaranteed| |
| 43 | + |
| 44 | + |
| 45 | +:::note |
| 46 | +만일 side-car 를 사용한다면, 모든 side-car 의 CPU / Memory Request = Limit 를 맞춰야 Guaranteed 가 된다는 것이다. |
| 47 | +::: |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## QoS 예시 |
| 52 | + |
| 53 | +### 1) BestEffort |
| 54 | + |
| 55 | +```yaml |
| 56 | +... |
| 57 | + spec: |
| 58 | + containers: |
| 59 | + - name: app |
| 60 | + image: busybox |
| 61 | + - name: fluentd |
| 62 | + image: fluent/fluentd |
| 63 | +... |
| 64 | +``` |
| 65 | + |
| 66 | +### 2) Burstable |
| 67 | + |
| 68 | +```yaml |
| 69 | +... |
| 70 | +spec: |
| 71 | + containers: |
| 72 | + - name: app |
| 73 | + image: busybox |
| 74 | + resources: |
| 75 | + request: |
| 76 | + memory: "128Mi" ### request, limit 의 memory 를 다르게 설정 |
| 77 | + cpu: "500m" |
| 78 | + limit: |
| 79 | + memory: "256Mi" |
| 80 | + cpu: "500m" |
| 81 | + - name: fluentd |
| 82 | + image: fluent/fluentd |
| 83 | + resources: |
| 84 | + request: |
| 85 | + memory: "128Mi" |
| 86 | + cpu: "500m" |
| 87 | + limit: |
| 88 | + memory: "128Mi" |
| 89 | + cpu: "500m" |
| 90 | +... |
| 91 | +``` |
| 92 | + |
| 93 | +```yaml |
| 94 | +... |
| 95 | +spec: |
| 96 | + containers: |
| 97 | + - name: app |
| 98 | + image: busybox |
| 99 | + resources: |
| 100 | + request: |
| 101 | + memory: "128Mi" |
| 102 | + cpu: "500m" |
| 103 | + limit: |
| 104 | + memory: "128Mi" |
| 105 | + cpu: "500m" |
| 106 | + - name: fluentd |
| 107 | + image: fluent/fluentd |
| 108 | + resources: |
| 109 | + request: |
| 110 | + memory: "128Mi" |
| 111 | + cpu: "200m" ### side-car 의 request, limit 의 cpu 를 다르게 설정 |
| 112 | + limit: |
| 113 | + memory: "128Mi" |
| 114 | + cpu: "500m" |
| 115 | +... |
| 116 | +``` |
| 117 | + |
| 118 | +### 3) Guaranteed |
| 119 | + |
| 120 | +```yaml |
| 121 | +... |
| 122 | +spec: |
| 123 | + containers: |
| 124 | + - name: app |
| 125 | + image: busybox |
| 126 | + resources: |
| 127 | + request: ### request, limit memory, cpu 동일하게 설정 |
| 128 | + memory: "128Mi" |
| 129 | + cpu: "500m" |
| 130 | + limit: |
| 131 | + memory: "128Mi" |
| 132 | + cpu: "500m" |
| 133 | + - name: fluentd |
| 134 | + image: fluent/fluentd |
| 135 | + resources: |
| 136 | + request: ### request, limit memory, cpu 동일하게 설정 |
| 137 | + memory: "128Mi" |
| 138 | + cpu: "200m" |
| 139 | + limit: |
| 140 | + memory: "128Mi" |
| 141 | + cpu: "200m" |
| 142 | +... |
| 143 | +``` |
| 144 | + |
| 145 | +--- |
| 146 | + |
| 147 | +## 우선 순위 매커니즘 |
| 148 | + |
| 149 | +kubernetes 에서 리소스가 부족하면 QoS 에 따라 어떤 Pod 의 Container 의 Application 을 Kill 할지 추정한다. |
| 150 | + |
| 151 | +:::note |
| 152 | +BestEffort < Burstable < Guaranteed |
| 153 | +::: |
| 154 | + |
| 155 | +Guaranteed 는 node 에서 메모리를 필요로 하는 경우에만 kill 된다. |
| 156 | + |
| 157 | +:::note |
| 158 | +kubelet 은 eviction 제거 순서를 결정하기 위해서 Pod 의 QoS 클래스를 사용하지 않는다. |
| 159 | +QoS 클래스를 사용하여 메모리와 같은 리소스를 회수할 때 가장 가능성이 높은 Pod 를 추정한다. |
| 160 | +::: |
| 161 | + |
| 162 | +--- |
| 163 | + |
| 164 | +### Pod Qos 에 따른 oom_score_adj 값 |
| 165 | + |
| 166 | +|Pod Qos| oom_score_adj | |
| 167 | +|--|-----------------------------------------------------------------------------------| |
| 168 | +|Guaranteed| -997 (고정) | |
| 169 | +|Burstable| min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999) | |
| 170 | +|BestEffort| 1000 (고정) | |
| 171 | + |
| 172 | +`Guaranteed` 는 -997 의 고정 값을 가지고 있고, |
| 173 | +`BestEffort` 는 1000 의 고정 값을 가지고 있다. |
| 174 | +`Burstable` 는 Pod 의 Manifest 에 명시된 Container 의 Memory Request 값이 **높을수록 낮은 값**을 가지며, 2~999 사이의 값을 가진다. |
| 175 | + |
| 176 | +Container 의 Process 의 oom_score 값과 kubernetes 가 설정한 oom_score_adj 값의 합이 높을 수록 |
| 177 | +OOM Killer 에 의해서 선택될 확률이 높아진다. |
| 178 | +합이 0 이면 OOM Killer 선택 대상에서 제외되며, 합이 1000을 넘어가면 OOM Killer 에 의해서 반드시 제거된다. |
| 179 | + |
| 180 | +:::success |
| 181 | +<b>요약</b> |
| 182 | +✔ Guaranteed Qos 를 갖는 Pod 의 Container 는 OOM Killer 에 의해 거의 선택되지 않는다. |
| 183 | +✔ BestEffort Qos 를 갖는 Pod 의 Container 는 OOM Killer 에 의해 반드시 선택되어 제거된다. |
| 184 | +✔ Burstable QoS 를 갖는 Pod 의 Container 는 Memory 사용량이 없어도 oom_score_adj 값이 999 가 되기 때문에 높은 확률로 OOM Killer 에 의해 제거될 확률이 높다. |
| 185 | +✔ Memory 사용량이 높으면 oom_score_adj 값이 낮더라도 oom_score 값이 높기 때문에 높은 확률로 OOM Killer 에 의해 선택되어 제거 될 수 있다. |
| 186 | +::: |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +## 간단한 예시 |
| 191 | + |
| 192 | +Pod1, Pod2, Pod3 이 각각 Guaranteed, Burstable, BestEffort QoS 분류가 되어 있다고 가정 |
| 193 | + |
| 194 | +| Pod |Qos| |
| 195 | +|------|--| |
| 196 | +| Pod1 |Guaranteed| |
| 197 | +| Pod2 |Burstable| |
| 198 | +| Pod3 |BestEffort| |
| 199 | + |
| 200 | +--- |
| 201 | + |
| 202 | +### Q. Pod1 이 리소스를 추가로 요청하고 node 에는 여유 리소스가 없는 상태일 때는? |
| 203 | + |
| 204 | +<BlueText><span style={{ fontSize: '1.5rem', }}>A. </span></BlueText> Pod2 와 Pod3 이 같은 메모리 사용량을 사용하고 있다면, **BestEffort** 로 QoS 분류가 되어 있는 Pod3 을 죽이고, |
| 205 | +Pod3 에 있는 리소스들을 Pod1 이 필요한 만큼의 리소스를 할당하고 node 의 여유 자원으로 회수하게 된다. |
| 206 | + |
| 207 | +:::warning |
| 208 | +Pod2와 Pod3이 사용하는 메모리에 따라서 eviction 순서가 바뀔 수 있다. |
| 209 | +::: |
| 210 | + |
| 211 | +--- |
| 212 | + |
| 213 | +### Q. Pod1, Pod2 만 운영 중일때, Pod2 가 현재 node 의 여유자원 보다 많은 리소스를 요청하게 된 경우는? |
| 214 | + |
| 215 | +<BlueText><span style={{ fontSize: '1.5rem', }}>A. </span></BlueText> Pod2 의 QoS 는 **Burstable** 이고, Pod1 의 QoS 는 **Guaranteed** 이므로, |
| 216 | +Pod2 의 우선순위가 낮기 때문에 추가적인 리소스를 할당 받지 못하고 죽게 될 가능성이 높다. |
| 217 | + |
| 218 | +--- |
| 219 | + |
| 220 | +### Q. 만일 같은 QoS 일 때는? |
| 221 | + |
| 222 | +<BlueText><span style={{ fontSize: '1.5rem', }}>A. </span></BlueText> OutOfMemory Score 에 따라 어떤 Pod 를 Kill 할지 비교하여 정한다. |
| 223 | + |
| 224 | + |
| 225 | +--- |
| 226 | + |
| 227 | +## 📚 Reference |
| 228 | + |
| 229 | +* [Configure Quality of Service for Pods](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/) |
| 230 | +* [k8s 파드의 우선순위(Pod QoS, Quality of Service)](https://kimjingo.tistory.com/139) |
| 231 | +* [Node-pressure Eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#node-oom-behavior) |
| 232 | +* [Another OOM killer rewrite](https://lwn.net/Articles/391222/) |
| 233 | + |
| 234 | + |
| 235 | + |
0 commit comments