Next.js,github action, ec2, docker를 사용한 배포 자동화

김인태·2024년 7월 9일
0

참 오랜만에 포스팅을 쓴다.
현재 진행하고 있는 프로젝트의 배포를 하면서 동시에 자동화를 하기위해
제목과 같은 프로세스를 진행했고, 동시에 오랜시간 삽질했기 때문에 이 과정들을 잊지 않기 위해서
이 포스팅을 적는다.

** 생략하는 것

EC2 인스턴스 생성과정, Next.js 프로젝트 생성과정

** 이 포스팅에서 보여드릴 것

workflow 설정, ec2에 도커 설치 및 구동, 트러블 슈팅

1. workflow 설정

github action을 사용해서 자동하를 해줄 것입니다
직접 레포지토리에 .github/workflow/[workflow이름].yml 이런식으로 만들어주셔도 되지만,

action 탭에 들어가서 flow를 정하고 수정하면 굳이 폴더를 만드는 작업을 안하실 수 있습니다.
저는 노드를 선택해서 수정하였습니다.

스크립트는 이렇습니다.

name: seoulful-client

on:
  push:
    branches: ['main']

jobs:
  build:
    runs-on: ubuntu-latest

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

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

      - name: Docker 빌드 설정
        uses: docker/setup-buildx-action@v3

      - name: Docker Hub Login
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Docker Build & Push check
        run: |
          docker build -t ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest .
          docker push ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest

스크립트에 대한 설명을 해보겠습니다.

1. on: push: branches: ['main']

이 워크플로우는 main 브랜치에 푸시가 발생할 때마다 실행됩니다.

2. jobs: build: runs-on: ubuntu-latest

build라는 작업이 정의되어 있으며, 이 작업은 최신 Ubuntu 환경(ubuntu-latest)에서 실행됩니다.

3. steps

build 작업 안에 여러 단계가 정의되어 있습니다. 각 단계는 차례로 실행됩니다.

Step 1: Checkout Repository

name: Checkout Repository
uses: actions/checkout@v3

이 단계에서는 actions/checkout 액션을 사용하여 현재 리포지토리를 체크아웃합니다. 이로써 워크플로우가 리포지토리의 코드를 사용할 수 있게 됩니다.

Step 2: Set up Node.js

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

이 단계에서는 actions/setup-node 액션을 사용하여 Node.js 버전 20을 설정합니다. 이를 통해 Node.js 환경에서 필요한 명령어들을 실행할 수 있습니다.

Step 3: Docker 빌드 설정

name: Docker 빌드 설정
uses: docker/setup-buildx-action@v3

이 단계에서는 docker/setup-buildx-action 액션을 사용하여 Docker 빌드를 설정합니다. Buildx는 Docker의 확장 빌드 기능을 제공합니다.

Step 4: Docker Hub Login

name: Docker Hub Login
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

이 단계에서는 docker/login-action 액션을 사용하여 Docker Hub에 로그인합니다. 로그인에 필요한 사용자 이름과 비밀번호는 GitHub Secrets에 저장된 값을 사용합니다.

Step 5: Docker Build & Push check

name: Docker Build & Push check
run: |
bash
docker build -t ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest .
docker push ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest

이 단계에서는 Docker 명령어를 직접 실행하여 Docker 이미지를 빌드하고, Docker Hub에 푸시합니다. 이미지 태그는 latest로 설정되며, 리포지토리 이름은 GitHub Secrets에 저장된 값을 사용합니다.

위의 시나리오들이 main 브랜치에 push 되었을 때 반영됩니다.
저는 docker를 통해서 build, push만하고, run은 ec2에서 환경변수와 함께 사용합니다

아래 명령어를 ec2에 접속해서 사용합니다!
이렇게 하는 이유는 도커 이미지에 있는 환경변수가 노출될 수 있기 때문에 그런 것입니다.
환경변수를 ec2에 등록하는 방법도 알려드리겠습니다.

 docker run -d -p 3000:3000 --name seoulful-client \
            -e NEXT_PUBLIC_NAVER_MAP_CLIENT_ID=  NEXT_PUBLIC_NAVER_MAP_CLIENT_ID= 5sxg3hx97b \ 
            -e NEXT_PUBLIC_NAVER_MAP_CLIENT_SECRET=NEXT_PUBLIC_NAVER_MAP_CLIENT_SECRET \
            -e NEXT_PUBLIC_KAKAO_REST_API=$NEXT_PUBLIC_KAKAO_REST_API \
            -e NEXT_PUBLIC_SERVER_URL=NEXT_PUBLIC_SERVER_URL \
            [본인 도커 유저이름]/[이미지 이름]

