코딩/배포

AWS EC2와 RDS에 NestJS Github Action 배포하기

춘 몽 2024. 11. 25. 22:01
728x90

작업의 흐름

  1. EC2, RDS 생성/작업
  2. Dockerfile 작성
  3. ci_cd.yaml 작성 (Github Action)
  4. Github Action 세팅

AWS

EC2 생성

https://youtu.be/Jd7xdvwY_VQ?si=WPGOnAYwiUS0pwtD


EC2 기본 작업

sudo su

root 권한 주고

apt update

패키지 목록 업데이트

apt-get upgrade -y

설치된 패키지 업그레이드


램늘리기 (선택 사항)

더보기
// 사양 확인
free

// 스왑 파일 생성
dd if=/dev/zero of=/swapfile bs=128M count=16

// 권한 업데이트
chmod 600 /swapfile

// 스왑 영역 설정
mkswap /swapfile

// 스왑공간에 파일 추가하여 즉시 사용
swapon /swapfile

// 프로시저 상태 확인 - 파일 잘 만들어졌나
swapon -s

// 편집기 열고
vi /etc/fstab

// 제일 아래에 내용 추가
/swapfile swap swap defaults 0 0

// 확인
free

Docker 설치

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

설정

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

최신버전 설치

docker ps

설치 확인

 

Docker 공식 홈페이지 - ubuntu에 Docker 설치

RDS 생성

https://springdream0406.tistory.com/238

 

AWS RDS 프리티어 요금 부과 안되게 생성하기 (작성 중)

생성AWS RDS의 데이터베이스 생성으로 들어간다.(글에서 다루지 않은 부분은 기본설정 그대로 놔둡니다.)  DB 인스턴스 식별자는 AWS의 DB탭에 표시되는 이름이며,마스터 사용자 이름은 DB 연결시

springdream0406.tistory.com


EC2에 ENV 파일 생성

sudo vi .env

github workflows에 적기에는 코드가 길어지고, 매번 .env 파일을 생성하고 지우는 행위는 아닌 것 같아서 직접 .env 파일 생성


EC2에 docker-compose.yaml 파일 생성

마찬가지로 docker-compose.yaml을 사용할 경우 github workflows의 코드가 깔끔해져서 생성함.

services:
  프로젝트명:
    image: 도커이미지 이름
    ports:
      - '포트:포트'
    env_file:
      - .env

아래 자동화에서 

${{secrets.DOCKERHUB_USERNAME}}/${{secrets.PROJECT_NAME}}:latest
이렇게 적었으므로 그에 맞게 도커이미지 이름 작성하기

 

포트는 일반적으로 80:3000


공개키 허용 (Github Action에서 PEM Key를 이용한 연결을 사용할 경우)

EC2 서버 내부에서

sudo vi /etc/ssh/sshd_config

로 sshd_config 파일을 열고 맨 밑에

PubkeyAuthentication yes
PubkeyAcceptedKeyTypes=+ssh-rsa

추가 후 esc -> : -> wq

systemctl restart ssh

재시작


PostgreSQL SSL 설정

https://springdream0406.tistory.com/242

 

database "postgres", no encryption

