쿠버네티스에게 Github Actions 설치에 대해 묻다

Image not Found

Introduction

안녕하세요. 버즈빌 DevOps 팀의 엔지니어, Cade 입니다. 최근 CI/CD에서 가장 인기 있는 플랫폼 중 하나인 깃헙 액션 (Github Actions), 많이들 사용하고 계실 텐데요. 오늘은 이 깃헙 액션을 가장 효율적으로 사용할 수 있도록 자체 호스팅(self-hosted)으로 설치하고 운영하는 방법에 대해 제가 고민하고 진행했던 경험을 공유하려고 합니다.

들어가기 전에, 저희는 왜 깃허브(Github)가 제공하는 깃헙 호스팅(github-hosted)으로 깃헙 액션을 사용하지 않는지부터 설명해 드리면 좋을 것 같아요. 깃헙 호스팅은 GitHub에서 관리되기 때문에 유지 관리의 번거로움을 덜 수 있어 편리합니다. 하지만 여러 가지 단점 또한 존재하는데요.

먼저 빌드 환경에 대해 완전한 제어가 불가능합니다. 예를 들어 특정 운영체제 버전이나 도구를 사용하고 싶을 때가 있는데, 깃헙 호스팅을 선택하면 러너(Runner) 환경에서 제한된 행동밖에 하지 못하고, 불필요한 운영 비용과 전체적인 CI 시간 비효율성이 발생합니다. 또 내부 통신망 접근에 대한 보안적인 문제가 발생하거나, 까다로운 네트워크 작업이 필요해집니다.

저희는 이와 같은 단점 때문에 자체 호스팅으로 깃헙 액션의 러너를 운영하게 되었습니다.

ARC (Actions Runner Controller) 란?

깃헙 액션 러너를 자체 호스팅으로 실행시키는 2가지 방법이 있습니다. EC2와 같이 단일 인스턴스에 설치하는 방법과, ARC(Actions Runner Controller)를 사용해 쿠버네티스(Kubernetes) 위에 설치하는 방법이 있는데 저희는 쿠버네티스에 설치해 운영하기로 했습니다.

여기서 ARC는 깃헙 액션용 ​​자체 호스팅 실행기를 조정하고 확장하는 쿠버네티스 오퍼레이터입니다. 쉽게 말해 깃헙 액션 워크플로가 실행되는 러너를 쿠버네티스 파드로 실행할 수 있도록 기능을 제공하는데요. 저희가 ARC를 선택한 결정에는 다음과 같은 이유가 있습니다.

비용 효율성

단일 인스턴스에 설치하여 가상 머신(VM)을 기반으로 한 오토 스케일링 방법은 가상 머신을 오버 프로비저닝하여 리소스 낭비를 초래하거나 가상 머신을 언더 프로비저닝하여 빌드 속도를 늦추고 개발자 경험(DX)을 저하할 수도 있습니다. 여기서 ARC를 사용하게 되면 각 러너 파드마다 워크플로 성격별로 최적화된 리소스를 할당해 비용 효율적으로 운영할 수 있습니다.

확장성

ARC에서는 쿠버네티스의 강력한 기능을 활용하여 워크로드 할당량에 따라 러너 파드를 자동으로 확장합니다. 이렇게 하면 러너 리소스를 낭비하지 않고 인프라를 보다 효율적으로 사용할 수 있습니다.

속도

ARC에서는 항상 일정한 수의 대기 상태의 러너 파드를 유지하도록 구성할 수 있습니다. 뿐만 아니라 쿠버네티스는 요청에 따라 새로운 러너 파드를 상당히 빠르게 할당할 수도 있습니다. 이러한 이유들로 러너가 Pending 상태의 시간이 거의 없으므로 많은 워크플로가 더 빠르게 실행될 수 있습니다.

