728x90
쿠버네티스 모범 사례을 요약한 내용입니다.
- 쿠버네티스 리소스를 관리하고 최적화하는 모범 사례를 살펴보기
- 워크로드 스케줄링, 클러스터 관리, 파드 리소스 관리, 네임스페이스 관리, 애플리케이션 확장에 대해 논의
- 어피니티(Affinity), 안티어피니티(AntiAffinity), 테인트(Taint), 톨러레이션(Toleration), 노드 셀렉터(NodeSelector)를 이용한 고급 스케줄링 기술을 알아보기
- 리소스 제한과 요청, 파드 서비스 품질, PodDisruptionBuget, LimitRanger, 안티어피니티 정책을 알아보기
8.1 쿠버네티스 스케줄러
- 스케줄러는 컨트롤 플레인에서 호스팅되는 핵심 컴포넌트
- 스케줄러는 파드를 클러스터에 어떻게 배치할지를 결정
- 클러스터와 사용자가 정의한 제약에 따라 리소스를 최적화
- 논리 조건과 우선순위 기반의 스코어 알고리즘 사용
8.1.1 논리 조건
- 스케줄링할 노드를 결정할 때 사용하는 첫 번째 기능은 논리 조건 함수
- 강한 제약을 내포하고 있고 참 또는 거짓을 반환
- ex) 파드가 4GB 메모리를 요청할 때 특정 노드가 요건을 만족하지 못하면 파드 스케줄링 후보에서 제거됨
- 노드가 스케줄링 불가로 설정된 상태라면 후보에서 제거됨
- 아래와 같은 논리 조건들이 있음
8.1.2 우선순위
- 상대적인 값을 기반으로 모든 유효한 노드의 순위를 매김
- 우선순위 점수를 합산하여 최종 우선순위 점수를 노드에 부여
- ex) 파드 하나에 600밀리코어가 필요한 경우, 두 개의 노드중 하나는 900밀리코어, 하나는 1800밀리 코어라면 후자가 더 높은 우선순위를 가짐
- 노드가 같은 우선순위를 반환한다면 스케줄러는 selectHost()함수를 사용하여 라운드 로빈 토너먼트 방식으로 노드를 선정
- 아래와 같은 우선순위 조건들이 있음
8.2 고급 스케줄링 기술
- 쿠버네티스는 스스로 파드 스케줄링을 최적화함
- 넉넉한 리소스를 가진 노드에게만 파드를 배치
- 균등한 리소스 사용률을 유지하면서 레플리카셋의 파드를 여러 노드에 분산시켜 가용성을 높임
- 쿠버네티스의 리소스 스케줄링 방식을 변경할 수 있음
- 영역 실패로 인한 장애를 막기 위해 가용한 영역 간에 파드를 분산 시킬 수 있음
- 여러 파드를 단일 호스트에 배치하여 성능을 향상 시킬 수 있음
8.2.1 파드어피니티와 안티어피니티
- 파드간의 배치 규직을 설정 할 수 있음
- 스케줄링 방식을 변경하거나 스케줄러의 배치 결정을 오버라이드 할 수 있음
- ex) 안티어피니티 규직으로 레플리카셋의 파드를 여러 데이터 센터 영역에 분산 시킬 수 있음
- 파드어피니티 예제 - 동일한 노드에 파드를 스케줄링 할수 있음
- 안티어피니티 예제 - 동일한 노드에 파드를 스케줄링 하지 못하게 할수 있음
- 안티어피니티 규직을 설정하는 예제
- 파드안티어피니티가 설정되어 있으면 스케줄러는 한 노드에 여러 레플리카를 배치하지 않음
8.2.2 노드 셀렉터
- 특정 노드에 파드를 스케줄링하는 가장 간단한 방식
- 키/값 쌍이 있는 레이블 셀렉터를 이용하여 스케줄링을 결정
- ex) GPU같은 특수한 하드웨어를 가진 노드에 파드를 스케줄링할 수 있음
- 노드 셀렉터는 노드 테인트도 함께 사용할 수 있음
- GPU 워크로드 전용 노드를 예약하고 노드 테인트를 사용하여 GPU가 있는 노드를 자동으로 선택할 수 있음
- </aside>
- </aside>
- 노드에 레이블을 생성하고 파드 명세에서 노드 셀렉터를 사용하는 예제
- 노드 셀렉터를 상요하면 disktype=ssd 레이블을 가진 도으에게만 파드를 스케줄링함
kubectl label node <node_name> disktype=ssd
- 파드 명세서
8.2.3 테인트와 톨러레이션
- 테인트는 파드가 스케줄링되는 것을 거절하기 위해 노드에 사용됨
- 안티어피니티와 같지만 다른 방식과 용도로 사용이 됨
- ex) 특정 노드에 특정 성능 요건을 가진 파드만 필요하고 그 외의 다른 파드는 스케줄링 하지 않는 상황
- 테인트는 톨러레이션과 함께 동작
- 두 조합으로 안티어피니티 규칙을 세밀하게 조정할 수 있음
- 일반적으로 다음과 같은 사례에서 테인트와 톨러레이션을 사용
- 특수한 한드웨어를 가진 노드
- 전용 노드 리소스
- 성능이 낮은 노드 회피
- 스케줄링과 실행 중인 컨테이너와 관련해 여러 테인트 타입이 있음
- NoSchedule
- 톨레이션이 일치하지 않는 파드가 스케줄링되는 것을 막는 강한 테인트
- PreferNoSchedule
- 다른 노드에 스케줄링될 수 없는 파드만 스케줄링
- NoExecute
- 노드에 이미 실행 중인 파드를 축출
- NodeCondition
- 특정 조건을 만족시키는 노드를 테인트
- NoSchedule
- [그림 8-1]은 gpu=true:NoSchedule로 테인트된 노드의 예제
- 파드 명세 1은 톨러레이션 키는 gpu이므로, 테인트된 노드의 스케줄 됨
- 파드 명세 2의 롤러레이션 키는 no-gpu이므로 노드에 스케줄되지 않음
8.3 파드 리소스 관리
- 파드 리소스를 절적하게 관리하는 것은 중요함
- CPU와 메모리 사용률을 관리하여 클러스터의 활용도를 최적화 해야함
- 리소스는 컨테이너 수준과 네임스페이스 수준으로 관리 할 수 있음
- 네트워크와 스토리지 리소스도 있지만 쿠버네티스에서는 아직 요청과 한계를 설정할 방법이 없음
- 스케줄러가 리소스를 촤적화하고 지능적으로 배치하려면 애플리케이션의 요구사항을 잘 파악해야함
8.3.1 리소스 요청
- 리소스 요청에는 컨테이너가 스케줄링하기 위해 x크기의 CPU와 메모리를 필요로 한다고 정의
- 만약 8GB를 요청했는데 노드에 7.5GB의 메모리만 존재하면 파드는 스케줄링되지 못함
- 리소스가 가용해질 때까지 대기 상태가 됨
- 클러스터의 가용한 리소스를 보기 위해 kubectl top을 사용
- kubectl top nodes
- 8000Mi의 메모리가 필요한 파드를 스케줄링 해보기
- 가용한 노드가 없기 때문에 대기 상태라 되는 것을 볼수 있음
- kubectl describe pods memory-request
- resources: requests: memory: "8000Mi"
8.3.2 리소스 제한과 파드 서비스 품질
- 리소스 제한으로 파드의 최대 CPU와 메모리 크기를 정의
- 제한에 도달할 때 리소스마다 다른 일이 발생
- CPU: 지정된 제한보다 사용되지 못하게 막힘
- 메모리: 한계에 도달하면 파드가 재시작 됨
- 파드는 동일한 호스트나 다른 호스트에서 재시작 될 수 있음
- 컨테이너에 제한을 지정하는 것이 공정하게 리소스를 분배하기 위한 모벌 사례
- 파드가 생성되면 QoS중 하나가 할당 됨
- 보장
- CPU와 메모리 모두 요청과 제한이 일치
- 폭발
- 제한이 요청보다 높게 할당될 때
- 컨테이너는 요청을 보장 받지만 제한까지만 치솟을 수 있음
- 최선의 노력
- 요청 또는 한계를 설저앟지 않을 경우
- 보장
<aside> 💡 보장 QoS이고 파드에 여러 컨테이너가 존재하는 경우, 컨테이너별로 CPU와 메모리 요청과 제한을 설정 해야함, 모든 컨테이너에 요청과 제한이 설정되지 않으면 보장 QoS가 할당되지 않음
</aside>
8.3.3 PodDisruptionBudget
- 언젠가는 호스트에서 파드가 축출됨, 축출에는 두가지 유형이 있음
- 자발적 중단
- 클러스터의 유지보수, 클러스터 오토스케일러의 할당 해제, 파드템플릿 업데이트 등
- 비자발적 중단
- 하드웨어 장애, 네트워크 분할, 커널 패닉, 리소스 부족 등
- 자발적 중단
- 파드가 축출될 때 미치는 영향을 최소화 하기 위해서는 PodDisruptionBudget을 설정
- 자발적 축출 이벤트 기간에 가용한 최소 파드와 불가용한 최대 파드 정책을 설정 할 수 있음
- ex) 주어진 시간동안 특정 파드의 20%가 다운될 수 없도록 지정 가능
- ex) 항상 가용해야할 레플리카 수 x를 정책에 정의 가능
- 최소 가용
- app:front-end의 최소 가용을 5로 PodDisruptionBudget에 설정
- 항상 5개의 레플리가 파드가 가용하도록 설정
- app:front-end의 최소 가용을 5로 PodDisruptionBudget에 설정
- 최대 불가용
- PodDisruptionBudget에 최대 20%의 레플리카가 불가용
- 자발적 중단 과정에서 최대 20%의 파드를 축출
- PodDisruptionBudget에 최대 20%의 레플리카가 불가용
<aside> 💡 PodDisruptionBudget를 퍼센트로 지정하면 파드 수가 명확히 정해지지 않음, 예를 들어 7개의 파드를 가지고 있고 maxAvailable을 50%로 지정했다면 가장 가까운 정수로 반올림하므로 maxAvailable은 4개의 파드가 됨
</aside>
8.3.4 네임스페이스를 사용한 리소스 관리
- 네임스페이스는 배포된 리소스를 논리적으로 구불할 수 있음
- 리소스쿼터, RBAC, 네트워크 정책을 설정 할 수 있음
- 멀티테넌시(Multitenancy)기능으로 팀이나 애플리케이션에 전용 인프라를 지정하지 않고도 워크로드를 분리할 수 있음
- 네임스페이스를 설정하는 방법을 설계할 때는 특정 애플리케이션에 대한 접근을 제어하는 방법을 고려해야함
- 단일 클러스터를 사용하는 여러 팀이 있을 경우 가장 나은 방법은 팀에 네임스페이스를 할당하는 것
- 클러스터를 한 팀 전용으로 사용한다면 클러스터에 배포할 서비스별로 네임스페이스를 할당
- 팀 조직과 역활에 맞춰 설계해야함
- 쿠버네티스에 기본으로 설정되는 네임스페이스
- kube-system
- coredns, kube-proxy, metrics-server와 같은 쿠버네티스 내부 컴포넌트는 여기에 배포
- default
- 리소스 객체 안에 네임스페이스를 지정하지 않을때 사용되는 기본 네임스페이스
- kube-public
- 익명이나 인증되지 않은 콘텐츠, 예약된 시스템에 사용
- kube-system
- 기본 네임스페이스를 사용하면 리소스 관리할 때 실수하기 쉬움으로 피하는 것이 좋음
- kubectl을 이용해 네임스페이스 관련 작업을 할 때, —namespace 플래그 또는 줄여서 -n을 넣어야함
- kubectl create ns team-1 kubectl get pods --namespace team-1
- 특정 네임스페이스에 kubectl 컨텍스트를 설정하면, 모든 명령에 —namespace 플레그를 추가할 필요가 없어 편리함
- kubectl config set-context my-contex --namespace-team-1
8.3.5 리소스쿼터
- 여러 팀과 단일 클러스터를 공유할 때는 네임스페이스에 리소스쿼터를 설정 해야함
- 단일 네임스페이스가 할당된 리소스 이상을 사용할 수 없도록
- 계산 리소스
- request.cpu: CPU 요청의 합은 이 값을 초과할 수 없음
- limits.cpu: CPU 제한의 합은 이 값을 초과할 수 없음
- request.memory: 메모리 요청의 합은 이 값을 초과할 수 없음
- limit.memory: 메모리 제한의 합은 이 값을 초과할 수 없음
- 스토리지 리소스
- requests.storage: 스토리지 요청의 합은 이 값을 초과할 수 없음
- persistentvolumeclaims: 네임스페이스에 존재할 수 있는 퍼시스턴트볼륨클레임의 합은 이 값을 초과할 수 없음
- storageclass.request: 특정 스토리지클래스와 연관된 볼륨클레임은 이 값을 초과할 수 없음
- storageclass.pvc: 네임스페이스에 존재하는 퍼시스턴트볼륨클레임 합은 이 값을 초과할 수 없음
- 객체 카운트 쿼터의 예
- 카운트/pvc
- 카운트/서비스
- 카운트/디플로이먼트
- 카운트/레플리카셋
- 네임스페이스별로 리소스쿼터를 정교하게 분할할 수 있음
- 멀티테넌트 클러스터에서 리소스 사용을 보다 효율적으로 조절할 수 있음
- 네임스페이스에서 리소스쿼터를 설정하는 YAML 파일
- 리소스쿼터 적용
kubectl apply auota.yaml -n team-1
- 리소스쿼터를 초과하는 디플로이먼트 배포 해보기
- 오류 발생: 2Gi 메모리 쿼터를 초과했기 때문
kubectl run nginx-quotatest --image-nginx --restart=Never --replicas=1 --port=80 --requests='cpu=500m,memory=4Gi' --limits='cpu=500m,memory=4Gi' -n team-1
- apiVersion: v1 kind: ResourceQuota metadata: name: mem-cpu-demo namespace: team-1 spec: hard: requests.cpu: "1" requests.memory: 1Gi limits.cpu: "2" limits.memory: 2Gi persistentvolumeclaims: "5" requests.storage: "10Gi"
8.3.6 LimitRange
- 사용자가 request와 limits을 파드에 설정하지 않을 경우
- 어드미션 컨트롤러를 제공해 설정이 되지 않을 경우 자동 설정을 할 수 있음
- 쿼터와 LimitRange를 작업할 네임스페이스 생성
- kubectl create ns team-1
- LimitRange의 limits안의 defaultRequest를 네임스페이스에 적용
kubectl apply -f limitranger.yaml -n team-1
- apiVersion: v1 kind: LimitRange metadata: name: team-1-limit-range spec: limits: - default: memory: 512Mi defaultRequest: memory: 256Mi type: Container
- LimitRange의 기본 제한과 요청이 적용되었는지 확인
- kubectl run team-1-pod --image=nginx -n team-1
- 파드의 요청과 제한이 설정되었는지 확인
Limits: memory: 512Mi Requests: memory: 256Mi
- kubectl describe pod team-1-pod -n team-1
- 리소스쿼터를 사용할 때는 LimitRange를 사용해야 함
- 요청과 제한이 명세에 설정되지 않으면 배포가 거절됨
8.3.7 클러스터 확장
- 클러스터에 배포하기에 앞서 클러스터 안에서 사용할 인스턴스 크기를 결정 해야함
- 클러스터에서의 좋은 시작점이 무엇일지 파악해야함
- CPU와 메모리의 적절한 균형을 목표로 세우는 것도 하나의 방법
- 인스턴스의 크기를 결정한 후 쿠버네티스의 핵심 기능을 사용하여 확장을 관리 할 수 있음
- 수동 확장
- 클러스터를 수동으로 확장한다는 것은 일반적으로 새로운 노드 수를 결정한다는 뜻
- 서비스는 클러스터에 새로운 노드를 추가함
- 도구로 노드 풀을 생성할 수 있음
- 이미 실행 중인 클러스터에 새로운 인스턴스 유형의 풀을 추가할 수 있음
- 단일 클러스터에 워크로드가 혼재되어 있을 때 매우 유용함
- 노드 풀을 이용하면 단일 클러스터 내에서 여러 인스턴스 유형을 섞을 수 있음
- ex) 많은 CPU를 사용하는 워크로드와 메모리를 많이 사용하는 워크로드를 섞을 수 있음
- 클러스터 자동 확장을 위해서는 고려할 사항이 많음
- 리소스가 필요한 때 사전 예방적으로 수동 확장하는 것부터 시작하는 것이 좋음
- 워크로드의 변동성이 심한 경우라면 클러스터 자동 확장이 유용할 수 있음
- 클러스터를 수동으로 확장한다는 것은 일반적으로 새로운 노드 수를 결정한다는 뜻
- 클러스터 자동확장
- 쿠버네티스는 클러스터의 최소 가용 노드와 최대 가용 노드를 설정할 수 있는 부가 기능인 클러스터 오토스케일러를 제공
- 오토스케일러는 대기 상태의 파드가 존재할 때 확장을 결정 함
- 4000Mib 메모리 요청이 있을 경우 2000Mib만 가용하다면 파드는 대기상태가 됨
- 새로운 노드가 클러스터에 추가되는 즉시 대기중인 파드가 스케줄링 됨
- 오토스케일러의 단점은 파드가 대기 상태가 되어야 새로운 노드가 추가 된다는 점
- 워크로드가 온라인 되려면 결국 새로운 노드를 대기 해야함
- 쿠버네티스 v1.15 이후부터는 오토스케일러가 사용자 정의 메트릭 기반의 확장을 지원하지 않음
- 오토스케일러가 더는 리소스가 필요 없는 클러스터의 크기를 줄일 수 있음
- 노드를 드레인하고 파드를 새로운 노드에 다시 스케줄링
8.3.8 애플리케이션 확장
- 쿠버네티스는 클러스터에서 애플리케이션을 확장할 수 있는 여러 방법을 제공
- 디플로이먼트의 레플리카 수를 수동으로 변경하여 애플리케이션을 확장 할 수 있음
- 레플리카셋이나 복제 컨트롤러를 통해 변경할 수 있지만 권장하진 않음
- 수동 확장은 정적인 워크로드 또는 워크로드가 급증하는 시점을 알고 있을 때 유용
- 예기치 않은 급증이 발생하거나 정적이지 않은 워크로드라면 수동확장은 적합하지 않음
- 자동으로 워크로드를 확장할 수 있은 HPA를 제공
- 디플로이먼트 매니패스트를 적용하여 디플로이먼트를 수동으로 확장하는 방법을 알아보자
- kubectl scale 명령을 이용하면 디플로이먼트의 규모를 확장할 수 있음
- kubectl scale deployment frontend --replicas 5
- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: frontend spec: replicas: 3 template: metadata: name: frontend labels: app: frontend spec: containers: - image: nginx:alpine name: frontend resources: requests: cpu: 100m
8.3.9 HPA를 이용한 확장
- HPA는 CPU, 메모리, 사용자 정의 메트릭 가반으로 디플로이먼트를 확장할 수 있음
- 디플로이먼트를 감시하여 metrics-server로부터 메트릭을 풀
- 가용 파드의 최소 수, 최대 수를 설정
- ex) 최소 수 3, 최대 수 10으로 HPA 정책을 정의하였다면 디플로이먼트가 80% CPU를 사용률에 도달했을 때 확장됨
- 애플리케이션 버그나 이슈 때문에 HPA가 레플리카를 무한정 늘리지 않도록 하려면 최소 수와 최대 수를 설정하는 것이 중요
- HPA는 메트릭 동기화, 레플리카 확장과 축소를 위한 기본 설정을 가짐
- horizontal-pod-autoscaler-sync-period
- 메트릭 동기화 시간은 기본 30초
- horizontal-pod-autoscaler-upscale-delay
- 두 확장 사이의 레이턴시는 기본 3분
- horizontal-pod-autoscaler-downscale-delay
- 두 축소 사이의 레이턴시는 기본 5분
- horizontal-pod-autoscaler-sync-period
- 상대적인 플래그를 사용하여 기본값을 변경할수 있지만 신중해야함
- 워크로드의 변동성이 큰 경우 특수한 사례에 맞게 워크로드를 최적화할 수 있도록 연습 해야함
- 이전 예제에서 배포한 프론트엔드 HPA 정책을 설정 해보기
- 80포트로 디플로이먼트를 노출
- kubectl expose deployment frontend --port 80
- 자동 확장 정책을 설정
- 최소 1에서 최대 10까지 확장할 수 있도록 설정
- CPU부하가 50%에 도달하면 확장 작업 수행
kubectl autoscale deployment frontend --cpu-percent=50 --min=1 --max=10
- 부하를 생성해 확장 되는 것을 확인
- kubectl run -i 00tty load-generator --image=busybox /bin/sh while true; do wget -q -0- <http://frontend.default.svc.cluster.local>; done kubectl get hpa
8.3.10 사용자 정의 메트릭을 사용한 HPA
- 메트릭 서버 API를 이용하면 사용자 정의 메트릭을 사용하여 확장할 수 있음
- 사용자 정의 메트릭 API와 메트릭 애그리케이터는 서드파트 공급자가 플러그인으로 메트릭을 확장할 수 있도록 하며 HPA는 이러한 외부 메트릭을 기반으로 확장 가능
- 사용자 정의 메트릭을 사용하여 자동으로 확장하면 애플리케이션에 특화된 메트릭이나 외부 서비스 메트릭으로 확장 가능
8.3.11 수직 파드 오토스케일러
- VPA는 레플리카를 확장하지 않으므로 HPA와 다름
- VPA는 자동으로 파드 요청을 늘리고 줄이기 때문에 이러한 요청을 수동으로 조절할 필요가 없음
- 아키텍처로 인해 확장할 수 없는 워크로드의 경우 자동으로 리소스를 확장하는데 효과적임
- MySQL를 스테이트리스 프론트엔드와 같은 방식으로 확장할 수 없음
- MySQL의 마스터 노드가 워크로드에 따라 자동으로 확장되도록 설정할 수 있음
- VPA는 HPA보다 더 복잡하며 다음 세개의 컴포넌트를 가짐
- Recommender
- 현재와 과거의 리소스 사용량을 모니터링하고 컨테이너의 CPU와 메모리 요청의 추천값을 제공
- Updater
- 파드가 정확한 리소스를 가지고 있는지 확인하고 그렇지 않으면 종료
- 컨트롤러는 업데이트된 요청으로 컨트롤러를 다시 생성
- Admission Plugin
- 정확한 리소스 요청을 새로운 파드에 요청
- Recommender
- v1.15부터는 운영 배포에서 VPA를 권장하지 않음
8.4 리소스 관리 모범 사례
- 파드안티어피니티를 사용해 워크로드를 여러 가용 영역으로 분산하여 고가용성을 보장
- 테인트를 사용해 특수한 하드웨어가 필요한 워크로드에만 해당 노드에 스케줄링되록 함
- NodeCondition 테인트를 사용하여 노드 실패나 성능저하를 사전에 방지
- 파드 명세에 노드 셀렉터를 적용하여 특수한 하드웨어를 가진 노드에 스케줄링 함
- 운영으로 이동하기 전에 다양한 노드 크기를 실험하여 비용과 성능의 적절한 조합을 찾음
- 다양한 성능 특성을 지닌 워크로드가 혼재되어 있다면 단일 클러스터에 노드 유형이 섞여 있는 노드 풀을 사용
- 클러스터에 배포된 모든 파드에 대해 메모리와 CPU 제한을 설정
- 여러 팀 또는 여러 애플리케이션이 고정한 리소스를 할당 받을 수 있도록 ResourceQuata를 사용
- 제한과 요청이 설정되지 않은 파드 명세에 기본 제한과 요청을 설정하기 위해 LimitRange를 구현
- 워크로드 프로필을 파악하기 전까지는 수동 클러스터 확장부터 시작
- 자동 확장을 사용할 수 있지만 노드 가동 시간과 클러스터 축소에 대한 추가전인 고민이 필요
- 변동성이 있거나 예상치 못한 정점이 있는 워크로드의 경우에는 HPA를 사용
8.5 마치며
- 쿠버네티스에는 안정적이고 사용률이 높고 효과적인 클러스터 상태를 유지하기 위한 많은 기능이 내장 되어 있음
- 클러스터 파드 크기를 설정하는 것은 처음엔 다소 어려울 수 있지만 모니터링하여 리소스를 최적화 하는 방법을 찾을 수 있음
- 쿠버네티스 리소스를 관리하고 최적화하는 모범 사례를 살펴보기
- 워크로드 스케줄링, 클러스터 관리, 파드 리소스 관리, 네임스페이스 관리, 애플리케이션 확장에 대해 논의
- 어피니티(Affinity), 안티어피니티(AntiAffinity), 테인트(Taint), 톨러레이션(Toleration), 노드 셀렉터(NodeSelector)를 이용한 고급 스케줄링 기술을 알아보기
- 리소스 제한과 요청, 파드 서비스 품질, PodDisruptionBuget, LimitRanger, 안티어피니티 정책을 알아보기8.1 쿠버네티스 스케줄러
- 스케줄러는 컨트롤 플레인에서 호스팅되는 핵심 컴포넌트
- 스케줄러는 파드를 클러스터에 어떻게 배치할지를 결정
- 클러스터와 사용자가 정의한 제약에 따라 리소스를 최적화
- 논리 조건과 우선순위 기반의 스코어 알고리즘 사용
- 스케줄링할 노드를 결정할 때 사용하는 첫 번째 기능은 논리 조건 함수
- 강한 제약을 내포하고 있고 참 또는 거짓을 반환
- ex) 파드가 4GB 메모리를 요청할 때 특정 노드가 요건을 만족하지 못하면 파드 스케줄링 후보에서 제거됨
- 노드가 스케줄링 불가로 설정된 상태라면 후보에서 제거됨
- 아래와 같은 논리 조건들이 있음
- 상대적인 값을 기반으로 모든 유효한 노드의 순위를 매김
- 우선순위 점수를 합산하여 최종 우선순위 점수를 노드에 부여
- ex) 파드 하나에 600밀리코어가 필요한 경우, 두 개의 노드중 하나는 900밀리코어, 하나는 1800밀리 코어라면 후자가 더 높은 우선순위를 가짐
- 노드가 같은 우선순위를 반환한다면 스케줄러는 selectHost()함수를 사용하여 라운드 로빈 토너먼트 방식으로 노드를 선정
- 아래와 같은 우선순위 조건들이 있음
- 쿠버네티스는 스스로 파드 스케줄링을 최적화함
- 넉넉한 리소스를 가진 노드에게만 파드를 배치
- 균등한 리소스 사용률을 유지하면서 레플리카셋의 파드를 여러 노드에 분산시켜 가용성을 높임
- 쿠버네티스의 리소스 스케줄링 방식을 변경할 수 있음
- 영역 실패로 인한 장애를 막기 위해 가용한 영역 간에 파드를 분산 시킬 수 있음
- 여러 파드를 단일 호스트에 배치하여 성능을 향상 시킬 수 있음
- 파드간의 배치 규직을 설정 할 수 있음
- 스케줄링 방식을 변경하거나 스케줄러의 배치 결정을 오버라이드 할 수 있음
- ex) 안티어피니티 규직으로 레플리카셋의 파드를 여러 데이터 센터 영역에 분산 시킬 수 있음
- 파드어피니티 예제 - 동일한 노드에 파드를 스케줄링 할수 있음
- 안티어피니티 예제 - 동일한 노드에 파드를 스케줄링 하지 못하게 할수 있음
- 안티어피니티 규직을 설정하는 예제
- 파드안티어피니티가 설정되어 있으면 스케줄러는 한 노드에 여러 레플리카를 배치하지 않음
- 특정 노드에 파드를 스케줄링하는 가장 간단한 방식
- 키/값 쌍이 있는 레이블 셀렉터를 이용하여 스케줄링을 결정
- ex) GPU같은 특수한 하드웨어를 가진 노드에 파드를 스케줄링할 수 있음
- 노드 셀렉터는 노드 테인트도 함께 사용할 수 있음
- GPU 워크로드 전용 노드를 예약하고 노드 테인트를 사용하여 GPU가 있는 노드를 자동으로 선택할 수 있음
- </aside>
- </aside>
- 노드에 레이블을 생성하고 파드 명세에서 노드 셀렉터를 사용하는 예제
- 노드 셀렉터를 상요하면 disktype=ssd 레이블을 가진 도으에게만 파드를 스케줄링함
kubectl label node <node_name> disktype=ssd
- 파드 명세서
- 테인트는 파드가 스케줄링되는 것을 거절하기 위해 노드에 사용됨
- 안티어피니티와 같지만 다른 방식과 용도로 사용이 됨
- ex) 특정 노드에 특정 성능 요건을 가진 파드만 필요하고 그 외의 다른 파드는 스케줄링 하지 않는 상황
- 테인트는 톨러레이션과 함께 동작
- 두 조합으로 안티어피니티 규칙을 세밀하게 조정할 수 있음
- 일반적으로 다음과 같은 사례에서 테인트와 톨러레이션을 사용
- 특수한 한드웨어를 가진 노드
- 전용 노드 리소스
- 성능이 낮은 노드 회피
- 스케줄링과 실행 중인 컨테이너와 관련해 여러 테인트 타입이 있음
- NoSchedule
- 톨레이션이 일치하지 않는 파드가 스케줄링되는 것을 막는 강한 테인트
- PreferNoSchedule
- 다른 노드에 스케줄링될 수 없는 파드만 스케줄링
- NoExecute
- 노드에 이미 실행 중인 파드를 축출
- NodeCondition
- 특정 조건을 만족시키는 노드를 테인트
- NoSchedule
- [그림 8-1]은 gpu=true:NoSchedule로 테인트된 노드의 예제
- 파드 명세 1은 톨러레이션 키는 gpu이므로, 테인트된 노드의 스케줄 됨
- 파드 명세 2의 롤러레이션 키는 no-gpu이므로 노드에 스케줄되지 않음
- 파드 리소스를 절적하게 관리하는 것은 중요함
- CPU와 메모리 사용률을 관리하여 클러스터의 활용도를 최적화 해야함
- 리소스는 컨테이너 수준과 네임스페이스 수준으로 관리 할 수 있음
- 네트워크와 스토리지 리소스도 있지만 쿠버네티스에서는 아직 요청과 한계를 설정할 방법이 없음
- 스케줄러가 리소스를 촤적화하고 지능적으로 배치하려면 애플리케이션의 요구사항을 잘 파악해야함
- 리소스 요청에는 컨테이너가 스케줄링하기 위해 x크기의 CPU와 메모리를 필요로 한다고 정의
- 만약 8GB를 요청했는데 노드에 7.5GB의 메모리만 존재하면 파드는 스케줄링되지 못함
- 리소스가 가용해질 때까지 대기 상태가 됨
- 클러스터의 가용한 리소스를 보기 위해 kubectl top을 사용
- kubectl top nodes
- 8000Mi의 메모리가 필요한 파드를 스케줄링 해보기
- 가용한 노드가 없기 때문에 대기 상태라 되는 것을 볼수 있음
- kubectl describe pods memory-request
- resources: requests: memory: "8000Mi"
- 리소스 제한으로 파드의 최대 CPU와 메모리 크기를 정의
- 제한에 도달할 때 리소스마다 다른 일이 발생
- CPU: 지정된 제한보다 사용되지 못하게 막힘
- 메모리: 한계에 도달하면 파드가 재시작 됨
- 파드는 동일한 호스트나 다른 호스트에서 재시작 될 수 있음
- 컨테이너에 제한을 지정하는 것이 공정하게 리소스를 분배하기 위한 모벌 사례
- 파드가 생성되면 QoS중 하나가 할당 됨
- 보장
- CPU와 메모리 모두 요청과 제한이 일치
- 폭발
- 제한이 요청보다 높게 할당될 때
- 컨테이너는 요청을 보장 받지만 제한까지만 치솟을 수 있음
- 최선의 노력
- 요청 또는 한계를 설저앟지 않을 경우
- 보장
- 언젠가는 호스트에서 파드가 축출됨, 축출에는 두가지 유형이 있음
- 자발적 중단
- 클러스터의 유지보수, 클러스터 오토스케일러의 할당 해제, 파드템플릿 업데이트 등
- 비자발적 중단
- 하드웨어 장애, 네트워크 분할, 커널 패닉, 리소스 부족 등
- 자발적 중단
- 파드가 축출될 때 미치는 영향을 최소화 하기 위해서는 PodDisruptionBudget을 설정
- 자발적 축출 이벤트 기간에 가용한 최소 파드와 불가용한 최대 파드 정책을 설정 할 수 있음
- ex) 주어진 시간동안 특정 파드의 20%가 다운될 수 없도록 지정 가능
- ex) 항상 가용해야할 레플리카 수 x를 정책에 정의 가능
- 최소 가용
- app:front-end의 최소 가용을 5로 PodDisruptionBudget에 설정
- 항상 5개의 레플리가 파드가 가용하도록 설정
- app:front-end의 최소 가용을 5로 PodDisruptionBudget에 설정
- 최대 불가용
- PodDisruptionBudget에 최대 20%의 레플리카가 불가용
- 자발적 중단 과정에서 최대 20%의 파드를 축출
- PodDisruptionBudget에 최대 20%의 레플리카가 불가용
- 네임스페이스는 배포된 리소스를 논리적으로 구불할 수 있음
- 리소스쿼터, RBAC, 네트워크 정책을 설정 할 수 있음
- 멀티테넌시(Multitenancy)기능으로 팀이나 애플리케이션에 전용 인프라를 지정하지 않고도 워크로드를 분리할 수 있음
- 네임스페이스를 설정하는 방법을 설계할 때는 특정 애플리케이션에 대한 접근을 제어하는 방법을 고려해야함
- 단일 클러스터를 사용하는 여러 팀이 있을 경우 가장 나은 방법은 팀에 네임스페이스를 할당하는 것
- 클러스터를 한 팀 전용으로 사용한다면 클러스터에 배포할 서비스별로 네임스페이스를 할당
- 팀 조직과 역활에 맞춰 설계해야함
- 쿠버네티스에 기본으로 설정되는 네임스페이스
- kube-system
- coredns, kube-proxy, metrics-server와 같은 쿠버네티스 내부 컴포넌트는 여기에 배포
- default
- 리소스 객체 안에 네임스페이스를 지정하지 않을때 사용되는 기본 네임스페이스
- kube-public
- 익명이나 인증되지 않은 콘텐츠, 예약된 시스템에 사용
- kube-system
- 기본 네임스페이스를 사용하면 리소스 관리할 때 실수하기 쉬움으로 피하는 것이 좋음
- kubectl을 이용해 네임스페이스 관련 작업을 할 때, —namespace 플래그 또는 줄여서 -n을 넣어야함
- kubectl create ns team-1 kubectl get pods --namespace team-1
- 특정 네임스페이스에 kubectl 컨텍스트를 설정하면, 모든 명령에 —namespace 플레그를 추가할 필요가 없어 편리함
- kubectl config set-context my-contex --namespace-team-1
- 여러 팀과 단일 클러스터를 공유할 때는 네임스페이스에 리소스쿼터를 설정 해야함
- 단일 네임스페이스가 할당된 리소스 이상을 사용할 수 없도록
- 계산 리소스
- request.cpu: CPU 요청의 합은 이 값을 초과할 수 없음
- limits.cpu: CPU 제한의 합은 이 값을 초과할 수 없음
- request.memory: 메모리 요청의 합은 이 값을 초과할 수 없음
- limit.memory: 메모리 제한의 합은 이 값을 초과할 수 없음
- 스토리지 리소스
- requests.storage: 스토리지 요청의 합은 이 값을 초과할 수 없음
- persistentvolumeclaims: 네임스페이스에 존재할 수 있는 퍼시스턴트볼륨클레임의 합은 이 값을 초과할 수 없음
- storageclass.request: 특정 스토리지클래스와 연관된 볼륨클레임은 이 값을 초과할 수 없음
- storageclass.pvc: 네임스페이스에 존재하는 퍼시스턴트볼륨클레임 합은 이 값을 초과할 수 없음
- 객체 카운트 쿼터의 예
- 카운트/pvc
- 카운트/서비스
- 카운트/디플로이먼트
- 카운트/레플리카셋
- 네임스페이스별로 리소스쿼터를 정교하게 분할할 수 있음
- 멀티테넌트 클러스터에서 리소스 사용을 보다 효율적으로 조절할 수 있음
- 네임스페이스에서 리소스쿼터를 설정하는 YAML 파일
- 리소스쿼터 적용
kubectl apply auota.yaml -n team-1
- 리소스쿼터를 초과하는 디플로이먼트 배포 해보기
- 오류 발생: 2Gi 메모리 쿼터를 초과했기 때문
kubectl run nginx-quotatest --image-nginx --restart=Never --replicas=1 --port=80 --requests='cpu=500m,memory=4Gi' --limits='cpu=500m,memory=4Gi' -n team-1
- apiVersion: v1 kind: ResourceQuota metadata: name: mem-cpu-demo namespace: team-1 spec: hard: requests.cpu: "1" requests.memory: 1Gi limits.cpu: "2" limits.memory: 2Gi persistentvolumeclaims: "5" requests.storage: "10Gi"
- 사용자가 request와 limits을 파드에 설정하지 않을 경우
- 어드미션 컨트롤러를 제공해 설정이 되지 않을 경우 자동 설정을 할 수 있음
- 쿼터와 LimitRange를 작업할 네임스페이스 생성
- kubectl create ns team-1
- LimitRange의 limits안의 defaultRequest를 네임스페이스에 적용
kubectl apply -f limitranger.yaml -n team-1
- apiVersion: v1 kind: LimitRange metadata: name: team-1-limit-range spec: limits: - default: memory: 512Mi defaultRequest: memory: 256Mi type: Container
- LimitRange의 기본 제한과 요청이 적용되었는지 확인
- kubectl run team-1-pod --image=nginx -n team-1
- 파드의 요청과 제한이 설정되었는지 확인
Limits: memory: 512Mi Requests: memory: 256Mi
- kubectl describe pod team-1-pod -n team-1
- 리소스쿼터를 사용할 때는 LimitRange를 사용해야 함
- 요청과 제한이 명세에 설정되지 않으면 배포가 거절됨
- 클러스터에 배포하기에 앞서 클러스터 안에서 사용할 인스턴스 크기를 결정 해야함
- 클러스터에서의 좋은 시작점이 무엇일지 파악해야함
- CPU와 메모리의 적절한 균형을 목표로 세우는 것도 하나의 방법
- 인스턴스의 크기를 결정한 후 쿠버네티스의 핵심 기능을 사용하여 확장을 관리 할 수 있음
- 수동 확장
- 클러스터를 수동으로 확장한다는 것은 일반적으로 새로운 노드 수를 결정한다는 뜻
- 서비스는 클러스터에 새로운 노드를 추가함
- 도구로 노드 풀을 생성할 수 있음
- 이미 실행 중인 클러스터에 새로운 인스턴스 유형의 풀을 추가할 수 있음
- 단일 클러스터에 워크로드가 혼재되어 있을 때 매우 유용함
- 노드 풀을 이용하면 단일 클러스터 내에서 여러 인스턴스 유형을 섞을 수 있음
- ex) 많은 CPU를 사용하는 워크로드와 메모리를 많이 사용하는 워크로드를 섞을 수 있음
- 클러스터 자동 확장을 위해서는 고려할 사항이 많음
- 리소스가 필요한 때 사전 예방적으로 수동 확장하는 것부터 시작하는 것이 좋음
- 워크로드의 변동성이 심한 경우라면 클러스터 자동 확장이 유용할 수 있음
- 클러스터를 수동으로 확장한다는 것은 일반적으로 새로운 노드 수를 결정한다는 뜻
- 클러스터 자동확장
- 쿠버네티스는 클러스터의 최소 가용 노드와 최대 가용 노드를 설정할 수 있는 부가 기능인 클러스터 오토스케일러를 제공
- 오토스케일러는 대기 상태의 파드가 존재할 때 확장을 결정 함
- 4000Mib 메모리 요청이 있을 경우 2000Mib만 가용하다면 파드는 대기상태가 됨
- 새로운 노드가 클러스터에 추가되는 즉시 대기중인 파드가 스케줄링 됨
- 오토스케일러의 단점은 파드가 대기 상태가 되어야 새로운 노드가 추가 된다는 점
- 워크로드가 온라인 되려면 결국 새로운 노드를 대기 해야함
- 쿠버네티스 v1.15 이후부터는 오토스케일러가 사용자 정의 메트릭 기반의 확장을 지원하지 않음
- 오토스케일러가 더는 리소스가 필요 없는 클러스터의 크기를 줄일 수 있음
- 노드를 드레인하고 파드를 새로운 노드에 다시 스케줄링
- 쿠버네티스는 클러스터에서 애플리케이션을 확장할 수 있는 여러 방법을 제공
- 디플로이먼트의 레플리카 수를 수동으로 변경하여 애플리케이션을 확장 할 수 있음
- 레플리카셋이나 복제 컨트롤러를 통해 변경할 수 있지만 권장하진 않음
- 수동 확장은 정적인 워크로드 또는 워크로드가 급증하는 시점을 알고 있을 때 유용
- 예기치 않은 급증이 발생하거나 정적이지 않은 워크로드라면 수동확장은 적합하지 않음
- 자동으로 워크로드를 확장할 수 있은 HPA를 제공
- 디플로이먼트 매니패스트를 적용하여 디플로이먼트를 수동으로 확장하는 방법을 알아보자
- kubectl scale 명령을 이용하면 디플로이먼트의 규모를 확장할 수 있음
- kubectl scale deployment frontend --replicas 5
- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: frontend spec: replicas: 3 template: metadata: name: frontend labels: app: frontend spec: containers: - image: nginx:alpine name: frontend resources: requests: cpu: 100m
- HPA는 CPU, 메모리, 사용자 정의 메트릭 가반으로 디플로이먼트를 확장할 수 있음
- 디플로이먼트를 감시하여 metrics-server로부터 메트릭을 풀
- 가용 파드의 최소 수, 최대 수를 설정
- ex) 최소 수 3, 최대 수 10으로 HPA 정책을 정의하였다면 디플로이먼트가 80% CPU를 사용률에 도달했을 때 확장됨
- 애플리케이션 버그나 이슈 때문에 HPA가 레플리카를 무한정 늘리지 않도록 하려면 최소 수와 최대 수를 설정하는 것이 중요
- HPA는 메트릭 동기화, 레플리카 확장과 축소를 위한 기본 설정을 가짐
- horizontal-pod-autoscaler-sync-period
- 메트릭 동기화 시간은 기본 30초
- horizontal-pod-autoscaler-upscale-delay
- 두 확장 사이의 레이턴시는 기본 3분
- horizontal-pod-autoscaler-downscale-delay
- 두 축소 사이의 레이턴시는 기본 5분
- horizontal-pod-autoscaler-sync-period
- 상대적인 플래그를 사용하여 기본값을 변경할수 있지만 신중해야함
- 워크로드의 변동성이 큰 경우 특수한 사례에 맞게 워크로드를 최적화할 수 있도록 연습 해야함
- 이전 예제에서 배포한 프론트엔드 HPA 정책을 설정 해보기
- 80포트로 디플로이먼트를 노출
- kubectl expose deployment frontend --port 80
- 자동 확장 정책을 설정
- 최소 1에서 최대 10까지 확장할 수 있도록 설정
- CPU부하가 50%에 도달하면 확장 작업 수행
kubectl autoscale deployment frontend --cpu-percent=50 --min=1 --max=10
- 부하를 생성해 확장 되는 것을 확인
- kubectl run -i 00tty load-generator --image=busybox /bin/sh while true; do wget -q -0- <http://frontend.default.svc.cluster.local>; done kubectl get hpa
- 메트릭 서버 API를 이용하면 사용자 정의 메트릭을 사용하여 확장할 수 있음
- 사용자 정의 메트릭 API와 메트릭 애그리케이터는 서드파트 공급자가 플러그인으로 메트릭을 확장할 수 있도록 하며 HPA는 이러한 외부 메트릭을 기반으로 확장 가능
- 사용자 정의 메트릭을 사용하여 자동으로 확장하면 애플리케이션에 특화된 메트릭이나 외부 서비스 메트릭으로 확장 가능
- VPA는 레플리카를 확장하지 않으므로 HPA와 다름
- VPA는 자동으로 파드 요청을 늘리고 줄이기 때문에 이러한 요청을 수동으로 조절할 필요가 없음
- 아키텍처로 인해 확장할 수 없는 워크로드의 경우 자동으로 리소스를 확장하는데 효과적임
- MySQL를 스테이트리스 프론트엔드와 같은 방식으로 확장할 수 없음
- MySQL의 마스터 노드가 워크로드에 따라 자동으로 확장되도록 설정할 수 있음
- VPA는 HPA보다 더 복잡하며 다음 세개의 컴포넌트를 가짐
- Recommender
- 현재와 과거의 리소스 사용량을 모니터링하고 컨테이너의 CPU와 메모리 요청의 추천값을 제공
- Updater
- 파드가 정확한 리소스를 가지고 있는지 확인하고 그렇지 않으면 종료
- 컨트롤러는 업데이트된 요청으로 컨트롤러를 다시 생성
- Admission Plugin
- 정확한 리소스 요청을 새로운 파드에 요청
- Recommender
- v1.15부터는 운영 배포에서 VPA를 권장하지 않음
- 파드안티어피니티를 사용해 워크로드를 여러 가용 영역으로 분산하여 고가용성을 보장
- 테인트를 사용해 특수한 하드웨어가 필요한 워크로드에만 해당 노드에 스케줄링되록 함
- NodeCondition 테인트를 사용하여 노드 실패나 성능저하를 사전에 방지
- 파드 명세에 노드 셀렉터를 적용하여 특수한 하드웨어를 가진 노드에 스케줄링 함
- 운영으로 이동하기 전에 다양한 노드 크기를 실험하여 비용과 성능의 적절한 조합을 찾음
- 다양한 성능 특성을 지닌 워크로드가 혼재되어 있다면 단일 클러스터에 노드 유형이 섞여 있는 노드 풀을 사용
- 클러스터에 배포된 모든 파드에 대해 메모리와 CPU 제한을 설정
- 여러 팀 또는 여러 애플리케이션이 고정한 리소스를 할당 받을 수 있도록 ResourceQuata를 사용
- 제한과 요청이 설정되지 않은 파드 명세에 기본 제한과 요청을 설정하기 위해 LimitRange를 구현
- 워크로드 프로필을 파악하기 전까지는 수동 클러스터 확장부터 시작
- 자동 확장을 사용할 수 있지만 노드 가동 시간과 클러스터 축소에 대한 추가전인 고민이 필요
- 변동성이 있거나 예상치 못한 정점이 있는 워크로드의 경우에는 HPA를 사용
- 쿠버네티스에는 안정적이고 사용률이 높고 효과적인 클러스터 상태를 유지하기 위한 많은 기능이 내장 되어 있음
- 클러스터 파드 크기를 설정하는 것은 처음엔 다소 어려울 수 있지만 모니터링하여 리소스를 최적화 하는 방법을 찾을 수 있음
728x90
'Kubernetes > 쿠버네티스 모범 사례 스터디' 카테고리의 다른 글
파드와 컨테이너 보안 (1) | 2023.11.14 |
---|---|
네트워킹, 네트워크 보안, 서비스 메시 (1) | 2023.11.14 |
글로벌 애플리케이션 분산과 스테이지 (1) | 2023.11.14 |
버전, 릴리스, 롤아웃 (1) | 2023.11.14 |
지속적 통합, 테스트, 배포 (1) | 2023.11.14 |