한 줄 요약
GitHub Actions는 이벤트가 생기면 잡을 대기열에 올리고, 조건에 맞는 러너가 그 잡을 가져가 실행하는 구조예요. 러너는 내 서버에 직접 설치할 수도 있고, GitHub 러너에서 SSH로 서버에 접속해 배포할 수도 있어요.
1. 핵심 개념
워크플로, 잡, 스텝의 기본 구성은 이전 글(GitHub Actions 기초)에서 다뤘어요. 이 글은 각 요소가 실제로 어떻게 맞물려 동작하는지에 집중해요.
1.1 액션(Action)
액션은 워크플로에서 반복되는 작업을 묶어 둔 재사용 가능한 작업 단위예요. 코드 내려받기, 언어 설치, 클라우드 인증 같은 일을 대신해요.
GitHub 공식 문서는 직접 만드는 액션을 세 가지 형태로 구분해요.
형태 설명
| JavaScript 액션 | 러너에서 JavaScript 코드로 바로 실행돼요 |
| Docker 컨테이너 액션 | 컨테이너 안에서 실행돼요. 실행 환경을 고정할 수 있어요 |
| 복합(Composite) 액션 | 여러 스텝을 하나의 액션으로 묶어요 |
워크플로에서는 uses:로 액션을 불러와요.
- uses: actions/checkout@v6
1.2 잡(Job)
잡은 같은 러너 한 대에서 실행되는 스텝 묶음이에요.
- 같은 잡 안의 스텝은 같은 러너에서 순서대로 실행돼요. 그래서 파일을 주고받을 수 있어요.
- 잡끼리는 기본적으로 병렬로 실행돼요. 순서가 필요하면 needs를 써요.
- 잡이 다르면 러너도 다를 수 있어요. 앞 잡에서 만든 파일을 뒤 잡에서 쓰려면 아티팩트 업로드·다운로드 같은 별도 전달이 필요해요.
아티팩트(artifact): 워크플로 실행 중 만들어져 저장되는 파일이에요. 빌드 결과물, 테스트 리포트 등이 해당해요.
1.3 러너(Runner)
러너는 잡을 실제로 실행하는 서버예요. 공식 문서에 따르면 러너 한 대는 한 번에 잡 하나만 실행해요.
종류 설명
| GitHub 호스티드 러너 | GitHub이 제공하는 Ubuntu, Windows, macOS 가상 머신이에요. 워크플로 실행마다 새로 준비된 가상 머신에서 동작해요 |
| 셀프 호스티드 러너 | 내가 관리하는 서버에 러너 프로그램을 설치해 사용하는 방식이에요 |
1.4 샤드(Shard)
샤드는 GitHub Actions의 공식 구성 요소 이름이 아니에요. 큰 작업을 여러 조각으로 나눠 동시에 실행하는 방식을 부르는 말이에요. CI에서는 주로 테스트를 나눠 여러 잡에서 병렬로 실행하는 것을 뜻해요.
- GitHub Actions는 strategy.matrix로 같은 잡을 여러 개 만들어요.
- 테스트 도구가 "전체 중 몇 번째 조각을 실행할지" 나눠요.
- 예를 들어 Playwright 공식 문서는 --shard=1/4 같은 옵션과 matrix를 함께 쓰는 예시를 제공해요.
matrix: 변수 조합마다 잡을 하나씩 자동으로 만들어 주는 기능이에요.
샤드 기능을 제공하는지는 테스트 도구마다 달라요. 사용하는 도구의 공식 문서를 확인해야 해요.
1.5 왜 중요한가
- 잡과 러너의 관계를 알면, 파일이 왜 다음 잡에 없는지 같은 문제를 바로 이해할 수 있어요.
- 러너 종류에 따라 배포 방식과 보안 설정이 달라져요.
- 샤드를 쓰면 오래 걸리는 테스트를 여러 러너에 나눠 실행할 수 있어요.
2. 쉽게 이해하기
GitHub Actions를 배달 대행 플랫폼에 비유해 볼게요.
- 이벤트는 주문 접수예요.
- 잡은 배달 건 하나예요. 접수되면 대기열에 올라가요.
- 러너는 배달 기사예요. 한 번에 한 건만 맡아요.
- GitHub 호스티드 러너는 플랫폼 소속 기사예요.
- 셀프 호스티드 러너는 우리 가게가 직접 고용한 기사예요.
- 액션은 표준 포장 키트예요. 매번 새로 만들지 않고 가져다 써요.
- 샤드는 큰 주문을 여러 봉지로 나눠 기사 여러 명이 동시에 배달하는 방식이에요.
중요한 점은 기사가 플랫폼에 "제 배달 있나요?"라고 먼저 물어본다는 거예요. 이 원리가 셀프 호스티드 러너의 네트워크 구조를 결정해요.
3. 동작 원리: 이벤트에서 실행까지

