GitHub Actions Self-Hosted

왜 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)에서도 아래 사항을 강조합니다.

  1. Public 리포지토리에 연결하지 마세요. 외부 PR의 워크플로가 runner에서 임의 코드를 실행할 수 있습니다. Fork PR을 통한 secret 탈취, 크립토마이닝 등 실제 피해 사례가 보고되었습니다.
  2. 최소 권한 원칙을 적용하세요. runner 사용자에게 sudo 권한을 주지 않고, Docker socket 접근도 필요한 경우에만 허용합니다.
  3. 작업 후 환경을 초기화하세요. --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 서비스 장애 여부도 함께 확인하세요.

실전 체크리스트

  1. 전용 사용자 생성, root 실행 금지
  2. Public repo에 self-hosted runner 연결 금지
  3. --ephemeral 또는 ARC로 빌드 간 격리 확보
  4. systemd 서비스로 자동 재시작 설정
  5. tool cache를 persistent storage에 마운트
  6. Docker layer cache 전략 수립
  7. Runner offline 알림(Slack/Telegram webhook) 연동
  8. 토큰 자동 갱신 스크립트 준비

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) 관련 글

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux