새소식

Recent Study/Kubernetes 학습과정

12. QoS classes (Guaranteed, Burstable, BestEffort) & Node Scheduling

  • -

QoS class

Qos classes

 위 Qos는 노드에 이렇게 리소스가 있다고 가정하고, 이 노드 위에 Pod가 3개 만들어져서 자원을 모두 사용하고 있는 상태라고 가정합니다. 이 상태에서 Pod1이 추가적으로 자원이 필요할 때 노드에는 추가적으로 할당받을 수 있는 자원이 없기 때문에, 선택을 해야합니다. k8s는 앱의 중요도에 따라 이런 것을 관리할 수 있도록 3가지 단계로 Quality of Service를 지원해주고 있습니다.

 

현재 위 상태에서는 1. Best Effort가 부여된 Pod가 가장 먼저 다운이 돼서 자원이 반환됩니다. Pod1은 필요한 자원을 추가로 얻게 됩니다. 다음엔 어느 정도 노드에 자원이 남아있지만, Pod2에서 그보다 많은 자원을 요구하는 상황이 됐다면, 2. Burstable이 적용된 Pod가 다음으로 다운이 되면서 자원이 회수됩니다. 그래서 3. Guaranteed가 마지막까지 Pod를 안정적으로 유지시켜줍니다.

 

 이제 Pod에 어떻게 이런 클래스들을 보유할 수 있는지 알아보면 QoS는 특성 속성이 있어서 설정하는 것은 아니고, 컨테이너에 리소스 설정이 있는데 여기에 Request, Limit에 따라서 메모리와 CPU를 어떻게 설정하는지로 결정이 됩니다.

 

 Guaranteed는 Pod에 여러 컨테이너가 있다면 이 모든 컨테이너마다 Request, Limit가 있어야 합니다. 그리고 그 안에 메모리와 CPU 둘다 설정이 되어 있어야 하고, 각 컨테이너 안에 설정된 메모리와 CPU 값은 Request와 Limit가 같아야 합니다. 이 규칙에 모두 맞아야 k8s가 이 Pod를 Guaranteed 클래스로 판단하는 것 입니다. 그리고 Pod의 어떤 컨테이너에도 Request와 Limit가 설정되어 있지 않다면 BestEffort가 됩니다. 

 

 Burstable은 BestEffort와 Guaranteed 중간이라고 보면 되는데 가령 컨테이너마다 Requet, Limit는 설정되어 있지만 Request 수치가 더 작다던가 아니면 Request만 설정되어 있고, Limit는 설정되어 있지 않는 경우 또는 Pod에 컨테이너가 2개인데 하나는 완벽하게 설정이 되어 있지만 다른 하나가 설정되어 있지 않다면 모두 Burstable 설정이 됩니다.

 

 이렇게 3가지 클래스는 컨테이너의 Request, Limit를 어떻게 설정하느냐에 따라서 결정이 됩니다. 다음엔 Burstable 등급에 이렇게 여러 Pod들이 있을 경우 누가 먼저 삭제가 되는지도 알 필요가 있는데 그것은 OOM 스코어라고, Out of memory score라는 것에 따라서 결정이 됩니다. 현재 2개 Pod의 Request, 메모리가 5, 8GB로 설정된 상태인데 그 안에 돌아가고 있는 앱에 실제 메모리가 똑같이 4GB만큼 사용되고 있다고 가정하면, Pod2 메모리 사용량은 75%, Pod3 메모리 사용량은 50% 입니다. 그럼 OOM Score가 Pod2가 75%로 더 크기 때문에 k8s는 OOM score가 더 높은 Pod2를 먼저 제거하게 됩니다.

 

 

Node Scheduling

 Pod가 기본적으로 스케줄러에 의해 노드에 할당되지만, 사용자의 의도에 의해 내가 노드를 지정할 수 있고, 운영자가 특정 노드를 사용하지 못하게 설정할 수 있습니다. k8s는 이를 다양한 기능으로 지원하고 있고, 어떤 상황에서 사용되는 기능들에 대해 설명하겠습니다.

