Github Action 워크플로우에서 SSH 접속하여 배포 자동화하기

Nowon LeeNowon Lee
3 min read

usealarm.com 서비스는 프론트엔드와 포켓베이스(Pocketbase) 백엔드로 구성되어있다. 프론트엔드는 Cloudflare Pages로 배포하도록 세팅했고, 백엔드는 변경사항이 발생할 때마다 SSH로 클라우드 가상 머신에 접속하여 서버를 수동으로 업데이트하고 있었다.

레포지터리를 업데이트해도 서버를 배포하는 걸 잊어버릴 때도 종종 있어서 백엔드 업데이트를 자동화할 수 있지 않을까 싶어서 방법을 찾아봤다.

가상 머신 상태

  • 현재 가상 머신에는 깃허브 레포지터리에 접근할 수 있는 SSH 키가 이미 세팅되어있다.

  • 추가적인 보안을 위해 SSH 키에 비밀번호까지 설정했다.

  • 깃허브 설정에 공개 키를 등록하고 git clone 명령어로 프로젝트를 클론했다. 공개 키가 known_host 파일에 등록되어 있어 연결하려는 호스트에 대한 추가적인 검증이 필요하지 않았다.

깃허브 액션을 도입해서 레포지터리의 특정 브랜치가 업데이트되었을 때 워크플로우가 실행되도록 했다. .github/workflows 폴더 내 yaml 파일을 생성하고 워크플로우를 작성한다.

name: Backend Deploy

on:
  push:
    branches: [main]
    paths:
      - "!web/**"

main 브랜치가 업데이트(push)되면 액션이 실행되도록 한다. paths에 값을 지정함으로써 어떤 파일이 업데이트되었을 때 액션 실행의 여부를 결정할 수 있다. 파일명 앞에 느낌표(!)가 있으면 이 파일이 업데이트되었을 때는 액션이 실행되지 않는다.

여러 프로젝트 내 의존성 관리를 용이하게 하기 위해 모노레포로 구성되었고 web 폴더에는 프론트엔드 프로젝트가 세팅되어 있기 때문에 web 폴더가 업데이트될 때는 백엔드를 업데이트할 필요가 없었다.

수동으로 배포할 땐 터미널에서 ssh 명령어로 가상 머신에 접속할 수 있었는데 워크플로우에서는 가상 머신에 접속하기 위해 appleboy/ssh-action 액션을 도입할 수 있다. 액션에서 SSH를 통해 가상 머신에 접속할 수 있고 원하는 스크립트를 실행시킬 수 있다.

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # Previous Steps

      - name: SSH connection
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_SSH_HOST }}
          username: ${{ secrets.REMOTE_SSH_USERNAME }}
          key: ${{ secrets.REMOTE_SSH_KEY }}
          port: ${{ secrets.REMOTE_SSH_PORT }}
          script: |
            cd ${{ secrets.REMOTE_HOME }}
            echo 'echo ${{ secrets.REMOTE_GIT_PASSWORD }}' > ~/.ssh/passphrase.sh && chmod a+x ~/.ssh/passphrase.sh
            eval `ssh-agent -s`
            DISPLAY=1 SSH_ASKPASS=~/.ssh/passphrase.sh SSH_ASKPASS_REQUIRE=force ssh-add ${{ secrets.REMOTE_GIT_KEY }}
            cd ${{ secrets.PROJECT }}
            export NVM_DIR=~/.nvm
            source ~/.nvm/nvm.sh         
            git pull
            nvm use 22
            pnpm install
            sudo systemctl restart SERVICE_NAME
            rm ~/.ssh/passphrase.sh

SSH 접속을 하기 위해서는 접속하려는 가상 머신의 주소, 계정, 개인 키, 포트 등이 필요하다. YAML 파일에 값을 그대로 적으면 제 3자가 탈취하여 악용할 수 있다. 깃허브 레포지터리 설정에서 액션에 필요한 암호(Action secrets)를 작성할 수 있다.

  • REMOTE_SSH_HOST: SSH로 접속하려는 주소

  • REMOTE_SSH_USERNAME: 계정 이름

  • REMOTE_SSH_KEY: SSH로 접속하기 위한 개인 키

  • REMOTE_SSH_PORT: 접속 포트

  • REMOTE_HOME: 홈 디렉토리 경로

  • REMOTE_GIT_KEY: 가상 머신에서 깃허브 계정에 접근할 수 있는 개인 키 경로. 액션에서 가상 머신에 접속하기 위한 SSH 키와는 다른 키이다.

  • REMOTE_GIT_PASSWORD: REMOTE_GIT_KEY의 비밀번호(Passphrase)

  • PROJECT: 프로젝트 경로

가상 머신에 직접 접속했을 때는 프로젝트를 git pull 명령어로 업데이트할 때 비밀번호를 직접 입력할 수 있었다. 액션에서 SSH 접속을 한 상태에서는 깃허브 SSH 키에 대해 비밀번호를 입력하기 위해 다음과 같은 방법을 쓸 수 있었다.

echo 'echo ${{ secrets.REMOTE_GIT_PASSWORD }}' > ~/.ssh/passphrase.sh && chmod a+x ~/.ssh/passphrase.sh
eval `ssh-agent -s`
DISPLAY=1 SSH_ASKPASS=~/.ssh/passphrase.sh SSH_ASKPASS_REQUIRE=force ssh-add ${{ secrets.REMOTE_GIT_KEY }}

passphrase.sh 파일을 생성하여 파일을 실행시키면 SSH 키 암호를 입력하도록 할 수 있다.

SSH Agent를 세팅하면 사용자가 한 번 입력한 비밀번호를 암호화 및 기억하여 다음 SSH 접속에 이용할 수 있다. SSH 접속을 사용하는 클라이언트는 에이전트와 통신하여 비밀번호를 입력하지 않고 안전하게 작업을 수행할 수 있다.

ssh-agent -s 명령어를 실행시키면 SSH 명령어가 에이전트와 통신하는데 필요한 환경 변수를 반환한다. eval 명령어를 통해 환경 변수를 등록할 수 있다. SSH 에이전트에 키를 등록하기 위해 ssh-add 명령어를 실행시킨다.

  • DISPLAY: 이 환경변수가 지정되어 있어야 SSH_ASKPASS에 지정된 프로그램을 실행시킨다.

  • SSH_ASKPASS: ssh-add 명령어를 실행시킬 때 SSH 키에 대해 비밀번호를 어디서 입력받을지 명시할 수 있다.

  • SSH_ASKPASS_REQUIRE: SSH_ASKPASS에 지정된 프로그램을 반드시 실행시키도록 한다.

프로젝트 업데이트가 끝나면 깃허브 SSH 키에 대한 비밀번호가 임시적으로 적혀있던 파일을 삭제해준다.

0
Subscribe to my newsletter

Read articles from Nowon Lee directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nowon Lee
Nowon Lee