이 3가지뿐만 아니라 저희 DevOps 팀에서는 95%가 넘는 운영도구들을 쿠버네티스 위에서 설치&운영하고 있기 때문에, 현재 인프라에 쉽고 빠르게 추가할 수 있다는 장점도 있었습니다.

ARC의 2가지 버전 : Community , Github

초기 ARC는 몇 명의 핵심 컨트리뷰터로 운영되는 오픈소스 커뮤니티 프로젝트로 시작되었습니다. 이 커뮤니티 버전은 2020년 2월에 출시되었습니다. 이 프로젝트는 쿠버네티스에서 Actions Runner를 실행시킬 방법을 찾고 있는 개발자들 사이에서 인기가 높아졌습니다. 이후로 많은 개발자가 깃허브에 프로젝트를 공식적으로 지원하도록 요청하기 시작했고, 2022년 12월 깃허브는 이 프로젝트를 공식적으로 채택하고 이를 완전한 깃허브 제품으로 전환하는 작업을 시작했습니다. 그 후, 깃허브는 2023년 7월에 Github supported 버전을 공식 출시했습니다.

ARC-diagrams

따라서 현재 ARC에는 두 가지 버전이 있습니다. 하나는 깃허브에서 지원하고 다른 하나는 커뮤니티에서 지원합니다. 두 버전은 다음과 같은 차이점이 존재합니다.

제목 Github supported ARC Community supported ARC
모드 명칭 Runner Scale Sets mode Pull driven scaling mode , Webhook driven scaling mode
apiVersion apis/actions.github.com/v1alpha1 actions.summerwind.net/v1alpha1
사용하는 러너 이미지 runner actions-runner-controller
유지 보수&관리 깃허브 커뮤니티
스케일 방식 long-poll 커넥션 Pulling or Webhook
특징 신규 기능에 대해 보수적이지만 안정적임. 신규 기능에 대해 개방적이지만 불안정함.
장점 - certmanager 추가 설치 불필요
- github API 속도 제한 문제 발생하지 않음
- 컨트롤러 디버깅이 수월함
- 러너 다중 라벨 지원
- 러너 크론식 스케줄링 기본 지원
최신 버전 (23.12 기준) v0.8.1 v0.23.7
출시일 2023.07 generally available 2020.02 available

깃허브 지원 ARC가 여러 장점을 가지고 있다는 것을 표로 설명해 드렸는데, 커뮤니티 지원 ARC와 비교해서 왜 그런 특징을 가졌는지 말씀드리겠습니다. 두 가지 모드의 가장 큰 차이점은 Client-Server 구조의 변화입니다.

ARC-client-server

깃허브 지원 ARC의 경우 인증 이후 https long-poll 연결을 시도하는 client가 되지만 커뮤니티 지원 ARC의 경우 생성 이벤트를 전달받는 server가 되기 때문에 https 통신을 위해 TLS가 필요해집니다. 이를 위해 cert-manager를 추가로 설치해줘야 하는 의존성이 생기게 됩니다.

뿐만 아니라, 가용 러너에 할당하기 위해 풀링 방식의 스케일링을 사용하는 커뮤니티 지원 ARC는 개인 또는 조직의 레포지토리 워크플로 상태를 가져오는 동작을 위해 깃허브 API를 과다하게 사용하게 됩니다. 이 과정에서 시간당 1,000개의 깃허브 API 개수 제한을 초과해 API 속도 제한 문제가 발생합니다. 웹 훅 방식을 선택하는 경우에도 트러블슈팅이 까다로운 문제가 있습니다.

그에 비해 깃허브 지원 ARC는 위의 단점이 모두 보완되었습니다. 물론 초기 오픈 소스이기 때문에 관리 및 운영에 시간이 다소 소요된다는 단점이 있습니다.

따라서 위와 같은 장단점을 고려해 ARC의 모드를 선택해야 합니다. 저희는 깃허브 지원 ARC인 Runner Scale Sets 모드를 선택해 설치했습니다. 여러 가지 이유 중 아래의 이유가 가장 주요했습니다.