2. ec2에 도커를 설치해보자!

1. 우분투 시스템 패키지 업데이트
sudo apt-get update

2. 필요한 패키지 설치
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

3. Docker의 공식 GPG키를 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

4. Docker의 공식 apt 저장소를 추가
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

5. 시스템 패키지 업데이트
sudo apt-get update

6. Docker 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io

7. Docker가 설치 확인
7-1 도커 실행상태 확인
sudo systemctl status docker

7-2 도커 실행
sudo docker run [이미지 이름]

도커 실행 파트에서는 위에서 말했듯이 env랑 같이 주입한다!

3. docker에 환경변수를 넣어보자

정정하자면 env라기보다는 환경변수를 넣는 것이다.
아주 간단하다!

echo "export NEXT_PUBLIC_NAVER_MAP_CLIENT_ID=clientkey" >> ~/.bashrc
echo "export NEXT_PUBLIC_NAVER_MAP_CLIENT_SECRET=secret" >> ~/.bashrc
echo "export NEXT_PUBLIC_KAKAO_REST_API=your_kakao_rest_api" >> ~/.bashrc
echo "export NEXT_PUBLIC_SERVER_URL=your_server_url" >> ~/.bashrc

위와 같이 .bashrc 또는 .profile 파일 등에 환경변수를 추가하고,

source ~/.bashrc

source 명령어를 입력해준다!
만약 내 인스턴스 내에서 어떤 환경변수가 쓰이고 있는지 궁금하다면 그냥

$ubuntu@your-ip : env

env를 입력해보세요! 그러면 환경변수가 쫘라락 나올겁니다

4. dockerfile을 작성해보자!

도커가 빌드할 때 필요한 dockerfile과, .dockerignore, 작성하고 뜯어봅시다!

FROM node:20.11.0-alpine AS base
# From {baseImage명}:{version}
RUN apk add --no-cache 'libc6-compat'

WORKDIR /app

COPY package.json package-lock.json ./

RUN rm -rf ./.next/cache

RUN npm install

COPY . .

RUN npm run build

EXPOSE 3000

ENTRYPOINT [ "npm", "run", "start" ]

베이스 이미지 설정

FROM node:20.11.0-alpine AS base
node:20.11.0-alpine

이미지를 사용하여 경량화된 Node.js 환경을 설정합니다.
alpine 버전은 크기가 작고 필요한 패키지만 포함하는 Linux 배포판입니다.

필요한 패키지 설치


RUN apk add --no-cache 'libc6-compat'
libc6-compat

패키지를 설치합니다. 이 패키지는 glibc 호환 라이브러리를 제공하여 애플리케이션의 호환성을 높입니다.
--no-cache 옵션은 빌드 캐시를 사용하지 않고 패키지를 설치하여 이미지를 더 작게 만듭니다.

작업 디렉토리 설정

WORKDIR /app

디렉토리를 작업 디렉토리로 설정합니다. 이후 명령어들은 이 디렉토리에서 실행됩니다.

패키지 매니저 파일 복사

COPY package.json package-lock.json ./
package.json 및 package-lock.json

파일을 컨테이너의 /app 디렉토리로 복사합니다.
이 파일들은 프로젝트의 의존성 정보를 포함합니다.

빌드 캐시 삭제

RUN rm -rf ./.next/cache

Next.js의 빌드 캐시를 삭제합니다. 이는 빌드 과정에서 불필요한 캐시를 제거하여 빌드를 깨끗하게 시작하기 위해 사용됩니다.

의존성 설치와 빌드

RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
ENTRYPOINT [ "npm", "run", "start" ]

npm install 명령어를 실행하여 package.json 파일에 명시된 모든 의존성을 설치합니다.
현재 디렉토리의 모든 파일을 컨테이너의 작업 디렉토리 (/app)로 복사합니다.
npm run build 명령어를 실행하여 프로젝트를 빌드합니다
이 포트는 애플리케이션이 실행될 포트입니다.
npm run start 명령어를 실행하여 product version을 실행합니다.

위의 작업들을 docker build 명령어가 입력되었을 때 읽으면서 실행됩니다.

