TL DR:
- GitHub Action의 비용은 일반 클라우드서비스 대비 4배정도 비쌈
- Self-Hosted로 전환하여 동일스펙 저렴비용 or 동일비용 높은스펙 확보 가능
- Cloud의 서비스와 유사하게, Managed vs Self-hosted 문제 > 관리포인트 증가
1. Intro
GitHub 기반의 환경에서 개발 환경을 구축할 경우, CI/CD 파이프라인 도구에 있어 Jenkins와 같은 별도 시스템을 외부에 구축하기도 하나, GitHub 환경과 긴밀히 통합되어있는 GitHub Action의 사용을 고려하기도 한다.
그러나 GitHub이 호스팅하는 GitHub-Hosted Runner의 경우 무료가 아니다. Enterprise 등급을 제외하고는 전체 파이프라인 작동시간에 대해 월간 2,000~3,000시간에 대해서 무료이지만, 그 외의 추가 사용시간에 대해 (일반적인) Linux 서버의 경우 시간당 0.008$의 비용이 청구된다.
현재 사용량이 무료 사용량 내에 해당한다면 GitHub-Hosted 액션을 사용하는게 유리하다. 별도의 보안적인 요구나 그외 특이 요구사항이 있지 않은 한, 굳이 Self-Hosted로 전환하여 추가적인 관리 부담 + Runner machine 운영비를 지불할 필요가 없기 때문이다.
그러나 사용량이 점점 많아져 무료 사용분으로 커버되지 않을 경우 그때부터는 Self-Hosted와의 편익을 비교해보는것이 좋다. 클라우드 서비스의 인스턴스 비용과 비교하기 위해, 추가 사용량에 대한 비용인 분당 0.008$를 시간당으로 환산하면 시간당 0.48$이다. 대부분의 기업 환경에서는 Private repository에서 운영할 것이기에, 프라이빗 레포의 깃헙호스트 러너의 스펙은 2코어 7기가 램이다. 이를 AWS의 기본 EC2 스펙에 대조해서 살펴보면, 대략 시간당 0.1$의 가격대로 4배정도 차이가 난다. 또한 EC2의 경우 Spot Instance나 RI/SP등의 정기계약으로 비용절감을 할 수 있다는점을 생각하면 비용의 차이는 꽤나 벌어진다
- 같은 Spec의 머신을 1/4+a 가격으로 사용하기 (시간당 0.48$ vs 시간당 0.1x$)
- 같은 비용으로, 2~4배 성능의 머신 사용하기 (2core,8GB Ram vs 4~8core,16~32GB Ram)
반대로 Self-Hosted Build 환경에는 관리 지점 증가라는 downside가 존재한다. 비용적인 측면으로만 접근하면 Self-hosted Runner를 사용하지 않을 이유가 없다. 그럼 무조건 좋아지기만 하는것인가? Self-hosted를 사용하기 위해서는 기존에 고려하지 않았던 여러 부분에 대해 결정을 해야한다
- Self-hosted Runner의 보안
- 기업의 코드나 내부 개발환경등 self-host 러너가 어디까지 접근할 수 있는가
- Self-host의 버전관리는 어떻게?
- (생략)의 환경 구성
- 추후 살펴보겠지만 기본 Self-host는 alpine급의 깡통이미지이다.
- 개발 환경에 맞게 러너의 구성을 어떻게 진행할것인가? 구성정보의 저장은?
- 빌드 / 배포마다 독립적인 환경을 어떻게 제공할 수 있을지?
- (생략)의 운영 안정성
- 빌드머신이 안돌아요! > 파이프의 문제? vs Github의 문제? + Runner의 문제?(NEW!)
- Runner 부하시 vs 평상시의 Gap을 어떻게 대응?
- Self-host의 장애상황이면 어떻게 문제의 원인을 확인하고 대응할것인가
시작할때는 위와같은 downside의 구체적인 내용을 알지 못했고, 기존 러너노드의 스펙제한에 아쉬움이 있었기에 Self-hosted를 구축해서 사용해보았다. 그 과정을 공유하고자 한다.
2. Github Action Controller(ARC)
Self-hosted Runner의 경우 빌드 머신을 단일 머신에 직접 구성한뒤 사용하거나, Action Runner Controller를 사용해 k8s 클러스터 상에 빌드 머신을 pod 형태로 구성하여 사용할 수 있다.
단일 머신 형태의 경우, 기대했던 작동을 구성하기 위해선 별도의 스크립트 작업이 필요하다. 빌드 머신에서는 매 작동마다 독립된 환경을 제공하여야 한다. 그러나 별도의 pre-job 또는 post-job 스크립트를 구성하지 않으면 이전 작업 환경의 환경 변수 또는 artifact등이 남아 독립적인 작업 환경을 구성할 수 없었다. 또한 머신 자체가 1개의 Runner로 등록되기 때문에 한번에 하나의 작업만을 수행할 수 있다. 하나의 머신을 다수의 러너로 구성하려면 별도의 작업이 필요했다.
매번 작업마다 독립적인 환경보장? 엥 그거 항상 컨테이너 강의 1장 1강에 나오는 내용아니냐.. 단일머신 환경에서 기존의 작업 환경을 이어서 할 수 있는 부분을 긍정적으로 보았으나 작동에 아쉬운점이 많아 k8s 위에 컨테이너로 빌드머신을 구성하도록 전환하였다.
내용에 summerwind
라는 키워드가 들어있으면 구 Community버전에 관한 정보라고 생각하면 된다.(또는 cert-manager 설치) k8s상에서 패키지 설치를 위해 자주 찾아보는 Helm(Artifact Hub)에서 또한, Artifact Hub에 등록된 Github Action Controller은 구 Community 버전으로 2023년 11월 이후로 업데이트가 중지되어있다. 현재 사용되는 GitHub supported ARC의 경우 Github Registry에서 관리되고 있다.
Github Supported ARC의 경우 크게 gha-runner-scale-set-controller 이라는 operator/controlplane(제어부)와 gha-runner-scale-set/dataplane(실행부)로 나누어져 구성되어 있다
- runner-scale-set-controller
- operator 패턴으로 클러스터 내부에서 github과의 연결
- HTTPS long poll 방식으로 인증 및 작업 현황 파악
- 신규 workflow job 생성 시 하단의 runner-set으로 전달
- gha-runner-scale-set
- 클러스터 내부에서 workflow job을 수행하는 runner 그룹
- autoscalingrunnerset
- scaling 가능한 runner 설정 관리
- ephemeralrunnerset
- 실제 작업을 수행하고 있는 runner 확인
3. Local에서 Github Action Controller 테스트하기
기본적인 runner의 구성, 등록 및 테스트를 위해서 minikube를 사용한 로컬 k8s 환경에 k8s runner 환경을 등록해볼 것이다.
3.1. Minikube 환경 구성
minikube start
기본 구성
3.2. Installing Actions Runner Controller
NAMESPACE="arc-systems"
helm install arc \
--namespace "${NAMESPACE}" \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
artifacthub가 아닌 Github registry 환경이므로 별도의 helm repo add 대신 chart 저장소를 다이렉트로 찍어 배포한다.
chart에 대한 values의 경우, values.yaml 을 통해 확인할 수 있다. controller의 replica 수, 환경변수, 리소스 request/limit, 파드 스케줄링(nodeSelector, tolerations, affinity, topologySpreadConstraints), 로깅 등에 대한 설정을 확인할 수 있다.
3.3. Configuring a runner scale set
INSTALLATION_NAME="arc-runner-set"
NAMESPACE="arc-runners"
GITHUB_CONFIG_URL="https://github.com/<your_enterprise/org/repo>"
#GITHUB_CONFIG_URL="https://github.com/nasir17git" #계정/조직 내 모든 repo에 등곡
#GITHUB_CONFIG_URL="https://github.com/nasir17git/logonme" #특정 repository에 등록
GITHUB_PAT="<PAT>"
#GITHUB_PAT="github_pat_1xxx"
helm install "${INSTALLATION_NAME}" \
--namespace "${NAMESPACE}" \
--create-namespace \
--set githubConfigUrl="${GITHUB_CONFIG_URL}" \
--set githubConfigSecret.github_token="${GITHUB_PAT}" \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set
ARC 컨트롤러와 유사하게, Runnerset 또한 해당 chart 저장소를 다이렉트로 찍어서 배포한다.
value 또한 values.yaml 통해 확인할 수 있다. 해당 release name으로 배포될 runner set의 배정 수준(조직,레포 단위) 및 인증정보(Github Apps 또는 PAT 토큰), Container mode (dind or dood), runner set 이름 및 replicas 개수 등을 설정할 수 있다.
배포가 끝나고나면, Github Web에서 Settings > Actions > Runners 항목에 Runner scale sets와 해당 설정으로 생성된 runner의 정보를 확인할 수 있다.
3.4. Using runner scale sets
# .github/workflows/demo.yaml
name: Actions Runner Controller Demo
on:
workflow_dispatch:
jobs:
Explore-GitHub-Actions:
# You need to use the INSTALLATION_NAME from the previous step
runs-on: arc-runner-set
steps:
- run: echo "🎉 This job uses runner scale set runners!"
위와 같은 workflow를 등록하여 배포된 ARC runner set에서 작업을 수행하는지 확인할 수 있다.
제일 큰 차이점은 runs-on:
필드에 ubuntu-latest
대신, 위에서 등록한 arc-runner-set
러너(그룹)명이 들어가있다는 점이다. 이를 통해 Github-Hosted가 아닌 Self-hosted runner에 workflow를 배정할 수 있다.
실제 action 수행기록 또한, 처음 Set up job시 k8s runner pod에 배정됨을 확인할 수 있다.
4. Outro
위의 테스트를 통해 기본적인 k8s cluster 상에 ARC pod를 배포하는 방법과, Github에 연결하여 workflow를 실행하는 과정을 살펴보았다.
그럼 이제 사용하면 되느냐? 하면 그렇지 않다. Github-Hosted Runner의 경우 원활한 사용을 위해 사전에 여러 개발도구가 내장되어 있다. (Ubuntu 24.04, Ubuntu 22.04) 그러나 Self-hosted Runner의 base image인 ghcr.io/actions/actions-runner의 경우 github action runner release와 runner container hooks만이 구성된 ubuntu 기반의 빈 머신이다. 실행시 github에 등록 및 종료시 github에 제거 외에 추가적인 도구는 하나도 구성되어있지않다 (curl, git 마저도!)
또는 빌드 환경 구성시 github action의 장점인 Action marketplace를 통해 구성할 수 있지 않느냐고 할 수 있다. 컨테이너 기반의 runner를 통해 매번 작업마다 독립된(=초기화된) 환경을 제공할 수 있지만 매번 setup단계를 반복적으로 수행하는것은 낭비아닐까.. 해당단계를 별도의 pvc에 마운트하여 캐싱을 한다면 설정시간/네트워크 사용량을 줄일 수 있지않을까?
마지막으로 self-hosted가 되면서 실제로 runner 내부에 접근할 수 있게되었다. workflow 작업 실패시 해당 state로 접근하는 설정 및 내부 구조등을 추후 글로 다룰 예정이다.