With the introduction of autoscaling runner scale sets, the existing autoscaling modes are now legacy. The legacy modes have certain use cases and will continue to be maintained by the community only.
– actions-runner-controller README.md

커뮤니티 지원 ARC가 레거시화 되었을 때의 마이그레이션 비용이 현재의 깃허브 지원 ARC의 부족한 기능으로 인한 운영 비용보다 더 크다고 생각했기 때문입니다.

Github 지원 ARC : Runner Scale Sets 모드

본격적으로 ARC를 설치하고 워크플로를 실행시켜보겠습니다. 설치는 헬름(helm)을 사용해 진행하면 됩니다. 헬름 차트는 2개를 설치해야 하는데, ARC와 Runner Scale Sets(실행기 확장 집합) 입니다. ARC는 깃허브 API를 직접 호출하고 상태를 업데이트하는 컨트롤러의 역할을 하고 Runner Scale Sets는 워크플로가 실행되는 러너 환경을 정의하는 역할을 합니다.

먼저 ARC부터 헬름 차트를 사용해 설치해보겠습니다. 자세한 차트 values는 문서를 참고해주세요.

NAMESPACE="arc-systems"
helm install arc \
    --namespace "${NAMESPACE}" \
    --create-namespace \
    oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

다음은 워크플로가 실행되는 환경을 정의하는 Runner Scale Sets를 헬름 차트를 사용해 설치해보겠습니다. 해당 차트의 경우 values를 다양하게 구성할 수 있는데, 저희는 이렇게 구성해보았습니다. 자세한 차트 values는 문서를 참고해주세요.

# githubConfigUrl는 러너에 구성하고 싶은 Github url 입니다.
## ex: https://github.com/myorg/myrepo or https://github.com/myorg
githubConfigUrl: "https://github.com/Buzzvil"
# git 인증을 위한 시크릿 명입니다.
githubConfigSecret: gha-runner-gha-rs-github-secret

## maxRunners는 실행기 확장 집합이 최대로 확장할 러너의 최대 수입니다.
maxRunners: 3
## minRunners는 실행기 확장 집합이 축소할 최소 러너 수입니다.
minRunners: 1

runnerGroup: "default"

## 생성할 Runner scale set의 이름입니다. (Defaults to the helm release name)
runnerScaleSetName: "buzzvil-test"

containerMode:
  type: "dind"  ## type can be set to dind or kubernetes

## template은 각 Runner Pod에 대한 PodSpec입니다.
template:
  spec:
    serviceAccountName: github-actions-runner
    containers:
      - name: runner
        image: 111111111111.dkr.ecr.ap-northeast-2.amazonaws.com/actions-runner:0.0.1
        command:
          - /home/runner/run.sh
        resources:
          limits:
            cpu: 2
            memory: 2Gi
          requests:
            cpu: 1
            memory: 1Gi

차트 구성에서 설명해 드리면 좋을 몇 가지가 있습니다.

1. 최소 러너 수 구성이 빠른 실행, 비용 효율성의 핵심!

## maxRunners는 실행기 확장 집합이 최대로 확장할 러너의 최대 수입니다.
maxRunners: 3
## minRunners는 실행기 확장 집합이 축소할 최소 러너 수입니다.
minRunners: 1

최소,최대 러너 수를 구성하고 각 러너 파드의 리소스 할당량을 지정할 수 있습니다. 최소 러너 수를 지정하면 그 수만큼 즉시 job에 할당될 수 있는 Idle 상태의 러너가 준비됩니다. 엔지니어 팀의 워킹 타임 동안 평균적으로 필요한 러너 수의 통계를 낸 다음, 최소 러너 수를 구성하면 러너 파드의 Pending 시간을 최소한으로 감소시킬 수 있습니다. 뿐만 아니라 워킹타임이 시작될 때와 끝날 때, cronJob 등을 통해 최소 러너 수를 조정하면 비용 효율적으로 ARC를 사용할 수 있게 되겠죠.

