https://newdeal123.tistory.com/99
이전 글을 보지 않으신 분들은 읽고 오시는 것을 추천드립니다 ;)
벌써 입사한 지 3개월이라는 시간이 지나고, 감사하게도 수습기간이 성공적으로 수료되었습니다.
짧은 기간이었지만 정말 다양하고 많은 일들을 한 것 같은데, 회고형식으로 무슨 일들을 했고 어땠는지 살펴보도록 하겠습니다.
첫 주는 새로운 지식을 습득하기에도 급급했던 것 같습니다. 정말 하루하루 낯설고 새로운 정보들이 눈과 귀에 꽂히고 정신없이 돌아갔던 기억이 납니다. 우선 에드테크 스타트업의 프로덕트를 아는 것부터 알고 배울 것이 정말 많다 보니 정보들이 막 막 쏟아지는 기분이었습니다. 뿐만 아니라 마이크로 서비스로 찢어진 수십 가지 도메인들에 대한 이해도 필요했고 DevOps팀에서 사용하는 도구들과 플랫폼에 대해서도 익혀야 했습니다.
현재 저희 DevOps팀에서 사용하는 기술과 도구들을 정리하면,
- Cloud: AWS
- IaC: Terraform
- Monitoring: Grafana, Thanos, Loki, Prometheus, DataDog, Cloudwatch
- Service Mesh: Istio, Envoy
- CI/CD: Drone CI, Jenkins, Spinnaker, ArgoCD
- etc: Karpenter, Helm, fluentd 등
정도가 있는데, 절반 가량은 사용해 본 적이 없었습니다.(당연한가?) 각 기술과 도구들이 명확한 목적과 방향을 가지고 도입된 것이 신기했고 여러 백그라운드 지식과 사용 방향성을 익히는 데에 시간을 많이 쏟았던 것 같네요. (질문도 많이 드린 것 같아요. ㅎㅎ)
특히 팀원분들과 여러 이야기를 나누고 회의를 하면서 저희 DevOps팀이 지향하는 로드맵과 비전도 이 회사와 이 팀에서 경력을 쌓으면 좋은 커리어와 경험이 될 것 같다는 생각이 들었습니다. 지금 여기서 이야기는 못하지만 곧 아주 멋진 솔루션과 이야기들로 포스팅을 올릴 수 있을 것 같네요.
버즈빌에서는 입사 후 3개월 동안은 PB 기간(수습 기간)을 거칩니다. 이때 수행한 과제들로 평가를 받고, 더 함께 할지를 서로 정하는 시간이죠. 다른 회사들의 온보딩 과제와는 다르게 실제 회사와 조직에게 필요한 과제들을 수행한다는 점이 포인트입니다.
그중 제가 수행했던 몇 가지 과제들을 소개할까 합니다. 디테일하거나 보안상의 이슈가 있을 것 같은 부분은 쓰지 못해서 내용에 허점이 있을 수도 있다는 점은 참고해 주세요. 🥲
Drone CI를 Kops에서 Karpenter로 마이그레이션
저희 DevOps 팀에서는 CI 도구로 Drone CI를 주로 사용하고 있습니다. 이게 뭐야..? 하시는 분들도 많을 것 같은데, 초기 당시에는 꽤 획기적인 프로젝트였다고 합니다. 제가 입사하기 전부터 도입된 도구인데 다음과 같은 장점이 있습니다.
- Drone CI는 컨테이너 기반의 CI로, 모든 단계에서 Docker 컨테이너를 활용해 빌드 파이프라인이 시간이 덜 걸리고 설정과 실행이 쉽습니다.
- Jenkins 등의 도구와 비교했을 때 유지보수, 디버깅이 훨씬 간단하고 수 천 개의 파이프라인으로 확장되더라도 쉽게 확장 가능합니다.
- Go언어로 작성되어 있고 설정 파일도 간단한 YAML 파일로 구성할 수 있어 개발자들에게 Self-Service로 제공하기 적합합니다.
물론 최근에는 Github Action이 많은 기능 지원과 커뮤니티 확장으로 부상하고 있지만, 초기의 Drone CI의 폼을 보면 굉장히 매력적인 도구였음을 알 수 있습니다.
Drone CI는 클라우드 형식이 아닌 저희가 운영하는 클러스터에서 Self-hosted 방식으로 운영되고 있습니다. 특히 클러스터 관리 솔루션으로 KOps로 관리되고 있는 운영 클러스터에 존재했죠.
KOps도 분명 장점이 있는 도구이지만 여러 가지 단점이 존재해 (관리가 복잡함, EKS의 다양한 기능을 안 쓸 이유가 별로 없음.) EKS 기반 클러스터로 옮기는 과제가 주어졌죠. 특히 운영 클러스터의 경우는 Karpenter로 프로비저닝 된 EKS 클러스터 환경을 가지고 있는데 Karpenter도 굉장히 좋은 도구입니다.
Karpenter는 KOps를 대체하는 포지션은 아닙니다. KOps는 EKS와 같은 클러스터 관리 솔루션 포지션이고, Karpenter는 기존 Kubernetes에서 Cluster Autoscaler 포지션을 대체하는 것입니다. Karpenter만으로도 따로 설명할게 많은데, 새로 포스팅을 파던지 해야겠네요. 요약하자면, Karpenter는 AWS에서 만든 Open Source로 클러스터의 애플리케이션 리소스 요청량, 노드 한계치 등을 분석하여 효율적이고 신속하게 오토 스케일링을 진행하는 프로비저닝 tool입니다.
그렇게 운영도구들을 KOps에서 Karpenter기반 EKS로 옮기려는 전략이 저희 팀의 로드맵이었고 레거시인 기존 KOps환경에서 존재하던 여러 문제점들을 해결할 것이라고 기대했죠.
중요한 건 꺾이지 않는...
drone은 CI 도구다 보니 마이그레이션 할 때 가장 중요한 건 다운타임이었습니다. 각 팀의 엔지니어 분들이 하루에도 몇백 번씩 사용하는 중요한 도구이다 보니, 업무시간에 다운타임이라도 발생하면 생산성에 지대한 영향을 줄 수 있음이 분명했죠. 그 와중에 마이그레이션 할 클러스터의 kubernetes 버전도 다르고 AMI도 다르고 하다 보니 테스트도 충분히 수행해야 했습니다. 어떤 전략을 세울 수 있을까요?
Drone CI는 기본적으로 아키텍처가 server와 runner로 이뤄져 있습니다. server는 github의 webhook request를 받아 runner로 빌드를 생성하도록 하거나 엔지니어 분들이 확인할 수 있는 대시보드의 정보들을 DB에서 긁어와 제공하기도 하는 역할을 합니다. runner는 server로부터 생성 명령이 오면 해당 정보를 토대로 빌드 pod를 생성하고, 빌드 pod와 주기적으로 통신하며 step을 순차적으로 진행하도록 하는 역할을 맡고 있죠.
결국 이 server와 runner는 모두 이전시켜야 하니까, runner부터 조금씩 차례로 이전시키는 것이 올바른 순서라고 판단했습니다. runner를 두 개의 클러스터에 모두 띄워 새로 이주할 클러스터에 빌드 pod들이 잘 동작하는지 테스트하기로 했죠.
다행히 drone runner 설정값 중 마이그레이션 작업의 테스트와 배포에 도움을 주는 요소들이 있었는데,
- DRONE_RUNNER_CAPACITY 값으로 기존 screen 클러스터로 흐르는 통신과 새로 이전할 클러스터로 흐르는 통신의 비율을 조절해 부담 없이 semi 카나리 배포를 할 수 있습니다.
- DRONE_RUNNER_LABELS 값으로 테스트 중인 레포지토리만 새로 이전할 클러스터로 흐르게 할 수 있었습니다. (테스트했던 레포지토리를 지금 확인하니까 커밋이 130개나..!)
자. 그럼 이제 테스트 환경 준비는 마쳤습니다. 새로 이전할 클러스터에서 테스트만 잘 통과하면 마이그레이션이 수월해지겠죠?
오류... 또 오류...
몇 개의 레포지토리의 CI 스텝 중 내부 배포 도구인 Spinnaker로 웹훅 트리거를 보내는 부분이 있습니다. 기존에는 Drone과 Spinnaker 모두 같은 클러스터에 있다 보니 클러스터 내부 DNS를 사용해 통신해도 큰 문제가 없었습니다. 그런데 Drone 혼자 이민을 가버리니까 CI 스텝 중에 경로를 못 찾는 문제가 발생했습니다. 그렇다고 레포지토리 모두 순회공연하면서 Spinnaker의 엔드 포인트를 바꾸기엔 너무 공수가 많이 드니까, 다른 방법을 생각해야 했습니다.
이를 위해서 ExternalName 타입의 service를 사용하여 클러스터 DNS 통신을 Spinnaker의 Private 도메인으로 리다이렉트 시켰습니다. 따라서 drone이 다른 클러스터에 있더라도 같은 AWS 망 내에 존재하니, Spinnaker와 통신을 할 수 있게 되었죠.
또 다른 오류도 있었습니다. 드론 플러그인을 여러 개 사용하고 있는데, 그중 브랜치별로 작업환경이 섞이는 걸 방지하고 캐싱으로 속도를 빠르게 하기 위해 drone s3-cache 플러그인을 사용 중이었죠. 그런데 그 s3 플러그인 버전에서 버그가 존재했는데, 자격증명을 위해 액세스 키를 주입하는 플러그인 오픈소스 코드의 함수 파라미터는(id, secret, token)이지만, 내부 구현상으로 매개변수 token 위치에 region을 넣고 있었더랍니다..(다행히 다음 버전에선 bug fix 되었습니다.) 그래서 region을 명확히 명시하고자 주입했던 yaml 구성이 문제가 되었고 region 값을 뻬야 정상 동작할 수 있었습니다.
그럼 이 오류는 왜 새로 이전할 클러스터에서만 발생했냐.. 하면 이전 클러스터와 사용하는 인스턴스 AMI가 달라서 발생하는 오류이기 때문입니다. 인스턴스의 애플리케이션은 보안 자격 증명을 검색할 때 다음과 같은 명령을 통해 IAM 역할의 보안 자격 증명을 검색합니다.
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
하지만 IMDsv1, IMDsv2 버전의 경로가 달라졌음에도 IAM 역할을 가져오는 부분에서 드론 플러그인이 사용하는 패키지인 minio-go의 내부 구현 방식이 이전 버전의 경로를 따르고 있었던 것입니다. (코드 참고: https://github.com/minio/minio-go/blob/v6.0.5/pkg/credentials/iam_aws.go#L51-L54C2)
일반적인 생각으로는 말도 안 되는 오류여서 오픈소스 코드를 파고파고 들어가서 발견했다 보니 (삽질도 많이 하고 도움도 받았지만..) 더욱 기억이 나네요.
그 외에도 몇 개의 오류해결이 많았지만 추후에 시간이 있다면 포스팅으로 더 올릴 수 있을 것 같습니다.
karpenter에서 CI 도구 운영하기
결국 충분한 테스트를 거치고 오류들을 해결해 가며 Drone CI를 무사히 클러스터 마이그레이션에 성공시킬 수 있었습니다. 새로 Drone CI가 보금자리로 잡은 곳은 karpenter 프로비저닝 도구로 운영되는 EKS 환경입니다.
무사히 마이그레이션을 마쳤지만 조금 더 욕심나는 목표가 있었습니다.
- 비용효율적으로 운영하고 싶다 : CI 도구의 특성상 주로 업무시간에 사용되는 경향이 있으므로 비업무시간에는 스케일 인을 시키고 싶었습니다.
- 그러면서도 안정적으로 운영하고 싶다 : CI 도구를 Self-hosted로 운영하기 힘든 이유는 여러 가지가 있지만, 무엇보다 CI 빌드들이 너무 시끄러운 이웃이라는 이유도 있습니다. (갑자기 6 코어를 필요로 하는 job도 있으니까요..) CI 빌드들도 방해받지 않으면서도 해당 클러스터의 다른 워크로드들도 영향을 받지 않았으면 좋겠습니다.
- 빌드 캐시 히트율도 높았으면 좋겠다 : 이미지 캐시레이어들이나 레포지토리 캐시 등이 디스크를 꽤 잡아먹는데, 모든 노드들에 EBS를 부착해 PVC를 제공하는 건 비용효율적이지 못하고, 캐시 히트율도 떨어집니다.
그래서 Karpenter의 Consolidation 오토스케일링 방법을 사용하기로 했습니다. 그럼 먼저 Karpenter의 오토스케일링 방법 종류에 대해서 먼저 알아보면,
- Consolidation(통합) : 두 개 노드의 파드들을 합쳐 한 개로 만들 수 있으면 합쳐 scale-in을 수행하는 방식입니다. 진행 중인 파드들을 scale-in 과정에서 의도치 않게 죽일 수 있지만, `karpenter.sh/do-not-evict:"true"` annotaition을 추가해 파드들이 안 쫓겨나도록 할 수 있습니다. (예시로 CI 빌드 파드들이 통합 옵션을 사용하면서도 해당 annotations 덕분에 강제종료되지 않고 운영되게 할 수 있습니다.)
- Emptiness(공백) : Karpenter는 마지막 워크로드(데몬 셋이 아닌) 포드가 노드에서 실행을 중지할 때를 기록합니다. 그 시점부터 Karpenter는 프로비저너에서 설정한 ttlSecondsAfterEmpty 시간(초) 동안 기다린 다음 Karpenter는 노드 삭제를 요청합니다. 이 기능은 더 이상 워크로드에 사용되지 않는 노드를 제거하여 비용을 낮출 수 있습니다. 안정적으로 운영할 수 있지만 비용효율은 통합 옵션보다 떨어지는 특징이 있습니다.
저는 Karpenter의 Consolidation 옵션을 사용한 Provisioner를 생성해 Drone CI 빌드들만의 노드를 격리시키기로 결정했습니다. 이게 무슨 의미인가 하면, 현재 Drone CI가 몸담고 있는 클러스터에는 많은 운영도구들이 존재하고, 앞서 말한 시끄러운 이웃 문제와 캐시 빌드율 문제를 해결하기 위해 CI 빌드만이 스케줄링되도록 노드를 격리하는 겁니다. (당연히 taint-tolerations, nodeAffinity 옵션을 동시에 걸어야 합니다.)
그리고 비용 효율을 위해 해당 Provisioner에 Consolidation 옵션을 추가해 CI 빌드가 적을 때는 scale-in이 빠르게 일어나도록 했죠. 좋습니다.
여기까지는 다 좋았는데, 한 가지 문제가 있었습니다. scale-in이 너무 빠르게 일어나다 보니, scale-out도 너무 빈번하게 이뤄지는 겁니다. karpenter는 cluster autoscaler보다 scale-out시 시간이 매우 단축되었지만, 아쉽게도 2~3분의 시간이 걸립니다. 애써 클러스터 마이그레이션으로 CI 빌드 Pending 시간을 줄였는데 노드 scale-out이 이뤄질 때마다 시간이 추가되는 것은 너무 아쉽습니다.
또 이를 해결하기 위해서 PriorityClass를 음수 값으로 준 over-provision 용 Deployment의 replicas 조절하는 cronJob으로 이 문제를 해결했습니다.
뭔가 말이 길어 보이지만, 뜯어보면 쉽게 이해할 수 있습니다. PriortyClass를 음수 값으로 준 over-provision 용 Deployment는 어떤 역할을 하는 것일까요?
priorityClass는 파드의 우선순위를 지정하는 방법 중 하나입니다. 만약 클러스터 내의 리소스가 부족해지는 상황이 발생하여 파드들이 쫓겨날 때, 우선순위가 높은 파드들이 먼저 스케줄링되며, priorityClass가 음수인 파드들은 가용한 자원이 없을 때 쫓겨날 수 있습니다. 이미지를 보면 알 수 있지만 이 deployment는 모두 더미 파드들입니다. 하지만 priorityClass 값이 음수 인 덕분에 스케줄러가 새로운 파드를 스케줄 할 때 요 더미 파드들이 보이면 바로 쫓아내 버리죠.
즉 이 더미 파드들의 replicas를 업무시간대에만 늘려 놓으면 기본적으로 노드가 의도한 개수만큼은 떠있게 됩니다. 그러면서도 새로운 CI 빌드들이 스케줄링될 때면 더미 파드들을 쫓아내 버려 scale-out때의 2~3분의 스케일링 시간을 CI 빌드의 Pending 시간에 소요하지 않게 되죠.
그리고 업무시간이 끝날 시간대에 다시 replicas를 최소로 축소해 버리면 자주 쓰이지 않는 시간대에 비용효율적으로 운영할 수 있습니다. 그리고 이 replicas를 조절하는 방법은 cronJob으로 수행하게 하면 됩니다! 그렇게 제가 수행하고 싶었던 3가지 문제의 토끼들을 한 번에 잡을 수 있었답니다.
CI 도구 개선 작업의 영향 -> 개발자 생산성 향상
모든 클러스터 이전 작업이 끝나고, 운영 고도화 작업을 잘 끝내고, 개발 플랫폼 생산성을 측정하는 대시보드를 확인했습니다.
그 결과 작업 후에 CI 잡이 도는 Pending 시간이 눈에 띄게 개선이 되어 P95 Pending 시간이 10초 내로 줄어드는 멋진 결과를 낼 수 있었습니다. 비롯해 인프라의 문제로 발생하는 이런저런 에러(빌드 실패)의 비율도 개선이 많이 되었습니다. drone이 아픈 것 같다며 Slack에서 들어오는 메시지도 현저히 줄어들게 되었고요.
단순하게 CI 빌드 시간이나 에러 비율을 개선했다고 해서 이 작업이 조직에 어떤 생산성의 효율을 가져왔을 것이라 생각할 수 있을까요?
maily.so/saascenter/posts/64bf5ac2
이와 관련해서 CTO님이 공유해 주신 흥미로운 기사가 있어 공유해 드릴게요. 빌드 주기 동안 얼마나 많은 시간을 절약할 수 있는지 비교하고 이를 개발자가 생산성을 높이기 위해 얼마나 더 많은 시간을 투자해야 하는지 제곱해서 실제 비즈니스 비용을 알아보는 내용입니다. 실제 기사에서는 컴퓨팅 하드웨어 장비의 옵션을 높여 생산성을 높이는 의의를 설명하고 있지만, 이번 작업의 경우 마이그레이션 작업으로 생산성을 높이는 효과를 불러왔으므로 결론적인 효과는 비슷할 것입니다.
개발자가 빌드가 실행될 때까지 기다리기만 하고 그 기간 동안 다른 작업을 하지 않는다고 가정해 보겠습니다.(이는 좋은 상황은 아니지만 실제로 일어날 수 있는 일입니다)
그렇다면 이로 인해 기업은 어떤 손해를 볼까요? StackOverflow의 2022 개발자 설문조사에 따르면 미국 내 개발자의 평균 연간 비용은 부가 혜택, 세금 등을 포함하여 연간 약 15만 달러입니다. 이를 시간당으로 환산하면 약 75달러(USD)입니다. 즉, 개발자가 1시간 동안 빌드 실행을 기다리면서 그 시간 동안 아무것도 하지 않는다면, 기업은 여전히 그 개발자의 시간 동안 평균 75달러를 지출하고 있으며, 개발자가 더 많은 코드를 빌드하는 데 집중할 수 있는 시간을 잃고 있는 것입니다.
개발자의 평균 시간당 요금을 $75라고 가정할 때, 개발자가 대기하거나 컨텍스트 전환을 하지 않도록 더 많은 컴퓨팅 성능을 위해 더 많은 비용을 지불하는 것이 거의 항상 합리적이라는 것을 보여줍니다.
가장 비싼 컴퓨팅 옵션(64코어 및 256GB RAM의 경우 시간당 $15)도 개발자 한 명의 시간당 비용의 5분의 1에 불과합니다. 개발자 급여가 증가하면 하드웨어 비용이 감소하거나 작업을 실행하는 데 걸리는 시간이 감소하며, 이러한 반비례 관계는 더 나은 장비를 구입해야 하는 이유를 가리키고 있습니다.
가장 비싼 건 개발자들의 시간이라고 합니다. 빌드 시간을 한없이 기다리거나 인프라의 오류로 의심되는 상황을 제보하고 해결하는 시간 등으로 낭비되는 조직의 생산성도 큰 문제지만 조직의 개발 플랫폼의 신뢰도가 낮아지는 건 DevOps 문화의 치명적인 영향을 끼쳤을 것입니다.
첫 시작은 입사 후 첫 과제에 대한 이야기였지만 끝맺음이 좀 더 큰 주제에 대해 이야기하는 것 같아 마무리가 잘 되었는지 모르겠네요..! 배운 것도 많았고 삽질도 많았지만 단독으로 맞는 첫 프로젝트를 나름대로 성공적으로 마무리 지은 것 같아 뿌듯했습니다.
Drone CI 마이그레이션 외에도 여러 가지 다양한 일들을 했는데, 기회가 나면 시리즈(3)로 돌아오도록 하겠습니다.
요즘은 IDP에 많은 관심을 가지고 있습니다. 특히 port라는 플랫폼을 관심 있게 보고 있는데, 어쩌면 개인적으로 활용하고 포스팅을 올릴 수도 있을 것 같습니다. 관련해서 추천해주시고 싶은 플랫폼이나 오픈소스가 있다면 댓글로 달아주시면 감사드리겠습니다.
미세먼지가 많은 요즘입니다. 마스크 꼭 끼고 다니시고 감기 조심하세요! 감사합니다.
'🏋️♀️ DevOps, SRE' 카테고리의 다른 글
Kaniko : 도커 없이 kubernetes에서 이미지 빌드하기 (0) | 2023.06.08 |
---|---|
쿠버네티스의 4가지 설계 원칙 : Understand the "Why?" (1) | 2023.06.04 |
[k8s] 당신의 pod에서 nslookup이 실패하는 이유 (0) | 2023.02.18 |
신입 데브옵스 (DevOps) 엔지니어 되기 - (1) (25) | 2023.02.06 |
[k8s] Kubernetes CronJob 뜯어보기 - 구현 원리, best practices (4) | 2022.11.15 |