① 이벤트 발생 (push, PR 등)
② GitHub이 워크플로 실행(run)을 만들고 잡을 대기열에 등록
③ 잡의 runs-on 조건에 맞는 러너를 찾음
④ 러너가 잡을 가져가 스텝을 순서대로 실행
- uses: 액션을 내려받아 실행
- run: 셸 명령 실행
⑤ 로그와 결과(성공/실패)를 GitHub으로 전송
3.1 러너가 잡을 받는 방식
공식 문서에 따르면 셀프 호스티드 러너는 GitHub에 HTTPS 롱 폴링(long poll) 으로 연결해요.
- 러너가 GitHub에 연결을 열고 최대 50초 동안 잡 할당을 기다려요.
- 응답이 없으면 연결을 끊고 새 롱 폴링을 다시 열어요.
- 통신은 러너에서 GitHub 방향(outbound) HTTPS 443 포트로 이뤄져요.
롱 폴링: 클라이언트가 서버에 요청을 보내 두고, 새 소식이 생길 때까지 연결을 유지하는 방식이에요.
3.2 러너를 고르는 기준
공식 문서에 따르면 GitHub은 잡의 runs-on 라벨과 그룹에 맞는 러너를 찾아요.
- 조건에 맞고 온라인이면서 쉬고 있는 러너가 있으면 잡을 배정해요.
- 러너가 60초 안에 잡을 가져가지 않으면 잡은 다시 대기열로 돌아가요.
- 맞는 러너가 없으면 러너가 온라인이 될 때까지 잡이 대기해요.
4. 샤드 실습: 테스트를 4개로 나눠 실행하기
Playwright 공식 문서의 예시를 단순화한 구조예요.

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v6
- name: 의존성 설치
run: npm ci
- name: 테스트 실행 (4개 중 일부)
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- matrix 값 조합에 따라 잡이 4개 만들어져요.
- 각 잡은 1/4, 2/4, 3/4, 4/4 조각을 맡아요.
- 러너가 충분하면 4개 잡이 동시에 실행돼요.
matrix 관련 설정
설정 의미
| fail-fast | true(기본값)면 하나가 실패할 때 나머지 진행·대기 잡을 취소해요 |
| max-parallel | 동시에 실행할 최대 잡 수를 제한해요 |
공식 문서 기준 matrix는 워크플로 실행 하나당 최대 256개 잡까지 만들 수 있어요.
각 샤드의 결과는 잡마다 따로 생겨요. 하나의 리포트로 합치려면 아티팩트로 모은 뒤 병합하는 잡이 추가로 필요해요.
5. 셀프 호스티드 러너: 서버에 러너 설치하기
5.1 설치 절차
- 저장소 Settings → Actions → Runners로 이동해요.
- New self-hosted runner를 선택해요.
- 운영체제(Linux 등)와 아키텍처를 고르면 설치 명령이 표시돼요.
- 서버에서 표시된 명령대로 러너를 내려받고 설정해요.
# 화면에 표시된 명령 예시 (값은 화면에 나온 그대로 사용)
./config.sh --url https://github.com/<소유자>/<저장소> --token <화면에 표시된 토큰>
# 직접 실행 (터미널을 닫으면 종료)
./run.sh
설정 명령에 쓰는 토큰은 등록용 임시 토큰이에요. 화면에 표시된 값을 바로 사용해요.
5.2 서비스로 등록하기
서버가 재부팅돼도 러너가 자동으로 실행되게 하려면 서비스로 등록해요. systemd를 쓰는 Linux에서는 러너 폴더의 svc.sh를 사용해요.
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status
5.3 워크플로에서 사용하기
셀프 호스티드 러너에는 self-hosted, 운영체제, 아키텍처 라벨이 기본으로 붙어요.
jobs:
deploy:
runs-on: [self-hosted, linux]
steps:
- name: 최신 이미지로 재시작
run: |
docker pull ghcr.io/<소유자>/<저장소>:main
docker compose -f <compose 파일 경로> up -d
러너가 서버 안에서 실행되므로, 배포 명령을 서버에서 직접 실행해요.
5.4 셀프 호스티드 러너의 네트워크 특징
러너가 GitHub에 먼저 연결하는 구조라서, GitHub에서 서버로 들어오는 포트를 열 필요가 없어요. 서버에서 GitHub 방향 HTTPS(443) 통신만 가능하면 돼요.
6. SSH로 배포하기: GitHub 호스티드 러너에서 서버 접속
러너를 서버에 설치하지 않고, GitHub 호스티드 러너가 SSH로 서버에 접속해 명령을 실행하는 방식이에요.
6.1 준비
- 배포 전용 SSH 키를 만들고, 공개키를 서버의 ~/.ssh/authorized_keys에 등록해요.
- 저장소 Secrets에 아래 값을 등록해요.
Secret 이름 값
| SSH_PRIVATE_KEY | 배포용 개인키 |
| SSH_HOST | 서버 주소 |
| SSH_PORT | SSH 포트 |
| SSH_USER | 접속 계정 |
| SSH_KNOWN_HOSTS | 서버의 호스트 키 정보 (known_hosts 형식) |
known_hosts: 접속할 서버가 진짜 그 서버인지 확인하기 위해 서버의 공개 호스트 키를 저장해 두는 파일이에요.
6.2 워크플로 예시
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH 설정
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- name: 서버에서 배포 명령 실행
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT }}
SSH_USER: ${{ secrets.SSH_USER }}
run: |
ssh -i ~/.ssh/deploy_key -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
"docker pull ghcr.io/<소유자>/<저장소>:main && docker compose -f <compose 파일 경로> up -d"
- Secrets 값은 env로 전달한 뒤 셸 변수로 사용했어요.
- known_hosts를 미리 등록해 두면 접속 대상 서버를 검증할 수 있어요.
6.3 SSH 방식의 네트워크 특징
SSH 방식은 GitHub 러너에서 서버로 들어오는 연결이에요. 그래서 서버 방화벽에서 SSH 포트를 허용해야 해요. GitHub 호스티드 러너의 IP 주소는 고정되어 있지 않고 대역이 넓어서, IP만으로 접근을 제한하기는 어려워요.
7. 셀프 호스티드 러너 vs SSH 배포