2. IAM user ACCESS KEY 없이 CI를 돌릴 수 있게 하려면?

template:
  spec:
    serviceAccountName: github-actions-runner

ServiceAccount 리소스를 생성해 serviceAccountName 필드에 생성한 이름을 넣어 IRSA(IAM Roles for Service Accounts)를 구성할 수 있습니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: github-actions-runner
  namespace: arc-systems
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/github-actions-runner

이렇게 구성하면 각 러너 그룹마다 지정된 역할을 할당해 AWS 리소스 접근에 대한 권한을 제한할 수 있게 됩니다.

3. 사용할 Runner 이미지는 조직에 맞게 직접 빌드해서 사용하자!

    containers:
      - name: runner
        image: 111111111111.dkr.ecr.ap-northeast-2.amazonaws.com/actions-runner:0.0.1

러너의 이미지는 기본 러너 이미지를 베이스로 해 직접 필요한 도구를 설치해 빌드한 다음 사용하시는 것을 적극 권장해 드립니다. 커뮤니티 버전의 ARC 기본 러너 이미지와 다르게, 깃허브 버전의 ARC 기본 러너 이미지는 굉장히 슬림한 형태로 제공되고 있습니다. (심지어 git cli도 기본내장되어 있지 않습니다…)

# Dockerfile을 작성해 Runner 커스텀 이미지를 만드는 예시

FROM ghcr.io/actions/actions-runner:2.311.0

# 필요한 도구 설치 예시
RUN sudo apt-get update && sudo apt-get install -y git curl gcc jq unzip wget 

4. runnerGroup으로 레포지토리별, 워크플로별 사용 가능한 러너를 지정해 줄 수도 있습니다.

runnerGroup: "default"

[조직] → [설정] → [Actions] → [Runner groups] 탭에 들어가면 레포지토리나 워크플로를 지정하여 특정 러너에 대한 접근을 제어할 수 있습니다. 리소스 요구량이 큰 러너나 특수한 보안적 요구사항을 가지고 있는 러너에 러너 그룹을 묶어 주어 접근을 ARC로 편하게 관리할 수 있게되죠. 기본 Default 그룹을 사용한다면 그룹의 모든 레포지토리와 워크플로가 접근할 수 있게 됩니다. RunnerGroup

5. dind 모드, kubernetes 모드? 어떤 점이 다른거야?

containerMode:
  type: "dind"  ## type can be set to dind or kubernetes

깃헙 액션의 서비스 컨테이너나 컨테이너로 실행 등의 처리를 위해서 ARC에서는 컨테이너 모드를 선택해 기능을 지원해야 합니다. 컨테이너 작업의 지원을 위한 두 모드의 차이를 짧게 요약하면 다음과 같은데요.

  • dind : 러너 파드의 사이드카 형태로 docker-in-docker 컨테이너가 붙는 형태입니다. docker 명령어를 사용할 수 있어 도커 이미지 빌드가 가능합니다. 하지만 특권(privileged) 모드로 실행되어 보안 수준이 강력하지 않습니다.

  • kubernetes : ARC가 러너 컨테이너 후크를 사용하여 컨테이너용 파드를 새로 생성합니다. 러너 파드와 컨테이너용 파드는 퍼시스턴트 볼륨을 사용해 작업을 공유하게 됩니다. 보안 수준이 강력하지만 docker 명령어를 기본적으로 사용할 수 없어 도커 이미지 빌드 방법에 제한이 생깁니다. (kaniko 등을 사용해 빌드 가능)

더 자세한 동작의 구분은 공식 문서를 참고해주세요. 저희는 dind 모드를 선택해 설치를 진행하겠습니다.


6. Github API 인증을 위해 시크릿도 추가해주기

