- Published on
github actions 자동배포 구성과 운영 정리
- Authors

- Name
- Indo Yoon
github actions 자동배포는 저장소에 변경이 들어오는 순간 빌드와 서버 반영까지 하나의 흐름으로 묶는 방식이에요. 핵심은 워크플로 파일 하나에 트리거, 러너, 시크릿, 배포 명령을 역할별로 나눠 적고, 서버 접속 정보는 코드가 아니라 시크릿에 두는 것입니다. 아래에서 구성 순서와 상황별 선택 기준, 자주 걸리는 지점을 차례로 정리할게요.
한눈에 보기
자동배포의 흐름은 생각보다 단순해요. 저장소 이벤트가 워크플로를 깨우고, 깨워진 러너가 코드를 내려받아 빌드와 테스트를 마친 뒤, 마지막 단계에서 서버에 결과물을 반영합니다. 이때 접속 정보와 배포에 필요한 값은 저장소 시크릿이나 환경 시크릿으로 빼 두고, 워크플로 파일에는 참조만 남깁니다.
전체 흐름을 단계로 나누면 다음과 같아요.
- 트리거:
push,pull_request,workflow_dispatch같은 이벤트 중 무엇으로 배포를 시작할지 정합니다. - 러너: GitHub이 제공하는 호스티드 러너를 쓸지, 직접 관리하는 셀프 호스티드 러너를 쓸지 고릅니다.
- 빌드와 테스트: 애플리케이션을 패키징하고 최소한의 검증을 수행합니다.
- 배포: SSH 원격 실행, 컨테이너 이미지 푸시, 클라우드 CLI 호출 등 대상 서버에 맞는 방식으로 반영합니다.
- 검증: 헬스 체크와 로그 확인으로 배포 성공 여부를 판단합니다.
여기서 꼭 기억할 점은 "빌드"와 "배포"를 분리해서 생각하는 습관이에요. 빌드 단계는 실패해도 서비스에 영향이 없지만, 배포 단계는 어디서 실패하느냐에 따라 서비스가 잠깐 멈출 수 있습니다. 그래서 검증 단계를 배포 단계 안에 함께 넣는 경우가 많아요.
배포 방식 비교
같은 자동배포라도 서버에 결과물을 넣는 방법에 따라 준비물과 실패 지점이 달라집니다. 아래 표는 백엔드에서 자주 쓰는 세 가지 방식을 비교한 것이에요.
| 항목 | 서버에서 직접 pull | SSH 원격 실행 | 이미지 빌드 후 배포 |
|---|---|---|---|
| 준비 난이도 | 낮음 | 보통 | 높음 |
| 서버 접속 정보 위치 | 서버 내부 키 | 저장소 시크릿 | 레지스트리 자격 증명 |
| 빌드 부하 위치 | 대상 서버 | 러너 | 러너 |
| 롤백 방법 | 이전 커밋으로 재배포 | 이전 커밋으로 재배포 | 이전 이미지 태그로 교체 |
| 잘 맞는 상황 | 단일 소규모 서버 | 단일·소수 서버, 기존 운영 방식 유지 | 다수 인스턴스, 무중단 교체 |
서버에서 직접 pull하는 방식은 러너가 서버에 명령만 던지면 되지만, 빌드 부하가 운영 서버로 몰린다는 단점이 있어요. SSH 원격 실행은 기존 배포 스크립트를 거의 그대로 살릴 수 있어서 이전 비용이 적습니다. 이미지 빌드 후 배포는 준비물이 많지만, 여러 대의 서버를 같은 결과물로 맞추기 쉬워요.
단계별로 만드는 자동배포
저장소와 서버 준비
배포 대상 서버에는 배포 전용 계정을 만들고, 그 계정이 애플리케이션 디렉터리와 서비스 관리 권한만 갖도록 범위를 좁혀 두세요. GitHub Actions가 접속할 때 쓰는 키는 사람이 쓰는 키와 분리하는 편이 안전합니다. 배포 계정의 authorized_keys에 공개 키를 등록하고, 개인 키는 다음 단계에서 설명할 시크릿에 넣습니다.
서버 쪽 배포 절차는 저장소 안에 스크립트로 두는 방식을 권해요. 러너가 저장소를 체크아웃한 뒤 그 스크립트를 원격에서 실행하면, 배포 절차가 코드처럼 리뷰되고 변경 이력도 함께 남습니다. 서버에만 존재하는 스크립트는 시간이 지나면 아무도 내용을 모르게 되기 쉬워요.
시크릿 등록
저장소 설정의 Secrets and variables 메뉴에서 시크릿을 추가합니다. 최소한 호스트, 사용자, 개인 키를 각각 나눠 등록하고, 필요하면 배포 디렉터리 경로나 서비스 이름도 변수로 빼 두세요. 값을 하나로 합쳐 두면 나중에 서버를 추가할 때 손댈 곳이 늘어납니다.
운영 환경과 스테이징 환경을 함께 운영한다면 저장소 시크릿보다 환경 시크릿이 편해요. 워크플로의 job에 environment를 지정하면 환경별로 다른 값을 자동으로 참조할 수 있고, 승인 규칙도 함께 걸 수 있습니다.
워크플로 파일 작성
저장소 루트의 .github/workflows/deploy.yml 경로에 파일을 만듭니다. 아래는 SSH로 원격 스크립트를 실행하는 최소 구성 예시예요.
name: deploy
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: deploy-production
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: SSH 키 준비
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "${{ secrets.SSH_HOST }}" >> ~/.ssh/known_hosts
- name: 원격 배포 스크립트 실행
run: |
ssh "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" 'bash -s' < scripts/deploy.sh
각 항목의 의미를 짚어 볼게요. on은 워크플로를 깨우는 조건이고, runs-on은 실행 환경입니다. steps는 위에서부터 순서대로 실행되며, 어느 단계든 실패하면 그 뒤 단계는 실행되지 않습니다. concurrency는 같은 그룹의 워크플로가 겹쳐 실행되지 않도록 막아 주고, environment는 환경별 시크릿과 승인 규칙을 연결합니다.
scripts/deploy.sh 안에는 코드 갱신, 의존성 설치, 서비스 재시작, 헬스 체크를 순서대로 적어 두세요. 재시작 직후 바로 성공으로 판단하지 말고, 애플리케이션이 응답할 때까지 기다린 뒤 실패하면 이전 상태로 되돌리는 로직을 넣는 편이 안전합니다.
첫 실행과 로그 확인
워크플로를 커밋하고 푸시하면 Actions 탭에서 실행이 시작됩니다. 각 단계의 로그를 열어 어디까지 진행됐는지 확인하고, 실패했다면 시크릿 이름이 정확한지, 서버 방화벽이 러너의 접속을 허용하는지부터 봅니다.
배포가 끝난 뒤에는 서버에 직접 들어가 서비스 상태와 애플리케이션 로그를 확인하세요. 자동배포는 "명령이 성공했다"는 사실만 알려 줄 뿐, 서비스가 정상 동작하는지는 알려 주지 않습니다. 이 확인 절차를 스크립트 안에 넣어 두면 사람이 매번 들어가지 않아도 됩니다.
상황별 선택
- 단일 서버에 기존 배포 스크립트가 있다면 SSH 원격 실행이 가장 빠른 길이에요. 스크립트를 그대로 재사용할 수 있고, 러너는 명령을 전달하는 역할만 합니다.
- 여러 인스턴스를 같은 상태로 맞춰야 한다면 이미지 빌드 후 레지스트리 배포를 고려하세요. 롤백도 이전 태그로 교체하는 방식이라 예측이 쉽습니다.
- 클라우드 관리형 서비스를 쓴다면 장기 자격 증명 대신 OpenID Connect로 권한을 위임하는 방식을 검토하세요. 저장소에 장기 키를 두지 않아도 됩니다.
- 운영 배포에 승인이 필요하다면
environment에 보호 규칙을 설정하세요. 지정된 검토자가 승인해야 다음 단계가 진행됩니다. - 사내망처럼 외부에서 접속할 수 없는 서버라면 셀프 호스티드 러너를 그 망 안에 두는 구성이 현실적이에요. 대신 러너 자체의 보안과 업데이트를 직접 책임져야 합니다.
주의할 점
첫째, 시크릿은 로그에서 마스킹되지만 스크립트가 값을 변형해 출력하면 보호되지 않습니다. 디버깅용 출력문을 남길 때는 특히 조심하세요.
둘째, 포크에서 올라온 풀 리퀘스트에는 시크릿이 전달되지 않습니다. 외부 기여자의 PR에서 배포가 돌아야 한다면 승인 후 실행되는 별도 워크플로로 분리해야 해요.
셋째, GITHUB_TOKEN 권한은 필요한 만큼만 엽니다. 워크플로 파일 상단에서 권한을 명시하면 기본 권한이 넓게 열리는 상황을 피할 수 있습니다.
넷째, 배포 키는 주기적으로 교체하고, 사람이 쓰는 키와 절대 섞지 마세요. 키가 유출됐을 때 영향을 줄이는 가장 확실한 방법은 권한을 좁히고 교체 주기를 짧게 가져가는 것입니다.
다섯째, ssh-keyscan으로 호스트 키를 그대로 신뢰하면 중간자 공격에 노출될 수 있습니다. 서버의 공개 호스트 키를 미리 확인해 시크릿이나 저장소 파일로 고정해 두는 편이 안전해요.
자주 묻는 질문
github actions 자동배포는 무료인가요?
공개 저장소에서는 표준 러너를 무료로 쓸 수 있고, 비공개 저장소는 계정 플랜에 포함된 사용량과 초과 과금 정책을 따릅니다. 정확한 한도와 과금 기준은 GitHub 공식 가격 문서에서 확인하는 것이 가장 정확해요.
배포가 실패하면 자동으로 롤백되나요?
기본 동작은 롤백이 아니라 실패입니다. 워크플로는 실패한 단계에서 멈출 뿐 이전 상태로 되돌리지 않으므로, 배포 스크립트 안에 이전 버전 복원 절차를 직접 넣거나 실패 시 복원 job을 이어 붙여야 해요.
서버에 직접 접속할 수 없는 환경에서도 쓸 수 있나요?
가능합니다. 사내망 안에 셀프 호스티드 러너를 두고 그 러너가 서버에 접근하도록 구성하면 외부에서 서버로 직접 들어가지 않아도 됩니다. 다만 러너가 상시 실행되는 만큼 패치와 접근 제어를 직접 관리해야 합니다.
배포 중에 새 커밋이 올라오면 어떻게 되나요?
concurrency를 지정하지 않으면 두 실행이 겹칠 수 있어요. 같은 그룹 이름을 지정하고 cancel-in-progress 값을 상황에 맞게 설정하면, 이전 실행을 취소하거나 새 실행을 대기시키는 방식으로 정리할 수 있습니다.
출처
- GitHub Docs - Workflow syntax for GitHub Actions
- GitHub Docs - Using secrets in GitHub Actions
- GitHub Docs - Deploying with GitHub Actions
배포 자동화를 붙인 뒤에는 운영 절차 문서를 함께 정리해 두면 좋아요. 서버 점검 주기와 장애 대응 순서를 다룬 서버 운영 체크리스트 정리와, 배포 중단을 줄이는 방법을 다룬 무중단 배포 방식 비교 글을 이어서 읽어 보시면 운영 흐름을 잡는 데 도움이 될 거예요.