깃허브에 AWS 시크릿 키와 시크릿 ACCESS 키를 등록해줘야 한다.
git에서 Settings에 들어가면 저장소 키를 등록할 수 있다.
우리는 여기에 등록이 필요하다.
ACCESS_KEY와 SECRET_ACCESS_KEY 는 IAM 사용자의 키이다.
IAM > 사용자에 들어가 전에 사용자를 만들때 나온 .csv 파일을 확인해 Github에 등록해준다.
키를 발급하지 못했다면 여기 문서에서 확인이 가능하다.
AWS 문서
secret 을 등록하는 이유는 github action에서 변수에서 사용하기 위해 필요하다.
다음은 배포를 위한 deploy.yml이다.
name: Deploy
on:
push:
branches:
- develop
env:
S3_BUCKET_NAME: til-web
CODE_DEPLOY_APPLICATION_NAME: til_web
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: til_fe_dev
jobs:
build:
environment: develop
runs-on: ubuntu-latest
steps:
- name: Checkout source code.
uses: actions/checkout@v3
- name: Check Node v
run: node -v
- name: Generate Environment Variables File
run: |
echo "NEXT_PUBLIC_MODE=$NEXT_PUBLIC_MODE" >> .env.development
echo "NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID=$NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI=$NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW=$NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW" >> .env.development
env:
NEXT_PUBLIC_MODE: ${{ secrets.NEXT_PUBLIC_MODE }}
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID }}
NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI }}
NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW }}
- name: Cache node modules
uses: actions/cache@v3
id: cache
with:
# node_modules라는 폴더를 검사하여
path: node_modules
# 아래 키값으로 cache가 돼있는지 확인합니다.
key: npm-packages-${{ hashFiles('**/package-lock.json') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: npm install
- name: Build
run: |
npm run build:dev
- name: zip create
run: |
zip -qq -r --symlinks ./til-dev.zip .
shell: bash
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Upload to S3
run: |
aws s3 cp --region ap-northeast-2 ./til-dev.zip s3://$S3_BUCKET_NAME/til-dev.zip
- name: Deploy For Development
run: |
aws deploy create-deployment \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
--s3-location bucket=$S3_BUCKET_NAME,key=til-dev.zip,bundleType=zip
다음은 전체 yml 코드이며 dev 서버와 prod 서버가 분리 되어있어서 yml 또한 분리되어 있는데 자세한 내용은 저장소를 살펴보는게 더 좋을 것 같다.
코드를 하나씩 보며 설명하겠다.
name: Deploy
on:
push:
branches:
- develop
여기는 이제 develop 브랜치에 push 가 이뤄졌을 때 동작하기 위한 코드이다.
env:
S3_BUCKET_NAME: til-web
CODE_DEPLOY_APPLICATION_NAME: til_web
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: til_fe_dev
env 를 설정해 yml 내에서 변수를 사용하는 것이다. 자주 쓰이는 것들을 위로 선언해놨다.
jobs:
build:
environment: develop
runs-on: ubuntu-latest
steps:
- name: Checkout source code. # 깃허브 레포지토리 체크.
uses: actions/checkout@v3
- name: Check Node v # 노드 버전 확인
run: node -v
- name: Generate Environment Variables File # 환경 변수 세팅
run: |
echo "NEXT_PUBLIC_MODE=$NEXT_PUBLIC_MODE" >> .env.development
echo "NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID=$NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI=$NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI" >> .env.development
echo "NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW=$NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW" >> .env.development
env:
NEXT_PUBLIC_MODE: ${{ secrets.NEXT_PUBLIC_MODE }}
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_ID }}
NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI }}
NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW: ${{ secrets.NEXT_PUBLIC_GOOGLE_LOGIN_CLIENT_PW }}
이제 가장 중요한 github action의 step들이다.
jobs:
...
steps:
...
- name: Cache node modules # 설치된 의존성 파일 설치.
uses: actions/cache@v3
id: cache
with:
# node_modules라는 폴더를 검사하여
path: node_modules
# 아래 키값으로 cache가 되있는지 확인합니다.
key: npm-packages-${{ hashFiles('**/package-lock.json') }}
- name: Install Dependencies # 캐시된게 없다면 npm install
if: steps.cache.outputs.cache-hit != 'true'
run: npm install
- name: Build # next 프로젝트 빌드
run: |
npm run build:dev
- name: zip create # S3에 올릴 zip 파일 생성. (빌드한 프로젝트를 압축)
run: |
zip -qq -r --symlinks ./til-dev.zip .
shell: bash
이제 npm i를 통해 패키지들을 설치하고 빌드후 zip 파일을 생성하는 과정입니다.
jobs:
...
steps:
...
- name: Configure AWS credentials # AWS 인증절차
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Upload to S3 # ZIP 파일을 S3에 업로드
run: |
aws s3 cp --region ap-northeast-2 ./til-dev.zip s3://$S3_BUCKET_NAME/til-dev.zip
- name: Deploy For Development # S3에 올라간 zip 파일을 CodeDeploy를 통해 가져온다.
run: |
aws deploy create-deployment \
--deployment-config-name CodeDeployDefault.AllAtOnce \ # Code Deploy에서 설정했던 Deploy 방식.
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \ # AWS의 Code Deploy 애플리케이션의 배포 그룹 이름.
--s3-location bucket=$S3_BUCKET_NAME,key=til-dev.zip,bundleType=zip # Code Deploy가 S3에서 프로젝트를 찾을 수 있도록 경로 지정. (bucket=버킷 이름/test-build.zip)
(주석은 설명을 위한 것일 뿐 실행되지 않습니다. 시도를 위한 코드는 가장 처음의 코드를 봐주세요.)
AWS에서 CodeDeploy가 동작하면 appspec.yml을 바라보기 때문에
EC2에서 실행할 단계를 설정해줘야 한다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/deploy
overwrite: yes
permissions:
- object: /home/ubuntu/deploy
owner: ubuntu
group: ubuntu
mode: 755
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 500
runas: ubuntu
deploy.sh 는 다음과 같다.
#!/bin/bash
REPOSITORY=/home/ubuntu/deploy
REPOSITORY_PROD=/home/ubuntu/deploy
echo "DEPLOYMENT_GROUP_NAME: ${DEPLOYMENT_GROUP_NAME}"
if [ "${DEPLOYMENT_GROUP_NAME}" == "til_fe_prod" ]; then
echo "운영 서버 배포"
cd "${REPOSITORY_PROD}"
# production 환경인 경우에 대한 처리
sudo npm install
pm2 describe til-product > /dev/null
if [ $? -eq 0 ]; then
# 실행 중인 경우
echo "til-product 프로세스가 실행 중입니다."
sudo npm run pm2:reload:prod
else
# 실행 중이 아닌 경우
echo "til-product 프로세스가 실행되지 않았습니다."
sudo npm run pm2:start:prod
fi
elif [ "${DEPLOYMENT_GROUP_NAME}" == "til_fe_dev" ]; then
echo "개발 서버 배포"
cd "${REPOSITORY}"
sudo npm install
pm2 describe til-dev > /dev/null
if [ $? -eq 0 ]; then
# 실행 중인 경우
echo "til-dev 프로세스가 실행 중입니다."
sudo npm run pm2:reload:dev
else
# 실행 중이 아닌 경우
echo "til-dev 프로세스가 실행되지 않았습니다."
sudo npm run pm2:start:dev
fi
fi
나는 서버를 pm2로 설정했기 때문에 pm2를 실행하는 코드가 들어있다.
개발서버와 실서버 모두 deploy.sh를 바라보기에 분기처리를 해놨다.
배포되는 그룹에 따라 실서버인지 개발서버인지 구분을 했고 pm2 서버 명령어가 다르다.
pm2 가 실행되어 있으면 재실행>= 실행되어 있지 않으면 start를 해줬다.
pm2 설정은 다음과 같다.
나는 여기 블로그를 참고를 많이 했다.
module.exports = {
apps: [
{
/* 개발 환경용 서버 */
name: 'til-dev', // pm2 이름
cwd: './', // 경로
script: 'npm',
args: 'run start:dev', // package.json 명령어 실행
instances: 1, // 단일 쓰레드
autorestart: false,
watch: false,
env: {
Server_PORT: 3000,
NODE_ENV: 'development',
},
},
{
/* 배포 환경용 서버 */
name: 'til-product',
cwd: './',
script: 'npm',
args: 'run start:prod',
instances: -1, // 클러스터 모드
autorestart: false,
watch: false,
// wait_ready: true,
env: {
Server_PORT: 3000,
NODE_ENV: 'production',
},
},
],
};
여기도 마찬가지로 개발, 운영서버를 나눠서 분리해뒀고, pm2를 실행할때 각각의 옵션들을 설정해줬다.
이렇게 작업이 완료되었으면 develop 브랜치에 push 할 때 github action에서 다음과 같이 동작할 것이다.
actions에서 각 steps에 맞는 작업들이 이뤄진다.
AWS Code Deploy에서도 배포가 잘 되는 것을 볼 수 있다.
next.js를 Vercel을 사용하지 않고, aws를 하나 하나 세팅해보았는데,
대학교 때 혼자 배포해본 경험말고 정말 오랜만에 다시 해보았다.
느낀 점은 CI/CD 구성은 나에게 정말 어려운 작업이였다.. Vercel로 하는 것은 정말 편해보였는데.. 왜 Vercel로 배포하는지 알 것 같다 🫡🫡🫡
그래도 하나하나 모두 세팅해보면서 자동 배포 세팅해보는 경험을 해서 다행이다.
기록도 해두었으니 다음에 한번 더 할 기회가 생긴다면 참고할 때 도움이 될 것 같다.