Github Actions이 출시된 지 시간이 꽤 지나고 CI는 Github Actions으로 통합되는 분위기가 되어가고 있습니다.
이번에 Github Actions을 kubernetes에서 self-hosted로 설치하고 운영하면서 생긴 여러 이슈가 있어 공유해드리려고 합니다.
글이 좀 길 수 있습니다.ㅠ 2개로 쪼갤까 고민하다 합쳐서 올리게 되었네요.
(발표자료를 먼저 만들고 블로그에 공유하는 것이라 발표 슬라이드처럼 생긴 이미지가 많을 수 있습니다!)
Github Actions을 도입하고자 할 때 선택할 수 있는 여러 가지 방법이 존재하는데요, 먼저 대부분이 알고 계신 Github-hosted 방식입니다. 서버를 따로 프로비저닝 할 필요 없이 사용할 수 있어 운영 비용이 크게 발생하지 않는다는 점이 큰 장점입니다.
다만 Github이 관리하고 지원하는 base OS만 가능하고, 최대 2vCPU,7GB만 지원하는 등의 한계도 존재합니다. 물론 현재 베타서비스로 그보다 큰 리소스도 지원하는 것으로 알고 있지만 아직 정식 출시는 하지 않은 걸로 압니다.
두 번째로 단일 인스턴스 위에 Self-Hosted로 구축하는 방법이 있는데, 마지막으로 설명할 k8s위에 구축하는 방법과 비교해 어떠한 장점도 사실 느끼지 못해서 크게 언급하지는 않겠습니다. 더 구체적으로 이야기하자면 제가 이전에 CI 도구를 k8s위에 운영한 경험이 존재했고 무엇보다 단일 인스턴스에 도구 설치하고 운영하는 게 너무너무너무너무 별로입니다. pod처럼 격리된 실행도 불가능하고요.
마지막으로 이번에 이야기해 드릴 kubernetes 위에 Self-Hosted 방식으로 구축하는 것입니다. kubernetes의 장점을 그대로 가져갈 수 있는데, 각 job 특성별로 리소스를 다르게 할당해 줄 수도 있고, 오토 스케일링도 빠르고 깔끔합니다. 게다가 IRSA기반으로 따로 워크플로에서 AWS 토큰 넣고 하는 방식을 안 해도 되죠. 또 PVC와 CSI 드라이버를 사용하면 도커 이미지 레이어 캐싱도 아름답게 수행할 수 있어서 빌드 시간도 확 절감할 수 있습니다. (저희는 이 기법과 다른 기능을 더해서 빌드 시간을 약 58% 정도 줄일 수 있었습니다.)
kubernetes에서 self-hosted로 Github Actions를 운영하기 위해선 Actions Runner Controller (ARC) 요놈을 설치해줘야 합니다. 기본적으로 ARC라고 불리는 이 친구는 Github Actions을 kubernetes 상에서 운영하기 위한 쿠버네티스 오퍼레이터입니다.
github 서버에서 쏴주는 요청을 도메인에서 받기 위해 Ingress가 구성되고 인증서 처리를 위해 cert-manager 구성으로 Certifiate, Issuer 등이 추가로 설치됩니다. 그렇게 받은 요청을 actions-controller에게 쏴주죠.
이 컨트롤러는 추가로 self-hosted runner에게 순차적으로 job의 내용을 쏴주거나 실행과 종료를 모두 컨트롤합니다.
ARC는 helm을 통해 설치할 수 있는데, 해당 차트를 참고하면 됩니다. values가 다양하고 많아서 첫 구성에 시간이 걸릴 수도 있습니다. 추가로 cert-manager도 설치되어야 하는데 cert-manger 이야기도 하면 너무 길어질 것 같아서, 이후에 따로 포스팅해보도록 하겠습니다.
설치가 완료되었다면 Github API로 인증하기 위해 Github APP을 등록해야 하죠. (문서 참고)
이다음으로 actions-controller가 컨트롤하고 실제 워크플로가 실행될 self hosted runner가 필요한데, 요놈은 차트로 설치되진 않고 직접 추가적으로 설치해줘야 합니다. CRD인 RunnerDeployment와 RunnerSet 둘 중 하나로 설치하면 되는데, 차이점은 Deployment와 Statefulset의 차이와 같습니다.
이 RunnerDeployment spec을 통해 job이 실행될 pod의 replica나 resources 등을 정의해 줄 수 있습니다. 뿐만 아니라 repo, org 지정도 가능하죠. 잘 설치했다면 다음과 같이 Runner가 대기상태에 있는 걸 확인할 수 있습니다.
자, 그럼 스케일링 방법은 어떻게 할까요? github actions는 workflow가 들어올 때마다, 존재하는 runner에 실행하는 방식입니다. 즉, runner가 모두 실행되고 있으면 큐에 순차적으로 쌓여저서 workflow들이 대기를 해야하는 단점이 있습니다. Pending시간이 길어지면 중요한 핫픽스 배포도 늦어지고 DX도 끔찍하게 저하되겠죠.
그래서 ARC에는 HorizontalRunnerAutoscaler (HRA)라는 CRD를 제공합니다.
Self Hosted Runner를 오토 스케일링 하는 친구입니다. 이 HorizontalRunnerAutoscaler의 다양한 auto scaling 방식을 지원을 하는데요, 각 방식마다 구현 방식과 그에 따른 장단점의 차이가 존재합니다. 크게 Pull-Driven 스케일링과 Webhook-Driven 스케일링을 지원합니다.
Pull Driven은 이름처럼 주기적인 interval 마다 Github로부터 정보를 읽어와서 스케일링하는 방식이고, Webhoook Driven은 Github이 워크플로 생성, 종료 등의 이벤트들을 ARC로 쏴주는 방식입니다.
Pull Driven 방식은 2개의 메트릭으로 나뉘어 2개의 종류가 있고, Webhook Driven 방식은 1개의 종류가 있습니다. 각 스케일링 종류에 대해 알아보겠습니다.
먼저 "TotalNumberOfQueuedAndInProgressWorkflowRuns"라는 메트릭을 사용하는 Pull-Driven Scaling 방식입니다. (길다 길어..) 이 메트릭은 주어진 repo 집합에 대해 Pending 상태의 모든 워크플로 개수를 폴링 합니다. 예를 들어, 플로우는 다음과 같습니다.
1. newdeal-example 레포에서 4개의 워크플로가 pending 상태
2. HRA가 이 정보를 폴링 (interval은 설정 가능)
3. 4개의 runner를 추가로 올림.
4. “scaleDownDelaySecondsAfterScaleOut” 시간 후에 pending 상태의 runner가 없다면 스케일 다운,
장점과 단점을 정리하면 다음과 같습니다.
장점
특정 리포지토리를 지정해서 지정된 리포지토리 집합으로 제한할 수 있습니다. (즉, 화이트리스팅 방식)
작업 대기열의 깊이에 따라 러너 수를 스케일링합니다. 즉, 대기 중인 작업에 대해 러너의 1:1 스케일링입니다.
단점
리포지토리 목록이 확장 메트릭에 포함되어야 합니다. 즉, 화이트리스팅 방식이므로 대규모 환경이나 셀프 서비스 환경에서는 리포지토리 목록을 유지 관리하는 것이 실행 가능하지 않을 수 있습니다. (레포지토리 목록을 일일이 추가해줘야 합니다.)
충분히 빠르게 확장되지 않을 수 있습니다. 이 메트릭은 풀 기반으로 동작하므로 interval 주기에 따라 설정된 러너 수를 폴링하므로, 결과적으로 확장 성능은 이 interval 주기에 한정되어 스케일링에 지연이 발생할 수 있습니다.
이 메트릭을 유지하려면 비교적 많은 양의 API 요청이 필요하므로 환경 규모와 동기화 기간 구성이에 따라 API 속도 제한 문제가 발생할 수 있습니다.
다음은 “PercentageRunnersBusy” 메트릭을 사용하는 Pull-Driven 스케일링 방식입니다. 이 ㅂ아식은 Github API 정보를 폴링해 Running 상태의 runner의 개수가 얼마나 존재하는지 확인한 다음 스케일링 합니다. 예시 플로우는 다음과 같습니다.
1. 전체 10개 runner 중 8개의 runner가 “Running" 상태
2. “scaleUpThreshold” 값인 75%를 넘었으므로 “scaleUpAdjustment” 값인 2만큼 runner를 늘림
3. 전체 12개 runner 중 3개의 runner가 “Running” 상태
4. “scaleDownThreshold” 값인 30% 보다 적으므로 “scaleDownAdjustment” 값인 1만큼 runner를 줄임
장점과 단점을 정리하면 다음과 같습니다.
장점
TotalNumberOfQueuedAndInProgressWorkflowRuns 메트릭과 동일하게 “지정된 repo로만 제한 기능”을 (화이트리스팅) 지원합니다.
뿐만 아니라, organization 단위로 GitHub 조직 전체의 확장을 지원해서 특히 대규모로 작업하는 팀에 유용합니다.
러너 카운트를 확장하기 위한 구성 방식으로 백분율 증가/감소 기준과 고정 증가/감소 카운트 기준 모두 지원
단점
충분히 빠르게 확장되지 않을 수 있습니다. 이 메트릭은 풀 기반으로 동작하므로 interval 주기에 따라 설정된 러너 수를 폴링하므로, 결과적으로 확장 성능은 이 interval 주기에 한정되어 스케일링에 지연이 발생할 수 있습니다.
실제 Pending 중인 작업 수에 대한 카운트가 아닌 여러 필드값을 기반으로 확장,축소를 수행하므로 원하는 러너 수가 실제 워크플로 큐 길이에 비해 새 러너를 오버 프로비저닝하거나 언더 프로비저닝할 가능성이 있으며, 이것이 문제가 될 수도 있습니다.
마지막으로 Webhook Driven 방식입니다. 웹훅이벤트에 따라 runner를 스케일링하도록 ARC를 구성할 수도 있습니다. 풀 기반 스케일링과 비교해서, 웹후크에서 자동 스케일링의 주요 이점은 스케일링이 필요하다는 사실을 ARC에 즉시 알린다는 점입니다.
웹훅 workflow_job 웹훅 수신하고 웹후크 트리거에 대해 구성된 HRA를 업데이트하여 RunnerDeployments를. workflow이 수신되면 각 이벤트가 단일 러너를 더하거나 빼는 형식으로 스케일링 됩니다.
제가 선택한 스케일링 방식은 Webhook Driven 방식이었습니다. Duration 값만 잘 정하면 단점이 안보였거든요.
그렇게 설치까지 무사히 마치고 테스트도 잘 통과했지만.... 이게 끝이 아닙니다.
새로운 모드 등장!
작성일 기준 약 한 달도 안 된 따끈따끈한 뉴스인데, 새로운 Runner Scale set 모드가 등장했습니다.
사실 갑자기 발표된 건 아니고, 22년 12월에 "Moving to a new home!"이라는 공지와 함께 Actions Runner Controller 프로젝트가 완전한 Github 프로젝트로 이관됨을 발표했습니다.
이 변경은 이전에 적은 내용들을 완전히 갈아엎을 정도의 큰 변경입니다. 왜 큰일이냐고요?
먼저, 기존의 ARC는 Github 공식 지원&관리 대상이 아니였습니다.
Github가 공식적으로 ARC에 대해 새로운 아키텍처를 들고 나온 것입니다. 그 새로운 아키텍처란,
- K8s CRD API가 완전히 바뀌었습니다. ( actions.summerwind.net/v1alpha1 -> apis/actions.github.com/v1alpha1 )
- Helm chart가 완전히 바뀌었습니다.
- 앞서 설명한 스케일링 방식이 Runner Sets라는 모드로 통합되었습니다.
- Runner pod 이미지도 바뀌었습니다.
추가로 공식 문서도 완전히 바뀌었습니다. (Runner Sets 모드 위주) 그래서 강하게 추측할 수 있는 것은 이것이죠.
Github의 공식 지원으로 커뮤니티 드라이븐 소스(summerwind 버전)는deprecated 될가능성이 큽니다.
그러니 앞으로 Kubernetes에서 Github Actions을 self-hosted로 설치, 운영하고자 하시는 분이라면 이 변화에 관심을 기울이고 변화에 민감하게 대응해야 합니다. 그럼 새로운 Runner Sets 모드라는 건 뭘까요? 알아봅시다.
controller-manager를 chart를 참고해 helm을 통해 설치해야 합니다. (참고)
runner-scale-set이라는 CRD도 helm으로 설치해줘야 합니다. (이전의 RunnerDeployment와 비슷한 친구) (참고)
설치가 완료되었다면, Github API로 인증하기 위해 Github App을 등록해야 합니다.
아키텍처가 조금 복잡한데, 플로우를 다음과 같이 따라가 봅시다.
문서의 내용을 번역한 것이므로, 자세한 내용은 아래 공식 문서를 참고해 주세요.
이렇게 새로운 모드인 Runner Scale Sets 모드를 알아봤는데, 이전의 ARC와 어떤 점이 달라졌는지 보이셨나요? 새로워진 ARC가 뭐가 좋아졌는지 살펴보면 다음과 같습니다.
- 더 이상 필요조건으로 cert-manager가 필요하지 않습니다.
- 이전에는 client가 GitHub server였지만, 이제는 controller-manger pod가 client가 되었기 때문입니다.
- (Pull driven 스케일링 경우) 스케일링 시간이 단축되고 github api 속도 제한 문제가 발생하지 않습니다.
- GitHub app token이 더 이상 Pod로 전달되지 않아 보안성이 우수합니다.
- 알 수 없는 오류에 대해 디버깅하기 훨씬 좋아졌습니다.
아직 0.5.0v밖에 되지 않았기에 얼마나 더 장점이 추가될지 기대되기도 하고, 한편으로는 아직까지 완전히 안정적이지는 않아서 여러 버그나 부족한 점이 있을 수 있습니다.
지속적으로 이슈와 토론에 관심을 가져야 현 Runner Scale Sets 모드를 운영할 수 있겠네요.
CI 도구를 설치하고 운영할 준비를 마쳤다면 CI 모니터링을 구축해야 할 단계입니다.
CI 도구의 지속적인 관리가 없으면 장애 대응시 핫픽스 배포 & DX에 치명적인 결함을 줄 수 있습니다. 꾸준히 관심을 가지고, 들여다봐야 하죠.
따라서 파이프라인,잡 성공률과 실행시간 & 실패 비율과 실패율 높은 job 필터링 등등 모니터링과 SLO 관리가 필요합니다.
Prometheus-grafana 등의 모니터링 구축을 고민했지만
Datadog - Github Integrations이 워낙 강력해서 매우 쉽게 대시보드를 구축할 수 있었습니다.
그 외에도 간편하게 Github Actions에서 synthetics 테스트를 구현, 대시보드를 구축할 수도 있고
Ci test에서 코드 커버리지 수집&관리, flaky test 관리, E2E test visibility 관리 등
Intelligent Test Runner (특정 커밋에 대해 영향을 받는 테스트만 실행, 나머지는 스킵) 기능도 있고
빌드 이벤트&로그 수집으로 문제 원인의 빠른 파악 …
최근에는 CI 뿐만 아니라 전체적인 기능에 대해 “DataDog 잘쓰기” 에 관심이 많습니다.
비용만 아니면 …
여기까지 Github Actions kubernetes self-hosted로 설치&운영하기에 대한 경험을 적었습니다.
만약 k8s위에 구축하려고 하시는 분이 있다면 Actions Runner Controller를 설치할 것인지, Runnser Scale Sets 모드로 설치할 것인지에 대한 고민이 추가될 것 같습니다.
현재로서는, 당장 Actions Runner Controller 쪽이 더 안정적이고 참고할 만한 문서나 자료도 많지만
어쨌든 언젠가 Github이 관리하는 Runner Scale Sets 모드가 금방 따라잡을 것 같네요. 물론 현재 Runner Scale Sets 모드는 버그도 있고 불안정한 것 같습니다ㅠ 기술부채나 이후 마이그레이션 작업 난이도 등을 잘 고려해 선택해야 할 것 같습니다.
앞으로도 운영하면서 생기는 여러 이슈들을 공유해 드리면 좋을 것 같네요.
감사합니다.
'🏋️♀️ DevOps, SRE' 카테고리의 다른 글
엔보이(envoy) 딥-다이브 (1) : 엔보이가 패킷을 받아 처리하기 까지 (1) | 2024.02.11 |
---|---|
Renovate bot을 사용해 argoCD로 배포한 helm 차트 버전을 체계적으로 관리해보자 (1) | 2024.01.09 |
DevOps vs SRE , 차이점과 공통점에 대해 (0) | 2023.08.22 |
짧은 생각 모음집 - crossplane, eBPF 편 (2) | 2023.07.06 |
Kaniko : 도커 없이 kubernetes에서 이미지 빌드하기 (0) | 2023.06.08 |