시스템
Github Runner - EC2 Self-Hosted Runner 설정
달빛궁전-
2026. 6. 28. 20:46
개요
GitHub Actions는 무료 플랜 기준 월 2,000분의 러너 실행 시간을 제공합니다. 최근 AI 활용을 비롯한 빌드 파이프라인 사용량 증가로 인해 제공량이 조기 소진됨에 따라, 유료 플랜 전환 대신 비용 효율적인 대안으로 AWS EC2 기반의 Self-Hosted Runner를 구축하고자 합니다.
본 문서에서는 AWS EC2를 GitHub Actions Runner로 등록하여 빌드 및 컴파일 작업을 EC2 인프라에서 처리하는 방법을 다룹니다. 특히 인프라 운영 비용을 최소화하기 위해 아래 두 가지 방안을 비교 및 정리했습니다.
AWS EC2 온디맨드(On-Demand) 인스턴스를 활용한 기본 구성
AWS EC2 스팟(Spot) 인스턴스 + AWS EventBridge Scheduler 조합을 통해 업무 시간대에만 인스턴스를 운영하는 비용 최적화 구성
Step 1: 기존 VPC 및 Public Subnet ID 조회하기
새로 만드는 것이 아니라 기존 VPC의 서브넷을 사용해야 하므로, 먼저 현재 리전에 있는 서브넷 목록을 조회하여 ID를 확인
1. 서브넷 목록 및 정보 조회
아래 명령어를 실행하면 해당 리전의 모든 서브넷 ID, CIDR 블록, 그리고 퍼블릭 IP 자동 할당 여부(MapPublicIpOnLaunch)가 표 형태로 출력
aws ec2 describe-subnets \
--region ap-northeast-2 \
--query "Subnets[*].[SubnetId, VpcId, CidrBlock, MapPublicIpOnLaunch, Tags[?Key=='Name'].Value | [0]]" \
--output table
- **확인할 부분:** 출력된 표에서 `True` 표시가 있거나 이전에 생성해 둔 Public Subnet의 `subnet-xxxxxxxxxxxxxxxxx` (SubnetId)와 `vpc-xxxxxxxxxxxxxxxxx` (VpcId)를 기록
- _여기서 얻은 VpcId는 다음 단계의 보안 그룹 생성에 사용_
Step 2: Key Pair (키 페어) 생성하기
EC2에 접속할 때 사용할 인증 키 파일(ra-portal-runner-key.pem)을 로컬 PC에 생성하고 다운로드합니다.
# 1. 키 페어 생성 및 파일 저장
aws ec2 create-key-pair \
--key-name ra-portal-runner-key \
--region ap-northeast-2 \
--query 'KeyMaterial' \
--output text > ra-portal-runner-key.pem
# 2. 키 파일 권한 변경 (Mac/Linux 환경 필수)
chmod 400 ra-portal-runner-key.pem
- 결과물 (
<your-key-pair>):ra-portal-runner-key
Step 3: Security Group (보안 그룹) 생성 및 규칙 설정
인스턴스에 적용할 방화벽인 보안 그룹을 생성합니다. 단계 1에서 확인한 VPC ID를 <vpc-id> 자리에 입력하세요.
1. 보안 그룹 생성
aws ec2 create-security-group \
--group-name ra-portal-runner-sg \
--description "Security group for GitHub Runner" \
--vpc-id <단계1에서-확인한-vpc-id> \
--region ap-northeast-2 \
--query 'GroupId' \
--output text
- 위 명령어를 실행하면
sg-xxxxxxxxxxxxxxxxx형태의 보안 그룹 ID가 화면에 출력됩니다. 이를 메모합니다.
aws ec2 create-security-group \
--group-name ra-portal-runner-sg \
--description "Security group for GitHub Runner" \
--vpc-id vpc-0deea1bdec4f02c3f \
--region ap-northeast-2 \
--query 'GroupId' \
--output text
2. 인바운드 규칙 추가 (SSH 22번 포트 허용)
인스턴스 관리를 위해 내 IP 또는 전체 IP(0.0.0.0/0)로부터의 SSH 접속을 허용합니다. (방금 얻은 sg- ID를 입력)
aws ec2 authorize-security-group-ingress \
--group-id <방금-생성된-sg-id> \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 \
--region ap-northeast-2
aws ec2 authorize-security-group-ingress \
--group-id sg-0d4fd499ccf5ed14c \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 \
--region ap-northeast-2
참고: GitHub Runner는 기본적으로 아웃바운드(성공 시 외부로 나가는 통신) 기반으로 동작하므로, 단순 러너 구동이 목적이고 웹서버 등의 외부 접속이 필요 없다면 보안을 위해 22번 포트 조차 열지 않거나 본인의 공인 IP만 지정하는 것이 안전합니다.
EC2 생성 명령어
위 단계들에서 수집한 값을 매핑한 최종 구동 스크립트 형태입니다. 대괄호 토큰들을 실제 값으로 바꾸어 실행
일반 VM
aws ec2 run-instances \
--image-id [AMI_ID] \
--instance-type t3.small \
--key-name [KEY_PAIR_NAME] \
--security-group-ids [SECURITY_GROUP_ID] \
--subnet-id [SUBNET_ID] \
--associate-public-ip-address \
--block-device-mappings '[{"DeviceName":"/dev/xvda","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=[INSTANCE_NAME]}]' \
--region [REGION_NAME]
스팟(Spot) VM
aws ec2 run-instances \
--image-id [AMI_ID] \
--instance-type t3.small \
--key-name [KEY_PAIR_NAME] \
--subnet-id [SUBNET_ID] \
--security-group-ids [SECURITY_GROUP_ID] \
--associate-public-ip-address \
--instance-market-options '{"MarketType":"spot","SpotOptions":{"SpotInstanceType":"persistent","InstanceInterruptionBehavior":"stop"}}' \
--block-device-mappings '[{"DeviceName":"/dev/xvda","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=[INSTANCE_NAME]}]' \
--query "Instances[0].InstanceId" \
--output text \
--region [REGION_NAME]
또는 AWS Console에서 수동 생성이 편하면:
| 설정 | 값 |
|---|---|
| AMI | Ubuntu 22.04 LTS (amd64) |
| Instance type | t3.small |
| VPC | 기존 ra-portal VPC |
| Subnet | Public subnet (인터넷 접근 필요) |
| Public IP | 자동 할당 |
| Storage | 30GB gp3 |
| Security Group | outbound 전부 허용, inbound SSH만 (22) |
| Key pair | 기존 것 또는 새로 생성 |
| Name tag | "원하는 name tag" |
Step 4: EC2에 SSH 접속 후 환경 세팅
ssh -i <your-key.pem> ec2-user@<ec2-public-ip>
1. Docker 설치
sudo dnf install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ec2-user
2. Node.js 20
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install -y nodejs
node --version
3. Python 3.12
sudo dnf install -y python3 pip
python3 --version
4. Terraform 1.10.0
sudo dnf install -y unzip wget
wget https://releases.hashicorp.com/terraform/1.10.0/terraform_1.10.0_linux_amd64.zip
unzip terraform_1.10.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/
rm terraform_1.10.0_linux_amd64.zip
terraform --version
5. jq + git
sudo dnf install -y jq git
6. uv (AgentCore용)
curl -LsSf https://astral.sh/uv/install.sh | sh
7. Docker 그룹 반영
sudo usermod -aG docker ec2-user
newgrp docker
docker ps
Step 3: GitHub Actions Runner 설치 & 등록

