숨고의 AWS WarmPool 적용기
안녕하세요.
숨고 Platform Chapter에서 DevOps Engineer로 일하고 있는 Ray입니다.
이번 포스팅에서는 숨고에서 사용하고 있는 EKS의 오토스케일링 속도를 개선하기 위해 AWS Warm Pool을 도입하게 된 과정에 대해서 공유해보려고 합니다.
도입 배경
평상시의 숨고는 최적의 비용으로 서비스를 운영하기 위해 트래픽에 맞는 노드와 파드를 유지하며 운영하고 있습니다. 하지만 다양한 분야의 고수와 고객이 있는 서비스의 특성상 트래픽이 유동적인 편입니다. 이때, 예상치 못하게 트래픽이 폭증하는 경우에는 스케일링이 트래픽의 증가폭을 따라가지 못하면서 서비스의 안정성과 신뢰성에 영향을 줄 수 있습니다.
예를 들어, 현재 숨고의 쿠버네티스 세팅을 기준으로 파드의 CPU 사용량이 60%를 넘는다면 HPA(Horizontal Pod Autoscaler)를 통해 스케일링이 진행되고 새로운 파드가 스케쥴링이 되어야 합니다. 하지만 만약 이 상황에서 적절한 타이밍에 스케쥴링 될 노드가 준비되지 않는다면, 새로운 노드가 준비되고 파드가 구동되는 잠깐의 시간동안 서비스의 안정성이 떨어지거나 예상하지 못한 장애가 발생할 수 있습니다.
따라서 적절한 HPA 기준을 유지하면서도 트래픽이 유동적인 숨고 서비스의 특성에 맞게 스케일링, 스케쥴링 속도를 개선할 필요가 있다고 판단했습니다.
방법 1. 노드를 미리 늘려놓는다면?
가장 간단하게는 갑작스러운 트래픽에 대비해 미리 여분의 노드를 준비시켜 두는 방법이 있습니다.
하지만 이 방법을 사용하게 되면 기존 비용 최적화 운영 대비 불필요한 비용이 소모될 것으로 판단했습니다. 따라서 노드를 무작정 늘리는 방법 대신 다른 방법을 고민해야 했습니다.
방법 2. Karpenter라는게 있다던데?
이후 다양한 방법을 고민 하던 중 두 번째로 검토한 것이 바로 Karpenter였습니다. Karpenter는 AWS 기반의 고성능 오픈소스 Kubernetes Cluster Autoscaler(이하 CAS)로, 공식 문서에서는 다양한 인스턴스 유형에 맞출 수 있는 높은 유연성과 노드 그룹 없이도 각각의 인스턴스를 직접 관리한다는 점을 특징으로 꼽고 있습니다.
숨고는 EKS로 관리되는 노드들의 오토 스케일링을 위해 이미 CAS를 사용하고 있었기 때문에, Karpenter를 통해 저희가 고민하던 문제를 해결할 수 있지 않을까 하는 기대감으로 테스트를 시작했습니다.
Group-less node provisioning
위에서 설명드린 Karpenter의 특징 중 저희가 주목한 것은 노드 그룹 없이도 각각의 인스턴스를 직접 관리한다는 점입니다. Karpenter에서는 이 특징을 Group-less node provisioning이라고 설명하고 있습니다.
예를 들면 CAS는 EKS의 노드 그룹을 직접 컨트롤하여 연관되는 오토스케일링 그룹(Auto Scaling Group, 이하 ASG)의 Scale을 수정함으로써 새로운 노드를 확보하는 반면, Karpenter의 경우 파드 스케쥴링에 필요한 스펙의 인스턴스를 직접 확보합니다.
또한 기존 CAS와 달리 Pod가 스케쥴링 되는 시점도 차이가 있습니다. CAS를 이용하여 오토스케일링을 하게 될 경우에는 새로운 노드가 준비(Ready 상태)되고 나서 파드가 스케쥴링 되지만, Karpenter의 경우 시작하는 노드에 파드가 즉시 스케쥴링되도록 합니다.
이러한 특징을 고려했을 때 Karpenter로 전환하게 되었을 때 저희가 원하는 스케쥴링 속도 개선에 있어서 유의미한 결과가 있을 것이라 판단하여 테스트를 진행했습니다.
테스트 결과
테스트는 Karpenter에서 제공하는 공식 문서를 참고하여 숨고에서 테스트 용도로 사용중인 클러스터에 Karpenter를 설치하는 방식으로 진행하였습니다.
우선 사용중인 CAS를 제거하고 Karpenter를 설치한 뒤, 기본 Nginx 이미지를 사용하는 간단한 파드가 스케쥴링 되어 Ready 상태에 도달하기까지 얼마의 시간이 걸리는지를 비교해 보았습니다.
그 결과 이전에 사용하던 CAS에 비해 평균적으로 10초에서 15초 정도 빨라진 것을 확인할 수 있었습니다.
하지만 이런 결과에도 불구하고 저희는 바로 Karpenter를 도입하지는 않았습니다. 10초에서 15초라는 유의미한 개선을 보인 점은 긍정적이었지만, 속도 개선을 통해서 서비스의 안정성을 높인다는 본래의 목적을 고려했을 때 Karpenter가 출시된 지 얼마 되지 않았다는 점이 고민이었습니다.
또한 숨고는 아직 운영중인 노드 그룹의 종류가 많지 않아 이를 관리하는데 큰 어려움이 없었기 때문에 Group-less node provisioning의 활용성이 예상보다 높지 않았던 것도 아쉬운 점이었습니다.
이에 Karpenter가 아닌 다른 방법을 찾아보았습니다.
방법 3. Warm pool
다음으로 검토한 방법은 AWS의 여러 Auto Scaling 방법 중 하나인 Warm Pool이었습니다.
Warm Pool의 기본 개념은 맨 처음에 고민했던 방법인 노드를 미리 마련해두는 것과 비슷합니다. 하지만 노드를 정지(Stopped) 또는 최대 절전 모드(Hibernation)로 준비시켜 놓는 점이 가장 큰 차이점이라고 할 수 있습니다.
정지, 또는 최대 절전 모드를 사용하게 되면 노드를 단순히 확보해두는 것보다 빠르게 스케일링이 될 텐데요. 컴퓨터를 처음부터 켜는 것보다 절전 모드에서 켜는 것이 더 빠른 것처럼 인스턴스도 새로 프로비저닝되어 Running까지 걸리는 시간보다 Stopped에서 Running까지 가는 것이 더욱 빠르기 때문입니다.
* AWS EC2 내의 인스턴스 수명 주기 및 각 상태에 대한 차이점은 공식 문서를 참고해주세요.
Warm Pool을 구성하기 위해선 EKS로 관리되는 노드 그룹의 ASG에 Warm Pool을 추가하여 정지되어있는 노드들을 미리 준비시킬 수 있습니다. 먼저 EKS에서 관리되는 각 노드 그룹에 속해있는 ASG로 이동합니다.
EKS에서 노드 그룹을 생성하게되면 해당 노드 그룹에 해당하는 k8s로 시작되는 이름을 가진 ASG가 자동으로 생성됩니다. 그 후 ASG를 선택한 뒤 인스턴스 관리 메뉴에서 Warm Pool를 생성할 수 있습니다.
Warm Pool 생성을 누르게 되면 다음과 같이 Warm Pool을 어떻게 구성할지 지정할 수 있습니다.
Warm Pool은 정지 모드와 최대 절전 모드로 운영이 가능합니다. 정지 모드가 우리가 생각하는 말 그대로의 ‘정지’ 상태라면, 최대 절전 모드는 간단히 말해 RAM의 내용을 노드와 연결된 스토리지의 루트 볼륨에 저장한 채로 정지시킵니다. 때문에 정지 모드보다 빠르게 노드가 준비된다는 장점이 있습니다.
더 자세한 프로세스 설명을 위해 ASG의 원하는 용량(desired)이 3개, Warm Pool 준비 용량이 2개인 경우를 가정해보았습니다.
Warm Pool을 생성하게 되어 ASG에 인스턴스가 관리되고 있는 모습은 다음과 같습니다.
Warm Pool을 생성하고 나면 위의 그림처럼 최소 Warm Pool 크기로 지정한 갯수만큼의 노드가 생성 되고, 프로비저닝이 완료된 후 정지 상태로 준비됩니다.
이후 CAS를 통해 스케일링이 되어 ASG의 원하는 용량(desired)이 증가하였을 경우에는아래의 그림처럼 새로운 노드가 생성되는 것이 아닌 Warm Pool Zone에 준비되어 있던 노드가 실행됩니다.
이후에는 Warm Pool을 설정할때 최소 준비 갯수만큼의 노드가 다시 준비되어 이후 또 스케일링이 되더라도 미리 준비되어있던 노드가 빠르게 실행되게 됩니다.
테스트 결과
Warm Pool에 대한 테스트는 Karpenter를 테스트할 때와 동일한 환경으로, 기본 Nginx 이미지를 사용하는 간단한 파드가 스케쥴링 되어 Ready 상태까지 얼마나 걸리는지를 비교해보았습니다.
Warm Pool 적용 전에는 Running까지 평균 75초 정도 소요되는 것을 확인할 수 있었습니다.
Warm Pool을 적용한 이후에는 Running까지 평균 55초 정도 소요되는 것을 확인할 수 있었습니다.
운영 중인 클러스터의 상황에 따라 달라질 수 있겠지만, 테스트 클러스터 환경에서는 유의미하게 속도가 개선된다는 사실을 확인할 수 있었습니다.
결론
앞선 테스트 및 챕터 구성원들간의 논의 결과, 숨고 서비스에는 Warm Pool을 적용하기로 결정하였습니다.
먼저, 저희가 처음에 기대했던 속도 개선에 있어서는 테스트를 통해 유의미한 속도 개선이 있다고 판단했습니다. Warm Pool을 사용했을 때 Karpenter와 동일하거나 더 빠른 속도로 스케일링되는 것을 확인할 수 있었습니다. 이후 최대 절전 모드를 활용하게 된다면 이번에 테스트한 정지 모드보다 더욱 빠르게 스케일링 될 수 있다는 기대를 해볼 수도 있었습니다.
뿐만 아니라, 운영에 있어서도 CAS에 Warm Pool을 설정하여 운영하는것이 Karpenter를 설치하여 운영하는 것 보다 더 편리하다는 생각이 들었습니다. 아무리 문서화가 잘 되어있더라도 AWS가 자체 서비스로 제공하고 있는 Warm Pool과 외부 오픈소스인 Karpenter는 관리자의 사용성에서 차이가 느껴질 수 밖에 없었습니다.
그 외에는 최신 기술이 갖고 있는 불안정성과 Karpenter가 가지고 있는 장점들을 전부 활용하기 어려운 숨고의 환경 또한 Karpenter 대신 Warm Pool을 선택한 이유 중 하나입니다. 위에서 설명드렸던 Group-less node provisioning을 제외하면 속도 측면에서 Warm Pool에 비해 Karpenter가 유의미한 차이를 보이지 못했기 때문입니다.
물론 비교적 최신 기술인 Karpenter를 사용해보는 것이 조직에 좋은 경험이 될 것이라는 생각도 있었고, 개발자로서 새로운 기술에 대한 호기심도 있었던 것이 사실입니다. 하지만 이번 작업의 궁국적인 목적이 최신 기술의 사용이 아닌 서비스의 안정성을 높이기 위함이라는 점을 돌이켜보았을때 현 시점에서는 Warm Pool을 사용하는 것이 적합하다고 판단했습니다.
회고
저희가 스케일링 속도를 개선해야할 상황이라는 것은 바꿔 말하면 숨고가 지속적으로 성장하고 있다는 반증이라고 생각합니다. 하지만 이러한 성장은 자연스럽게 클라우드 비용의 증가로 이어지기 쉽기 때문에, 성능 개선만 고려할 수 없는 것이 현실입니다. 이번 Warm Pool의 도입 과정은 성능의 개선과 비용의 증가 사이의 적절한 지점에 대해 구성원들과 고민할 수 있는 좋은 계기였다고 생각합니다.
또한 이번에 속도에 대한 문제를 해결하기 위해 여러 오픈 소스를 찾거나 AWS의 기능을 찾고 활용 방법을 고민하면서 개발자로서 더 성장한 기분입니다. 이번 포스팅이 저희와 비슷한 고민을 하고 계신 분들에게 조금이나마 도움이 되었으면 좋겠습니다.
긴 글 읽어주셔서 감사드립니다.
참고 자료
- https://karpenter.sh/
- https://docs.aws.amazon.com/ec2/index.html#amazon-ec2-auto-scaling
- https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html
- #aws
- #devops
- #eks
- #kubernetes
- #development
Ray Jeong
DevOps Enginner