본문 바로가기

프로젝트/나만의명언집

[나만의 명언집] GithubActions 을 통한 CI 와 AWS CodePipeline (CodeDeploy)을 통한 CD 구축 | NextJS 프로젝트를 EC2 인스턴스로 배포하는 과정

반응형

 

들어가기 전

아마 두 CI/CD 도구를 혼합해서 사용하는 경우는 많이 없을 것 같긴 합니다(현장마다 어떻게 활용하고 있을지 자세히 모르니, 추측일 뿐입니다). 보통 Github Actions + AWS CodeDeploy 만 같이 사용하거나, 아에 CodePipe 라인으로 빌드, 커밋, 배포 까지 한 방에 처리할 수도 있겠죠.  
 
이 두 방식을 같이 사용하는 이유는 학습 목적도 있지만, AWS CodePipe 라인이 제공하는 로그 추적의 이점이 있기 때문입니다. 실제 인스턴스 터미널에서 CodeDeploy 로그를 확인하기 전에 어떤 부분에서 문제가 발생하였는지 대략적, 시각적으로 확인 가능하고, 버킷 마다 버전관리를 자동으로 해주기 때문에 필요에 따라 문제가 발생하면, 이전 버전으로 롤백하여 배포할 수 있도록 해주는 기능이 좋아 보였습니다. 
 
배포 하면서, 참고한 자료는 하단에 모두 링크해 둘 예정이니 참고하시길 바랍니다. 

 

비고 

- 2024.06.24 :  deploy.sh 파일의 하단에 pm2의 주석 부분에 잘못 설명된 부분이 있어서 수정했습니다.

구상중인 CI/CD 파이프라인과 절차 간략 요약

전체적으로 해당 포스트를 참고하신다면, 아래 절차를 따르게 될 겁니다.
 

 

push

우선 로컬에 있는 소스파일들을 커밋하여 깃허브 레포지토리에 push 하는 순간 깃허브 액션이 동작합니다.
 

깃허브 액션을 통한 S3 업로드

깃허브 액션은 내부적으로 npm run build 를 통해 배포용 파일을 만들고, 해당 파일을 포함해 npm run start  구동에 필요한 모든 파일들을 하나로 압축한 project.zip 파일을 생성합니다. 그리고 생성한 파일을 S3 버킷에 업로드 합니다.
 

AWS CodePipeline 에 의한 S3 버킷 변경사항 감지

AWS CodePipeline 이 S3 버킷에 업로드 된 파일의 변화를 감지하고, AWSCodeDeployRole 에 의해 AWS CodeDeploy 는 S3 버킷에 있는 파일을 읽어 옵니다.
 

AmazonEc2RoleforAWSCodeDeploy 에 의한 AWS EC2 접근과 CodeDeploy 에이전트에 의한 배포 스크립트 실행

앞서 읽어온 project.zip 파일은 CodePloy 에이전트에 의해  appspec.yml 파일에 입력한 설정에 따라 스크립트를 실행 합니다. 이 때 AWS EC2 에 AmazonEc2RoleforAWSCodeDeploy  을 통해 접근 권한을 얻어야 하고, 접근 이후에 앞서 말한 스크립트를 순차적으로 실행하여 최종적으로 배포를 종료하게 됩니다.
 

AWS CodePipeline 간단히 살펴보기

아래는 참고용으로 들고 와봤습니다. 코드 파이프라인을 전적으로 사용하면 아래 흐름대로 CI/CD 파이프라인을 구축할 수 있습니다.

https://docs.aws.amazon.com/ko_kr/codepipeline/latest/userguide/welcome-introducing.html

 


AWS CodePipeline 실행을 위한 스크립트 설정

이는 나중에 언급할까 했지만, 향후 CodePipeline 을 사용하여 EC2 에 배포 시 사용되는 중요한  부분이고, 로컬 파일에 포함 시켜야 하는 부분이라 시작 전에 정리하고 갑니다.
 

배포 후 실행할 스크립트 및  pm2 구성파일 설정

우선 CodeDeploy 에이전트가 인식 후 실행할 파일인  appspec.yml scripts/deploy.sh  를 src 폴더와 동일한 경로에 추가해줍니다. 그리고 pm2 실행을 위해 필요한 환경 구성 파일은 ecosystem.config.js 파일도 동일한 위치에 추가해 줍니다.

my-nextjs-project/
├── appspec.yml # 추가할 파일 : AWS CodeDeploy 에이전트가 실행할 명령어 설정 파일(꼭 .yml 이어야 합니다.)
├── package.json
├── next.config.js
├── public/
├── src/
│   ├── pages/
│   ├── components/
│   ├── styles/
├── scripts/ # 추가할 폴더와 파일 : appspec.yml 에 등록되어 실행할 훅을 모아둡니다. 훅은 각 생명주기별로 실행할 스크립트를 관리합니다.
│   ├── deploy.sh 
├── .gitignore
├── node_modules/
├── ecosystem.config.js # 추가할 파일 : pm2 실행을 위한 환경 구성파일
└── README.md

 

appspec.yml 파일 작성

해당 파일은 향후 project.zip 을 EC2 인스턴스에 배포 후 실행할 스크립트를 정의하는 파일입니다. 여기서 정의할 것은 projectp.zip 파일을 어느 경로에서 다운로드 할 것인지, 동일한 파일이 있으면 붙여넣기 할 것인지 등을 지정합니다. 또한, 필요에 따라서는 종속성 파일을 설치하게 할 수도 있습니다.

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/project
    overwrite: yes

# overwrite 로 해결되지 않는 파일 덮어쓰기 문제를 개선하기 위해 추가합니다.
file_exists_behavior: OVERWRITE

# file 에 지정한  /home/ec2-user/project 경로에 접근 후 쓰기 및 실행 작업을 수행하려면 권한을 부여해야 합니다.
permissions:
  - object: /home/ec2-user/awsbeginer-bucket-deploy
    owner: root
    group: root
    mode: 775

hooks:
  AfterInstall: 
    - location: scripts/deploy.sh
      timeout: 300
      runas: root

 

scripts/deploy.sh 파일 작성

스크립트를 실행할 환경인 #!/bin/bash 를 상단에 입력 해주고, 앞서 소스 파일을 다운로드 한 후 project 폴더에 압축풀기가 되었을 때, 이후 배포를 위한 일련의 동작을 순서대로 입력 합니다.

#!/bin/bash

# project.zip 압축풀기가 완료된 소스파일들이 모인 경로로 이동합니다.
cd /home/ec2-user/project

# node 가 설치된 경로의 환경변수 추가: npm 과 pm2 를 사용하려면 설정해야 합니다.
export PATH=$PATH:/home/ec2-user/.nvm/versions/node/v20.11.0/bin

# 종속성이 있으면 설치합니다.
npm install

# 서버를 재시작 합니다 -> 여기서 --only 다음의 'start'는 애플리 케이션 실행을 위한 사용자 작명입니다. 
# pm2 에 대한 자세한 부분은 npm pm2 를 찾아보세요. 
pm2 start ecosystem.config.js --env production --only start

 

pm2 환경구성 파일 정의 | ecosystem.config.js

pm2 로 관리할 애플리케이션에 대한 정보를 저장하는 파일입니다. 스크립트에서 pm2 를 실행하는 경우 해당 파일을 사용해서 인식할 수 있습니다. 그렇지 않으면 네임스페이스를 찾을 수 없다는 에러가 뜰 수 있습니다.