NodeName, NodeSelector, NodeAffinity

 

 NodeName, NodeSelector, NodeAffinity는 Pod를 특정 노드에 할당되도록 선택하기 위한 용도로 사용됩니다. 위와 같이 노드들이 있고, 노드들마다 가용한 Cpu 자원들이 이렇게 있다고 가정을 합니다. 그리고 라벨도 달려 있는데 노드1~3까지 서버가 한국에 있어서 이렇게 라벨을 달아서 그룹핑을 하고 4,5는 미국에 있도록 설정했습니다. 그리고 이상태에서 Pod를 하나 만들면 k8s 스케줄러는 Cpu 자원이 가장 많은 노드에 이 Pod를 할당시킵니다.

 

 내가 원하는 노드를 선택하고 싶을 때 첫째로 NodeName을 사용하면 스케줄러와는 상관없이 바로 해당 노드의 이름으로 Pod가 할당됩니다. 명시적으로 노드를 할당할 수 있다는 점이 좋아보이지만, 실제 상용 환경에서는 노드가 추가되고 삭제되면서, 노드 이름이 계속 변경될 수 있기에 이는 잘 사용되지 않습니다.

 

 특정 노드를 지정할 때 권장되는 건 NodeSelector인데 이렇게 Pod에 Key와 value를 달면 해당 라벨이 달려있는 노드에 할당이 됩니다. 근데 라벨의 특성상 여러 노드에 같은 Key, value를 가진 라벨을 달 수 있기 때문에 이런 경우 Pod에 NodeSelector를 주게 되면 스케줄러에 의해 두 노드 중에 자원이 많은 노드로 Pod가 할당이 됩니다. 근데 불편한점은 Key, value가 딱 매칭이 되는 노드에만 할당이 된다는 점과 만약 매칭이 되는 라벨이 없으면 Pod는 어느 노드에도 할당이 되지 않습니다.

 

 이것을 보완해서 사용할 수 있는데 NodeAffinity 입니다. Pod에 Key만 설정해도 그룹 중에 스케줄러를 통해서 자원이 많은 노드에 Pod가 할당되고, 만약 해당 조건에 맞지 않는 키를 가지고 있더라도 스케줄러가 판단을 해서 자원이 많은 노드에 할당이 되도록 옵션을 줄 수 있습니다.

 

 Pod [ Affinity, Anti-Affinity ]여러 Pod들을 한 노드에 집중해서 할당을 하거나, 겹치는 노드없이 분산을 해서 할당할 수 있습니다. 예로 웹, 서버가 있는데 이 2개의 Pod는 한 PV에 연결이 되어있고, 이 PV가 Hostpath를 사용한다고 했을 때 이 2개 Pod는 같은 노드 상에 있어야만 문제가 없게 됩니다. 그래서 이 2개 Pod를 같은 노드에 할당 하려면 Pod Affinity을 사용해야 하는데 웹 Pod가 스케줄러에 의해 특정 한 서버에 할당이 되면 그 Hostpath에 PV가 생깁니다. 그러면 서버 Pod가 웹 Pod에 있는 노드에 들어가려면 Pod를 만들 때 Pod Affinity 속성을 넣고 웹에 있는 라벨을 지정하면 됩니다. 그럼 이 서버 Pod도 웹 Pod가 있는 노드로 할당됩니다. 

 

 Pod Anti-Affinity에 대한 설명으로 이 두 Pod는 master가 죽으면 slave가 백업을 해줘야 하는 관계인데, 이 2개 Pod가 같은 노드로 들어갈 경우 노드가 다운이 되면 둘 다 장애가 나기 때문에 서로 다른 노드의 스케줄링이 되야 합니다. 그래서 master가 스케줄러에 의해서 어느 한 노드에 들어가고 slave Pod를 만들 때 Anti-Affinity를 줘서 master Pod의 Key, value 설정을 하면 이 Pod는 master와 다른 노드에 할당이 됩니다. 

 

 

 Toleration, Taint 입니다. 특정 노드에 아무 Pod나 할당이 되지 않도록 제한을 위해 사용합니다. 노드5는 높은 사양의 그래픽을 요구하는 앱을 돌리는 용도로 GPU를 설정했을 때 운영자가 1) Taint를 설정해놓습니다. 그러면 일반적인 Pod들은 스케줄러가 이 노드로 할당을 시키지 않고, 직접 Pod가 노드5를 지정하고 할당을 하려해도 안됩니다. 이 노드에 할당하려면 Toleration을 달아야 할당이 가능합니다. 이렇게 좀 많지만 간략하게 Pod가 노드에 스케줄링 되는 다양한 형태들을 살펴봤습니다.

 

 

 

상세 설명

1. Node Affinity

 

- Match Expressions

