Develop

GitHub Actions SSH 배포 완벽 가이드 — 서버에 자동 배포하는 방법

수동 FTP 파일 복사에서 벗어나, GitHub Actions 워크플로와 SSH 접속 프로토콜을 활용해 단 한 번의 git push만으로 클라우드 서버에 안전하고 신속하게 빌드 결과물을 배포하고 서비스를 재가동하는 실전 CI/CD 가이드를 공유합니다.

GitHub ActionsSSH배포CI/CDdevtools
GitHub Actions 워크플로에서 SSH로 서버에 배포가 완료된 로그 화면 — 자동 배포 파이프라인 예시
  • ·GitHub Actions에서 SSH 개인 키는 반드시 GitHub Secrets에 저장하고 워크플로에서 참조해야 한다
  • ·ssh-keyscan으로 서버의 호스트 키를 사전에 known_hosts에 추가해야 SSH 접속 시 호스트 확인 프롬프트를 건너뛸 수 있다
  • ·appleboy/ssh-action 같은 서드파티 액션으로 SSH 명령 실행을 간단하게 구성할 수 있다
  • ·배포 스크립트를 서버에 미리 작성해두고 Actions에서 해당 스크립트만 실행하면 워크플로 파일을 간결하게 유지할 수 있다
옛날에는 빌드가 끝나면 파일들을 일일이 FileZilla 같은 FTP 툴을 켜서 서버로 드래그 앤 드롭해 넘겼습니다. 파일 하나가 전송 누락되면 서비스가 뻗고, 내가 지금 올린 파일이 최신본이 맞는지 헷갈리는 공포스러운 배포 밤샘 작업의 연속이었죠. GitHub Actions를 도입해 SSH 기반 자동 배포 파이프라인을 뚫고 난 뒤론 이런 불안감이 완전히 사라졌습니다. 이제는 main 브랜치에 코드를 머지하기만 하면 백그라운드에서 알아서 빌드하고, 서버에 무중단으로 코드를 갈아 끼워 줍니다. 배포가 이렇게 편하고 짜릿한 것인지 진작 알았어야 했습니다.

1. 배포 전용 SSH Key 보안 구성

GitHub Actions 안전한 SSH 배포를 위한 비밀 키 생성과 서버 권한 설정 요령

서버의 루트 비밀번호를 GitHub Actions 스크립트에 날것으로 적는 것은 자살 행위와 같습니다. 안전한 SSH 통신을 위해 ssh-keygen -t ed25519 명령으로 배포 전용 키 쌍을 새로 굽는 것이 정석이죠. 생성된 공개 키(public key)는 대상 클라우드 서버의 ~/.ssh/authorized_keys에 조심스럽게 기입해 두고, 비밀 키(private key)는 오직 내 깃허브 저장소의 Secrets 안전금고에만 꽁꽁 숨겨둡니다. 여기서 주의할 점은 서버 내 .ssh 폴더 권한은 700, authorized_keys 파일 권한은 600으로 굳건히 잠가두어야 운영체제 보안 커널 수준에서 SSH handshake 요청을 튕겨내지 않고 수락해 줍니다.

2. 워크플로 파이프라인 빌드

GitHub Actions YAML 작성과 SSH 접속 프롬프트 우회 방법

배포 워크플로 YAML 파일을 설계할 때, 첫 SSH 접속 시 터미널이 대기 상태로 머물며 접속 승인 여부를 묻는 대화형 프롬프트(Are you sure you want to continue connecting?)를 만나는 장벽이 있습니다. CI 가상머신에서는 키보드 입력을 줄 수 없으니 배포가 멍하니 멈추다 타임아웃으로 실패하게 되죠. 이를 해소하기 위해 ssh-keyscan 도구를 워크플로 중간에 수배하여 대상 서버의 공개 호스트 키 정보를 사전에 known_hosts에 자동 이식해 놓거나, 편리한 appleboy/ssh-action 서드파티 액션을 기용해 내부에 호스트 접속 인자값들을 정돈해 주는 것이 파이프라인 통과의 지름길입니다.

# .github/workflows/deploy.yml
name: Deploy to Server

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            cd /var/www/myapp
            git pull origin main
            npm ci --production
            npm run build
            pm2 restart myapp

배포 다운타임을 제로로 만드는 PM2 reload 연동 기법

서버 내부에서 코드를 땡겨온(git pull) 뒤 단순히 pm2 restart 명령을 날리면, 기존 Node 프로세스가 완전히 차단되고 새 프로세스가 켜질 때까지 약 2~5초간 사용자가 접속할 수 없는 끔찍한 다운타임이 강제 발생합니다. 이를 극복하려면 PM2의 reload 기능을 기용해야 합니다. PM2 reload는 클러스터 모드로 돌아가는 인스턴스들을 하나씩 차례로 내렸다가 깨우는 롤링 배포 방식을 실행하므로, 배포가 도는 중에도 최소 한 개 이상의 서버 스레드가 유저 요청을 보위하여 완벽한 Zero-downtime 무중단 실무 환경을 완수해 줍니다.

3. PM2 리로드 무중단 배포 및 슬랙 알림

GitHub Actions SSH 배포 성공과 실패를 Slack으로 알림 받는 방법

배포가 성공적으로 안착했는지 혹은 실시간 빌드 중 다운되어 에러가 났는지를 팀 메신저로 전파하는 구조를 구성하는 것이 운영 관리상 현명합니다. 슬랙에서 Incoming Webhook API 주소를 개설한 뒤 깃허브 Secrets에 묻어두고, 워크플로 마지막 절차에 if 조건(always, failure) 분기 매칭을 추가해두면 젠킨스를 빌리지 않고도 배포 생태계 성공 현황판을 안전하게 알림 수령할 수 있습니다.

자주 묻는 질문

GitHub Actions SSH 접속 도중 'Host key verification failed' 에러가 터져요.+

배포 러너(runner)가 대상 서버의 IP나 도메인 서명을 모를 때 발생하는 보안 차단 에러입니다. YAML 설정에 ssh-keyscan -p ${{ secrets.SSH_PORT }} -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 구문을 꼭 수동 수록하여 서버 호스트의 서명을 가상 환경에 미리 주입해 두었는지 꼼꼼히 점검하셔야 합니다.

서버에서 배포 도중 npm install 단계에서 메모리가 부족해 서버가 먹통이 됩니다.+

RAM 1GB 내외의 저가형 마이크로 인스턴스에서 흔히 겪는 현상입니다. 깃허브 액션 가상 머신(runner) 단에서 빌드 프로세스(npm run build)를 미리 돌려 번들링 완료된 정적 파일 본체만 서버로 전송받아 띄우는 구조로 리팩토링하시거나, 서버에 Swap 메모리를 2GB 수준으로 개설해 두면 거뜬히 극복됩니다.

관련 글