module.exports = {
    apps: [
      {
        name: "start", // 앱의 이름
        script: "./node_modules/next/dist/bin/next", // Next.js 스크립트 경로
        args: "start", // Next.js 앱을 시작할 때 사용할 인수
        exec_mode: "fork", // 실행 모드: cluster 또는 fork 중 선택
        instances: "2", // 클러스터 모드에서 실행할 인스턴스 수 (CPU 코어 수만큼)
        autorestart: true, // 프로세스 자동 재시작 활성화
        watch: false, // 파일 변경 감지 활성화 (개발 중에만 활용)
        max_memory_restart: "1G", // 1GB 이상 메모리 사용 시 재시작
        env: {
          NODE_ENV: "production", // Node.js 환경 설정
        },
      },
    ],
  };

 
 

각 옵션에 대한 설명이 궁금하다면

더보기

- name: 애플리케이션의 이름을 지정합니다. 
- script: 애플리케이션을 실행할 스크립트 파일의 경로를 지정합니다. 여기서는 Next.js의 실행 스크립트인 ./node_modules/next/dist/bin/next를 사용합니다.
- args: 애플리케이션을 시작할 때 전달할 인수를 설정합니다. 여기서는 "start"를 인수로 사용하여 Next.js 애플리케이션을 시작합니다.
- exec_mode: pm2에서 실행할 모드를 설정합니다. "fork"는 각 프로세스를 별도로 실행하는 모드입니다.
- instances: 클러스터 모드에서 실행할 프로세스(인스턴스)의 수를 설정합니다. CPU 코어 수에 따라 조정할 수 있습니다. 
- autorestart: 프로세스의 자동 재시작 여부를 설정합니다. true로 설정되어 있어 프로세스가 비정상적으로 종료될 경우 자동으로 재시작합니다.
- watch: 파일 변경 감지 기능을 활성화할지 여부를 설정합니다. 개발 중에는 주로 활용되며, 여기서는 배포 이후에 실행되므로 false로 설정되어 있어 비활성화 됩니다.
- max_memory_restart: 설정된 값(여기서는 "1G") 이상의 메모리를 사용할 경우 프로세스를 재시작합니다.
- env: 애플리케이션 실행 환경을 설정합니다. 여기서는 NODE_ENV 환경 변수를 "production"으로 설정하였습니다.

 


 

Github Actions 과 S3 버킷, 정책 권한

여기서는 깃허브 액션을 통해 S3 버킷에 빌드된 파일을 올리기 위한 몇 가지 작업을 수행합니다. 

.github 워크플로우 경로 설정

우선 프로젝트 폴더의 src 경로와 마주보는 위치에 .github > workflows > 작명.yml  와 같이 만들어 줍니다.

 

워크플로우 작성

워크 플로우를 아래와 같이 작성해줍니다. 예시를 참고해서 자신의 프로젝트 환경에 맞게 바꿔줍시다. 현 예시대로 한다면 CI 이후 S3 버킷에 배포하는 작업 까지 수행합니다. 그리고,  AWS 자격증명 부분이 있는데, 이후 과정에서 이를 만들어줄겁니다.

name: CI and Deploy

on:
  push:
    branches:
      - deploy

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    env:
     ## 환경변수
     
      BUCKET: wisesayings-build-buket # 버켓이름
      DIST: project.zip # 빌드 파일 경로
      REGION: ap-northeast-2 # S3 배포 지역이름
      # 혹은 깃허브 시크릿도 저장 가능 : AUTH_SECRET: ${{secrets.AUTH_SECRET}} 

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
        
    # npm run build 캐시를 가져옵니다.
      - name: Cache Primes
        id: cache-primes
        uses: actions/cache@v4
        with:
          # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node
          path: |
            ~/.npm
            ${{ github.workspace }}/.next/cache
          # Generate a new cache whenever packages or source files change.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          # If source files changed but packages didn't, rebuild from a prior cache.
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
       
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm run test

      - name: Build project
        run: npm run build
       
      - name: Archive files # 노드 모듈 제외한 모든 파일들 재귀적으로 압축하여 project.zip 로 변환
        run: |
          zip -r project.zip . -x "node_modules/*"
      - name: Verify project.zip
        run: ls -l project.zip
    
      # AWS S3 접근에 대한 자격증명 확인
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{env.REGION}}

     # S3 에 업로드
      - name: Upload to S3
        run: aws s3 cp ${{env.DIST}} s3://${{env.BUCKET}}/project.zip

 

S3 버킷 생성

버킷 생성 시 모든 옵션을 기본값으로 설정 합니다. 아래 액세스 차단 또한 기본값으로 설정하고, 버킷 만들기를 해줍니다. 정적 파일을 배포하는 것이 목적이 아니므로 외부에서 접근할 필요가 없기 때문입니다.

 
 

S3 버킷에 대한 업로드 권한을 부여하는 정책 생성(IAM)

IAM > 정책 > 정책생성

IAM 대시보드에서 정책 페이지로 들어온 후 우측 상단의 정책 생성을 클릭합니다.

 

권한 지정 > JSON > 정책 생성

권한 지정 화면에서 JSON 탭을 클릭한 후 하단에 첨부한 정책을 입력해줍니다. 버킷이름이라 적힌 부분에 S3 버킷 이름 입력해주시면 됩니다. 하단 정책을 입력하면, 사용자에게 S3 버킷에 대한 업로드 권한을 줄 수 있는 정책이 만들어집니다.  이후 나오는 화면은 페이지 설명을 참고하여 작성 후 정책 생성 까지 마무리 해줍니다.

 

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::버킷이름",
        "arn:aws:s3:::버킷이름/*"
      ]
    }
  ]
}

 
 
정책 페이지로 돌아와서 검색 시 앞서 생성한 정책이 표시되는 것을 확인하였다면, 다음 단계로 넘어 갑시다.

 

S3 버킷에 대한 업로드 권한을 가진 사용자 생성(IAM) | 정책과 사용자 연결

이번에는 앞서 생성한 정책과 사용자를 연결해주는 단계 입니다.

IAM > 사용자 > 사용자 생성

IAM 대시보드에서 사용자 선택 후  우측 상단의 사용자 생성을 클릭합니다.

 

사용자 이름

식별하기 쉬운 사용자 이름을 입력 후 다음으로 넘어갑니다.

 

권한 설정 > 직접 정책 연결 > 정책 선택 > 사용자 생성

앞서 만들어둔 정책과 연결하기 위해 직접 정책 연결을 선택하고, 정책 이름의 키워드를 입력 후 나타난 정책을 선택해줍니다.

 
잘못 선택한 내용이 없는지 확인하고 사용자 생성 까지 해줍시다.

 

사용자 계정의 액세스 키 발급

사용자 > 보안 자격 증명 > 액세스 키 만들기

앞서 과정을 거쳤다면 사용자 화면에서 생성된 사용자 이름이 표시 될 겁니다. 해당 사용자 이름을 클릭 하여 들어가 줍니다.

 
그 후 나타나는 탭 중에서 보안자격증명 을 클릭하고, 하단에 액세스 키 만들기를 클릭해 줍니다.

 

액세스 키 모범 사례 및 대안과 키 발급