: Selector, Label은 Key, value가 모두 같아야 매칭이 되지만, 이는 여러 조합으로 Pod를 선택할 수 있습니다. 이렇듯 Node Affinity 사용할 때 이 속성을 통해서 Pod는 노드들을 선택할 수 있는데, Key를 그룹핑 단위로 그리고 Pod를 하위 식별자로 붙여진 Label들이 노드에 붙여져 있고, Pod를 Key가 kr인 그룹 안에 할당을 하고 싶을 때 Match Expressions를 사용할 수가 있습니다. 그러면 스케줄러가 이 두 노드들 중 자원이 많은 쪽으로 Pod를 배치하고, Node Affinity에서 Match Expressions의 Operator로 사용할 수 있는 종류는 위와 같습니다. 대부분 Replicas에서 배웠던 기능과 같고 Gt, Lt는 내가 지정한 밸류보다 값이 크거나 작은 노드를 선택하는 옵션이 추가됐습니다. 

 

- Required vs preferred

: 노드 2개가 있을 때 Node Affinity로 required 속성을 가진 Pod가 노드에 없는 Key를 가지고 있다면, 이 Pod는 노드에 절대 스케줄링 되지 않습니다. 근데 만약 Key가 ch인 노드가 있으면 선호를 할 뿐이지 반드시 그 노드에 할당되어야 하는 것은 아닌 Pod일 때는

 

 preferred 옵션을 달면 해당 Key가 없더라도 적절한 노드에 할당이 됩니다. preferred 속성에는 웨이트 라는 필수 값이 있는데 이 값에 대해서 설명을 하자면, Key가 다른 라벨을 가진 2개의 노드가 있고, CPU는 50 vs 30으로 노드 1이 더 많다고 가정합니다. 그리고 preferred 속성을 가진 Pod를 만드는데 두 노드에 키가 모두 있기 때문에 두 노드 중 Cpu 자원이 많은 노드 1에 할당이 될텐데 이때 웨이트의 옵션을 설정할 수 있습니다. 선호도에 대한 가중치를 주는 건데 이 Pod는 Key가 Us, kr인 노드에 할당이 될 수 있지만, 그래도 Key가 kr인 노드에 가중치를 좀 더 주겠다고 설정을 한 것입니다. 그러면 스케줄러는 최초 Cpu 자원을 보고 점수를 매겨놔서 노드1에 할당을 시키려고 했지만, Pod의 가중치가 합산이 돼서 60 vs 80으로 노드2에 Pod를 할당시킵니다. 계산이 이렇게 간단하지만 대략적으로 한 것이니 가볍게 넘어가시면 됩니다. 

 

 

 

2. Pod Affinity

 

 Pod Affinity는 위처럼 노드들이 있고, 두 노드는 a 팀이라는 Key를 가진 라벨을, 남은 두 노드는 b 팀이라는 Key를 가진 라벨이 담겨있습니다. Pod Affinity는 type 웹이라는 라벨을 가진 웹 Pod가 스케줄러에 의해 노드1에 할당 됐습니다. 그럼 웹 Pod와 같은 노드에 Pod를 넣으려면 위처럼 podAffinity라는 속성으로 match Expressions가 있는데, 이번에는 이 match Expressions는 노드의 라벨을 가리키는게 아니라 Pod의 라벨과 매칭되는 조건을 찾습니다. 그래서 웹 Pod와 같이 노드 1에 할당될 수 있는데, 밑에 topologyKey는 노드의 키를 봅니다. 즉 match Expressions에 있는 조건의 라벨을 가진 파드를 찾지만 노드의 Key가 a팀에 있는 범위 내에서만 찾겠다는 내용입니다.

만약 웹 Pod가 노드 3에 들어갔다면 이 서버 Pod는 Pending 상태가 되고 해당 조건을 만족할 때까지 노드에 할당되지 않습니다.

 

 Pod Anti-Affinity는 타입의 Master의 라벨을 가진 master Pod가 노드 4에 스케줄링 되었을 때 이와 다른 노드에 할당하기 위해서 Slave Pod에는 podAntiAffinity를 달고 match Expressions로 Master 파드에 라벨을 달면, 그 Pod가 있는 노드에는 할당이 되지 않습니다. 마찬가지로 topologyKey를 줘서 B팀이라는 Key를 가진 노드를 범위로 한정을 시킬 수 있고, PodAffinity에서도 앞에 Node Affinity에서 배운 Required, preferred 옵션이 적용됩니다.

 

 

 

3. Taint, Toleration