항목 셀프 호스티드 러너 SSH 배포
| 실행 위치 | 내 서버 | GitHub 호스티드 러너 |
| 연결 방향 | 서버 → GitHub (outbound 443) | GitHub 러너 → 서버 (inbound SSH) |
| 서버 방화벽 | 들어오는 포트 개방 불필요 | SSH 포트 허용 필요 |
| 서버 관리 | 러너 프로그램 설치·업데이트·보안 관리 필요 | 러너 관리 불필요 |
| 비밀 정보 | 서버 안에서 직접 실행 | SSH 개인키를 Secrets에 보관 |
| 적합한 경우 | 비공개 저장소, 서버 내부 자원 접근이 필요할 때 | 러너 설치가 어렵거나 여러 서버에 접속할 때 |
어느 쪽이 낫다는 공식 기준은 없어요. 저장소 공개 여부, 서버 방화벽 정책, 관리 인력을 기준으로 선택해요.
8. 핵심 정리
- 액션은 재사용 작업 단위, 잡은 한 러너에서 실행되는 스텝 묶음, 러너는 잡을 실행하는 서버예요.
- 러너 한 대는 한 번에 잡 하나를 실행하고, 잡은 runs-on 조건에 맞는 러너에 배정돼요.
- 샤드는 공식 구성 요소가 아니라, matrix와 테스트 도구로 작업을 나눠 병렬 실행하는 방식이에요.
- 셀프 호스티드 러너는 서버에서 GitHub으로 HTTPS 롱 폴링을 하므로, 들어오는 포트가 필요 없어요.
- SSH 배포는 GitHub 러너가 서버로 접속하므로, 서버에서 SSH 포트를 허용해야 해요.
9. 추가 정보
보안 주의사항
- 공개 저장소와 셀프 호스티드 러너: 공식 문서는 셀프 호스티드 러너를 비공개 저장소에서만 사용하도록 권장해요. 공개 저장소의 포크에서 올라온 풀 리퀘스트가 러너 서버에서 위험한 코드를 실행할 수 있기 때문이에요.
- 러너 실행 계정 권한: 러너 계정에 필요한 권한만 주세요. Docker 공식 문서에 따르면 docker 그룹 권한은 root 수준 권한과 같아요.
- 배포용 SSH 키 분리: 개인 접속용 키를 재사용하지 말고 배포 전용 키를 만들어요. 접속 계정의 권한도 배포에 필요한 범위로 제한해요.
- 공개 글 작성 시: 서버 IP, 포트, 계정명, 키 파일 이름은 실제 값을 쓰지 않아요.
러너 관리
- 공식 문서에 따르면 러너 프로그램은 새 버전이 나오면 자동으로 업데이트돼요.
- 14일 넘게 GitHub에 연결하지 않은 셀프 호스티드 러너는 자동으로 제거돼요.
관련 글
'운영체제 및 플랫폼 > Docker(도커)' 카테고리의 다른 글
| 쿠버네티스(Kubernetes) 입문 — 도커와 무엇이 다른지 쉽게 정리 (0) | 2026.09.30 |
|---|---|
| 배포 전략 비교 — 롤링, 블루-그린, 카나리 배포 차이 정리 (0) | 2026.09.29 |
| Spring Boot 도커 배포 실습 — Dockerfile 작성부터 서버 실행까지 (0) | 2026.09.27 |
| GitHub Actions로 도커 이미지 빌드·푸시하기 — GHCR 자동 업로드 파이프라인 만들기 (0) | 2026.09.25 |
| GitHub Actions 사용법 기초 — 워크플로·잡·스텝 개념 한 번에 정리 (0) | 2026.09.24 |