기타를 클릭하고 넘어가 줍시다. 그 후 선택사항이 보이는데, 필요 없으면 바로 넘어가 줍시다.

 
액세스 키 검색이 나오면, 해당 키를 안전한 곳에 보관하고, 완료를 누르고 나와 줍니다.

 

github에서 액세스 키를 비밀키로

이번에는 앞서 발급 받은 액세스 키를 github actions 동작 시 S3 버킷 접근에 대한 자격증명으로 사용하기 위해 비밀키로 설정해줄 것입니다. 이 부분은 별도 포스트에서 작성하였기 때문에, 링크를 걸어둡니다.
 
https://duklook.tistory.com/553#깃허브-액션에서-사용할--몇-가지-비밀키-생성-|-aws-s3-액세스-키를-.env-로

 

[푸드피커] Github Actions 을 통한 AWS S3+CloudFront 배포 자동화 구축

[이전 포스트] AWS S3 + CloudFront 를 사용한 정적사이트 배포 [푸드피커] AWS S3 + CloudFront 를 활용한 정적사이트 배포시작에 앞서이번에 푸드피커 사이트를 S3와 Cloud Front 를 사용하여 배포를 해보았

duklook.tistory.com

 
과정을 잘 따라왔다면, 현재 비밀키로 아래 두 가지가 등록되어 있어야 합니다. 

 

깃허브 액션을 통한 S3 배포 테스트 하기

위 단계 까지 따라오셨다면, 이제 Github actions 을 사용하여 S3 에 배포할 준비가 완료된 것 입니다. 앞서 처음에 .gihhub/workflows/ci-cd.yml 파일이 실행되려면 , 아래와 같이 branches 로 지정한 브랜치명인 deploy 에 소스파일을 커밋 후 push 했을 때 입니다. 즉, git push origin deploy 를 입력하고, 소스파일이 레포지토리에 저장되기 전에 깃허브 액션이 실행되고, 앞서 지정한 모든 작업들이 순차적으로 실행되게 됩니다.

on: # 이벤트를 등록합니다.
  push:
    branches:
      - deploy # push 이벤트 시 깃허브 액션을 수행할 브랜치명

 
push 후 깃허브의 프로젝트 레포지토리로 들어왔을 때, Actions 탭에서 실행중인 깃허브 액션의 워크플로우 상세를 볼 수 있습니다. 아래와 같이 녹색깔 마크가 표시된다면 정상적으로 업로드 된 것입니다.

 
업로드 성공 후 AWS S3 버킷에 들어가주니 정상적으로 업로드 된 것을 볼 수 있었습니다. 만일 업로드가 되지 않았다면, 깃허브 액션 로그를 하나씩 열어보시면서 어느 부분에서 알게 모르게 실패하였는지 확인해보셔야 합니다. 

 


CodeDeploy 관련 IAM 생성

깃허브 액션을 통한 CI 는 마무리 되었습니다. 이제 CodePipeline 과 CodeDeploy 와 관련한 몇 가지 설정을 해줌으로써 CD 역할을 해주도록 처리해주기만 하면 됩니다.


 
우선 AWS IAM 에 들어가서 배포에 필수적인 역할 두 가지에 대한 권한을 생성해야 합니다. 첫 번째로 EC2 인스턴스에 접근하기 위한 권한을 추가하고, 그 뒤에 CodePipeline의 CodeDeploy 설정에서 사용할 권한을 추가해주는 작업을 해줄겁니다.
 

AWS IAM 생성 | AWS EC2 에 대한 CodeDeploy 접근 권한

역할 생성

IAM 대쉬보드로 들어와서 좌측 메뉴에서 역할 을 눌러줍니다. 그리고 우측 상단에 역할 생성을 클릭하여 들어가 줍니다.

 

신뢰할 수 있는 엔티티 선택

그럼 아래 화면이 나옵니다. 여기서 신뢰할 수 있는 엔티티 유형은 AWS 서비스를 선택해주고, 사용사례는  EC2 를 해줍니다.

 

권한추가

권한추가 화면에서는 Codedeploy 가  EC2 에 대한 접근 할 수 있는 권한인 AmazonEC2RoleForAWSCodeDeploy 를 찾아서 체크 해주고 다음으로 넘어가 줍니다.

 

이름 지정, 검토 및 생성

이제 생성할 권한 정책의 이름을 본인이 쉽게 식별 가능한 이름으로 지정해준 뒤, 앞서 선택한 항목들이 맞는지 체크한 뒤 역할 생성을 해줍니다.

 
 
역할 항목에 돌아와서 앞서 생성된 Role 이 보이는지 체크 해줍니다.

 
 

AWS IAM 생성 | 코드 배포를 위한 CodeDeploy 에 대한 사용권한

앞서 역할을 두 가지 생성해야 한다고 언급했었습니다.  이번에 생성하는 권한은 CodeDeploy 에 대한 모든 사용 권한을 가지는 역할을 생성하는 것입니다.
 

[ ]신뢰할 수 있는 엔티티 선택

앞서와 동일하게 신뢰할 수 있는 엔티티는 AWS 서비스를 선택하고, 사용 사례는 CodeDeploy 를 선택해 줍니다. 그리고 최하단에 사용 사례에 대한 라디오 선택 항목이 보이는데 CodeDeploy (기본값)를 유지한 상태로 다음으로 넘어 갑니다.

 

[ ] 권한추가

권한 추가는 아래와 같이 AWSCodeDeployRole 이 기본으로 정해진 역할로 나옵니다.  CodeDeploy 에 대한 사용사례에 맞는 권한이 기본으로 설정되어 있으므로 건드릴 필요 없이 다음으로 넘어 갑니다.

 

[ ] 이름 지정, 검토 및 생성

식별 가능한 역할 이름을 지정해주고, 앞서 선택했던 항목과 큰 차이가 없는지 확인하고 역할 생성을 눌러줍니다.

 

생성한 역할 최종 확인

IAM 대쉬보드에서 역할을 클릭하고, 앞서 생성한 역할 이름이 정상적으로 표시되는지 확인합니다. 

 

EC2 생성하기 

EC2 생성하기 부분은 별도의 포스트로 작성하여 관리하고 있습니다. EC2 생성하기 부분만 보시면 됩니다. 보실 분은 참고해주세요. 다만 EC2 를 새로 생성하실 때, IAM instance profile 항목에 앞서 우리가 생성했던 EC2CodeDeploy 역할을 선택해줍니다. 이미 만들어 두셨다면, <기존에 존재하는 EC2에서 CodeDeploy IAM 설정하기 파트> 부터 보시면 됩니다.
 
또한, EC2 인스턴스와 코드파이프라인을 연동하기 위해서는 Tags 를 추가해줘야 합니다. 아마 기본값으로 설정되어 있을 텐데 없다면, 태그 추가가 보이는 항목에서 추가해주면 됩니다. 까먹으셨더라도 향후 챕터에서 생성하는 방법을 설명하므로 걱정하지 않으셔도 됩니다.

 

