Skip to main content

Command Palette

Search for a command to run...

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

SSH 통신을 통해 가상 머신에 접속하고 프로젝트 업데이트 및 배포를 자동화하기 위한 깃허브 액션 작업 과정을 기록했습니다.

Updated
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 키에 대한 비밀번호가 임시적으로 적혀있던 파일을 삭제해준다.

More from this blog

AI 에이전트에서 작업 맥락을 관리하기 위해 사용한 방법

AI 에이전트의 불편함 Codex CLI와 같은 AI 코딩 에이전트를 통해서 프로젝트 개발 속도를 높일 수 있다. 그리고 혼자 작업했을 때는 놓칠 수 있는 부분도 발생할 수 있는데 AI 에이전트를 통해 보완할 수 있었다. 컴포넌트에 대해서 접근성 태그 작성 API 호출하는 비동기 코드 작성할 때 에러 처리 그리고 AI 에이전트를 잘 활용하면 불가능하

Mar 8, 202610 min read

지시사항을 분할하여 Codex로 효율적인 작업하기

이슈 Codex와 같은 AI 기반 코드 에이전트는 출력 토큰의 수가 제한되어 있다. 그래서 다양한 작업을 하나의 프롬프트에 전부 몰아서 작성하면 원하는 결과가 나오지 않을 수 있다. Codex와 같은 LLM 기반 서비스는 출력 토큰 수를 초과하게 되면 일부 단계가 누락되거나 특정 요소를 과하게 요약할 수 있고 이는 전체적인 답변 퀄리티를 낮출 수 있기 때문이다. 그래서 다양한 AI 공식문서는 특정 작업을 더 작은 단위로 분할하는 방식을 강조한다...

Nov 3, 20255 min read

Key를 활용하여 컴포넌트 상태를 초기화하기

개요 컴포넌트의 상태가 업데이트되어도 컴포넌트 내부에 배치된 컴포넌트의 상태는 그대로 유지되는 경우가 있었다. 예전에는 Props 전달 및 내부에서 useEffect를 통해서 상태를 초기 상태로 업데이트하도록 했는데 useEffect를 남용하게 되면 상태 변화 추적이 어려워지는 문제가 있었다. 그런데 useEffect 외에도 Key를 통해서 컴포넌트 상태를 초기화할 수 있다는 사실을 알게 되었다. 리스트 렌더링 React 프론트엔드에서 목록 형...

Sep 21, 20253 min read

내가 AGENTS.md를 작성하는 방법

Codex CLI 요즘 프로젝트 개발에 Codex CLI를 활용하는 이유는 다음과 같다. 특정 프로젝트를 작업할 때 Claude Code는 종종 사용량을 초과한 것과 다르게 Codex CLI는 ChatGPT Plus 요금제만으로도 토큰 사용량 초과 걱정 없이 충분하게 활용할 수 있었다. 타 CLI 기반 AI 개발 도구에 비해서 요구사항을 더 정확하게 구현하고 꼭 필요한 작업만 진행하기 때문에 코드를 검토하는 시간을 줄일 수 있다. 반복적인 코드...

Sep 21, 20254 min read
N

Nowon Lee

22 posts