AWS EC2에 SSH으로 - GitHub Actions 배포 자동화 (1)
DevOps

AWS EC2에 SSH으로 - GitHub Actions 배포 자동화 (1)

반응형

 

  배포 자동화를 자주 이용하지는 않았습니다. 물론 처음에는 자동화 배포를 적용했지만, 제가 느끼기엔 EC2 인스턴스가 크지 않아 수동으로 배포하는 것이 더 컨트롤하기 쉽고 빠르며, 변수상황에 대처하기가 너무 좋다고 생각했습니다. 그렇다 보니 반복적인 작업이 마냥 귀찮게만 느껴지지는 않은 상황이었습니다. 하지만 관리해야할 서버, 프로젝트가 늘어남에 따라 자동화 배포를 하기로 적용하기로 결정했습니다.

자동화 배포를 처음 접했을 때..

 

Jenkins, Github-Actions, AWS-Codebuild, GCP-Cloudbuild 배포 자동화할 수 있는 방향은 많습니다. 큰 프로젝트는 Jenkins, 가벼운 자동화 배포는 github actions을 많이 이용하는 것 같습니다.
그 중에서도 저는 AWS-Codebuild / Deploy (CodePipelin)만 사용 해보았었는데, github actions가 생각보다 간단하고 편리하다고 하여 github actions로 적용하기로 마음먹었습니다.

github actions로 배포를 하는방법을 검색하였고, 블로그마다 조금식 사용법이 달랐는데

  1. github actions에서 빌드를 하고, AWS S3에 AWS-Codebuild를 통해 올리고, EC2에서 해당 파일을 S3로 다운받고 배포
  2. SSH로 ec2에 곧바로 접속하여 레포를 삭제(rm -rf)하고 git clone 또는 git pull 하여 코드를 최신화 하여 실행

1번을 사용한다고 했을 때 Github-Actions과 AWS S3 추가하여 신경쓸 것이 더 늘어난다는 생각이 들어서 번거롭다고 생각하였습니다. 오히려 AWS-CodePipelin을 통해 깃과 연결하고 자동화 배포를 하는게 훨씬 나아보였습니다. 

2번을 선택하게 되면 빌드를 EC2에서 하거나, 레포에 컴파일된 폴더를 올려야 합니다. ( ec2에서 빌드를 하면 무리가.. )

 

더보기

추가적으로 자동화 배포 전에 EC2에서 빌드를 하게되면 생각보다 견디지 못하고 서버가 죽는일이 빈번하여 레포에 컴파일된 폴더를 올려 EC2에 내려받아 바로 사용하는 방법을 쓰고 있었습니다. 수동으로 배포하다 보니 이 방법이 매우 편하고 빨랐지만, 자동화 배포를 적용하는 시점에서 개선하고 싶었습니다. 특히 동일하게 빌드를 하는 것이 장점이라 생각했습니다.


그래서 찾아본 1, 2번 방법에서 힌트를 얻어 github actions로 빌드, 배포를 다 하는 방법으로 자동화 배포를 구현하게 되었습니다. 다른 문제점에 맞닥드리면 개선 해야겠지만, 심플하고 빠른 것을 원했기 때문에 만족감은 아주 좋았습니다.

 

사전 작업

  1. EC2에 빌드된 파일을(컴파일된 파일을) 복사할 곳을 위해 폴더를 만듬
    ( ubuntu를 사용하였고 /home/ubuntu 에서 server 폴더를 예시로 만들었습니다.)
  2. 1번에서 만든 server 폴더에 github actions에서(컴파일된 파일을) 다운을 받을 수 있게 chmod 명령어로 권한을 부여 
    (기본적으로 775로 만들어 질텐데 777로 변경 합니다.)

적용 했던 방법

1. 우선 깃의 레포지토리에서 settings 탭에 들어갑니다. 그리고 ssh 접속을 위한 환경변수 세팅을 해줍니다.

 

아래는 제가 작성한 yml 코드 기준입니다. 

  • HOST : EC2 IP 또는 주소
  • KEY : Private Key (.pem key)
    메모장 또는 vscode에서 해당 파일을 열람해서 해당 내용을 모두 복사 붙여넣기 하면 됩니다.
  • PORT : 포트 번호 (일반적인 ssh는 22포트)
  • USER_NAME : .pem 파일로 접속 할 때 사용하는 유저네임
    ssh -i 명령어 참고 또는 EC2 인스턴스에서 whoami 명령어로 확인 가능합니다. 보통 Ubuntu를 사용하면 "ubuntu", AWS Linux를 사용하면 "ec2-user"

 

 

환경설정을 완료 하였으면 위 이미지 처럼 set up a workflow yourself 눌러 yml 파일을 작성하면 됩니다.

아래 부분은 깃코드를 분석해 추천해주는 템플릿 같은 내용입니다. 제가 작성한 내용은 Simple workflow를 참고하였습니다.

 

# release.yml
name: dev branch auto ci process script

on: # event, job을 실행시킬 상황
  push:
    branches: [release]
  pull_request:
    branches: [release]