[나만의명언집] NextJS, Posgres 기반 앱을 EC2에 천천히 배포해보자(with WIndow 10 , PUTTY, PSCP, 아마존 리

포스트 목적해당 포스트는 나만의 명언집 프로젝트를 AWS EC2 에 배포하는 과정을 정리하는 것을 목적으로 합니다. 다양한 EC2 배포 포스트를 보았지만, 각 과정에서 해당 명령어의 설명이나 이러

duklook.tistory.com


[잠깐 알고가기] AWS EC2 생성 전에 알아보는 사용자 데이터 편집

AWS EC2 를 생성할 때, 최하단의 고급 세부 정보를 드롭다운 후 최하단으로 스크롤 하다보면 사용자 데이터 편집이라고 보입니다. 

참고로 User data is already is base64 encoded(사용자 데이터가 이미 base64로 인코딩되어 있음) &nbsp;확인란을 선택하지 않는 한 인스턴스 시작 시 입력은 base64로 인코딩됩니다.

 
여기서, 사용자 데이터는 인스턴스를 시작할 때 실행할 명령 또는 명령 스크립트를 제공할 사용자 데이터를 지정합니다.
 
예를 들어 sudo yum -y update 라고 입력해두면, 인스턴스가 생성되는 동시에 yum 패키지가 현재 운영체제의 버전가 호환되는 최신 버전으로 업데이트 됩니다. SSH 로 인스턴스 내부에 접속 후 실행할 명령어들을 미리 정의하는 공간이라고 보시면 됩니다. 따로 설정할 생각이 없고, 직접 접속 후 패키지 설치 등을 하실거라면 굳이 건드릴 필요가 없기에 AWS EC2 생성 시에도 선택 사항으로 나와 있습니다. 
 
자세한 내용은 아래 링크를 참고해주세요.
 

 

시작 시 Linux 인스턴스에서 명령 실행 - Amazon Elastic Compute Cloud

시작 시 Linux 인스턴스에서 명령 실행 Amazon EC2에서 인스턴스를 시작할 때 사용자 데이터를 인스턴스에 전달하여 일반적인 구성 작업을 자동으로 수행하는 데 사용하도록 할 수 있고, 인스턴스가

docs.aws.amazon.com

 
 


기존에 존재하는 EC2에서 CodeDeploy IAM 설정하기

기존에 EC2 가 생성된 상태라도 IAM 을 수정할 수 있습니다. 이번 파트에서는 앞서 생성된 Ec2CodeDeploy 를 IAM 으로  지정해볼 것입니다.

AWS EC2의 인스턴스 들아가기

검색창에 EC2 를 검색하고, 보이는 대시보드에서 인스턴스를 선택합니다.

 
그 후 인스턴스 ID 를 클릭하여 인스턴스 관리 페이지로 들어가줍니다.

 

IAM 수정하기

보이는 화면에서 우측 상단에 인스턴스 상태를 드롭다운 하고, 보안을 클릭 하고, 좌측에 보이는 IAM 역할 수정을 클릭합니다.
 

 
그러면 앞서 생성한 IAM 역할이 보입니다. 이를 클릭해주고,  IAM 역할 업데이트를 눌러주시면 끝입니다.

 
변경이 되었다면, 인스턴스 세부 정보에서  IAM 역할이 변경된 것을 볼 수 있어야 합니다.

 

EC2 내부에서 CodeDeploy 에이전트 설치하기

아마존 리눅스 환경에서 진행하는 예시를 기준으로 합니다.

 
이번 파트는 CodeDeploy 를 운영체제 환경에서 사용할 수 있도록 관련 파일들을 설치 및 설정하는 작업을 수행해야 합니다.
 
우선 EC2 인스턴스에 접속해 줍시다(여기 까지 오신 분들은 접속이 가능하다고 전제하에 진행합니다).

## 이 부분은 따라 하시면 됩니다.
sudo yum update # 패키지 종속성 최신 버전으로 업데이트
sudo yum install ruby # rudy 패키지 설치
sudo yum install wget # wget 패키지 설치 : 웹 으로 부터 CodeDeploy 에이전트를 Get 요청 시 사용할 패키지
cd /home/ec2-user # 홈 디렉토리 이동 -> CodeDeploy 에이전트를 설치할 경로

## 이 부분은 예시 입니다. 추가적인 명령어 사용방법은 하단에서 설명합니다.
wget https://[bucket-name].s3.[region-identifier].amazonaws.com/latest/install # CodeDeploy 에이전트 설치 파일을 다운로드

 

[매우 중요] wget 설치 관련 중요사항 및 서울 리전 에이전트 설치와 환경설정

규칙

여기서 중요한 포인트를 확인하고 갑시다. wget ~ 다음에 입력된 각 명령어 중에서 bucket-name은 해당 지역의 CodeDeploy 리소스 키트 파일이 포함된 Amazon S3 버킷의 이름이고, region-identifier 는 해당 지역의 식별자입니다.

예를들어 wget https://aws-codedeploy-us-east-2.s3.us-east-2.amazonaws.com/latest/install

와 같이 입력했다면, 미국 동부(오하이오) 에 있는 s3 버킷에 접근하여 CodeDeploy 에이전트를 설치한다는 의미입니다.

서울 리전에서 에이전트 설치 파일 받아오는 법

그렇다면, 서울을 리전으로 두고 있는 대한민국의 경우는 어떻게 할까요? 바로 아래와 같이 입력해주시면 됩니다.  서울리전에 있는 ap-northeaset-2 을 식별자로 가진 S3 버킷(버킷명: aws-codeploy-ap-northeast-2)에 접근하여 CodeDeploy 에이전트를 받아오게 됩니다.

wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install # CodeDeploy 에이전트 설치

 
결론은 자신의 EC2 리전에 맞게 CodeDeploy 에이전트를 설치하면 됩니다. 설치 후 200 OK 가 보인다면 성공입니다.

 

CodeDeploy 에이전트 실행권한 설정

 /home/ec2-user 경로에서 ls 을 입력해주면, 앞서 wget 으로 설치한 결과인  install 파일이 생성된 것을 볼 수 있습니다.

 
하지만 이대로는 해당 파일을 실행할 수 없기 때문에,   install 파일에 대한 실행 권한을 설정해야 합니다.  이를 위해
chmod +x ./install 이라 입력해줍니다. 이렇게 되면 ./ 경로의 install 파일에 대한 실행권한을 획득(+x) 합니다.

chmod +x ./install

chmod 옵션 설명 요약표

 

에이전트 설치 및 실행 확인하기

install 파일에 대한 실행권한이 있으므로 이제 해당 파일을 실행해줍니다. 관리자 권한으로 sudo ./intsall auto 라고 입력하면 자동으로 최신버전의 CodeDeploy 에이전트를 설치하고 실행해줍니다.

sudo ./install auto

 
 
정상적으로 에이전트가 실행중인 지 확인하려면 아래 명령어를 입력해주세요.

sudo service codedeploy-agent status

 
PID 가 표시되면서 실행중임을 나타내면 정상동작하고 있는 것입니다.

 
 


AWS CodePipeline 들어가기 전

이번 파트 부터는 AWS CodePipeLine 을 설정해보는 시간을 가져볼 겁니다. CodeDeploy 가 CD(지속적인 배포/전달)에 중점을 두고 있다면(즉, 빌드 후 배포하는 과정) CodePipeline 은 이러한 CD 를 한 곳에서 편리하게 관리하고 자동화 할 수 있도록 도와주는 시스템이라 보면 됩니다. 사실 CodeDeploy 뿐만 아니라 깃허브 등의 소스코드 저장에서 커밋, 빌드, 배포 까지 모두 수행 가능하게 해주는 CI/CD 도구 이지만, 현 포스트에서는 버전관리와 CD 를 위한 목적으로 사용됩니다.
 
따라서 해당 파트에서는 AWS CodeDeploy 내에서 애플리케이션 생성과 배포 그룹 지정 등의 작업을 수행하고, Code Pipeline 내에서 파이프라인을 생성하는 작업을 수행할 것입니다. 좀 긴편이므로 인내를 가지고 하나씩 수행해 봅시다.

Code Deploy

지금부터는 배포에 필요한 애플리케이션을 생성해야 합니다. 각 단계에 맞춰 잘 진행해주세요.

Code Deploy  |  애플리케이션 생성

CodePipeline 의 대시보드에 들어오신 후 CodeDeploy 를 찾아줍니다. 드롭다운 후 애플리케이션 항목을 클릭합니다.

 
 
그럼 우측 상단에 애플리케이션 생성이 보일텐데 이를 클릭해줍니다.

 
 
애플리케이션 생성 페이지가 나오면, 애플리케이션 이름 컴퓨팅 플랫폼을 선택해 줍니다. 현재 EC2 인스턴스 내에 배포하므로 EC2/온프레미스를 플랫폼으로 지정합니다. 그리고 애플리케이션 생성을 눌러 주고 나와줍시다.

 

Code Deploy |  배포 그룹 생성

앞서 애플리케이션 항목에 들어오시면 새롭게 생성된 애플리케이션 정보가 보일 겁니다. 우측 에 보시면 배포 그룹 생성이 보이는데 이를 클릭해 줍니다.

 

배포 그룹 이름과 서비스 역할

배포 그룹 이름 지정과 서비스 역할이 상단에 보일겁니다. 그룹 이름을 지정하고, 서비스 역할 부분은 현재 생성하는 그룹이 Code Deploy 에 대한 부분이므로 앞서 IAM 에서 생성했던 CodeDeployRole 을 선택해줍니다.

배포 유형

그 다음 배포 유형은 현재 위치를 선택합니다. 

 

환경구성

환경 구성은 당연히 EC2 인스턴스에 배포하므로 Amazon EC2 인스턴스를 선택합니다. 
 
환경 구성에서 추가적으로 태그 그룹을 지정해주어야 합니다. 해당 태그 그룹으로 식별된 인스턴스에 대해서 배포가 이루어지므로 필수값입니다.
 

 
 
앞서 예시와 같이 현재 일치하는 고유한 인스턴스가 없다고 뜰 수 있습니다.  이는 EC2에 존재하는 태그가 아니기 때문입니다

 
 
이를 연동시켜 주려면,  EC2 대시 보드 -> 인스턴스 -> 태그 항목으로 들어가셔서 태그 관리 를 클릭해 줍니다. 그러면 새로운 태그를 추가하거나 기존 태그를 수정할 수 있는 창이 뜨므로 앞서 추가했던 태그와 동일하게 입력해줍니다. 

 
그 후 다시 확인해보면 재유효화가 되면서 일치하는 인스턴스가 있다고 변경됩니다.

 

배포설정 / 로드 밸런서

배포 설정은 기본으로 지정된 값을 사용합니다. 로드 밸런서의 경우에는 있으면 트래픽 부하 분산 관리에 매우 좋겠지만, 단일 인스턴스만 있는 경우에는 굳이 필요하지는 않습니다. 다중 인스턴스를 사용하고 있는 경우에는 고려할 필요는 있습니다.  저는 단일 인스턴스만을 사용하므로 로드 밸런서를 체크하지 않고 넘어 가겠습니다
 

참고로 로드밸런서는 별도로 생성해준 후 연동시켜 주어야 하며, 프리티어가 아니라면 비용이 추가될 수 있습니다.)

 

