이전에 CI를 구현했다는 가정하에 진행하도록 하겠다 아래 링크를 참조해서 CI를 먼저 하길 바란다
https://vuddus526.tistory.com/269
[DevOps] Github Actions CI 구현하기 (1)
1. Github Actions 이란? Github Actions 는 Github 에서 제공하는 CI/CD 툴이다 build, test, deploy 등 필요한 Workflow 를 등록해두면 Gihtub 의 특정 이벤트 (push, pull request) 가 발생했을 때 해당 워크 플로우를 수행
vuddus526.tistory.com
조건
1) CI 구현해둔 상태
2) EC2, S3 만들어진 상태
위 조건이 다 되었으면 아래부터 천천히 따라 해보도록 한다
1. EC2 설정 추가하기
1) Tag 추가
CodeDeploy를 생성할 때 어떤 인스턴스에서 수행할 지 구분하는 값으로 태그를 사용한다
인스턴스 클릭 -> 작업 -> 인스턴스 설정 -> 태그관리
2) 태그 추가하기
3) 태그확인
4) IAM 역할 추가
EC2 인스턴스에서 S3에 올려놓은 파일에 접근할 수 있도록 권한을 추가해준다
IAM 에서 왼쪽에 역할을 눌러서 관리 페이지로 이동해서 역할을 만든다
AWS서비스를 선택하고 사용사례는 EC2를 선택한다
S3 접근 권한을 추가 해준다
원하는 이름으로 설정을 해준다 그리고 다른건 디폴트 맨밑에서 생성하기
EC2 인스턴스에서 IAM연결하기, 작업에 보안에 IAM 역할 수정을 누른다
아까 만든 IAM 역할을 연결해 준다
2. EC2에서 CodeDeploy 설치하기
1) 설치 가능한 패키지 리스트를 최신화 하기
$ sudo apt update
2) 루비 다운로드 받기
$ sudo apt install ruby-full
3) wget 다운로드 받기
$ sudo apt install wget
4) ubuntu 폴더로 접근하기
$ cd /home/ubuntu
5) codeDeploy 다운로드 받기
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
6) 권한 설정
$ chmod +x ./install
7) 최신버전으로 설치
$ sudo ./install auto > /tmp/logfile
8) 설치된거 확인하기
$ sudo service codedeploy-agent status
3. CodeDeploy 생성
1) CodeDeploy 전용 IAM 역할 만들기
아까 S3 역할 만들때 처럼 똑같이 만들어주는데
여기서 하나 다른점은 엔터티 선택에서
EC2 말고 다른 AWS 서비스의 사용 사례 에서 CdoeDeploy 검색해서 찾는다
2) CodeDeploy 어플리케이션 생성하기
AWS 검색창에 CodeDeploy 찾아서 들어온다
그리고 왼쪽에 배포 밑에 애플리케이션이라는 카테고리를 클릭해서 들어간다
나는 미리 만들어둔게 있는데 저렇게 이름 마음데로 설정하고
컴퓨팅 플랫폼을 EC2/온프레미스로 설정하면 된다
3) CdoeDeploy 배포 그룹 생성
방금 만든 애플리케이션을 클릭하면 화면처럼 나오는데 여기서 배포그룹 생성을 누른다
배포그룹이름 마음데로 설정하고 서비스 역할에
이전에 만든 codeDeploy IAM을 선택하고 배포 유형은 현재위치로 한다
환경구성은 Amazon EC2 인스턴스로 하고
아래 키는 맨처음 만든 태그를 선택해주면 된다
온프레미스 인스턴스는 체크 안해도된다
사용한 에이전트 구성은 한번만으로하고 배포설정은 화면처럼 놔둔다
로드밸런서는 나중에 HTTPS를 하게되면 활성화 시키고
아니면 체크 풀어서 배포그룹을 생성하면 된다
4. Github Actions 에서 사용할 IAM 사용자 추가하기
먼저 IAM에서 왼쪽에 사용자를 클릭해서 사용자 추가를 해준다
이전에 사용자 역할 각각 만들때 권한을 하나씩 해줬는데
여기서는 둘다 선택한다
- AWSCodeDeployFullAccess
- AmazonS3FullAccess
여기서 중요한거 마지막에 Access Key와 Secret Key를 확인해서 꼭 저장해둔다
5. Github Repositoy에서 Secrets를 추가해준다
6. 자동배포를 위한 코드설정하기
구글 어디를 찾아봐도 폴더 구조를 보여주는 사진이 하나도 없어서 내가 올린다...
아래에 있는 코드 다 추가하기전에 확인해 봐야하는것은
Nginx를 사용하고 있지않는다 그러면 switch.sh는 작성할 필요없고
deploy.sh에서도 switch 부분은 주석을 해야 오류가 나지않는다
main.yml에서는 중간중간 slack web hook 같이 다루지 않은것도
껴있는데 주석하길 바란다
Nginx를 이용한 무중단 배포 게시글도 작성할 예정이라
그 글에서 사용하는 방법을 작성하도록 하겠다
1) CI에서 추가했던 main.yml을 아래와같이 수정해 준다
name: Java CI TEST with Gradle
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
env:
RESOURCE_PATH: ./src/main/resources/application.yml
PROJECT_NAME: mountainz
# Database
DB_URL: ${{ secrets.DB_URL }}
DB_USERNAME: ${{ secrets.DB_USERNAME }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
# JWT Secret
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
# AWS S3
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
S3_REGION: ${{ secrets.S3_REGION }}
AWS_REGION: ap-northeast-2
# CODE_DEPLOY
APPLICATION_NAME: ${{ secrets.APPLICATION_NAME }}
DEPLOY_GROUP_NAME: ${{ secrets.DEPLOY_GROUP_NAME }}
# SLACK WEBHOOK
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Generate Environment Variables File for Properties
uses: microsoft/variable-substitution@v1
with:
files: ${{ env.RESOURCE_PATH }}
env:
spring.datasource.url: ${{ env.DB_URL }}
spring.datasource.username: ${{ env.DB_USERNAME }}
spring.datasource.password: ${{ env.DB_PASSWORD }}
jwt.secretKey: ${{ env.JWT_SECRET_KEY }}
cloud.aws.credentials.access-key: ${{ env.AWS_ACCESS_KEY_ID }}
cloud.aws.credentials.secret-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
cloud.aws.s3.bucket: ${{ env.S3_BUCKET_NAME }}
cloud.aws.region.static: ${{ env.S3_REGION }}
logging.slack.webhook-uri: ${{ env.SLACK_WEBHOOK_URL }}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Build
- name: Build with Gradle
run: ./gradlew clean build
# 전송할 파일을 담을 디렉토리 생성
- name: Make Directory for deliver
run: mkdir deploy
# Jar 파일 Copy
- name: Copy Jar
run: cp ./build/libs/*.jar ./deploy/
# appspec.yml Copy
- name: Copy appspec
run: cp ./appspec.yml ./deploy/
# script file Copy
- name: Copy shell
run: cp ./scripts/* ./deploy/
# 압축파일 형태로 전달
- name: Make zip file
run: zip -r -qq -j ./$PROJECT_NAME.zip ./deploy
# S3 Bucket으로 copy
- name: Deliver to AWS S3
env:
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
run: aws s3 cp --region $S3_REGION --acl private ./$PROJECT_NAME.zip s3://$S3_BUCKET_NAME/
# Deploy
- name: Deploy
env:
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
run: aws deploy create-deployment --application-name $APPLICATION_NAME --deployment-group-name $DEPLOY_GROUP_NAME --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME.zip --region $S3_REGION
2) 최상단 root에서 scripts 폴더를 만들고 그안에 deploy.sh 랑 switch.sh를 만들어준다
deploy.sh
#!/bin/bash
BUILD_JAR=$(ls /home/ubuntu/app/deploy/mountainz-*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo "> build 파일명: $JAR_NAME" >> /home/ubuntu/app/deploy/deploy.log
echo "> build 파일 복사" >> /home/ubuntu/app/deploy/deploy.log
DEPLOY_PATH=/home/ubuntu/app/deploy/
cp $BUILD_JAR $DEPLOY_PATH
RESPONSE_CODE=${curl -s -o /dev/null -w "%http_code" http://localhost/api/nginx/profile}
echo "> $RESPONSE_CODE response code"
if [ ${RESPONSE_CODE} -ge 400 ]
then
echo "> NO RUNNING APP"
CURRENT_PROFILE=port2
else
CURRENT_PROFILE=$(curl -s http://localhost/api/nginx/profile)
fi
echo "> $CURRENT_PROFILE current profile"
if [ $CURRENT_PROFILE == port1 ]
then
IDLE_PROFILE=port2
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == port2 ]
then
IDLE_PROFILE=port1
IDLE_PORT=8081
else
echo "> no coincidence profile: $CURRENT_PROFILE"
echo "> set profile: port1"
IDLE_PROFILE=port1
IDLE_PORT=8081
fi
IDLE_APPLICATION=$IDLE_PROFILE-$JAR_NAME
IDLE_APPLICATION_PATH=$DEPLOY_PATH$IDLE_APPLICATION
ln -Tfs $DEPLOY_PATH$JAR_NAME $IDLE_APPLICATION_PATH
echo "> 현재 실행중인 애플리케이션 pid 확인" >> /home/ubuntu/app/deploy/deploy.log
IDLE_PID=$(pgrep -f $IDLE_APPLICATION)
if [ -z $IDLE_PID ]
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> /home/ubuntu/app/deploy/deploy.log
else
echo "> kill -15 $IDLE_PID"
kill -15 $IDLE_PID
sleep 10
fi
echo "> $IDLE_PROFILE 배포" >> /home/ubuntu/app/deploy/deploy.log
nohup java -jar -Duser.timezone=GMT+9 -Dspring.profiles.active=$IDLE_PROFILE $IDLE_APPLICATION_PATH >> /home/ubuntu/app/deploy/deploy.log 2>/home/ubuntu/app/deploy/deploy_err.log &
for RETRY in {1...10}
do
RESPONSE=$(curl -s http://localhost:$IDLE_PORT/api/nginx/health)
HEALTH_WORD_COUNT=$(echo $RESPONSE | grep 'up' | wc -l)
if [ ${HEALTH_WORD_COUNT} -ge 1 ]
then
echo "> health check done"
break
else
echo "> health check fail"
echo "> $RESPONSE"
fi
if [ ${RETRY} -eq 10 ]
then
echo "> health check loop fail"
echo "> NGINX Switching fail"
exit 1
fi
echo "> health check retrying"
sleep 10
done
echo "> Profile Switch"
sleep 10
sudo sh /home/ubuntu/app/deploy/switch.sh
switch.sh
echo "> 현재 구동중인 Port 확인"
CURRENT_PROFILE=$(curl -s http://localhost/api/nginx/profile)
# 쉬고 있는 set 찾기: port1이 사용중이면 port2가 쉬고 있고, 반대면 port1이 쉬고 있음
if [ $CURRENT_PROFILE == port1 ]
then
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == port2 ]
then
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE"
echo "> 8081을 할당합니다."
IDLE_PORT=8081
fi
echo "> 전환할 Port: $IDLE_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" |sudo tee /etc/nginx/conf.d/service-url.inc
PROXY_PORT=$(curl -s http://localhost/api/nginx/profile)
echo "> Nginx Current Proxy Port: $PROXY_PORT"
echo "> Nginx Reload"
sudo nginx -s reload
3) 최상단 root에 appspec.yml 파일도 만들어준다
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/app/deploy
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 60
runas: ubuntu
7. push 해서 자동배포되는지 확인하기
세팅이 끝났으면 push해서 자동배포 되는지 확인해보겠다
main.yml을 보면 알겠지만 main브랜치에서 push나 requestfull 할때
자동배포가 이루어진다
1) push 했을때 github에서 repository에 Actions에서 ci 하듯이 빌드된다
2) 빌드 완료되면 AWS에 CodeDeploy에서 배포 상태를 보여준다
'DevOps' 카테고리의 다른 글
[DevOps] 도메인 구매 + Route53 + ELB(HTTPS) (0) | 2022.11.27 |
---|---|
[DevOps] Route 53 (생활코딩) (0) | 2022.11.27 |
[DevOps] Github Actions CI 구현하기 (1) (0) | 2022.11.23 |
[DevOps] 도커 컨테이너 제어하기 Docker compose (생활코딩) (0) | 2022.11.21 |
[DevOps] 도커 이미지 만드는 법 Dockerfile & build (생활코딩) (0) | 2022.11.21 |