서론
쿠버네티스를 사용하면서 많은 서비스에 대한 인프라 환경에서 유연성과 가용성을 얻을 수 있었고, 특정 서비스를 배포하는 것에 두려움을 가지지 않을 수 있었습니다. 하지만 서비스를 배포하면서 자주 생각하게 되는 질문이 바로 “우리가 쿠버네티스를 최적으로 사용하고 있는건가?” 였습니다.
쿠버네티스로 서비스를 배포하는 것은 쉽습니다. 서비스의 부분을 담당하는 Pod들은 개별로 배포되며 각 Pod는 서비스의 각 부분을 담당하도록 구현되어 있습니다. Pod는 대체로 이미지로 작성되어 있으며, 적절한 환경변수 및 service, configmap, deployment 만 잘 설정해주면 원하는 인프라 내에서 잘 동작합니다.
하지만 쉽게 배포되는 건 “일반화”되어있다는 이야기이고, 개별적으로 배포 되는 것은 곧 “복잡성”이 높다는 이야기입니다.
다수의 별개의 동작하는 Pod에 대해서 각 Pod의 리소스가 적당히 사용되고 있는지를 알아내는 것은 거의 불가능에 가깝습니다. 그리고 이러한 문제를 해결하기 위해 특히 비용 측면에서 모니터링하기 위해 사용되는 서비스가 kubecost 입니다.
쿠버네티스란?
클러스터 환경에서 컨테이너화 된 애플리케이션을 자동으로 배포하고 확장 및 관리하는데에 필요한 요소를 자동화하는 오픈소스 플랫폼입니다. 여러 서버로 구성 된 클러스터 환경을 구축하는 것에 쿠버네티스를 사용하면 로드밸런싱, 네트워크, 스토리지, 모니터링 등 시스템 운영에 필수적인 여러 컴포넌트를 쉽게 구축 할 수 있습니다.
쿠버네티스는 일반적으로 yaml 파일과 같은 형식의 파일로 구성합니다. 이렇듯 모든 작업을 소스코드로 관리하면 운영 시스템에 적용하기 전 검토에 유용하고, 사전 검증도 편해집니다.
또한, 쿠버네티스는 ‘의도된 상태’를 기준으로 관리합니다. 최초 의도한 상태와 현재 진행중인 상태를 쿠버네티스 컨트롤러가 자동으로 끊임없이 확인( go 언어의 watch )하며 차이점이 발생하면 현재 상태를 자동으로 의도 된 상태로 변경합니다. 예를들면 Nginx 서버가 갑자기 예상치 않게 종료되면 쿠버네티스는 곧바로 새로운 pod를 생성합니다.
클러스터의 전체 상태를 지속적으로 체크하여 자원의 할당이나 물리적인 리소스 등을 조절하고 활용합니다.
쿠버네티스 최적화란?
쿠버네티스로 구축 된 환경을 최적화하는 것은 주로 “리소스 최적화”를 말할 수 있습니다. 물론 네트워크나 네임스페이스 등 쿠버네티스 내에서 클러스터 환경을 유연하게 작성하는 것도 중요한 최적화 방안에 속해있지만, 이 글에서는 리소스 최적화를 주로 이야기하고자 합니다.
쿠버네티스 리소스
쿠버네티스 잘 사용하기에 간략하게 표현 한 것처럼 쿠버네티스의 Pod를 생성 할 때 크게 2가지의 리소스가 있습니다.
- CPU
- Memory
그리고 두 리소스를 Pod 속성으로 설정 할 때 다음의 두 방식을 분리하여 설정합니다.
- request
- limit
설정한다면 다음과 같이 설정하게 됩니다.
- CPU
- request
- limit
- Memory
- request
- limit
여기서 Request와 Limit에 대해서 자세히 알아 볼 필요가 있습니다.
리소스 요청 기준 설정 ( request )
적어도 N 만큼의 리소스가 해당 노드에 공간이 남아 있어야 Pod를 띄우겠다는 의미입니다.
특정 노드그룹 혹은 Selector에 부합하는 노드에 Pod를 띄우는 것이 쿠버네티스의 기본 동작 방식입니다. 하지만 해당 조건에 충족하는 노드에 공간이 충분하지 않으면 새로운 노드를 생성하거나 공간이 비워 질 때까지 대기해야 합니다. 그렇지 않으면 OOM 혹은 CPU의 과도한 사용으로 해당 노드에서 동작하는 다른 로직에 영향을 줄 수 있습니다.
이렇듯 해당 로직을 수행하기에 충분한 양의 리소스가 있는지, 없으면 해당 노드에 Pod를 띄우지 않도록 설정하는 것이 Request입니다.
리소스 최대 사용 량 ( limit )
리소스 최대 사용량은 해당 Pod가 limit 으로 설정 한 량까지만 리소스를 사용 할 수 있도록 제한하는 것 입니다.
예를들면 Memory 사용 중 request 한 수준을 넘어가면 실제로 노드 내에 가용할 수 있는 공간이 있음에도 OOM 문제가 바로 발생하게 됩니다. 이러한 문제를 방지하기 위해서 “적어도 N 까지는 사용 할 수 있다”라고 표현하는 것이 limit 입니다.
하지만 limit은 잘 사용하기 어려운 부분이 있습니다. 꽤나 많은 사람들이 노드의 최소 및 평균 사용량을 request 로 설정하고 최대 사용량에 맞추어 limit을 설정합니다. 이 경우 문제가 될 수 있는 부분이 최대 사용량이 상당히 자주 발생한다면 Pod가 명시적으로 할당받은 공간(request 공간)을 넘는 경우가 자주 발생하게 되고, 이 Pod는 추가 리소스를 할당 받을 수도 있고 받지 못할 수 있는 경우를 자주 겪게 됩니다.
그래서 request 설정을 기본으로 여유롭게 설정해주어야 합니다.
최적화가 필요한 이유
개발을 서두르는 경우 쿠버네티스 자원에 대한 최적화에 신경을 쓰기 어렵습니다. 하지만 어느 순간 “비용”이 주요 이슈로 떠오르게되면 이제 리소스 최적화에 관심을 가지게 되는 것 같습니다.
불필요한 리소스는 곧 불필요한 지출로 이어집니다.
쿠버네티스는 최적화하는 방법이 상당히 복잡합니다. 사용하는 Pod의 동작이 획일화되는 경우는 거의 없고, 플랫폼 혹은 서비스등의 리소스를 분리시켜야 하며 대부분의 경우 Pod는 언제는 사라 질 수 있고 생겨 날 수 있기에 각 Pod가 얼마나 리소스를 사용하는지 모니터링 또한 어렵습니다.
하지만 최적화가 가능하면, 우리는 적절한 비용으로 쿠버네티스의 유연한 클러스터 구축을 사용 할 수 있게 됩니다.
최적화 방안
이 글에서 말하는 쿠버네티스의 최적화는 동작의 안정성을 유지하는 한도에서 리소스를 어떻게 적절하게 설정 할 것인지, 적절한 NodeSelector를 통해 노드풀을 적절하게 사용하는 것이라고 말할 수 있습니다.
예를 들면 다음의 그림을 보면 각 동작에 따라 Pod의 리소스를 적절히 설정해야 함을 알 수 있습니다.
ML과 같은 많은 연산에 CPU를 사용하는 경우 우리는 상당히 많은 연산을 수행해야하기 때문에 CPU가 많이 사용 될 수 있습니다. 하지만 대규모 데이터를 한번에 메모리에 담기보다 iterator 등을 사용하는 경우가 많기에 메모리가 생각보다 많이 사용되지 않을 수 있습니다.
반대로 대규모 데이터 처리의 경우에는 메모리를 페이징하는 것은 어렵습니다. 분산 처리 프레임워크를 사용하지 않는 이상 대규모 데이터를 메모리에 담게 됩니다.
그러면 분산 처리 프레임워크를 사용하면 됩니다.
분산 처리를 하는 경우 해당 Pod가 가진 리소스는 매우 적어지기에 이 경우에는 더 적은 리소스를 할당받아야 합니다.
기준값과 margin
쿠버네티스는 적절한 request 값을 설정하기 위해서 기준값을 설정하고 혹시 모를 리소스 추가 사용 상황에 대비해서 margin을 추가합니다.
예를 들면 SNS 서비스의 경우 평시 근무 시간에는 70%만 사용되다가 방학 중 주말의 경우 100% 이상의 리소스가 사용 될 수 있습니다. 이러한 경우 OOM 등의 리소스 관련 장애가 발생하지 않게하기 위해서 기준값을 70% ~ 100% 정도로 설정하고 margin을 30% 더 가져 갈 수 있습니다.
기본적으로 request를 설정 할 때는 기준 값과 margin을 곱해서 사용한다고 합니다.
쿠버네티스 모니터링 시스템 구축 ( kubecost )
가장 먼저 Pod의 각 메트릭을 수집 할 수 있는 시스템을 구축해보면 좋을 것 같습니다.
쿠버네티스 클러스터의 각 Pod의 사용량에 대해서 메트릭을 수집하여 모니터링 할 수 있는 프레임워크 중 가장 대표적인 프레임워크가 kubecost 입니다.
kubecost과 같은 쿠버네티스 모니터링 시스템을 구축하면 다음과 같은 이점을 가져 갈 수 있습니다.
- 사용량 확인
- 비용 모니터링
- 최적 리소스 추정
개별 Pod의 속성 값에는 기본적으로 해당 Pod가 가진 request 리소스 량이 정의되어 있습니다. 그리고 Cluster IP로 해당 Pod에 접근해서 리소스 사용량을 측정하면 실제로 사용하는 리소스도 알 수 있습니다. 이 두 정보를 가지고 우리는 얼마나 리소스를 사용하고 있는지 쉽게 알 수 있습니다.
실 구축을 통해 얻은 인사이트
- kubecost는 FREE 버전이 있으나, 멀티 클러스터 환경을 구축하기 위해서는 Enterprize 를 사용해야 합니다.
- AWS 등의 클라우드 버전을 사용하면 기본적인 멀티 클러스터 환경을 구축 할 수 있습니다.
- 기본적으로 helm을 통해 배포가능합니다.
- TSDB를 사용하기에 프로메테우스를 기본 데이터베이스로 활용합니다.
- 프로메테우스의 영속성을 보장하기 위해 외부 프로메테우스를 사용 할 수 있습니다.
- API를 통해 집계 된 데이터를 추출 할 수 있습니다.
- 클러스터 / 네임스페이스 / 노드 단위 등 다양한 기준에서 데이터를 조회 할 수 있습니다.
최적 리소스로의 배포 ( VPA )
VPA는 Vertical Pod Autoscaler의 약자로 명칭 그대로 Pod를 오토스케일링하는 시스템입니다. kubecost와 마찬가지로 Pod의 실제 리소스 사용량을 확인하여 평균 혹은 최대 사용 리소스를 기반으로 request 리소스 량을 추정하여 사용합니다.
정리
- Pod의 리소스 할당 시 request와 limit 이라는 개념을 사용합니다.
- request : Node가 Pod를 할당받기 위해 최소한으로 가져야 하는 양
- request가 너무 낮으면 해당 Pod가 Node 내에서 사용하는 양을 관리하기 어려움
- limit : Pod가 최대로 사용 할 수 있는 양
- CPU는 지연 등의 문제가 발생하나 Memory의 경우 limit을 넘어가면 OOM으로 장애가 발생
- request : Node가 Pod를 할당받기 위해 최소한으로 가져야 하는 양
- 쿠버네티스의 리소스 최적화 방안에는 각 Pod에서 활용하는 CPU 혹은 Memory의 request를 설정하는 것이 있습니다.
- 추가적으로 효율적인 node selector를 구분하는 것도 있습니다.
- Pod 리소스의 request는 간단하게 기준값과 margin 개념을 활용해서 설정 할 수 있습니다.
- kubecost 혹은 VPA와 같은 툴 혹은 서비스를 사용하여 각 Pod에 최적의 request를 추정 및 설정 할 수 있습니다.
참고
https://velog.io/@hl08217/Kubernetes-VPA%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Pod%EC%9D%98-Autoscaling-%ED%95%B4%EB%B3%B4%EC%9E%90
https://velog.io/@hl08217/Kubernetes-Pod-CPU-Memory-resource-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0
https://devocean.sk.com/blog/techBoardDetail.do?ID=164786&boardType=techBlog