나머지 유형

나머지 옵션은 모두 기본값으로 설정하고 넘어가 줍시다. 현재 빠른 배포를 위해 신경쓸 부분은 아니므로 기본값으로 충분합니다. 
 
 


생성이 완료되었으면 아래와 같이 기존 애플리케이션 배포 그룹 정보가 업데이트 된 것을 볼 수 있습니다.

 


 
이제코드파이프라인을 설정하는 부분입니다. 전반적으로 S3 버킷의 변경사항을  CodePipeline 에서 감지하고, Code Deploy 가 EC2 에 배포할 수 있는 환경을 구축하는 부분이므로 잘 참고해주세요.

CodePipeline | 파이프 라인 생성

이제 CI/CD 를 관리할 파이프라인을 생성해줄 시간입니다. 우선 파이프라인을 찾아서 들어가줍니다. 그러면 우측 상단에 파이프 라인 생성이 보일 텐데요. 이것을 클릭해 줍니다.

 

버전 설정과 서비스 역할 

파이프라인 이름, 유형, 실행 모드, 서비스 역할을 차례대로 지정해주니다. 최근 V2 라는 새로운 파이프라인 버전이 나왔습니다. 그러나 저는 표준화방식인 V1 버전을 사용할 것입니다.  이것을 보고 저를 따라하시기 보다는 하단의 [링크] 파이프라인 버전 선택과 관련해서 팁을 얻고자 한다면? 을 참고해주시길 바랍니다.

 2024.06.20 오후 5: 23분 경 재확인해보니 v2 사용을 강제하는 방향으로 바뀌었습니다. 이제는 V1 사용을 권장하지 않네요. V2를 사용합시다. 
두 버전은 사용량에 따른 비용 차이가 있는 편입니다. 그 만큼 V2에서 제공하는 기능이 많습니다. V1 에서는 새로운 파이프라인이 실행되면 기존 파이프라인이 대체되는 방식으로 동작하는 반면 V2 를 사용하면 대체, 대기 혹은 병렬로 모든 파이프라인을 처리할 수 있도록 선택의 폭이 커졌습니다. 또한 비용 부과 방식도 다릅니다. V1는 파이프라인 활성화 수를 기준으로 두는 반면 V2 는 빌드시간을 기준으로 합니다. 즉, 더 효율적인 방식으로 바뀌었습니다.

 
서비스 역할의 경우에는 새 서비스 역할을 선택하고, 해당 역할을 식별할 이름을 지정해줍니다.

 

변수와 고급설정

마지막으로 변수 추가 와 고급 설정 부분이 있습니다. 변수 추가는 파이프라인 실행시 추가할 환경변수를 설정하는 것이고, 고급 설정은 파이프라인 실행 시 배포에 사용할 리소스를 저장하고 있는 저장소와 암호화 키에 대한 부분입니다.
 
다만 변수의 경우에는 V2 버전을 선택해야 사용가능합니다. 이를 참고하셔서 프로젝트 환경에 맞춰서 해주시면 됩니다. 나머지는 기본값으로 설정하고 다음 단계로 넘어가 줍니다.

 

CodePileline | 소스 공급자와 스테이지

이 부분은 애플리케이션 소스를 어디서 가져오는가를 설정하는 부분입니다. Github 를 사용한다고 했으므로, 이를 기준으로 설명합니다.

소스 공급자

소스 공급자는 S3 를 사용하기 때문에 Amzon S3 를 선택해 줍니다. S3 객체 키를 우리가 앞서 업로드 시 지정한 파일의 이름을 의미합니다. 객체 키라고 부르는 이유는 S3 가 파일을 저장할 때 키-값 형태로 저장하기 때문입니다. 그래서 객체 라고 부릅니다.

변경감지옵션이 있는데 추가 비용을 아끼기 위해 AWS CodePipeline 을 선택 했습니다.

 

