kubectl describe pod 파드이름
# 할당된 리소스를 확인할 수 있다.
kubectl top pods
# 현재 파드가 사용하고 있는 cpu,memory양을 알 수 있다.
kubectl top nodes
# 현재 노드가 사용하고 있는 cpu,memory양을 알 수 있다.
kubectl describe node kube-node1
# 노드에 할당된 리소스를 확인할 수 있다
kubectl exec 파드이름 -- sha256sum /dev/zero
# 해당 파드에 강제로 리소스의 부하를 준다
# 쓰레기 값을 계속 넣어서 용량을 계속해서 잡아먹는다
1 . 리소스 제한 예시
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests: # 컨테이너가 할당 받을 수 있는 최소 리소스
memory: "64Mi"
cpu: "250m"
limits: # 컨테이너가 할당 받을 수 있는 최대 리소스
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
2 . limit만 설정한 예시
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod-lim
spec:
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
resources:
limits:
cpu: 0.5
memory: 20Mi
Pod Quality of Service Classes
kubectl describe pod 파드이름
# 파드의 QOS의 클래스를 확인할 수 있다
1 . BestEffort
2 . Burstable
3 . Guaranteed
kubelet이 리소스 부족으로 압박을 받기 시작하면 가장 먼저 종료하는 것은 BestEffort이고 그 다음은 Burstable을 종료시킨다.
제한된 리소스에 따라 종료시킬 파드의 우선순위를 고려해서 파드를 생성해야 한다.
kubectl get limits
# 생성한 리밋레인지 보기
kubectl describe limits 리밋레인지 이름
# 리밋레인지에 설정된 값 보기
1 . 리밋 레인지 예시
apiVersion: v1
kind: LimitRange
metadata:
name: myapp-limitrange
spec:
limits:
- type: Pod # 파드 단위로 제한
min:
cpu: 50m
memory: 5Mi
max:
cpu: 1
memory: 1Gi
- type: Container # 컨테이너 단위로 제한
defaultRequest: # 기본 request
cpu: 100m
memory: 10Mi
default: # 기본 limit
cpu: 200m
memory: 100Mi
min:
cpu: 50m
memory: 5Mi
max:
cpu: 1
memory: 1Gi
maxLimitRequestRatio:
cpu: 4
memory: 10
- type: PersistentVolumeClaim # 볼륨 단위로 제한
min:
storage: 10Mi
max:
storage: 1Gi
-> 리밋레인지에 리소스의 범위를 설정하고 해당 리소스를 생성하게되면 해당 범위에서만 생성이 가능하게 한다.
-> 범위를 넘어가는 리소스는 생성되지 않는다.
-> 컨테이너의 default 섹션은 만약 컨테이너를 생성할때 limit과 request를 설정하지 않으면 자동으로 해당 default의 값으로 limit과 request를 설정한다.
kubectl get quota # 생성한 쿼터 보기
1 . request와 limit을 지정한 쿼터
apiVersion: v1
kind: ResourceQuota
metadata:
name: myapp-quota-cpumem
spec:
hard:
requests.cpu: 500m
requests.memory: 200Mi
limits.cpu: 1000m
limits.memory: 1Gi
2 . 생성가능한 리소스의 개수를 제한한 쿼터
apiVersion: v1
kind: ResourceQuota
metadata:
name: myapp-quota-object
spec:
hard:
pods: 10
replicationcontrollers: 2
secrets: 10
configmaps: 10
persistentvolumeclaims: 5
services: 5
services.loadbalancers: 1
services.nodeports: 2
nfs-client.storageclass.storage.k8s.io/persistentvolumeclaims: 2
-> Deployment/RC/Sts등의 리소스를 통해 파드를 생성할 때 Replicas를 지정한다.
-> HPA가 이 Replicas의 수를 자동으로 조정해주는 구조이다.
-> HPA가 Replicas의 수를 조정하는 알고리즘
📒 HPA개념 참조
kubectl get hpa
# 생성한 hpa확인
1 . HPA사용 예시
apiVersion: autoscaling/v2 # 버전 유의
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa-cpu
spec:
scaleTargetRef: # replica수를 조정할 대상 설정
apiVersion: apps/v1
kind: Deployment
name: myapp-deploy-hpa
minReplicas: 2 # replica 최소값
maxReplicas: 10 # replica 최대값
metrics: # replica수를 조정할 때 참조할 지표 설정
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 70퍼센트를 기준으로 스케일링
1-1 . 디플로이먼트 생성
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-hpa
spec:
replicas: 3
selector:
matchLabels:
app: myapp-deploy-hpa
template:
metadata:
labels:
app: myapp-deploy-hpa
spec:
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
resources:
requests: # metrics로 참조할 cpu사용률
cpu: 50m
memory: 5Mi
limits:
cpu: 100m
memory: 20Mi
ports:
- containerPort: 8080
tmux # 여러 터미널을 띄우기 위해 tmux를 킨다
ctrl + b + " # 창을 세개로 분할한다
watch -n1 -d kubectl get hpa # hpa의 정보를 실시간으로 본다
watch -n1 -d kubectl top pods # 파드의 cpu사용률을 실시간으로 본다
kubectl exec 파드1 이름 -- sha256sum /dev/zero # 하나의 파드에 임의로 cpu용량 부하를 준다
-> 만약 임의로 한 파드에 용량의 부하를 줬을 경우 파드는 최대 limit값이 100까지 cpu를 사용할 것이고, 이것은 100/150 = 66%
이기 때문에 70%를 도달하지못해 scailing을 하지 않는다.
kubectl exec 파드2 이름 -- sha256sum /dev/zero
# 추가로 하나의 파드에 임의로 cpu용량 부하를 준다
-> 만약 임의로 또 한 파드에 용량의 부하를 줬을 경우 3개의 파드 총 cpu사용률을 200이 될것이고, 200/150 = 133%이므로 replica의 수를 증가시킨다.
만약 용량 부하가 걸린 파드를 삭제해서 cpu사용률을 낮추게 되면
autoscailing에 의해 파드의 개수를 최소개수까지 낮출것이다.
하지만 파드의 개수가 바로 변화되지 않는것을 알 수 있다.
이유는 바로 stabilizationWindowSeconds 때문이다.
파드를 노드에 배치하는 방법
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-nn
spec:
replicas: 2
selector:
matchLabels:
app: myapp-rs-nn
template:
metadata:
labels:
app: myapp-rs-nn
spec:
nodeName: kube-node1 # 특정 노드 지정
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 생성된 파드는 지정한 node1에만 배치된다
-> nodename은 특정 노드만 지정해서 사용해야하기 때문에 유연하지 않다. 따라서 보통 사용하지 않고 nodeselector를 사용한다.
kubectl label node kube-node1 gpu=lowend
kubectl label node kube-node2 gpu=highend
kubectl label node kube-node3 gpu=highend
# 각 노드에 레이블 설정
kubectl get nodes -L gpu
# 설정한 gpu확인
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-ns
spec:
replicas: 2
selector:
matchLabels:
app: myapp-rs-ns
template:
metadata:
labels:
app: myapp-rs-ns
spec:
nodeSelector: # label을 선택한다.
gpu: highend # 해당 레이블을 가진 노드들을 선택
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 노드 셀렉터에 의해 gpu: highend 레이블을 가진 node2,node3에만 파드가 배치된다.
-> nodename과 달리 노드이름을 지정하는 것이 아니라, 레이블을 통해 노드를 선택하기 때문에 유연하게 여러 노드를 선택할 수 있다.
kubectl label node kube-node1 gpu-model=3080
kubectl label node kube-node2 gpu-model=2080
kubectl label node kube-node3 gpu-model=1660
# 노드에 레이블 설정
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-nodeaff
spec:
replicas: 2
selector:
matchLabels:
app: myapp-rs-nodeaff
template:
metadata:
labels:
app: myapp-rs-nodeaff
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 요청(hard)
nodeSelectorTerms: # 노드의 레이블 선택, 반드시 하나를 선택해야하기 때문에 가중치가 없다
- matchExpressions:
- key: gpu-model
operator: In
values:
- '3080'
- '2080'
preferredDuringSchedulingIgnoredDuringExecution: # 요청(soft)
- weight: 10 # 선호하는 정도, 여러개의 선호가 있다면 가중치로 순위를 매김
preference:
matchExpressions: # 노드의 레이블 선택
- key: gpu-model
operator: In
values:
- titan
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
- 요청(soft) : 특정 노드를 선호하지만 되면 배치되고 아니여도 상관 x
- 요청(hard) : 특정 노드에 반드시 배치되어야 한다.
-> 요청한 노드가 더이상 파드를 받을 수 없거나, 해당 레이블을 가진 노드가 없다면 파드는 생성되지 않는다.
1 . cache 파드 생성
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-aff-cache
spec:
replicas: 2
selector:
matchLabels:
app: myapp-rs-aff-cache
tier: cache
template:
metadata:
labels:
app: myapp-rs-aff-cache
tier: cache # 파드에 레이블 설정
spec:
affinity:
podAntiAffinity: # 파드가 서로 배척한다
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: tier
operator: In
values:
- cache
topologyKey: "kubernetes.io/hostname"
# 노드의 hostname으로 구역을 나눈다
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
1-1 . front 파드 생성
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-aff-front
spec:
replicas: 2
selector:
matchLabels:
app: myapp-rs-aff-front
tier: frontend
template:
metadata:
labels:
app: myapp-rs-aff-front
tier: frontend # 파드에 레이블 설정
spec:
affinity:
podAntiAffinity: # 파드가 서로를 배척한다
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: tier
operator: In
values:
- frontend
topologyKey: "kubernetes.io/hostname"
podAffinity: # 파드가 붙어서 노드에 배치된다
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: tier
operator: In
values:
- cache
topologyKey: "kubernetes.io/hostname"
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 하나의 노드에 cache파드와 front파드가 한 쌍으로 묶여서 배치된다. 다른 노드에도 마찬가지로 한 쌍으로 묶여서 배치된다.
kubectl taint node kube-node1 env=production:NoSchedule
# taint 생성
kubectl taint node kube-node1 env-
# taint 해제
kubectl get nodes kube-control1 -o jsonpath='{.spec.taints}'
# 해당 노드의 taint 보기
1 . 위의 podAffinity / podAntiAffinity실습의 연장선이다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs-tol
spec:
replicas: 1
selector:
matchLabels:
app: myapp-rs-tol
tier: backend
template:
metadata:
labels:
app: myapp-rs-tol
tier: backend
spec:
affinity:
podAntiAffinity: # cache 레이블을 가진 파드를 배척
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: tier
operator: In
values:
- cache
topologyKey: "kubernetes.io/hostname"
tolerations: # 톨러레이션 부여
- key: env
operator: Equal
value: production
effect: NoSchedule
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 위의 podAffinity / podAntiAffinity실습에 의해서 node2,node3에 각각 cache,front 레이블을 가진 한쌍을 배치했었다.
-> podAntiAffinity설정에 의해 cache레이블을 가진 파드를 배척하기 때문에 node2,3에는 배치될 수 없다.
-> 따라서 node1에만 배치될 수 있는데 node1에 taint를 설정했기 때문에 taint가 가진 정책에 부합하는 tolerations를 가져야 된다.
-> taint정책에 부합하는 key,value를 가진 tolerations를 부여했기 때문에 node1에 배치되는 것을 볼 수 있다.
kubectl cordon kube-node3
-> 해당 노드에 스케줄링되는 것을 막는다.
kubectl drain kube-node3
kubectl drain kube-node3 --ignore-daemonsets
-> 해당 노드에 있는 모든 파드들을 퇴거(evict) 시킨다.
-> 하지만 데몬셋이 관리하는 파드들은 복제본 컨트롤러가 아니기 때문에 퇴거시키지 못한다. 따라서 --ignore옵션을 사용한다.
-> 다른 복제본 컨트롤러가 관리하는 파드들은 지워져도 다른 노드에 자동으로 생성되기 때문에 문제가 안된다 .
kubectl uncordon kube-node3 # 커든 해제
💡 drain을 하게 되면 자동으로 커든이 되기때문에 작업이 끝나고나면 커든을 해제해야 한다.