GitHub Actions 강좌 #8 셀프호스티드 러너: 도입 기준, 등록, 보안 원칙

4 분 소요

지금까지의 모든 잡은 runs-on: ubuntu-latest, 즉 GitHub가 운영하는 러너에서 돌았습니다. 관리가 전혀 필요 없는 훌륭한 기본값이지만, 이것으로 안 되는 상황이 세 종류 있습니다. 그때의 답이 러너를 직접 운영하는 셀프호스티드 러너이고, 이번 편은 도입 판단부터 보안 원칙까지를 다룹니다. 결론을 미리 두면, 필요가 실재할 때만 도입하는 도구입니다. 러너 운영은 서버 운영이라, 이유 없이 도입하면 관리 부담만 늘어납니다.

도입 기준: 세 가지 실재하는 필요 #

  1. 내부망 접근: 사내 네트워크 안의 자원(내부 API, 프라이빗 DB, 사내 아티팩트 저장소)에 접근해야 하는 파이프라인은 호스티드 러너로 처리할 수 없습니다. 내부망 안에 러너를 두는 것이 정석입니다.
  2. 비용: #1에서 본 대로 프라이빗 저장소의 호스티드 러너는 분당 과금입니다. 실행량이 많은 조직은 상시 가동 서버(또는 스팟 인스턴스) 위의 셀프호스티드 러너가 저렴해지는 손익분기가 옵니다. 반대로 실행량이 적으면 호스티드가 압도적으로 쌉니다. 러너를 놀려도 서버비는 나가기 때문입니다.
  3. 특수 사양: GPU가 필요한 ML 잡, 큰 메모리·디스크가 필요한 빌드, 특정 OS·아키텍처 등 호스티드 라인업에 없는 사양입니다.

이 셋 중 어디에도 해당하지 않는다면 이번 편은 “언젠가를 위한 지도"로 읽고 호스티드를 유지하는 것이 맞습니다.

등록과 라우팅: 라벨이 주소입니다 #

등록 절차 자체는 간단합니다. 저장소(또는 조직) Settings의 Runners 메뉴에서 New self-hosted runner를 누르면 러너 에이전트의 다운로드와 등록 명령이 안내됩니다. 대상 머신에서 그대로 실행하면 러너가 GitHub에 연결되어 대기 상태가 됩니다. 서비스로 등록해 부팅 시 자동 시작되게 하는 것까지가 기본 설치입니다.

워크플로 쪽에서는 runs-on의 라벨로 러너를 지정합니다.

.github/workflows/build.yml
jobs:
  build:
    runs-on: [self-hosted, linux, gpu]

러너는 등록 때 기본 라벨(self-hosted, OS, 아키텍처)을 갖고, 커스텀 라벨(gpu, internal 등)을 더할 수 있습니다. runs-on에 나열한 모든 라벨을 가진 러너만 잡을 받으므로, 라벨 설계가 곧 잡 라우팅 설계입니다. “GPU 잡은 gpu 라벨 러너로, 내부망 배포는 internal 라벨 러너로"처럼 용도별 라벨을 정해 두면 러너가 늘어나도 워크플로는 바뀌지 않습니다.

보안 원칙: 퍼블릭 저장소에는 쓰지 않습니다 #

셀프호스티드 러너의 가장 중요한 원칙은 이것입니다. 포크 PR을 받는 퍼블릭 저장소에 셀프호스티드 러너를 연결하지 않습니다. 이유는 구조적입니다. PR의 워크플로는 PR의 코드를 실행하는데, 퍼블릭 저장소의 포크 PR은 누구나 만들 수 있습니다. 즉 낯선 사람의 코드가 우리 러너 머신에서 실행되는 경로가 열리는 것이고, 러너가 내부망에 있다면 내부망에서 실행되는 것입니다. GitHub 호스티드 러너는 실행마다 폐기되는 격리 VM이라 감당되는 위험이지만, 우리 서버는 다릅니다. 셀프호스티드는 프라이빗 저장소 전용이라고 기억하는 것이 안전하며, 이 주제의 다른 반쪽(pull_request_target의 함정)은 다음 편에서 다룹니다.

같은 맥락에서 러너의 수명 관리도 원칙이 있습니다. 러너 머신을 오래 쓰면 잡들이 남긴 상태(캐시, 자격 증명 찌꺼기, 도구 버전)가 쌓여 “어제는 되던 빌드"의 원인이 됩니다. 가능하면 임시(ephemeral) 러너, 즉 잡 하나를 처리하고 폐기되는 방식으로 운영해 호스티드 러너의 깨끗함을 재현하는 것이 현행 모범입니다.

규모가 커지면: actions-runner-controller #

러너가 몇 대 수준을 넘어 수요에 따라 늘었다 줄어야 하는 규모가 되면, 쿠버네티스 위에서 러너를 Pod로 관리하는 공식 오픈 소스 actions-runner-controller(ARC)가 표준 답입니다. 대기 잡 수에 맞춰 러너 Pod를 자동으로 늘리고 줄이며, 임시 러너 원칙도 Pod 생명주기로 자연스럽게 구현됩니다. 쿠버네티스 운영을 전제한다는 진입 장벽이 있으므로, K8s 트랙을 아는 팀이 러너 대수 관리에 지치기 시작했을 때가 도입 시점입니다.

정리 #

이번 글에서 다룬 내용입니다.

  • 도입 기준은 내부망 접근, 실행량 기반 비용 손익분기, 특수 사양 세 가지입니다. 해당 없으면 호스티드 유지가 맞습니다
  • 등록은 Settings의 안내 명령으로 끝나고, runs-on의 라벨 조합이 잡 라우팅입니다
  • 포크 PR을 받는 퍼블릭 저장소에는 연결하지 않습니다. 낯선 코드가 우리 머신에서 도는 경로이기 때문입니다
  • 러너는 임시(ephemeral) 운영이 모범입니다. 오래 사는 러너는 상태가 쌓여 재현성 문제를 만듭니다
  • 규모가 커지면 쿠버네티스 위의 actions-runner-controller로 자동 확장합니다

다음 글(#9 보안 강화)에서는 워크플로 보안을 정면으로 다룹니다. GITHUB_TOKEN 권한 최소화, 서드파티 액션의 SHA 고정, 그리고 pull_request_target의 함정입니다.

X