빌드 스테이지

그 다음은 코드 빌드를 어떤 도구를 사용하여 행하는지 선택하는 부분입니다. 이 부분은 현재 프로젝트에서 사용하는 도구를 선택하면 되고, 별도의 방식을 통해 코드 빌드를 수행한다면 해당 스테이지를 건너뛰면 됩니다. 저의 경우에는 건너뛰도록 하겠습니다.

 

배포 스테이지

마지막은 누가 배포를 수행하는가에 대한 부분입니다. 우리는 AWS CodeDeploy 를 사용하여 배포하기 위해 앞서 챕터에서 CodeDeploy 에 대한 작업을 수행했었습니다. 따라서 아래와 같이 항목에 나타나는 것을 선택해주기만 하면됩니다.

 

파이프라인 생성과 실행

이 때 까지 설정한 명세가 보일 겁니다. 특이사항이 없다면 파이프라인 생성을 클릭해줍니다.

 
 
 
파이프라인을 생성하고 나면 즉시 배포가 진행되는 것을 볼 수 있습니다.
 

 
 

 

[나가는 말] 환경 구축 후기

드디어 배포자동화 구축을 마무리 하였습니다. 글에는 보이지 않지만, 배포 환경을 구축하고, 성공하기 까지 2024.06.19 ~ 2024.06.21  기간으로 총 3일 걸렸습니다. 이미 수동으로 배포환경을 구축하였기에 크게 어렵지 않을 것이라 생각하고, 하루를 잡았었는데, 예기치 못한 문제들이 발목을 잡아서 많은 시간이 걸렸네요.
 
배포의 경우에도 많은 문서와 예제 영상을 참고하며 시도했었는데, 분명히 같은 프로젝트 환경임에도 버전 차이의 문제인지 생각지 못한 부분에서 에러가 발생하여 배포까지 많은 시간을 소요했던 것 같습니다. 그럼에도 결국은 해냈다는 개인적인 뿌듯함? 덕분인지 3일의 시간이 아깝지 않았던 것 같습니다.

 
 
이 글이 저랑 비슷한 상황에 놓인 분들에게 도움이 될지는 모르겠지만, 저랑 같은 문제를 경험하는 분들에게 도움이 되었으면 좋겠습니다. 그럼 이만 글을 줄이도록 하겠습니다.


트러블 슈팅

[경고] No build cache found | 빌드 시 캐시 안 됨

