왜 Self-Hosted Runner인가
GitHub-hosted runner는 편리하지만, 빌드 시간 제한(6시간)과 무료 분수 한도, 내부 네트워크 접근 불가 등 실무에서 벽에 부딪히는 순간이 옵니다. Self-hosted runner는 이런 한계를 깨고, 자체 하드웨어(GPU, 대용량 메모리)를 CI/CD에 직접 투입할 수 있는 공식 방법입니다.
Self-Hosted Runner의 핵심 구조
Self-hosted runner는 GitHub Actions 서비스와 long-poll 방식으로 연결됩니다. Runner 프로세스가 GitHub API를 주기적으로 호출하여 할당된 job을 가져오고, 로컬에서 실행한 뒤 결과를 다시 보고합니다.
- Runner Application: .NET 기반 에이전트로,
config.sh로 등록하고run.sh또는 systemd 서비스로 실행합니다. - Labels:
self-hosted,linux,x64등 기본 레이블 외에 커스텀 레이블(gpu,staging)을 붙여 워크플로에서runs-on으로 선택합니다. - Runner Group: Organization 레벨에서 runner를 그룹으로 묶어 특정 리포지토리에만 할당할 수 있습니다.
설치와 등록: 단계별 절차
아래는 Ubuntu 22.04 기준입니다. 공식 문서(Adding self-hosted runners)를 따릅니다.
1단계: 전용 사용자 생성
보안을 위해 root가 아닌 전용 사용자로 실행합니다.
sudo useradd -m -s /bin/bash ghrunner
sudo usermod -aG docker ghrunner
2단계: Runner 다운로드 및 등록
su - ghrunner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.321.0.tar.gz -L
https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-linux-x64-2.321.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.321.0.tar.gz
./config.sh --url https://github.com/YOUR_ORG --token AXXXX...
--labels gpu,staging --name my-build-server
3단계: systemd 서비스 등록
sudo ./svc.sh install ghrunner
sudo ./svc.sh start
sudo ./svc.sh status
이렇게 하면 서버 재부팅 후에도 runner가 자동 시작됩니다.
보안: 반드시 지켜야 할 3가지 원칙
Self-hosted runner는 편리한 만큼 보안 위험도 큽니다. GitHub 공식 문서(Self-hosted runner security)에서도 아래 사항을 강조합니다.
- Public 리포지토리에 연결하지 마세요. 외부 PR의 워크플로가 runner에서 임의 코드를 실행할 수 있습니다. Fork PR을 통한 secret 탈취, 크립토마이닝 등 실제 피해 사례가 보고되었습니다.
- 최소 권한 원칙을 적용하세요. runner 사용자에게 sudo 권한을 주지 않고, Docker socket 접근도 필요한 경우에만 허용합니다.
- 작업 후 환경을 초기화하세요.
--ephemeral플래그로 1회용 runner를 운영하거나, 컨테이너 기반 runner(actions-runner-controller)를 사용하면 빌드 간 오염을 방지할 수 있습니다.
Ephemeral Runner: 1회용 실행으로 격리 강화
GitHub Actions runner v2.300 이상에서 --ephemeral 플래그를 지원합니다. 이 모드에서 runner는 job 하나를 실행한 뒤 자동으로 등록 해제됩니다.
./config.sh --url https://github.com/YOUR_ORG --token AXXXX...
--ephemeral --labels ephemeral,linux
Ephemeral runner는 외부에서 트리거하는 오케스트레이터(systemd timer, cron, Kubernetes controller)와 결합하면, 필요할 때만 runner를 띄우고 끝나면 즉시 정리하는 패턴을 구현할 수 있습니다.
Actions Runner Controller(ARC): Kubernetes 기반 자동 스케일링
Runner를 수동으로 관리하는 대신, Kubernetes 클러스터에서 Pod 단위로 자동 생성/삭제하는 것이 ARC(Actions Runner Controller)입니다. GitHub이 공식으로 지원하며, Helm chart로 설치합니다.
helm install arc
--namespace arc-systems --create-namespace
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
helm install arc-runner-set
--namespace arc-runners --create-namespace
-f values.yaml
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set
ARC는 webhook 이벤트를 받아 runner Pod를 동적으로 생성합니다. job이 끝나면 Pod가 삭제되므로 ephemeral 특성이 자동 보장됩니다.
성능 최적화 팁
- Tool cache 활용:
actions/setup-node등이RUNNER_TOOL_CACHE디렉터리에 런타임을 캐시합니다. 이 경로를 persistent volume으로 마운트하면 매 빌드마다 다운로드를 건너뜁니다. - Docker layer cache:
docker buildx와--cache-from/--cache-to를 결합하면 이미지 빌드 시간을 줄일 수 있습니다. Self-hosted runner에서는 로컬 레지스트리를 캐시 백엔드로 쓰면 네트워크 비용도 절약됩니다. - 병렬 job 분산: 러너를 여러 대 두고 같은 레이블을 붙이면 GitHub이 자동으로 라운드로빈 할당합니다. ARC에서는
maxRunners로 상한을 지정합니다.
모니터링과 장애 대응
Runner가 offline 상태로 바뀌면 워크플로가 pending에서 멈춥니다. 아래 사항을 점검하세요.
systemctl status actions.runner.*.service로 프로세스 상태 확인_diag/디렉터리의 Runner 로그에서 연결 오류, 인증 만료 메시지 확인- runner 토큰은 1시간 유효이므로, 자동 재등록 스크립트(PAT 기반 REST API 호출)를 준비하면 안정적입니다.
- GitHub Status(githubstatus.com)에서 Actions 서비스 장애 여부도 함께 확인하세요.
실전 체크리스트
- 전용 사용자 생성, root 실행 금지
- Public repo에 self-hosted runner 연결 금지
--ephemeral또는 ARC로 빌드 간 격리 확보- systemd 서비스로 자동 재시작 설정
- tool cache를 persistent storage에 마운트
- Docker layer cache 전략 수립
- Runner offline 알림(Slack/Telegram webhook) 연동
- 토큰 자동 갱신 스크립트 준비
Self-hosted runner는 CI/CD 파이프라인의 성능과 유연성을 높이는 강력한 도구입니다. 보안 원칙을 지키고, ephemeral 패턴이나 ARC를 도입하면 운영 부담도 줄일 수 있습니다.
더 깊은 DevOps 주제가 궁금하다면 Kubernetes NetworkPolicy 심화 가이드와 Terraform Workspace 심화 가이드도 확인해 보세요.
온프레미스 CI/CD 구축이나 인프라 자동화에 대해 더 알고 싶으신가요?
문의 페이지에서 편하게 질문해 주세요.
7) Self-Hosted Runner 설정 스크립트
#!/bin/bash
# self-hosted-runner-setup.sh
# Ubuntu 22.04+ 기준
set -euo pipefail
RUNNER_VERSION="2.315.0"
REPO_URL="https://github.com/your-org/your-repo"
RUNNER_TOKEN="YOUR_TOKEN" # Settings → Actions → Runners → New
# 1. 전용 사용자 생성
sudo useradd -m -s /bin/bash runner
sudo usermod -aG docker runner # Docker 빌드를 위해
# 2. Runner 다운로드 및 설치
sudo -u runner bash << 'EOF'
cd /home/runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner.tar.gz -L
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"
tar xzf actions-runner.tar.gz
./config.sh --url "$REPO_URL" --token "$RUNNER_TOKEN"
--name "on-prem-runner-1"
--labels "self-hosted,linux,x64,docker"
--work "_work"
EOF
# 3. Systemd 서비스 등록 (재부팅 시 자동 시작)
cd /home/runner/actions-runner
sudo ./svc.sh install runner
sudo ./svc.sh start
# 4. 상태 확인
sudo ./svc.sh status
8) 워크플로우에서 Self-Hosted Runner 지정
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: [self-hosted, linux, docker] # 라벨로 지정
steps:
- uses: actions/checkout@v4
- name: Run Tests
run: |
docker compose -f docker-compose.test.yml up --abort-on-container-exit
docker compose -f docker-compose.test.yml down -v
build-and-push:
needs: test
runs-on: [self-hosted, linux, docker]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build & Push
run: |
docker build -t registry.example.com/myapp:${{ github.sha }} .
docker push registry.example.com/myapp:${{ github.sha }}
deploy:
needs: build-and-push
runs-on: [self-hosted, linux]
steps:
- name: Deploy to K8s
run: |
kubectl set image deployment/myapp
myapp=registry.example.com/myapp:${{ github.sha }}
--record
9) 관련 글
- GitHub Actions CI/CD 가이드 — 워크플로우 설계 기초와 GitHub-hosted 러너 활용법입니다.
- Docker Multi-Stage Build — CI에서 사용할 최적화된 Docker 이미지 빌드 전략입니다.
- ArgoCD GitOps — Self-Hosted Runner + ArgoCD로 완전 자동화 배포 파이프라인을 구축합니다.
- Linux 서버 보안 설정 — Self-Hosted Runner 서버의 기본 보안 강화 가이드입니다.