구성도
Backend CI/CD Pipeline
- CodeCommit에 Dev, Prod 브랜치 생성 (two-branch 전략)
- 환경별(Dev,Prod) 프론트, 백엔드 파이프라인 구성 (총 4개) -- 이 페이지에서는 백엔드파이프라인만 설명!
- CodeBuild는 buildspec.yml(dev-buildspec.yml과 prd-buildspec.yml)을 통해 빌드 및 테스트 진행
- CodeDeploy는 appspec.yml을 통해 S3 내 아티팩트를 가져와 EC2 및 Auto Scaling Group에 배포
- Dev 환경은 EC2에 In-place 배포, Prod환경에서는 Auto Scaling Group에 Blue/Green 배포 전략 상이함
* CodePipeline은 승인자의 허가에 의해 진행된다.
이 글 내용은 Backend CI/CD Pipeline 구성을 진행하는 내용이다.
AWS CodeCommit 생성 및 로컬 컴퓨터와 연동은 Frontend CI/CD Pipeline 구성 페이지에서 작성하였으니 참고바란다.
아티팩트 보관용 S3 버킷 생성
웹 호스팅 용이 아니고 아티팩트 보관용이므로 정적 웹 사이트 호스팅 활성화 하지 않는다.
내부 사용자만 접근하므로 퍼블릭 액세스 차단이 필요하다.
Bucket Policy 구성
표시할 정책이 없습니다.
ACL 비활성화
내부 사용자만 접근하므로 ACL 활성화 필요 없다.
$ git init
- 새로운 Git 저장소로 초기화
- 이 명령어를 실행하면 .git 디렉토리가 생성되고, 현재 디렉토리는 Git 저장소로 전환됨
$ git clone https://git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/CODECOMMIT-REPOSITORY
- 원격 저장소를 로컬 저장소로 복제
$ cd backend/
- 복제된 로컬 저장소 폴더로 이동
$ git checkout test
test branch로 전환
$ git add .
- 현재 작업 디렉터리의 모든 변경사항을 스테이징 영역에 추가
- 새 파일 추가, 파일 수정, 파일 삭제 모두 포함
$ git status
- 변경된 파일 확인
$ git commit -m "commit message"
- '-m "commit message"' 부분은 커밋 메시지를 지정
$ git push origin test
- 로컬 저장소의 commit된 변경 사항을 원격 저장소로 전송
- '-u' : 로컬 저장소의 branch와 원격 저장소의 branch를 추적 관계로 설정. 특정 branch에서만 계속 배포할 때 권고. 설정하면 git push, git pull 할 때 브랜치 이름 생략가능.
$ git remote add origin https://git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/CODECOMMIT-REPOSITORY
- origin: 원격 저장소의 이름 의미(꼭 오리진 아니어도 됨)
- https://git-codecommit : 원격저장소의 URL
앞서 프론트엔드 파이프라인에서 설명했던 부분은 생략한다.
buildspec-dev.yml
version: 0.2
env:
variables:
ENV: "dev"
phases:
install:
runtime-versions:
java: corretto17
build:
commands:
- echo Build Starting on `date`
- chmod +x ./gradlew
- ./gradlew build
post_build:
commands:
- chmod +x ./scripts/modify_script.sh
- ./scripts/modify_script.sh $ENV
- echo $(basename ./build/libs/*.jar)
- pwd
artifacts:
files:
- build/libs/*.jar
- scripts/**
- appspec.yml
discard-paths: yes
cache:
paths:
- '/root/.gradle/caches/**/*'
buildspec-prd.yml (내용 모두 동일)
version: 0.2
env:
variables:
ENV: "prd"
phases:
install:
runtime-versions:
java: corretto17
build:
commands:
- echo Build Starting on `date`
- chmod +x ./gradlew
- ./gradlew build
post_build:
commands:
- chmod +x ./scripts/modify_script.sh
- ./scripts/modify_script.sh $ENV
- echo $(basename ./build/libs/*.jar)
- pwd
artifacts:
files:
- build/libs/*.jar
- scripts/**
- appspec.yml
discard-paths: yes
cache:
paths:
- '/root/.gradle/caches/**/*'
- 환경변수 ENV는 값 "prd"으로 설정
- backend는 Java 17 설치
- build: ./gradlew build 명령어를 통해 build/libs/*.jar 생성 (자바 빌드 파일)
- post_build:
- chmod +x ./scripts/modify_scripts.sh : modify_scripts.sh 스크립트 실행 권한 부여
- ./scripts/modify_scripts.sh $ENV : modify_scripts.sh 스크립트 실행 및 인자 값(ENV) 전달
- echo (첫 번째 jar 파일 이름 출력), pwd (잘 설정되었는지 확인 용)
- artifacts.base-directory는 빌드된 아티팩트가 생성될 기본 디렉터리 지정한다. 빌드된 아티팩트가 해당 디렉터리 내에 생성된다.
- artifacts.files는 아티팩트로 포함될 파일 지정한다. 파일 경로는 AWS Codecommit 기준으로 명시해야 한다.
- build/libs/*.jar 는 확장자가 jar인 파일 모두 지정하는 것이다. 여기서 java 실행을 위한 빌드된 코드를 의미한다.
- scripts 디렉터리 하위에는 buildspec-prd.yml, deploy-prd.sh 파일이 존재한다. CodeBuild 및 CodeDeploy가 참조한다.
- appspec.yml은 CodeDeploy가 참조하는 파일이다.
- discard-paths: yes 는 아티팩트를 S3에 배포할 때, 리포지토리 기준으로 가져올 때의 저장 경로들을 무시하고 S3 내부에는 모든 파일이 flat하게 저장된다. flat 저장 이유는 CodeDeploy가 아티팩트 가져와서 실행시킬 때 jar 디렉터리로 이동한 후 실행하지 않고, 가져와서 바로 실행시키기 위해서이다.
- **/* 는 경로에 있는 모든 하위 파일 및 디렉터리를 포함한다는 뜻이다.
- cache는 빌드 과정에서 빌드 속도를 높이기 위해 캐시 설정한다. 첫 배포는 캐싱 안되지만, 두번째부터 캐싱에 의해 좀 더 빨라진다. path는 캐시할 파일 경로를 지정한다.
CodeBuild 생성
Source가 AWS CodeCommit 이므로 지정하고, 브랜치 test 를 지정한다.
아티팩트는 Codebuild가 빌드한 아티팩트의 저장소 (ex. S3, ECR etc.)를 지정하는 것이다.
이 설정은 CodePipeline으로 만들지 않고 수동 빌드할 때 의미있는 설정이다. 아티팩트부터 설정이 다 무시될 예정..
CodePipeline으로 연결시 위의 설정들은 다 무시되고 CodePipeline 기준 설정을 따른다.
IAM Role 생성
빌드 프로젝트를 생성하면 기본적으로 생성되는 역할이 생긴다.
하지만 AWS Codebuild가 S3에 접근하려면 AmazonS3FullAccess 권한이 추가적으로 필요하다.
고로, 필요한 권한은 다음과 같다.
- CodeBuild 빌드 프로젝트 생성시 기본적으로 생성되는 Role
- AmazonS3FullAccess
CodeDeploy 생성
CodeDeploy가 EC2에게 배포를 수행할 수 있는 IAM Role 생성
자동적으로 AWSCodeDeployRole 정책이 추가된다.
AWS CodeDeploy Role에는 배포 대상 및 모니터링 권한이 포함되어 있다. ( AutoScaling, EC2, ELB, SNS, CloudWatch)
CodeDeploy 배포 그룹이 EC2 일 경우에는 기본적으로 생기는 AWSCodeDeployRole만으로도 권한 문제가 없다.
+ 그런데 AutoScaling Group인 경우, AWS CodeDeployRole 기본 권한만으로 부족하므로 추가해야한다.
추가할 Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:PassRole",
"ec2:CreateTags",
"ec2:RunInstances"
],
"Resource": "*"
}
]
}
이런식으로 필요한 권한은 추가해야 한다.
AutoScaling Group의 기능이 다양해서 AWS CodeDeployRole이 AutoScaling의 기능에 대한 모든 접근 권한을 갖고 있지 않는 것 같다.
애플리케이션 생성
CodeDeploy > 애플리케이션
컴퓨팅 플랫폼은 EC2/온프레미스 으로 애플리케이션을 생성한다.
배포 그룹 생성
한 애플리케이션에서 환경별(dev, prod) 배포 그룹을 생성한다.
배포 유형 : In-place 인 경우
배포 유형 : Blue/Green 인 경우
우리는 Auto Scaling 시작 템플릿에 CodeDeploy agent 설치가 포함되어있어서 따로 설치하지 않았다.
현재 ALB와 연결하였다.
배포 실패하는 경우 롤백하도록 구성하였다.
주의해야할 점은 배포 시 장애로 인해 배포되지 않고 롤백이 되었을 때, 개발자 입장에서는 정상적으로 배포되었다고 착각할 수 있으므로 콘솔상 확인도 반드시 필요하다.
수동으로 EC2 CodeDeploy agent 설치
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
sudo service codedeploy-agent restart
appspec.yml
version: 0.0
os: linux
files:
- source: /
destination: /solution
overwrite: yes
owner: user01
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 300
runas: user01
ValidateService:
- location: healthcheck.sh
timeout: 60
runas: user01
- CodeDeploy가 배포할 때 참조하는 파일
- ApplicationStart 할 때, deploy.sh 실행
- ValidateService 할 때, healthcheck.sh 실행
- CodePipeline 배포 단계의 view events에서 해당 hooks에서 잘못된 경우, 로그 확인이 필요함
deploy.sh
#!bin/bash
REPOSITORY=/solution/java
cd $REPOSITORY
echo $PWD
RESULT=$(ls -al)
echo $RESULT
# plan 이름의 파일을 제외한 jar파일 이름을 값으로 설정
JAR_NAME=$(ls $REPOSITORY | grep '.jar' | grep -v 'plain')
# jar파일 PID를 값으로 설정
CURRENT_PID=$(pgrep -f $JAR_NAME)
if [ -z $CURRENT_PID ]
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -9 $CURRENT_PID"
kill -9 $CURRENT_PID
sleep 3
fi
echo "> $JAR_NAME 배포"
# java 실행
nohup java -jar -Dspring.profiles.active=dev $JAR_NAME > /dev/null 2> /dev/null < /dev/null &
-Dcom.amazonaws.sdk.disableEc2Metadata=true
- properties와 같은 파일들을 AWS KMS 키로 암호화 시 복호화할 때, EC2 메타 데이터를 가져오므로 disable하면 안됨
sudo kill -9 $CURRENT_PID
- sudo를 쓰게 되면 사용자 비밀번호를 입력받게 됨. 입력하지 않을 경우, 해당 명령어 실행되지 않아 배포가 되지 않음
healthcheck.sh
#!/bin/bash
echo "> Health check 시작"
# 리포지토리 이동
REPOSITORY=/solution
cd $REPOSITORY
# plan 이름의 파일을 제외한 jar파일 이름을 값으로 설정
JAR_NAME=$(ls $REPOSITORY | grep '.jar' | grep -v 'plain')
for RETRY_COUNT in {1..15}
do
# jar파일 PID를 값으로 설정
CURRENT_PID=$(pgrep -f $JAR_NAME)
# 6060포트로 돌고있는지 결과 출력을 값으로 설정
NETSTAT=$(netstat -nltp | grep 6060)
# -n : $CURRENT_PID 값이 있으면 True, 없으면 False
if [ -n "$CURRENT_PID" ] && [ -n "$NETSTAT" ];
then
echo ">Health check 성공"
break
else
echo ">Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다"
fi
# -eq: equal. 10번 시도 안되면 exit 1
if [ $RETRY_COUNT -eq 10 ]
then
echo "> Health check 실패. "
exit 1
fi
echo "> Health check 연결 실패. 재시도..."
sleep 10
done
modify_script.sh
#!/bin/bash
ENV=$1
# modify healtcheck script if needed
if [ "$ENV" == "prd" ]; then
sed -i 's/-Dspring.profiles.active=dev/-Dspring.profiles.active=prd/g' ./scripts/deploy.sh
fi
echo "modify deploy scripts for $ENV environment"
- 환경별(Dev, Prod) deploy.sh이 통합되어있다.
- 어차피 CodeCommit 내 파일은 기본 dev로 되어있으므로 Prod로 돌릴 때는 Prod로 ENV 변수를 바꾼다
- Buildspec.yml에서 해당 스크립트가 실행되어 환경별 백엔드 파이프라인을 실행하도록 한다.
CodePipeline 생성
앞서 생성한 AWS CodeCommit CodeBuild를 자동화하는 파이프라인을 생성한다.
Step 1 파이프라인 설정 선택
자동으로 생성되는 역할을 지정한다.
Step 2 소스 스테이지 추가
AWS CodeCommit을 레파지토리로 사용할 것이므로 지정한다.
가져오고자 하는 레파지토리의 브랜치 이름 test 를 작성한다.
Step 3 빌드 스테이지 추가
CodeBuild를 사용하므로, 앞서 생성했던 Build 프로젝트를 지정한다.
저 Build 프로젝트에 S3로 배포한다는 내용이 포함되어 있다.
Step 4 배포 스테이지 추가
배포 공급자는 CodeDeploy를 선택한다.
CodeDeploy가 S3 내 올라와있는 아티팩트를 가져와 EC2에 배포한다.
배포 결과
성공적으로 배포된 모습을 관찰할 수 있다.
'AWS > Project' 카테고리의 다른 글
CodeSeries를 활용한 CI/CD - TroubleShooting (1) | 2024.07.01 |
---|---|
CodeSeries를 활용한 CI/CD 환경 구성 (0) | 2024.07.01 |
CodeSeries를 활용한 CI/CD 구성 (Frontend Pipeline) (0) | 2024.06.28 |
[Terraform] TroubleShooting (0) | 2024.06.22 |
[CI/CD] TroubleShooting (0) | 2024.06.22 |