# runner 전용 디렉토리
mkdir ~/actions-runner && cd ~/actions-runner
# libicu 패키지 설치
sudo dnf install -y libicu
# 최신 runner 다운로드 (x64 Linux)
curl -o actions-runner-linux-x64-2.323.0.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.323.0/actions-runner-linux-x64-2.323.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.323.0.tar.gz
# GitHub에서 토큰 발급 후 등록
# GitHub repo → Settings → Actions → Runners → New self-hosted runner에서 토큰 복사
캡쳐화면에 있는 Configure 복사
# systemd 서비스로 등록 (재부팅 시 자동 시작)
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status
Step 4: Workflow 파일 수정
모든 workflow에서 runs-on: ubuntu-latest → runs-on: self-hosted로 변경
Step 5: AWS EventBridge Scheduler 등록
07시 On, 12시 Off 스케줄
# 1. IAM Role 생성
aws iam create-role \
--role-name [PROJECT_NAME]-runner-scheduler-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "scheduler.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# 2. IAM Policy 추가
aws iam put-role-policy \
--role-name [PROJECT_NAME]-runner-scheduler-role \
--policy-name [PROJECT_NAME]-ec2-startstop \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "arn:aws:ec2:ap-northeast-2:[AWS_ACCOUNT_ID]:instance/[EC2_INSTANCE_ID]"
}]
}'
# 3. 계정 ID로 Role ARN 확인
ROLE_ARN="arn:aws:iam::[AWS_ACCOUNT_ID]:role/[PROJECT_NAME]-runner-scheduler-role"
# 4. 스케줄 생성 — 07:00 KST Start
aws scheduler create-schedule \
--name [PROJECT_NAME]-runner-start \
--schedule-expression "cron(0 7 * * ? *)" \
--schedule-expression-timezone "Asia/Seoul" \
--flexible-time-window '{"Mode": "OFF"}' \
--target "{
\"Arn\": \"arn:aws:scheduler:::aws-sdk:ec2:startInstances\",
\"RoleArn\": \"$ROLE_ARN\",
\"Input\": \"{\\\"InstanceIds\\\": [\\\"[EC2_INSTANCE_ID]\\\"]}\"
}" \
--region ap-northeast-2
# 5. 스케줄 생성 — 00:00 KST Stop (자정)
aws scheduler create-schedule \
--name [PROJECT_NAME]-runner-stop \
--schedule-expression "cron(0 0 * * ? *)" \
--schedule-expression-timezone "Asia/Seoul" \
--flexible-time-window '{"Mode": "OFF"}' \
--target "{
\"Arn\": \"arn:aws:scheduler:::aws-sdk:ec2:stopInstances\",
\"RoleArn\": \"$ROLE_ARN\",
\"Input\": \"{\\\"InstanceIds\\\": [\\\"[EC2_INSTANCE_ID]\\\"]}\"
}" \
--region ap-northeast-2
추가: 보안 고려사항
| 항목 | 대응 |
|---|---|
| IAM 권한 | EC2에 IAM Instance Profile 부여 (기존 github-deploy-role과 동일 권한) → OIDC 대신 Instance Profile 사용 가능 |
| Security Group | Inbound: SSH(22)만. Outbound: 전체 허용 (GitHub API, ECR, etc.) |
| GitHub Secrets | Self-hosted runner에서도 ${{ secrets.* }} 그대로 사용 가능 |
| OIDC 인증 | Self-hosted runner에서도 OIDC 동작함. 또는 Instance Profile로 대체 가능 |