: 노드들이 있고, 노드 1은 타 노드들과 달리 GPU 구성이 되어 있다고 가정합니다. 일반적인 Pod들이 이 노드에 스케줄링 되는 것을 막으려면 Taint라는 것을 노드에 달아줍니다. Taint 내용에는 Taint를 구분할 수 있는 라벨인 Key, Value가 있고 effect라는 옵션이 있어서 NoSchedule을 주면 타 Pod들이 노드 1에 할당되지 않습니다. effect에는 몇가지 옵션이 더 있는데, preferNoSchedule은 가급적 스케줄링이 안되도록 하는거라 Pod가 정 다른 노드에 배치될 수 없는 상황이라면 이 Taint가 붙은 노드에 할당을 허용해줍니다.

 

 실제로 GPU를 사용해야하는 Pod라서 노드 1에 할당이 돼야 되면 Pod를 만들 때 Toleration 주면 됩니다. 내용으로 Key, Operator, value, effect가 있고, 이 조건이 Taint 라벨과 맞아야 하고, Operator로는 Equal과 Exist만 있습니다. 그리고 effect의 종류까지 모두 매칭이 돼야만 이 노드1에 할당이 되지, 하나라도 틀리면 할당되지 않습니다. Pod의 Toleration 내용으로 매칭이 되는 Taint가 있는 노드를 찾는다고 생각할 수 있는데 Pod가 노드1의 스케줄링이 됐을 때 Pod에는 노드 1의 Taint와 매칭되는 Toleration이 있기 때문에 노드에 들어갈 수 있는 조건이 부합되는 것입니다. Pod 상태에서는 다른 노드에도 스케줄링이 될 수 있다는 것 입니다.(다른 노드에도 할당 될 수 있다는 뜻) 그래서 Pod의 별도 Selector를 달아서 이 Pod가 노드 1에 배치될 수 있도록 해야됩니다. 

 

 정리하면 Taint를 단 노드에는 아무 Pod나 들어갈 수 없고, 해당 Taint와 매칭이 되는 Toleration을 달고 있는 Pod만 들어오는게 허락이 되지만 Pod가 해당 노드로 스케줄링 되도록 하는 것은 별도의 옵션을 추가해야하는 것 입니다.

 

 별도로 NoExecute라는 효과도 있는데 NoSchedule과 차이점을 비교해서 설명하면 예로 Pod1이 노드2에 할당이 돼서 운영되고 있습니다. 이 상태에서 NoSchedule 효과를 가진 Taint를 노드에 달면 이 Pod 1은 어떻게 될까요? 지금까지 배운 Node Affinity, Pod Affinity, NoSchedule 옵션은 모두 Pod가 최초 노드를 선택할 때만 고려하는 조건이지, 이미 Pod가 노드에 안착되어 있는 상태에서 노드 라벨을 바꾸거나 NoSchedule 옵션에 Taint를 단다고 해서 파드가 삭제되거나 하지 않습니다. 그런데 Taint의 NoExecute 옵션은 이렇게 Pod 2가 노드 3에 할당이 되어 있을 때 NoExecute 효과를 단 Taint를 설정하게 되면 Pod2는 삭제가 됩니다. 그리고 이 노드3에 Taint가 달려 있어도 Pod가 삭제되지 않게 만드려면 Pod를 만들 때 마찬가지로 Taint의 내용과 매칭되는 Toleration을 달면 됩니다.

 

 여기서 toleration Seconds 옵션이 있는데, 이게 없으면 삭제가 되지 않지만, 이걸 달아주면 해당 시간 후에는 삭제가 됩니다. 그래서 위에는 60으로 설정했기 때문에, 이 파3는 노드3에 Taint가 들어오면 60초 동안은 안지워지고 있다가 60초 후에는 삭제가 됩니다.

 

 Noschedule은 Master 노드에 기본적으로 달려있어서 우리가 Pod를 만들 때 Master에 할당이 안되도록 하고 있고, 만약 ReplicaSet에 의해 Pod가 운영중인데 노드에 장애가 발생하게 되면 k8s는 해당 노드에 있는 파드들이 정상적으로 동작하지 않을 수 있기 때문에 NoExecute 옵션의 Taint를 자체적으로 해당 노드에 달거든요. 그러면 ReplicaSet은 자신의 파드가 하나 없어졌기 떄문에 다른 노드에 파드를 다시 만들어서 서비스가 잘 유지되도록 해줍니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.