워크플로를 실행하는 머신. 두 종류다.
| GitHub 호스티드 | 셀프 호스티드 | |
|---|---|---|
| 환경 | 매번 깨끗한 VM | 우리가 관리하는 머신 |
| 관리 | 없음 | 우리 몫 |
| 사양·네트워크 | 정해진 것 중 선택 | 자유롭게 — 사내망 접근 가능 |
| 비용 | 공개 무료 / 비공개 사용량 과금 | 인프라 비용 |
왜 쓰나
- 사내망 자원에 접근해야 한다 (내부 DB·레지스트리)
- 특수 하드웨어가 필요하다 (GPU, 특정 아키텍처)
- 빌드가 무거워 호스티드 사양으로 부족하다
- 캐시를 로컬에 두어 빌드를 크게 줄일 수 있다
그리고 성격이 정반대다
공식 문서의 경고가 그대로 요점이다 — 셀프 호스티드 러너는 일회성 깨끗한 가상 머신에서 돈다는 보장이 없고, 신뢰할 수 없는 코드에 의해 지속적으로 침해될 수 있다.
두 단어가 핵심이다. 지속적이라는 것 — 한 잡이 남긴 파일·설치한 패키지·훔쳐 둔 자격증명이 다음 잡에 그대로 남는다. 우리 CI 를 뚫으면 그 뒤로 도는 모든 빌드의 산출물에 손댈 수 있다는 뜻이다.
공개 저장소에 붙이지 않는다
누구나 PR 하나로 우리 인프라에서 임의 코드를 실행할 수 있다. 사내망 안에 있다면 그 코드가 내부망을 스캔한다. 이것은 설정으로 완화할 문제가 아니라 하지 말아야 할 조합이다.
일회성으로 만드는 법
./config.sh --url https://github.com/my-org/my-repo --ephemeral
--ephemeral 로 등록하면 러너가 잡 하나를 처리하고 스스로 등록을 해제한다. 여기에 컨테이너·VM 자동 생성을 붙이면 매 잡마다 새 환경이 뜬다 — 호스티드와 같은 성질을 얻는 것이다.
쿠버네티스에 러너를 파드로 띄우는 방식이 흔한데, 파드가 잡마다 새로 만들어지므로 일회성이 자연스럽게 지켜진다.
어느 잡이 어느 러너로 가나
jobs:
build:
runs-on: [self-hosted, linux, gpu] # 라벨로 고른다
라벨이 맞는 러너가 그 잡을 가져간다. 그런데 라벨만으로는 접근을 통제하지 못한다 — 라벨은 이름일 뿐이라 다른 저장소가 같은 라벨을 적으면 그 러너를 쓸 수 있다.
접근을 제한하려면 러너 그룹으로 어느 저장소가 쓸 수 있는지 지정해야 한다. 사내망에 붙은 러너라면 이 설정이 사실상 방화벽 규칙과 같은 무게를 갖는다.
무엇을 감시하나
- 러너에서 예상 밖의 아웃바운드 연결이 나가는지
- 잡이 끝난 뒤 남아 있는 프로세스가 있는지
- 러너 등록 토큰이 어디에 저장돼 있는지
일회성으로 돌리면 앞의 두 가지는 구조적으로 사라진다. 감시로 막는 것보다 남을 곳을 없애는 편이 확실하다.
써야 한다면
- 비공개 저장소에만 붙인다
- 잡마다 새 VM·컨테이너로 띄우고 끝나면 폐기한다 (일회성 러너)
- 러너 머신에 자격증명을 두지 않는다 — 필요한 것은 실행 시점에 주입한다
- 네트워크를 좁힌다 — 빌드에 필요한 곳만 열고 나머지는 막는다
- 러너를 격리된 네트워크 구획에 둔다
즉 셀프 호스티드를 쓰되 호스티드처럼 일회성으로 만들어 성격 차이를 지운다.
면접 함정
- ❌ "셀프 호스티드가 더 안전하다 — 우리 것이니까" → 반대다. 상태가 남고 신뢰 경계가 안쪽에 있다.
- ❌ "공개 저장소도 승인 설정을 켜면 된다" → 승인은 완화일 뿐이다. 권장은 붙이지 않는 것이다.
- ❌ "러너를 재사용해야 캐시가 살아 빠르다" → 캐시는 외부 캐시 서비스로 푼다. 상태 재사용과 맞바꿀 문제가 아니다.