깃허브 액션을 통해 빌드가 실행되면, No build cache found 가 경고로 표시될 수 있습니다. 빌드 간 에 캐시가 되면, 빌드 속도가 빨라지고 이는 리소스 낭비를 줄일 수 있는 아주 좋은 방안이기에 경고라고 무시하기 보다는 해결해주는 것이 좋습니다. 

 
앞서 경고 표시에서 캐시와 관련해서 읽어보라는 링크(https://nextjs.org/docs/pages/building-your-application/deploying/ci-build-caching)가 있습니다. 해당 링크를 타고 들어가서 다양한 CI 도구가 나오는데, 그 중에서 깃허브 액션을 찾아서 스크롤 해줍니다. 그럼 아래 코드를 추가해주라고 나와 있습니다. 이를 깃허브 액션의 .yml 내의 작업으로 추가해주면 됩니다. 

uses: actions/cache@v4
with:
  # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node
  path: |
    ~/.npm
    ${{ github.workspace }}/.next/cache
  # Generate a new cache whenever packages or source files change.
  key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
  # If source files changed but packages didn't, rebuild from a prior cache.
  restore-keys: |
    ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

 
저의 경우에는 아래와 같은 형식으로 기존 깃허브 액션의 작업으로 하나 추가해주었습니다.

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Cache Primes
        id: cache-primes
        uses: actions/cache@v4
        with:
          # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node
          path: |
            ~/.npm
            ${{ github.workspace }}/.next/cache
          # Generate a new cache whenever packages or source files change.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          # If source files changed but packages didn't, rebuild from a prior cache.
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

 
이렇게 되면 이제 깃허브 액션이 실행될 때  마다 빌드 간에 캐시가 공유되어서 재사용하므로 빌드 시간을 크게 단축할 수 있습니다. 실행된 깃허브 액션 워크플로우를 살펴보면 캐시된 빌드를 사용하여 빠르게 다음 단계로 넘어간 것을 볼 수 있습니다.
 

버킷 버전 비활성화로 코드 파이프라인 실행이 안 됨

코드 파이프라인에서 S3 버킷을 소스 스테이지로 사용하려면 S3 버킷의 버전 관리를 활성화 해주어야 합니다. 비활성화 시 아래와 같은 에러에 직면합니다.
 

 
이 문제를 해결하려면 AWS S3 버킷에 들어가서 속성 > 버킷 버전 관리 > 편집 으로 들어 가신 후 버전 관리를 활성화 해줍니다.

 

 
 
다시 파이프라인 대시보드로 와서 시도해주면 성공합니다.

 
 

error: No AWS CodeDeploy agent running 메시지가 나타나는 경우

해당 문제는 설치가 제대로 되지 않았거나, 설치된 내역이 현재 시스템에 제대로 반영되지 못해서 발생한 문제일 수 있습니다. 따라서 다음 명령어를 차례대로 눌러서 서비스 재부팅 후 상태를 확인해보세요.

systemctl start codedeploy-agent

systemctl status codedeploy-agent

 

깃허브 액션 실행 도중  npm run build  실패 | next.config.js 파일의 CORS 헤더 설정 시 undefined 가 될 수 있는 value 이 있으면 실패합니다.

github action 에서 빌드가 실패 하였습니다. 원인은 value 프로퍼티에 지정한 값이 문자열이어야  하는데, 문자열이 아닌 값이 할당되어 있었기 때문입니다.

Run npm run build
  
> wise-saying@0.1.0 build
> next build
`value` in header item must be string for route {"source":"/api/:path*","headers":[{"key":"Access-Control-Allow-Origin"},{"key":"Access-Control-Allow-Origin","value":"https://ssl.gstatic.com/accessibility/javascript/ext/loader.js?1709880302058"},{"key":"Access-Control-Allow-Methods","value":"GET, POST, PUT, DELETE, OPTIONS"},{"key":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"}]}
Error: Invalid header found
Error: Process completed with exit code 1.

 
자세히 보니 header 에 지정된 key 는 보이지만, value 이 누락되어 있는 곳이 보입니다. 문자열이 아닌 임의의 타입이 들어가면서 누락이 된 것 같습니다.

 
오류 내용 등을 보면, NextJS 프로젝트에서 CORS 관련 설정을 담당하는 파일에서 발생한 것으로 보입니다. 이를 담당하는 next.config.js 파일로 이동하여 어떤 상태인지 확인해보니 환경변수를 가져와서 value 에 할당하고 있는 것을 볼 수 이 있습니다. 문제 원인은 undefind 이 될 수 있는 환경변수 문제임이 확인 되었습니다.

 
value 에 호버 하니 string 혹은 undefined 가 될 수 있다고 나옵니다.

 
해당 변수는 배포 도메인 주소가 담겨 있습니다. 따라서 해당 변수가 undefined 가 되는 하드코딩된 도메인 주소가 할당되도록 수정해줍니다. 추가 해주니 string 타입으로 좁혀졌습니다. 해당 문제의 원인이 된 환경변수의 경우 사실상 로컬에서는 사용하지 않는 설정 이므로 애초에 환경변수를 가져와 사용할 필요가 없었을 것 같습니다.

 
 
이제 문제가 해결되었는지 재확인 해봅시다.
 

S3 업로드 실패 : .zip 파일을 찾을 수 없다고 합니다.

앞서 빌드 문제를 해결 하니, 이번에는 .zip 파일을 찾을 수 없다는 에러가 발생했습니다. 이로 인해 S3 버킷에 업로드 하지 못하고 작업이 중단되었습니다.

 
위 이미지에서 Archive files 작업이 프로젝트 파일을 .zip 형태로 만들어 주고 있는데, 에러 없이 정상적으로 압축이 되었습니다. 그럼에도 이 문제가 발생하였다면, 경로 지정을 잘못했을 가능성이 높다고 보여집니다.
 
혹시나 .zip 파일이 정상적으로 생성되지 않았는지 확인하기 위해 다음 작업을 추가해줬습니다. 각 각 생성된 .zip 파일이 현재 경로에 존재하는지 확인하고, 생성된 .zip 파일을 다운로드 할 수 있도록 해주는 작업입니다.

   - name: Verify project.zip
      run: ls -l project.zip

    - name: Upload artifact
      uses: actions/upload-artifact@v2
      with:
        name: project-zip
        path: project.zip

 
 
추가된 작업을 통해 알아본 결과 현재 작업 경로에 project.zip 이 존재하고,  다운로드 결과 정상적으로 압축된 파일이 다운로드 된 것을 확인할 수 있었습니다. 결국 어느 곳에서 제가 잘못 입력한 명령어로 인해 설치된 파일의 위치를 S3 에서 접근하지 못하고 있던 것입니다.

파일 경로 확인
파일 다운로드

 
 
아래는 기존 S3 업로드 작업의 설정 값들입니다. 

     # S3 에 업로드
     # --delete : 동일한 파일이 존재하면 제거 
      - name: Upload to S3
        run: aws s3 sync --delete ${{env.DIST}} s3://${{env.BUCKET}}/

 
이를 다음과 같이 수정 해주고 다시 깃허브 액션을 실행시켜 봅니다.  바뀐 점은 sync --delete 에서 cp 로 바뀌었고, 버킷에 업로드할 파일명이 원래는 / 으로 없었지만, /project.zip 으로 바꾸어 주었습니다. 원인은 압축된 파일을 어떤 이름으로 저장할지 명시하지 않아서 그런 것으로 보입니다.

 
다시 시도해보니, 다행히 정상적으로 마무리 되었습니다.

 
정확한 확인을 위해 AWS S3를 방문하였습니다. 제가 의도했던 project.zip 파일이 정상적으로 업로드 되었습니다. 

 
이번  문제가 발생했던 이유는 결국 S3 업로드 시 저장할 파일명을 명시해주지 못해서 발생한 문제였습니다. 그 뒤로 파일명을 명시하고 나서 업로드를 성공하였습니다.

참고) sync --delete 을 cp 로 바꾸어 줬는데, 이는 제가 해당 파일을 풀어서 올리지 않고 project.zip 형태로 압축하기 때문에 굳이 비교연산을 수행할 필요가 없다고 판단하여 바꿔준 것일 뿐 문제가 해결된 이유와는 무관합니다. 

 

빌드는 성공했는데, 알고보니 실패 |  TypeError : fetch failed -> 문제 원인은 undefined

빌드가 성공하여 잘 되었구나 생각했으나, 빌드 로그를 열어보니 fetch 실패 에러가 뜨고 있었습니다. 에러 발생 시 로그를 표시하도록 해두어서 사이트맵 관련 api 요청에서 실패한 것을 추측할 수 있습니다.

 
사이트맵을 생성할 때, 필요한 데이터를 서버로 부터 받아오게 되는데, 이 때 사용되는 경로가 실제 배포된 도메인 사이트의 URL 이 됩니다. 즉, 프로덕션 상태에서 환경변수를 읽을 때,  해당 도메인 사이트에 대한 환경변수를 읽어봐서 요청 주소로 활용하는데 이 때 환경변수가 undefined 가 되어서 발생한 문제 입니다.
 
아래는 사이트맵을 생성하는 sitemap.ts 파일이고, 그 내부에 동적으로 데이터를 받아오기 위해 getQuoteCategoryFromDb 라는 함수가 호출되고 있습니다.

 
해당 함수 내부에서는 api 요청을 처리하고 있고, url 변수에 config.apiPrefix 와 같이 어느 구성파일에서 값을 읽어오고 있습니다. 

 
config 파일로 들어오니 이미지와 다르게 환경변수만 참조하고 있어서 || 연산자를 사용하여 프로덕션 환경에서 사용할 프로토콜과 호스트를 입력해주었습니다. 

 
이후 다시 깃허브 액션을 시도하니 아까와는 다르게 타입에러 없이 정상적으로 빌드를 성공하였습니다.

 
 

The overall deployment failed because too many individual instances failed deployment, too few healthy instances are available for deployment, or some instances in your deployment group are experiencing problems. 이 나올 시 로그를 확인하세요.

The overall deployment failed because too many individual instances failed deployment, too few healthy instances are available for deployment, or some instances in your deployment group are experiencing problems.

 

AWS CodeDeploy 뷰 이벤트로 확인하는 방법

CodeDeploy 항목에서 배포 > 배포 로 들어가줍니다.

 
그럼 하단으로 스크롤하여 배포 수명 주기 이벤트 항목이 보이고, 목록의 이벤트 컬럼을 보면 View events 라고 적힌 링크가 있습니다. 해당 링크를 클릭하여 페이지를 이동합니다.

 
해당 페이지에서 하단으로 스크롤 하면 이벤트 목록이 나옵니다. 여기서 상태를 보고 실패가 표시된 부분을 클릭하면, 어떤 이유로 실패 이벤트가 발생하였는지 원인을 알 수 있습니다.

 
저의 경우에는 이미 존재하는 파일이 배포하고자 하는 경로에 있어서 배포 실패가 되었다고 하네요.

 

EC2 인스턴스 내 에서 확인하는 방법

 우선 앞서 CodeDeploy Agent 를 설치하였다면, EC2 인스턴스 내 에서 해당 배포와 관련한 로그를 살펴볼 수 있습니다.
 
해당 로그 경로는 아래 경로로 들어가시면 됩니다.

cd /var/log/aws/codedeploy-agent

## 한 번에 조회하려면
# cat cd /var/log/aws/codedeploy-agent/codedeploy-agent.log

 
그 후 ls 을 입력해보면  3 개의 로그 파일을 확인할 수 있습니다. 이 중에서 codedeploy-agent.log 를 살펴보면 됩니다.

 

cat codedeploy-agent.log

 
입력 하면 엄청 많은 로그가 표시되는데, 하나 씩 살펴보면 아래와 같이 에러가 발생한 부분을 알 수 있고, 유심히 보면 어떤 이유로 실패하였는지 알려주고 있습니다. 앞서 AWS 콘솔에서 확인했던 거와 동일하게 이미 존재하는 파일이 있어서 실패하였다고 나오네요.

/opt/codedeploy-agent/vendor/gems/logging-2.2.2/lib/logging/diagnostic_context.rb:474:in `block in create_with_logging_context'
2024-06-21T07:59:40 WARN  [codedeploy-agent(4048611)]: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Calling PutHostCommandComplete: "Code Error"
2024-06-21T07:59:40 INFO  [codedeploy-agent(4048611)]: Version file found in /opt/codedeploy-agent/.version with agent version OFFICIAL_1.7.0-92_rpm.
2024-06-21T07:59:40 INFO  [codedeploy-agent(4048611)]: [Aws::CodeDeployCommand::Client 200 0.018531 0 retries] put_host_command_complete(command_status:"Failed",diagnostics:{format:"JSON",payload:"{\"error_code\":5,\"script_name\":\"\",\"message\":\"The deployment failed because a specified file already exists at this location: /home/ec2-user/project/tailwind.config.ts\",\"log\":\"\"}"},host_command_identifier:"eyJiYXRjaElkIjoiNDA0NzY4Y2M4MzdmNzhmOGU2ZDA0ZDcyNjQzOWViYzEvcHVibGljMDAzIiwiZGVwbG95bWVudElkIjoiQ29kZURlcGxveS9hcC1ub3J0aGVhc3QtMi9wcm9kL29ycGhldXM6cHVibGljMDA1LzkwNTQxODA0NTEwMTpkLTY5Uk5PM1ZXNSIsImhvc3RJZCI6ImFybjphd3M6ZWMyOmFwLW5vcnRoZWFzdC0yOjkwNTQxODA0NTEwMTppbnN0YW5jZS9pLTAwYjYzYTNmMDZhYzc4NzRkIiwiY29tbWFuZElkIjoiQXBvbGxvRGVwbG95Q29udHJvbFNlcnZpY2V8YXJuOmF3czplYzI6YXAtbm9ydGhlYXN0LTI6OTA1NDE4MDQ1MTAxOmluc3RhbmNlL2ktMDBiNjNhM2YwNmFjNzg3NGR8NHwwIiwiY29tbWFuZE5hbWUiOiJJbnN0YWxsIiwiY29tbWFuZEluZGV4Ijo0LCJhdHRlbXB0SW5kZXgiOjF9")

 
이렇게 로그를 하나씩 확인하면서 문제의 원인이 되는 부분을 해결하면 됩니다.
 

Code Deploy 시 이미 존재하는 파일로 배포가 실패 했다고 뜬다면?

해당 에러를 해결 하려면 appspec.yml 파일에  file_exists_behavior: OVERWRITE 을 딱 한 줄 추가해주면 됩니다. 

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/project
    overwrite: yes
file_exists_behavior: OVERWRITE

permissions:
  - object: /home/ec2-user/awsbeginer-bucket-deploy
    owner: root
    group: root
    mode: 775

hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 300
      runas: root

 
 
 

Code Deploy 시 패키지 모듈을 찾지 못해서 빌드 실패

해당 문제가 발생한 경우에는 Ec2 인스턴스에 적용된 환경변수와 스크립트 파일이 실행 되는 시점의 환경변수 간에 차이 있는 경우 발생할 수 있습니다. 따라서 인스턴스 내에 해당 패키지가 설치된 경로를 환경변수로 설정하여 스크립트 실행 시 참조하게 하면 됩니다.

LifecycleEvent - AfterInstall
Script - scripts/deploy.sh
[stderr]/opt/codedeploy-agent/deployment-root/f6cb0a6a-acc1-4c4f-906d-f5527863d32b/d-TYRJTUVW5/deployment-archive/scripts/deploy.sh: line 7: npm: command not found
[stderr]/opt/codedeploy-agent/deployment-root/f6cb0a6a-acc1-4c4f-906d-f5527863d32b/d-TYRJTUVW5/deployment-archive/scripts/deploy.sh: line 11: pm2: command not found

 
export PATH ~ 형식으로 작성된 부분이 보입니다. 이렇게 되면 npm 과 pm2 를 실행 시 이전에 환경변수로 지정한 경로를 참조하게 됩니다. 

#!/bin/bash

# project.zip 압축풀기가 완료된 소스파일들이 모인 경로로 이동합니다.
cd /home/ec2-user/project

# 패키지가 설치된 경로의 환경변수 추가
export PATH=$PATH:/home/ec2-user/.nvm/versions/node/v20.11.0/bin

# 종속성이 있으면 설치합니다.
npm install

# 서버를 재시작 합니다 -> 여기서 'start'는 사용자 작명입니다. 
# pm2 에 대한 자세한 부분은 npm pm2 를 찾아보세요. 
pm2 restart 'start'

 

CodeDeploy |  pm2 네임스페이스를 읽지 못해서 배포 실패

보통 NodeJS 관련 프로젝트에서 프로덕션용 프로젝트의 서버를 관리하기 위한 패키지로 pm2 를 사욯합니다. 그리고 해당 pm2 의 환경을 구성하는 파일을 통해 어느 애플리케이션을 관리할지 식별할 수 있습니다. 보통 수동 배포할 때는 구성 파일이 없어도 문제가 되지 않으나, CodeDeploy 를 통해 .sh 파일에 적힌 스크립트를 실행할 때는 해당 스크립트가 실행되는 환경과 실제 EC2 인스턴스 환경 간에 차이가 발생하여 문제가 발생할 수 있습니다.

 

따라서 ecosystem.config.js 파일을 src 폴더와 동일한 경로에 생성해준 후 아래와 같이 관리할 애플리케이션에 대한 정보를 입력해주면 해결 됩니다.

module.exports = {
    apps: [
      {
        name: "start", // 앱의 이름
        script: "./node_modules/next/dist/bin/next", // Next.js 스크립트 경로
        args: "start", // Next.js 앱을 시작할 때 사용할 인수
        exec_mode: "fork", // 실행 모드: cluster 또는 fork 중 선택
        instances: "2", // 클러스터 모드에서 실행할 인스턴스 수 (CPU 코어 수만큼)
        autorestart: true, // 프로세스 자동 재시작 활성화
        watch: false, // 파일 변경 감지 활성화 (개발 중에만 활용)
        max_memory_restart: "1G", // 1GB 이상 메모리 사용 시 재시작
        env: {
          NODE_ENV: "production", // Node.js 환경 설정
        },
      },
    ],
  };

 

 

 


참고자료

1. 나에게 적합한 파이프라인 유형은? : https://docs.aws.amazon.com/ko_kr/codepipeline/latest/userguide/pipeline-types-planning.html
AWS 코드파이프라인 비용 비교 : https://aws.amazon.com/ko/codepipeline/pricing/
2. CodeDeploy 리소스 키트 참조: https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/resource-kit.html#resource-kit-bucket-names
3. EC2 내부에서 CodeDeploy 에이전트 설치하기: https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html
4. CodeDeploy 용 EC2 인스턴스 생성 : https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/instances-ec2-create.html#instances-ec2-create-console
5. CodeDeploy 튜토리얼: https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/tutorials-github-provision-instance.html
6. S3 버킷을 공급자로 하는 파이프라인 튜토리얼 : https://docs.aws.amazon.com/ko_kr/codepipeline/latest/userguide/tutorials-s3deploy.html
7. AppSpec 파일 작성 예제 : https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/reference-appspec-file-example.html 
8. 코드 파이프라인+코드 디플로이 + S3  -> EC2 배포 튜토리얼 : https://www.youtube.com/watch?v=GiKTzVj-FTc
 
 
 

반응형