깃허브 인증을 위한 시크릿 추가도 완료해주면 됩니다. 자세한 내용은 공식 문서를 참고해 진행해주세요.

apiVersion: v1 
kind: Secret 
metadata: 
  name: gha-runner-gha-rs-github-secret
  namespace: arc-systems 
type: Opaque 
data: 
  github_token: <BASE64 ENCODED GITHUB PAT>

마지막으로 헬름 차트 구성을 위한 values가 다 작성되었다면 컨트롤러를 설치했을 때처럼 헬름 명령어를 사용해 실행기 확장 집합을 설치해줍니다.

NAMESPACE="arc-systems"
helm install arc \
  --namespace "${NAMESPACE}" \
  --create-namespace \
  -f arc_values.yaml \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

모든 설치가 완료되면 다음과 같이 쿠버네티스에 controller와 listener , runner 워크로드가 생성된 ARC가 구성된 것을 확인할 수 있습니다.

ARC-pod

공식문서에서 다음과 같이 아키텍처 다이어그램을 소개하고 있습니다. 아키텍처 플로우가 길고 이해하기 까다로울 수 있는데, 3개의 리소스를 쉽게 풀어 요약하면 다음과 같습니다.

  • controller : 깃허브 API 직접 호출, 러너 상태 체킹 및 업데이트 담당

  • listener : 러너 스케일링을 담당. 깃허브 서버와 long-poll 커넥션을 맺고 부족한 러너 개수만큼 레플리카를 늘림.

  • runner : 실질적인 워크로드가 실행되는 격리된 환경. 깃허브 서버와 long-poll 커넥션을 맺고 잡(job)을 실행함.

arc-architecture 출처: Github, Actions Runner Controller

ARC의 자동 스케일링 실행기 ScaleSet 모드를 보여주는 다이어그램 그 중 controller는 EphemeralRunner , AutoScalingListener 등 다양한 컨트롤러 기능이 합쳐져 있는데, 그 중 상태 확인 시 유용한 CRD는 EphemeralRunner 로, 한눈에 실행 중인 워크플로의 ref 그리고 job의 이름이나 실행 중인 레포지토리 이름 등의 정보를 알 수 있습니다.

Ephemeralrunners

이렇게 추가된 러너들은 Actions - Runners 탭에서 확인할 수 있고, 앞서 values에서 작성한 네이밍을 라벨로 사용해 워크플로를 해당 러너에서 실행시킬 수 있게 됩니다.

마무리하며

지금까지 쿠버네티스에 깃헙 액션을 설치하는 여러 가지 방법에 대해 알아보고, 그 중 저희 조직에 가장 맞는 방법이라 판단한 깃허브 버전의 ARC를 설치하는 과정까지 설명해 드렸습니다. ARC는 지금까지도 여러 가지 기능에 대한 열띤 토론이 펼쳐지는 현재진행형의 오픈 소스입니다. 앞으로도 더 유용한 기능과 뛰어난 퍼포먼스가 기대됩니다.

이번에는 ARC를 설치하는 과정만 설명해 드렸는데요. 다음 기회에는 저희 DevOps팀이 ARC를 어떻게 모니터링 하는지, 러너 타입은 어떻게 구분해서 관리하는지, 깃헙액션 공용 워크플로를 어떻게 관리하는지 등 많은 부분을 설명해 드릴 수 있으면 좋을 것 같습니다.

이 포스팅이 깃헙 액션을 자체 호스팅으로 어떻게 설치할 것인지, 여러 자체 호스팅 모드들에 대해 어떤 장단점이 있는지 고민인 분들에게 도움이 되길 바랍니다. 긴 글 읽어주셔서 감사드리며, 좋은 하루 되세요!

버즈빌 개발자 지원하기 (클릭)

버즈빌 테크 리크루터와 Coffee Chat하기 (클릭)

You May Also Like

버즈빌, 아마도 당신이 원하던 회사!

지원하기