AWS의 RDS에 postgres DB를 생성하고 연결하게 되면 이러한 에러가 발생한다.이유는 암호화된 연결을 사용하지 않아서 이고, 간단하게 해결하고 싶다면 (NestJS 기준) DB설정 코드에 ssl: { rejectUnauthorized:

springdream0406.tistory.com


Dockerfile 작성

개발과 배포를 분리하기 위해 Dockerfile.prod 로 파일 생성함

이후 코드들이 그에 따라 조금씩 변경됨

## .dockerignore

.github/
.vscode/
coverage/
dist/
node_modules/
postgres/
.env*
.git/
.gitignore
docker-compose*
Dockerfile*
# Dockerfile.prod

## 이미지 크기를 줄이기 위해 2단계로 나눔

# 1단계
FROM node:22-alpine AS build

WORKDIR /usr/src/app

COPY package*.json ./
COPY pnpm-lock.yaml ./

RUN npm i -g pnpm

# 의존성만 설치
RUN pnpm i

COPY . .

# 빌드
RUN pnpm run build


# 2단계
FROM node:22-alpine AS production

WORKDIR /usr/src/app

# 빌드된 파일만 복사
COPY --from=build /usr/src/app/dist /usr/src/app/dist

# 프로덕션 의존성만 복사
COPY --from=build /usr/src/app/node_modules /usr/src/app/node_modules

CMD ["node", "dist/main.js"]

Github workflows

루트에 .github/workflows 폴더 생성

원하는이름.yaml 파일 생성

name: AWS EC2 CI/CD

on:
  push:
    branches: ['main']

jobs:
  #
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Create Env File
        env:
          ENV: ${{secrets.ENV}}
          DB_TYPE: ${{secrets.DB_TYPE}}
          DB_HOST: ${{secrets.DB_HOST}}
          DB_PORT: ${{secrets.DB_PORT}}
          DB_USERNAME: ${{secrets.DB_USERNAME}}
          DB_PASSWORD: ${{secrets.DB_PASSWORD}}
          DB_DATABASE: ${{secrets.DB_DATABASE}}
          REFRESHTOKEN_SECRET: ${{secrets.REFRESHTOKEN_SECRET}}
          ACCESSTOKEN_SECRET: ${{secrets.ACCESSTOKEN_SECRET}}
          FRONT_URL: ${{secrets.FRONT_URL}}
          SOCIAL_CALLBACK_URL: ${{secrets.SOCIAL_CALLBACK_URL}}
          SOCIAL_GOOGLE_ID: ${{secrets.SOCIAL_GOOGLE_ID}}
          SOCIAL_GOOGLE_SECRET: ${{secrets.SOCIAL_GOOGLE_SECRET}}
          SOCIAL_KAKAO_ID: ${{secrets.SOCIAL_KAKAO_ID}}
          SOCIAL_KAKAO_SECRET: ${{secrets.SOCIAL_KAKAO_SECRET}}
          SOCIAL_NAVER_ID: ${{secrets.SOCIAL_NAVER_ID}}
          SOCIAL_NAVER_SECRET: ${{secrets.SOCIAL_NAVER_SECRET}}
        run: |
          touch .env.test
          echo ENV="test" >> .env.test
          echo DB_TYPE="$DB_TYPE" >> .env.test
          echo DB_HOST="localhost" >> .env.test
          echo DB_PORT="$DB_PORT" >> .env.test
          echo DB_USERNAME="$DB_USERNAME" >> .env.test
          echo DB_PASSWORD="$DB_PASSWORD" >> .env.test
          echo DB_DATABASE="$DB_DATABASE" >> .env.test
          echo REFRESHTOKEN_SECRET="$REFRESHTOKEN_SECRET" >> .env.test
          echo ACCESSTOKEN_SECRET="$ACCESSTOKEN_SECRET" >> .env.test
          echo FRONT_URL="$FRONT_URL" >> .env.test
          echo SOCIAL_CALLBACK_URL="$SOCIAL_CALLBACK_URL" >> .env.test
          echo SOCIAL_GOOGLE_ID="$SOCIAL_GOOGLE_ID" >> .env.test
          echo SOCIAL_GOOGLE_SECRET="$SOCIAL_GOOGLE_SECRET" >> .env.test
          echo SOCIAL_KAKAO_ID="$SOCIAL_KAKAO_ID" >> .env.test
          echo SOCIAL_KAKAO_SECRET="$SOCIAL_KAKAO_SECRET" >> .env.test
          echo SOCIAL_NAVER_ID="$SOCIAL_NAVER_ID" >> .env.test
          echo SOCIAL_NAVER_SECRET="$SOCIAL_NAVER_SECRET" >> .env.test

      - name: Setup node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install pnpm
        run: npm i -g pnpm

      - name: Install dependencies
        run: pnpm i

      - name: Run tests
        run: pnpm test

  build:
    # test가 완료되어야 함
    needs: test
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Login to Dockerhub
        uses: docker/login-action@v2
        with:
          username: ${{secrets.DOCKERHUB_USERNAME}}
          password: ${{secrets.DOCKERHUB_ACCESSTOKEN}}

      - name: Build and Push Dokcer image
        run: |
          docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{secrets.PROJECT_NAME}}:latest -f Dockerfile.prod .
          docker push ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.PROJECT_NAME}}:latest

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: SSH to EC2 and deploy
        uses: appleboy/ssh-action@v0.1.3
        with:
          host: ${{secrets.EC2_HOST}}
          username: ${{secrets.EC2_USER}}
          key: ${{secrets.EC2_SSH_KEY}}
          script: |
            sudo docker compose down
            sudo docker rmi ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.PROJECT_NAME}}:latest
            sudo docker pull ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.PROJECT_NAME}}:latest
            sudo docker compose up -d

name은 github action에서 진행내역에 뜨는 이름

들여쓰기 주의

 

DOCKERHUB_ACCESSTOKEN 은 Docker Hub의 Account settings에서 발급받기

EC2_HOST 는 퍼블릭 IPv4 주소

EC2_USER 는 일반적으로 ubuntu

EC2_SSH_KEY 는 pemkey


Github Action 세팅

해당 레퍼지토리의 세팅에서 

로 가서 위에서 작성한 yaml에 들어가는 환경변수들 등록하기.

(EC2_SSH_KEY의 경우 start와 end 표시 전부 포함해서 그대로 복붙)


EC2 https 적용

https://springdream0406.tistory.com/241

 

배포 cookie 문제

소셜 로그인을 위해, jwt로 만든 refreshToken을 cookie에 담아서 주고받는 기능이 있는 프로젝트를, Front는 Netlify에, Back은 AWS EC2에 배포를 했다.문제1. EC2 https처음 만난 문제는 Back인 EC2의 https 변경이

springdream0406.tistory.com


참고

https://sjh9708.tistory.com/237

 

[AWS & Github Actions] CI/CD 파이프라인 구축 (Spring + Docker)

진행중인 프로젝트의 데브서버에 CI/CD 파이프라인을 구축하는 작업을 맡게 되었다.이전 포스팅에서 AWS EC2에 스프링 프로젝트를 Docker 컨테이너 사용과 함께 가장 심플한 형태로 배포해 본 적이

sjh9708.tistory.com

 

728x90