# job은 여러 개의 step으로 구성되며 Github Actions의 클라우드 서버에서 실행된다.
# 기본적으로 job은 병렬실행 되며, 의존관계를 갖도록 만들 수도 있다.
jobs: 
  build:
    # Job을 실행시키는 인스턴스 OS와 버전이며, 각 Job들은 독립적인 runner(container)에서 실행
    runs-on: ubuntu-latest
    steps: # 여러 개의 step으로 구성되며 step들은 순차적으로 실행된다.
      # 코드를 내려받는 액션
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "yarn"

      - name: yarn install build
        run: |
          yarn install
          yarn run build

      - name: 환경변수(whatap.conf 모니터링) #.env 등 활용가능 / ssh에 접속하여 스크립트로 생성 가능
        run: |
          echo "license=${{ secrets.WHATAP_LICENSE }}" > whatap.conf
          echo "whatap.server.host=${{ secrets.WHATAP_SERVER_HOST }}" > whatap.conf

      - name: copy file via ssh key
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER_NAME }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          source: "dist/*, package.json, yarn.lock, whatap.conf"
          target: "/home/ubuntu/homepage-server"

  deploy:
    needs: build #deploy를 실행시킬 선행조건
    name: deploy
    runs-on: ubuntu-latest 

    # ssh에 접속하여 script 실행
    steps:
      - name: excuting remote ssh commands
        uses: appleboy/ssh-action@master
        with:
          key: ${{ secrets.KEY }}
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER_NAME }}
          port: ${{ secrets.PORT }}
          script: |
            cd /home/ubuntu/server
            export NVM_DIR=~/.nvm
            source ~/.nvm/nvm.sh
            yarn install
            pm2 restart ./dist/main.js --name server

 

위 yml 파일은 nest.js 기준으로 release.yml 파일을 샘플링하여 작성하였습니다.

 

uses : github actions의 장점이 사람들이 만들어 놓은 액션을 사용할 수 있습니다. 그 중에서

yml 파일에서 job의 진행 순서는 아래와 같습니다.

 

build

  1. github actions에서 install, build 진행
  2. 환경 변수 파일 생성
  3. build한 컴파일된 파일(dist), 패키지 파일(package.json, yarn.lock), 환경변수 파일을 ec2로 scp를 이용하여 보냅니다.

deploy

  1. EC2에 SSH로 접속하여 명령어를 통해 복사한 폴더 위치로 이동하고, install 후 서버를 실행시킵니다.

 

Node 버전으로 인해 yarn isntall이 되지 않는 경우

더보기

 export NVM_DIR=~/.nvm
 source ~/.nvm/nvm.sh

 

스크립트에서 위 코드를 작성 해주어야 합니다. 이렇게 두 줄의 명령어를 사용하면 쉘에서 nvm을 사용할 수 있게 됩니다. 사용자가 터미널을 열 때 nvm 활성화되어 Node.js 버전을 쉽게 관리할 수 있게 됩니다. 해당 스크립트를 사용하면 기본 설정되어있는 default 버전을 사용하게 되니 설정해주시거나, node 버전을 선택하는 명령어를 작성하면 됩니다.

사용하지 않았을 시 아래와 같은 에러를 만날 수 있는데, node 버전을 올바르게 사용되고 있지 않아서 입니다. 

 

이렇게 yml 파일 작성을 완료하고 commit changes 버튼을 누르게 되면, .github/workflows 경로 아래에 해당 파일이 저장됩니다.
yml에서 on의 상황이 되면, 자동화 배포가 되는 상황을 actions 탭에서 볼 수 있습니다. 에러가 난 부분을 확인하고 yml 파일을 수정하면 되겠습니다. 보통 에러가 나면, script가 상황에 맞지 않거나 또는 ssh 접속이 안되어 생긴 에러 가능성이 높습니다. 만약 ssh를 접속하기 위한 값을 올바르게 넣었다면 ssh 포트를 전체 허용하지 않아 생긴 문제입니다.

 

 

문제점

단순하고 가볍게 배포를 한다면 좋은 방법이라 생각합니다. 하지만 여기서 문제점이 있습니다. 

 

1. deploy과정을 build에서 실행합니다.

build 에서 3번이 deploy에 해당 되는 내용인데 build에 포함 되어있습니다. 그러면 해당 액션을 deploy로 이동시켜주면 되지 않나? 라는 생각을 할 수 있지만, build와 deploy는 연속적으로 동작하지 않습니다.

쉽게 말하자면 jobs에서 build와 deploy로 각각의 방이 생성 된 것이고, build 과정에서 build한 파일(dist)을 deploy에서 쓸 수 없는 것입니다. 

 

 

2. ssh 전체 허용을 하고 있습니다.

당연하게도 해당 포트에 모두 허용하게 되면 보안으로부터 좋지 않습니다. 그래서 이 부분을 보안하기 위해 AWS IAM을 사용하여 특정 IP만 ssh포트를 허용할 것입니다.

 

문제점을 보완하기 위한 것은 다음 글에서 이어 작성하겠습니다. 

 

 

 

 

관련링크

https://jh-labs.tistory.com/416

https://doooodle932.tistory.com/163

반응형