개요
이번글에서는 쿠버네티스에서 Pod 배포시 CPU, MEM을 제한하는 방법을 알아본다. POD을 정의할 때 사용자는 CPU 와 MEM 를 각 컨테이너가 얼마나 필요한지 명시할 수 있다. 그러면 스케쥴러는 POD을 배치할 때 이부분을 고려하여 결정한다.
1. CPU/MEM 리소스 관리
1-1. Resource 요청/제한 관련 명세
아래 종류의 명세를 POD 정의 때 기입할 수 있다.
- spec.containers[].resources.limits.cpu
- spec.containers[].resources.limits.memory
- spec.containers[].resources.requests.cpu
- spec.containers[].resources.requests.memory
1-2. CPU 단위
CPU 리소스와 관련된 요청 및 제한은 cpu unit
으로 표현된다. 쿠버네티스에서 하나의 CPU는 아래와 같을 수 있다.
- 1 AWS vCPU
- 1 GCP Core
- 1 Azure vCore
- 1 IBM vCPU
- 1 Hyperthread on a bare-metal Intel processor with Hyperthreading
소수점은 허용되외 spec.containers[].resources.requests.cpu
를 0.5
와 같이 지정할 수 있다. 0.1
은 100m
과 같은 표현이다. 1
또는 1000m
이 1개의 core를 의미한다.
여기서 알아둬야 할 점은 CPU 리소스는 항상 절대적인 수치라는 점이다. 이말의 뜻은 CPU 리소스 0.1
이라는 수치는 single core, dual core, 48 core 머신에서 모두 동등한 양의 리소스를 의미한다.
1-3. Memory 단위
메모리 관련 요청/제한은 모두 바이트
단위를 사용한다. 정수를 사용할수도 있고 용량 단위에 따라 Ei
, Pi
, Ti
, Gi
, Mi
, Ki
를 사용할 수 있다. 또는 줄여서 E
, P
, T
, G
, M
, K
로도 사용할 수 있다.
1-4. CPU 및 Memory 관련 Pod 예제
아래 예제는 각 컨테이너가 0.25 cpu
와 64MB
의 메모리로 요청하고 최대 0.5 cpu
및 128MB
메모리를 가질 수 있다는 의미이다. 실제로는 POD 단위로 배포가 일어나므로 0.5 cpu
와 128MB
로 요청을 하고 최대 1 cpu
와 256MB
의 메모리로 제한된다고 이해하면 된다.
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
1-5. 어떻게 Pod 의 리소스 요청은 스케쥴 되는가?
사용자가 Pod을 생성할 때 쿠버네티스 스케쥴러는 어떤 노드에서 Pod이 실행되어야 될지 결정한다. 각 노드는 리소스 타입마다 max capacity를 가진다. 조금 당영한 얘기지만 스케쥴러는 각 리소스 타입마다 스케쥴된 컨테이너의 리소스 요청의 합이 노드의 capacity보다 낮게 유지되는것을 보장한다. 만약 노드의 실제 CPU나 메모리 리소스 사용이 낮더라도 스케쥴러는 capacity를 초과하는 Pod의 배치를 허용하지 않는다. 이것은 차후 리소스 사용이 늘어나 일어나게될 노드의 리소스 부족을 방지한다.
1-6. 어떻게 Pod의 리소스 제한은 동작하는가?
kubelet이 pod의 컨테이너를 시작할 때 CPU와 메모리 제한을 컨테이너 런타임으로 전달한다.
Docker를 사용한다면 아래와 같이 셋팅된다.
spec.containers[].resources.requests.cpu
* 1024 또는 2 중 큰값이docker run --cpu-shares
의 값으로 설정된다.spec.containers[].resources.limits.cpu
* 100이 컨테이너가 100ms 이내에 최대로 사용할 수 있는 총 CPU time으로 이를 넘어서는 안된다.
interval 100ms가 기본이고 최소값으로 1ms까지 줄일 수 있다.
spec.containers[].resources.limits.memory
값은 정수로 변환되어docker run --memory
값으로 사용된다.
만약 컨테이너가 메모리 limit을 넘으면 종료될 수 있다. 컨테이너가 재시작 가능하면 kubelet이 재시작 한다.
만약 컨테이너가 메모리 request를 넘으면 노드의 메모리가 부족할 시 Pod은 노드에서 쫓겨날 수 있다.
컨테이너는 일정시간 동안은 CPU limit을 넘어도 살아남도록 허용할수도있고 아닐수도 있다. 하지만 CPU 사용초과로 kill되지는 않는다.
컨테이너가 리소스 제한때문에 스케쥴되지 못하거나 kill되는 되는 확인하기 위해서는 Troubleshooting 섹션을 참고한다.
2. Local ephemeral storage
쿠버네티스 1.8 부터 ephemeral storage
를 사용할 수 있는데 로컬 임시 스토리지라고 생각하면 된다. 각 쿠버네티스 노드에서는 kubelet의 root directory(/var/lib/kubelet)과 로그 디렉토리(/var/log)가 노드의 루트 파티션에 저장된다. 이 파티션은 또한 Pod과 emptyDir 볼륨을 공유하는데 여기에 컨테이너의 로그, image layer, writable image가 쓰여진다.
이 파티션이 ephemeral
하고 어플리케이션들은 성능에 대한 어떤 SLA도 보장받지 못한다. local ephemeral storage
관리는 오직 루트 파티션(/
)에만 적용가능하다.
2-1. local ephemeral storage의 요청/제한
Pod의 각 컨테이너에 아래와 같은 명세를 줄 수 있다.
- spec.containers[].resources.limits.ephemeral-storage
-
spec.containers[].resources.requests.ephemeral-storage
단위는 바이트를 사용한다. 정수를 사용해도 되고 용량 단위에 따라
Ei
,Pi
,Ti
,Gi
,Mi
,Ki
를 사용할 수 있다. 또는 줄여서E
,P
,T
,G
,M
,K
로도 사용할 수 있다.
2-2. Local ephemeral storage Pod 예제
아래 예제에서 Pod 기준에서는 4GB
의 스토리지 요청과 8GB
의 스토리지 제한이 있다.
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
- name: wp
image: wordpress
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
2-3. 어떻게 Pod의 ephemeral storage 요청은 스케쥴 되는가?
Pod을 생성할 때 쿠버네티스 스케쥴러는 Pod을 어떤 노드에 배치할 지 결정한다. 스케쥴러는 컨테이너의 스토리지 요청의 합이 노드의 max capacity보다 낮게 배치되는것을 보장한다.
2-4. 어떻게 Pod의 ephemeral storage 제한이 동작하는가?
- 컨테이너 레벨 isolation에서는 만약 컨테이너의 writable layer 와 log 사용이 스토리지 제한을 초과하면 Pod은 쫓겨난다. Pod 레벨 isolation에서는 만약 로컬 ephemeral storage의 사용의 합과 Pod의 emptyDir 볼륨이 제한을 초과하면 Pod이 쫓겨난다.
3. 실제로 적용해보기
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sampleapp
labels:
env: test
spec:
replicas: 2
template:
metadata:
name: sampleapp
labels:
env: test
spec:
containers:
- name: nginx
image: idock.daumkakao.io/rectech/starport-v2:0.7.8
env:
- name: ENV_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
resources:
requests:
memory: "64Mi"
cpu: "250m"
ephemeral-storage: "2Gi"
limits:
memory: "128Mi"
cpu: "500m"
ephemeral-storage: "4Gi"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: sampleapp
operator: In
values:
- large
CPU Request
, CPU Limit
항목을 보면 정상적으로 리소스가 할당된 것을 확인 할 수 있다.
$ kubectl describe node search-so-dev52
Name: search-so-dev52
Roles: node
Labels: sampleapp=large
beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=search-so-dev52
node-role/worker=true
node-role.kubernetes.io/node=true
stargate=large
Annotations: io.cilium.network.ipv4-cilium-host=10.230.5.1
io.cilium.network.ipv4-health-ip=10.230.5.77
io.cilium.network.ipv4-pod-cidr=10.230.5.0/24
io.cilium.network.ipv6-health-ip=f00d::ae6:500:0:4341
io.cilium.network.ipv6-pod-cidr=f00d::ae6:500:0:0/96
node.alpha.kubernetes.io/ttl=0
volumes.kubernetes.io/controller-managed-attach-detach=true
Taints: <none>
CreationTimestamp: Fri, 25 Jan 2019 18:20:55 +0900
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
OutOfDisk False Thu, 14 Feb 2019 20:47:09 +0900 Fri, 25 Jan 2019 18:20:55 +0900 KubeletHasSufficientDisk kubelet has sufficient disk space available
MemoryPressure False Thu, 14 Feb 2019 20:47:09 +0900 Fri, 25 Jan 2019 18:20:55 +0900 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Thu, 14 Feb 2019 20:47:09 +0900 Fri, 25 Jan 2019 18:20:55 +0900 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Thu, 14 Feb 2019 20:47:09 +0900 Fri, 25 Jan 2019 18:20:55 +0900 KubeletHasSufficientPID kubelet has sufficient PID available
Ready True Thu, 14 Feb 2019 20:47:09 +0900 Fri, 25 Jan 2019 18:21:15 +0900 KubeletReady kubelet is posting ready status. AppArmor enabled
Addresses:
InternalIP: 10.61.43.102
Hostname: search-so-dev52
Capacity:
cpu: 32
ephemeral-storage: 911988260Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 65838004Ki
pods: 110
Allocatable:
cpu: 31900m
ephemeral-storage: 840488379025
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 65485604Ki
pods: 110
System Info:
Machine ID: 80403aab3ecd42139c55821f4807962f
System UUID: 32353537-3835-4753-4837-333153545342
Boot ID: fedfed3d-b8c1-45d7-84a1-6f70119eaa2c
Kernel Version: 4.13.0-45-generic
OS Image: Ubuntu 16.04.5 LTS
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://17.3.2
Kubelet Version: v1.11.5
Kube-Proxy Version: v1.11.5
PodCIDR: 10.230.5.0/24
ExternalID: search-so-dev52
Non-terminated Pods: (7 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
default sampleapp-547946678b-svdzd 250m (0%) 500m (1%) 64Mi (0%) 128Mi (0%)
default stargate-6464cc9647-6w525 250m (0%) 500m (1%) 64Mi (0%) 128Mi (0%)
ingress-nginx default-backend-55d45476bb-tkg82 10m (0%) 10m (0%) 20Mi (0%) 20Mi (0%)
kube-system cilium-ld2kd 100m (0%) 500m (1%) 64M (0%) 500M (0%)
kube-system fluentd-2l7zw 100m (0%) 0 (0%) 200Mi (0%) 200Mi (0%)
kube-system kube-proxy-search-so-dev52 150m (0%) 500m (1%) 64M (0%) 2G (2%)
kube-system nginx-proxy-search-so-dev52 25m (0%) 300m (0%) 32M (0%) 512M (0%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
885m (2%) 2310m (7%) 512602Ki (0%) 3511122176 (5%)
Events: <none>
4. Extended resources
다 읽어보지 않았지만 Device Plugin을 이용하면 GPU나 NIC에 대한 제한도 고려해 볼수 있을듯 하다.