{{}< youtube id="s23QvNz2WuY?si=L5HNCesomlIeqzfN" title="Running Github self hosted runner with EKS Auto" >}
*2025년 4월 5일, 저는 EKS Auto에서 Github Actions 셀프 호스팅 러너를 실행하는 방법에 대해 라이브 스트림을 진행했습니다. AWS Heroes Arshad Zackeriya 와 존스 자카리아 노엘*과 함께.
면책 조항: 🐶 비글은 다치지 않았습니다. 약간 짜증났을 수도 있지만 — 다치지 않았습니다.
결과인 '{성능, 속도, 비용}'은 놀라울 뿐만 아니라 완벽했고, 기업 수준에서 이 솔루션을 채택하기에 충분히 유망했습니다. 이 솔루션은 AWS에만 구애받지 않는 것이 아니라, 이 블로그에서 얻은 지식을 Azure(AKS), 구글(GKE), 또는 여러분이 직접 베어메탈 서버에서 K8을 실행할 때에도 확장할 수 있습니다.
바로 해결책을 시도하고 싶은 분들은 제 저장소를 참고하세요
조금 길어질 테니 커피 한 잔 들고 편안히 앉아 최적의 현상 자세™를 취하세요 — 아시죠?
- 건축
- 왜 이 해답을 선택했는가
- K8에서 자체 호스팅 러너 컨셉
- 테라포밍 코드 워크스루
- 이 해법을 시험하는 방법
- Github 대형 호스팅 러너와 EKS Auto의 러닝 러너 비교
- 솔루션 아키텍트 관점에서
Architecture
왜 이 해결책이 필요한가
저는 Colorkrew 에서 시니어 DevOps 엔지니어로 일하고 있는데, 저희는 다양한 제품을 보유하고 있으며 개발 워크플로우를 지원하기 위해 GitHub 저장소도 많이 운영하고 있습니다.
제품 포트폴리오를 확장하면서 CI/CD 파이프라인도 점점 복잡해지고 동시적이며 빈번해졌고, 그 결과 더 많은 연산 능력과 궁극적으로 더 견고한 인프라 계층이 필요해져 우리의 증가하는 요구를 지원하게 되었습니다.
기본 무료 머신(러너라 불림)에서 깃허브 액션을 실행하는 것이 느려지기 시작했고, 초기 해결책은 GitHub가 유료로 호스팅하는 대형 러너를 사용하거나, Kubernetes처럼 인프라에서 러너를 실행하는 것이었습니다.
그래서 저는 '성능, 속도, 비용 측면에서 두 솔루션을 비교'하기 시작했고, 이것이 GitHub의 자체 호스팅 러너를 EKS Auto에서 실행하는 이 프로젝트의 시작으로 이어졌습니다.
## K8
러너: 워크플로우에서 정의된 작업을 실제로 실행하는 기계들(서버나 가상 환경)입니다. 셀프 호스팅 러너, 또는 GitHub 호스팅 러너로 호출할 때. 러너는 일시적인 성격을 가지고 있습니다.
러너 스케일 세트: 이는 동질적인 성격을 가진 논리적 러너 그룹으로 생각하라. 즉, 특정 그룹 내 모든 러너는 동일한 구성을 갖는다는 의미이다. 저장소, 조직 또는 기업 수준에서 설치할 수 있다.
만약 이기종 구성을 원한다면, 즉 CI/CD 작업에 따라 러너별로 다른 구성을 원한다면 다중 러너 스케일 세트가 필요합니다.
러너 스케일 세트에 대해 알아야 할 또 다른 중요한 점은 항상 최소 및 최대 러너 수를 설정한다는 것입니다.
러너 스케일 세트 이름: 러너 스케일 세트는 이름으로 주소 지정할 수 있으니, GHA의 'runs on:' 속성에서 해당 작업을 할당할 때 명확히 해야 할 때 기억하세요.
*** 엔드포인트:** ARC는 두 개의 엔드포인트 'api.github.com'과 'pipelines.actions.githubusercontent.com'와 통신합니다. 조직의 방화벽, 프록시, NAT 게이트웨이 등 인터넷 접속에 사용되는 모든 것이 위 엔드포인트를 ARC 컨트롤러에 허용하도록 설정되어 있는지 확인하세요.
- ARC 컨트롤러: 2개의 원소/포드를 포함합니다.
- '컨트롤러-관리자': 첫 번째로 온라인에 들어오는 팟입니다. 이 팟은 클러스터 내 서로 다른 자원을 관리하는 여러 컨트롤러가 있습니다. 중요한 점은 'AutoScalingListener' 컨트롤러가 리스너 팟을 관리한다는 것입니다.
- 자원을 생성하고 원하는 수와 상태에 맞는지 확인하는 책임.
- 'Runner ScaleSet Listener': 스케일링 요구에 대한 의사결정을 관리합니다. 몇 개의 러너를 생성할지 결정하는 책임이 있습니다. 각 리스너는 자신의 팟을 가지고 있어, 러너 스케일 세트당 리스너 팟 1개가 있습니다. 러너 스케일 세트가 2개라면, 컨트롤러 매니저 같은 같은 네임스페이스에 리스터너 팟 2개를 두거나, 다른 네임스페이스(설정 가능)에 있습니다.
- '컨트롤러-관리자': 첫 번째로 온라인에 들어오는 팟입니다. 이 팟은 클러스터 내 서로 다른 자원을 관리하는 여러 컨트롤러가 있습니다. 중요한 점은 'AutoScalingListener' 컨트롤러가 리스너 팟을 관리한다는 것입니다.
제 비유😋🫣로 더 쉽게 설명할 수 있을 것 같았습니다
액션 러너 컨트롤러(ARC )는 마치 스마트 자동화된 커피숍의 매니저와 같습니다 — 고객이 얼마나 많이 들어오는지(워크플로우)를 모니터링하고 필요에 따라 바리스타(러너)를 즉시 채용하거나 해고합니다.
혹시 몰라 하루 종일 바리스타를 대기시키는 대신, ARC는 고객이 도착할 때만 임시 바리스타(쿠버네티스의 컨테이너)를 가동하고, 작업이 끝나면 해고합니다. 이렇게 하면 시스템이 빠르고, 효율적이며, 비용 효율적으로 유지됩니다.*
*러너 스케일 세트****를 사용하면 매장 혼잡도에 따라 원하는 바리스타 수를 정할 수 있고, 나머지는 ARC가 처리합니다.
자세한 엔드 투 엔드 워크플로우는 여기에서 더 읽어보실 수 있습니다
GitHub의 Terraform 코드 워크스루 보기
Amazon EKS에서 GitHub Actions 셀프 호스팅 러너 자동 확장 설정
소개
이 워크스루에서는 Amazon EKS에 GitHub Actions Runner Controller(ARC)를 설정하여 워크플로우 수요에 따라 셀프 호스팅 러너를 자동으로 확장할 예정입니다.
프로젝트 구조
다음은 저희 구현 구조입니다:
eks-auto-self-hosted-runners/
├── README.md
├── 건축/
├── commit_log.txt
├── 대본/
└── 테라포밍/
├── 베이스/
└── 모듈/
├── 아크/
├── EKS/
├── karpenter_config/
└── VPC/
1. 루트 디렉터리 - 주요 README 및 아키텍처 다이어그램을 포함함
2. 스크립트/ - 정리 및 성능 테스트를 위한 유틸리티 스크립트를 포함함
3. terraform/ - 주요 인프라 코드
base/ - Terraform 배포의 진입 지점
모듈/ - 재사용 가능한 테라폼 모듈:
arc/ - 액션 러너 컨트롤러 구성
EKS/ - EKS 클러스터 구성
karpenter_config/ - 노드 자동 확장 구성
VPC/ - 네트워크 인프라 구성
아키텍처 개요
저희 솔루션은 다음과 같은 구성 요소를 사용합니다:
- Amazon EKS Auto: Karpenter와 함께 러너 인프라를 호스팅하는 관리형 쿠버네티스 서비스로, 러너 컴퓨트를 위해 온디맨드 노드 프로비저닝을 지원합니다
- GitHub Actions Runner Controller(ARC): 셀프 호스팅 러너를 관리하는 Kubernetes 컨트롤러
- Terraform: 모든 구성 요소를 배포하고 관리하는 인프라 as 코드 도구
이 아키텍처는 GitHub Actions 워크플로우가 러너를 동적으로 요청할 수 있게 하며, 러너들은 EKS 클러스터에서 주문 시 프로비저닝되고 필요 없을 때 자동으로 축소됩니다.
1단계: 인프라 구축
VPC 구성
먼저 공개 및 개인 서브넷으로 구성된 VPC를 생성합니다. VPC 구성은 10.0.0.0/16 CIDR 블록을 사용하며, 서브넷은 두 개의 가용성 존에 분산되어 있습니다. 그
개인 서브넷은 EKS 노드를 호스팅하며, 공용 서브넷은 NAT 게이트웨이와 부하 분산기에 사용됩니다.
terraform/base/vpc.tf 설정은 VPC 모듈을 참조하며, Kubernetes 통합을 위한 적절한 태깅과 함께 필요한 모든 네트워킹 구성 요소를 설정합니다.
EKS 클러스터 구성
다음으로, terraform/modules/eks에서 정의된 EKS 모듈을 사용해 EKS 클러스터를 만듭니다. 우리 클러스터는 Kubernetes 버전 1.31을 실행하며 실행을 위한 시스템 노드 그룹을 포함하고 있습니다
필수 클러스터 서비스.
terraform/base/eks.tf 파일은 클러스터를 공개 엔드포인트 접근 권한으로 구성하고, 워커 노드를 사설 서브넷에 배치하여 보안을 강화합니다.
2단계: EKS Auto에 사전 설치된 Karpenter 자동 확장 설정
EKS Auto는 Karpenter가 사전 설치되어 있습니다. 우리는 Karpenter를 활용해 원하는 Ec2 유형, 용량, 구성에서 Ec2 스팟 인스턴스를 프로비저링하고 자동 확장하여 GHA 러너의 컴퓨팅을 지원합니다.
Terraform/base/karpenter_config.tf 내 Karpenter 구성은 비용 효율성을 위해 m7a 인스턴스 유형과 현물 가격 책정을 사용합니다. 통합 정책은 "WhenEmpty"로 설정되어 있으며 5분 타임아웃이 있어, 노드가 더 이상 필요하지 않을 때 제거됩니다.
주요 구성 매개변수는 다음과 같습니다:
• 인스턴스 유형: 8개의 CPU를 가진 m7a 계열
• 용량 유형: 비용 절감을 위한 스팟 인스턴스
• 저장 용량: 300GB 및 5000 IOPS
• 가용성 구역: US-동쪽-1a 및 US-동쪽-1b
3단계: 액션 러너 컨트롤러(ARC) 배포
이제 우리는 terraform/modules/arc에서 정의된 ARC 모듈을 사용하여 GitHub Actions Runner Controller를 배포합니다. 모듈은 terraform/base/arc.tf 에서 locals.tf 에서 설정 매개변수를 사용하여 참조됩니다.
ARC 배포는 두 가지 주요 구성 요소로 구성됩니다:
- 컨트롤러: 러너 포드의 수명 주기를 관리함
- 러너 집합: 러너의 구성을 정의한다
컨트롤러 배치
컨트롤러는 공식 GitHub Actions Runner Controller 저장소의 Helm 차트를 사용하여 배포됩니다. 구성은 terraform/modules/arc/controller.tf 에서 정의되어 있으며 helm/controller_values.yaml의 값을 사용합니다.
러너 세트 구성
우리는 두 가지 유형의 러너 세트를 배치합니다:
- 표준 러너: 일반적인 워크플로우 작업을 위한
- Docker-in-Docker (DinD) 러너: Docker 이미지를 구축해야 하는 작업을 위한
- 'Kubernetes 모드'라는 또 다른 유형의 러너 세트도 있 는데, 이는 조직이 슈퍼유저 컨텍스트와 함께 도커를 실행할 여유가 없을 때 사용해야 합니다. 예를 들어 DinD 러너가 Kubernetes 모드를 사용할 경우를 대비 합니다(이 블로그에서는 다루지 않음).
러너 세트는 terraform/modules/arc/runner_sets.tf 에서 정의되며, 각각 helm/arc_listener_values.yaml 과 helm/arc_listener_values_dind.yaml 의 값을 사용합니다.
참고: 컨트롤러 포드 와 러너 스케일 세트 포드는 신뢰성을 위해 주문형 ec2 인스턴스에 배포되도록 구성되어야 하며, 에퍼멀 러너 포드는 스팟 인스턴스를 실행하도록 설정해야 합니다.
# 온디맨드 노드용 컨트롤러 설정
nodeSelector:
karpenter.sh/nodepool: 시스템
내용:
- 키: "CriticalAddonsOnly"
운영자: "존재합니다"
# 온디맨드 노드용 리스너 구성
리스너템플릿:
사양:
nodeSelector:
karpenter.sh/nodepool: 시스템
내용:
- 키: "CriticalAddonsOnly"
운영자: "존재합니다"
4단계: GitHub 인증 설정
ARC는 GitHub 앱을 통해 GitHub와 인증합니다. 자격 증명은 Kubernetes 비밀에 저장되어 러너 컨트롤러가 GitHub와 인증하는 데 사용됩니다.
인증 설정:
- 필요한 권한이 있는 GitHub 앱 생성
- 앱용 개인 키를 생성합니다
- 앱 ID, 설치 ID, 개인 키를 terraform/modules/arc/secrets에 저장하세요 (git ignorated, 생성하는 것도 잊지 마세요)
- terraform/modules/arc/secrets.tf 파일은 이 자격 증명으로 Kubernetes 비밀을 생성합니다
5단계: 네임스페이스 관리
ARC 컴포넌트를 위한 전용 네임스페이스를 만듭니다:
- 아크 시스템: 제어기 부품에 대해
- 아크 러너: 러너 포드용입니다
이 네임스페이스는 terraform/modules/arc/namespaces.tf에서 정의되어 있습니다.
6단계: 정리 처리
리소스의 적절한 정리를 보장하기 위해, Scripts/cleanup-finalizers.sh 에 Kubernetes 리소스에서 최종 처리자를 제거하는 정리 스크립트를 포함시켰습니다. 이 스크립트는 Terraform 파괴 과정에서 호출되어 리소스가 제대로 정리되도록 합니다.
스크립트는 다음과 같은 다양한 자원 유형을 처리합니다:
• 오토스케일링 러너셋
• 일시적 러너셋
• 자동 스케일링 리스너
• 서비스 계정
• 롤바인딩
• 역할
작동 방식
- GitHub Actions 워크플로우가 실행될 때, 특정 라벨을 가진 러너를 요청합니다
- ARC 컨트롤러가 이 요청을 감지하고 EKS 클러스터에 러너 포드를 생성합니다
- 필요 시 Karpenter는 러너 포드를 호스팅할 새로운 EC2 인스턴스를 제공합니다
- 러너가 GitHub에 등록하고 워크플로우 작업을 실행합니다
- 작업이 완료된 후, 러너 포드는 종료됩니다
- 더 이상 러너가 필요 없을 때, 카펜터는 사용하지 않는 노드를 통합하고 제거합니다
이 접근법의 장점
- 비용 효율성: 러너는 필요할 때만 프로비저비저링되고 유휴 상태일 때는 자동으로 축소됩니다
- 유연성: 맞춤형 러너 환경은 특정 워크플로우 요구사항을 충족하도록 정의할 수 있습니다
- 확장성: 시스템은 다수의 동시 워크플로우를 처리할 수 있습니다
- 보안: 러너는 정의된 보안 컨텍스트를 가진 격리된 Kubernetes 포드에서 실행됩니다
- 신뢰성: 고장 난 러너는 자동으로 교체되어 워크플로우의 신뢰성을 보장합니다
이 해결책을 테스트하는 방법
저는 다양한 유형의 GHA 3 개를 만들어 다양한 시나리오를 테스트했습니다.
간단한 테스트
첫 번째 유형은 단순한 GHA이지만, 중요한 점은 'arc-runner-set' 레이블을 사용하는 런-온 매개변수입니다.
첫 워크플로우를 실행하면 약 1분 정도 걸리는데, 'Karpenter'가 Ec2 인스턴스를 프로비저닝하고 있기 때문입니다.
프로비저닝이 완료되고 Ec2 인스턴스가 이미 존재하므로 워크플로우를 다시 실행하면 액션이 즉시 실행됩니다.
Karpenter는 노드가 최소 5분 이상 사용하지 않으면 삭제합니다.
만약 지연에 민감한 요구가 있다면, 러너 스케일 셋 구성에 minRunners>0을 설정하세요
동시 작업 테스트
이번 GHA에서는 5개의 동시 작업을 실행하며, 각 작업은 푸시나 수동으로 정확히 1분씩 실행됩니다.
DinD 직업 시험
때로는 redis와 같은 컨테이너에서 마이크로서비스를 실행하고 e2e 테스트를 하고 싶을 때가 있습니다.
여기서 우리는 Docker Inside Docker 같은 GHA가 필요합니다.
여기서 중요한 점은 runs-on 파라미터 로, 특히 dind 워크플로우에 맞게 구성된 다른 러너 세트를 사용합니다
이 워크플로우에서 Busy Box 컨테이너는 메인 작업을 실행하며, 이 작업은 redis 서비스 컨테이너와 통신할 수 있습니다.
Github 대형 호스팅 러너와 EKS Auto의 러닝 러너 비교
퍼포먼스 & 스피드
8코어 CPU와 저희 솔루션의 성능과 속도를 비교해보려고 했습니다.
저는 자동 커밋 스크립트를 사용해 10분 동안 sleep-matrix GHA를 실행해봤고, 45초마다 모든 커밋이 발생해 EKS Auto 솔루션과 Github 대형 러너 모두에서 동시 작업 상황을 효과적으로 시뮬레이션했는데, 결과는 단연 우리 솔루션에 유리했습니다
저희 솔루션은 실행 시간이 1분 8초로 일정한 안정적으로 유지되고 있습니다.
가격
업데이트 [2025년 4월 21일] : 아까 EKS 자동 노드 관리 가격에서 실수를 했지만, 수정 후 저희 솔루션이 더 저렴하게 만듭니다
이 비교는 조금 까다롭고 100% 정확하지 않을 수 있지만, 70-80% 정도는 정확하다고 볼 수 있습니다.
30일 이상 한 달 동안 하루 1시간씩 동시 CI 워크로드(12개의 작업이 병렬로 실행됨)를 가정합니다. 우리 솔루션과 Github의 셀프 호스팅 러너 비용도 계산해 봅시다
저희 솔루션<
깃허브의 대형 러너<
Github Teams/Enterprise 구독료도 있습니다. 아마도 10명 개발자 팀을 위한 Team의 저렴한 편일 거라고 생각합니다
EKS에서 m7a.2xlarge 스팟 노드를 사용하는 셀프 호스팅 비용은 월 약 170.38달러이며, 같은 GitHub의 8코어 대형 호스팅 러너는 월 731.20달러입니다. *그래서 EKS Auto에서 제 솔루션은 76.7%의 엄청난 절감 효과를 내고 있습니다. *
참고: *EKS 솔루션에 Github Teams의 솔루션을 추가해도 (예를 들어 $40 더 들고, 170+40 =$210/월에 72% 큰 절감)
1시간이 아니라 하루에 2시간씩 동시 CI 워크로드(12개의 작업을 병렬로 실행)한다고 가정해 봅시다.
참고: 네트워킹 구성 요소인 'nat gateway' 같은 비용을 추가하지 않은 것은 알고 있습니다. 합산해도 GitHub의 대형 러너들이 더 저렴할 것입니다:D
솔루션 아키텍트 관점에서
Amazon EKS Auto와 GitHub Actions Runner Controller를 결합하여 AWS Well-Architected Framework와 일치하는 솔루션을 만들었습니다:
운영 우수성:
• Terraform을 통한 인프라 as code는 일관된 배포를 보장합니다
• 자동 스케일링 러너는 수동 용량 관리를 제거합니다
• 부품의 깔끔한 분리는 유지보수성을 향상시킵니다
보안:
• 범위 권한이 있는 GitHub 앱 인증
• EKS 보안 그룹과 IAM 역할은 최소 권한을 강제합니다
• 프라이빗 서브넷은 공격 표면을 줄입니다
신뢰성:
• 다중 AZ 배포 고가용성 보장
• 0에서 자동 확장하여 수동 개입 없이 다양한 작업 부하를 수용함
• EKS Auto의 Karpenter는 지능형 노드 프로비저닝을 제공하여 필요할 때 자원을 확보합니다
성능 효율성:
• 온디맨드 스케일링은 자원이 필요할 때만 사용되도록 보장합니다
• 스팟 인스턴스는 성능을 유지하면서 비용을 절감합니다
• 맞춤형 인스턴스 유형을 통해 특정 작업 부하 요구사항에 최적화할 수 있습니다
• Docker 내 도커 지원은 최소한의 오버헤드로 컨테이너 기반 워크플로우를 가능하게 합니다
비용 최적화:
• 워크플로우가 실행되지 않을 때 비용을 절감하는 스케일-투 제로 역량
• 스팟 인스턴스는 온디맨드 인스턴스에 비해 최대 90%의 상당한 비용 절감 효과를 제공합니다
• Karpenter의 WhenEmpty 정책을 통한 자원 통합 유휴 자원 감소
지속 가능성:
• 자동 확장을 통한 효율적인 자원 활용은 환경 영향을 최소화합니다
• 스케일-투 제로 기능은 유휴 시 에너지 소비를 줄입니다
• 유휴 자원 감소, 에너지 소비 감소
이 솔루션은 조직이 인프라를 완전히 통제하면서도 GitHub Actions 워크플로우를 실행할 수 있는 확장 가능하고 비용 효율적인 플랫폼을 제공합니다. 이 아키텍처는 소규모 개발팀에서 엔터프라이즈급 배포까지 쉽게 확장할 수 있으며, 보안이나 성능을 저해하지 않으면서 변화하는 워크플로우 요구사항에 적응할 수 있습니다.
EKS와 같은 AWS 관리 서비스를 활용하고 Terraform을 통한 인프라 코드 구현을 통해 운영 오버헤드를 줄이면서 필요한 유연성을 제공합니다
현대 CI/CD 파이프라인을 위한 것입니다. 그 결과, 팀이 인프라 관리보다 가치 제공에 집중할 수 있는 견고하고 효율적인 플랫폼이 탄생했습니다.
The Zacs의 프로그램 'Talking AWS '를 유튜브에서 팔로우하거나, AWS에 대한 지식을 공유하고 싶다면 연락해 주세요.
이 해결책에 대해 궁금한 점이 있으면 LinkedIn, X를 통해 연락해 주세요.
추진
🚀 스텔라 개발 다이어리 시리즈: 에피소드 1이 생방송됩니다!
웹3 스타트업을 처음부터 키우는 데 무엇이 필요한지 궁금해한 적이 있나요? Stellar Dev Journalies 시리즈에서는 해커톤 우승에서 자금을 확보하고 메인넷에 런칭하는 개발자 팀의 여정을 따라갑니다.
읽기