.dockerignoe
이미지 빌드시에 특정 파일이나 디렉토리를 제거하기 위해서 사용됩니다.
이미지 크기를 줄여서 빌드 속도를 향상시키고 보안을 높일 수 있습니다.

이와 같이 루트 폴더에 만들어주시고

Dockerfile
node_modules
dist
.eslintrc.js
.gitignore
.prettierrc
.env.local
.vscode
.next
.git
README.md

저는 이렇게 사용하였습니다.

자 다 마무리 하였습니다. 이제 인스턴스에서 돌려볼까요???!!

짜잔!! 아주 잘 나옵니다!!
고생하셨습니다...!! (물론 나 자신도..)
이렇게 시간이 오래 걸리는 작업인줄 몰랐는데 재밌었으면서 동시에 너무 힘든 작업이었습니다...
다음 게시물은 아마 빌드나 코드들 최적화를 하면서 그 결과를 포스팅하지 않을까합니다..
감사합니다..!!!

수정했습니다!!!!

+++ yml 파일과 , docker file 수정했습니다!

on:
  push:
    branches: ['main']

jobs:
  build:
    runs-on: ubuntu-latest

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

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

      - name: Install Dependencies
        run: npm install

      - name: Generate Environment Variables File for Production
        run: |
          echo "NAVER_MAP_CLIENT_ID=${{ secrets.NAVER_MAP_CLIENT_ID }}" >> .env
          echo "NAVER_MAP_CLIENT_SECRET=${{ secrets.NAVER_MAP_CLIENT_SECRET }}" >> .env
          echo "KAKAO_REST_API=${{ secrets.KAKAO_REST_API }}" >> .env
          echo "NEXT_PUBLIC_SERVER_URL=${{ secrets.NEXT_PUBLIC_SERVER_URL }}" >> .env

      - name: Docker 빌드 설정
        uses: docker/setup-buildx-action@v3

      - name: Docker Hub Login
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Docker Build & Push
        run: |
          docker build --build-arg NAVER_MAP_CLIENT_ID=${{ secrets.NAVER_MAP_CLIENT_ID }} \
                       --build-arg NAVER_MAP_CLIENT_SECRET=${{ secrets.NAVER_MAP_CLIENT_SECRET }} \
                       --build-arg KAKAO_REST_API=${{ secrets.KAKAO_REST_API }} \
                       --build-arg NEXT_PUBLIC_SERVER_URL=${{ secrets.NEXT_PUBLIC_SERVER_URL }} \
                       -t ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest .
          docker push ${{ secrets.DOCKERHUB_REPO }}/seoulful-client:latest

dockerfile

# 베이스 이미지 설정
FROM node:20.11.0-alpine AS base

RUN apk add --no-cache 'libc6-compat'

WORKDIR /app

COPY package.json package-lock.json ./

RUN rm -rf ./.next/cache

RUN npm install

# 빌드
COPY . .

ARG NAVER_MAP_CLIENT_ID
ARG NAVER_MAP_CLIENT_SECRET
ARG NEXT_PUBLIC_SERVER_URL
ARG KAKAO_REST_API

ENV NAVER_MAP_CLIENT_ID=$NAVER_MAP_CLIENT_ID
ENV NAVER_MAP_CLIENT_SECRET=$NAVER_MAP_CLIENT_SECRET
ENV NEXT_PUBLIC_SERVER_URL=$NEXT_PUBLIC_SERVER_URL
ENV KAKAO_REST_API=$KAKAO_REST_API

RUN npm run build

EXPOSE 3000

ENTRYPOINT ["npm", "run", "start"]

수정한 이유

어떻게 해도 naver map 이라던지 환경변수가 전부 주입이 안되는 문제가 발생했다.
그래서 map, marker 둘 다 표시가 안되었다..
그래서 문제는 뭐였냐?

서버쪽에서 사용하는 변수는 NAVER_MAP_CLIENT_ID 즉 NEXT_PUBLIC 이라는 접두사를 제거하고
클라이언트 쪽에서 사용하는 변수는 NEXT_PUBLIC을 붙여줘야 한다는 것!!!!

docker exec -it <컨테이너 id> /bin/sh

명령을 입력해 env를 확인했는데도 불구하고 왜 안됐을까 찾아보니
이런 이슈들이 있었다...
다음부턴 이런 실수 없도록 하자!!

profile
새로운 걸 배우는 것을 좋아하는 프론트엔드 개발자입니다!

0개의 댓글