-
Notifications
You must be signed in to change notification settings - Fork 2
CD(지속적 통합)
-
S3
접근 권한 부여 -
CodeDeploy
접근 권한 부여
-
public ip access
비활성화 -
secret-key
생성
다음과 같은 과정으로 이루어진다.
- 다른 레포에 있는 서브모듈 가져오기
- JDK 11 설치
- Gradle 캐싱 (속도 증가를 위해)
- 권한 부여 및 프로젝트 빌드
- 결과물 압축
- AWS 인증
- S3 업로드
name: deploy
on:
push:
branches:
- main
env:
S3_BUCKET_NAME: salmal-s3
PROJECT_NAME: salmal
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 체크아웃
uses: actions/checkout@v3
- name: 서브모듈 레파지토리 가져오기
uses: actions/checkout@v3
with:
token: ${{ secrets.SUBMODULE_TOKEN }}
submodules: recursive
- name: JDK 11 설치
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Gradle 캐싱
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Gradle 명령 실행을 위한 권한을 부여
run: chmod +x gradlew
- name: Gradle 빌드
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: build
- name: Make zip file (zip 으로 압축)
run: zip -r ./$GITHUB_SHA.zip .
shell: bash
- name: Configure AWS credentials (AWS 인증 설정)
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Upload to S3 (S3에 업로드)
run: aws s3 cp --region ${{ secrets.AWS_REGION }} ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
# - name: CodeDeploy (CodeDeploy를 통해 EC2에 전달)
# run: aws deploy create-deployment --application-name trilo-system-deploy --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name develop --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip
결과물을 보면 다음과 같이 S3 버킷 저장소에 프로젝트 결과물이 잘 올라간 것을 확인해볼 수 있다.
- VPC(Virtual Private Cloud) 란,
같은 시스템 내에서 독립된 네트워크를 구성
하기 위한가상의 네트워크 공간
이다. - 서브넷이란, IP 주소에서 네트워크 영역을 부분적으로 나눈 부분 네트워크를 뜻한다. VPC 를 좀 더 잘게 쪼개어 네트워크를 좀 더 세부적으로 분리할 수 있다.
- 인터넷 게이트웨이란, 쉽게 말해 인터넷 간의 통신을 가능하게 해주는 AWS 컴포넌트이다. 이게 없으면 인터넷과의 연결이 불가능하다.
- 라우팅 테이블이란, 네트워크 상의 특정 목적지까지의 거리와 가는 방법등을 명시하고 있는 테이블이다.
- Amazon Linux 2
- pem 키 생성 후 SSH 연결
JAVA 11 설치
sudo amazon-linux-extras install java-openjdk11
# 패키지 매니저 업데이트, ruby 설치
sudo yum update
sudo yum install ruby
sudo yum install wget
# 서울 리전에 있는 CodeDeploy 리소스 키트 파일 다운로드
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
# 설치 파일에 실행 권한 부여
chmod +x ./install
# 설치 진행 및 Agent 상태 확인
sudo ./install auto
sudo service codedeploy-agent status
만약 실행중이 아니라면 다음의 커멘드로 codedeploy 실행시켜야한다.
sudo service codedeploy-agent start
EC2 에서
S3, CodeDeploy
권한을 부여받아 사용할 수 있도록 IAM 권한 사용자를 생성한다.
아래의 권한을 가지는 CodeDeploy-S3-Role
이라는 이름의 역할
을 생성한다.
- 역할명 : CodeDeploy-S3-Role 생성
- 연결 권한: AmazonS3FullAccess, AWSCodeDeployFullAccess
- Name : S3-CodeDeploy-FullAccess
이후 해당 역할을 다음과 같은 과정을 통해 EC2에 부여한다
- EC2 → 인스턴스 선택 → 우클릭 → 보안 → IAM 역할 수정
- CodeDeploy-S3-Role 연결
우선 CodeDeploy 를 생성한다.
- CodeDeploy - 애플리케이션 생성
- 애플리케이션 이름 : salmal-deploy
- 컴퓨팅 플랫폼 : EC2/온프레미스
이후 IAM 역할을 생성하고
- 역할 이름 : CodeDeploy-Role
- 사용 사례 : CodeDeploy
- 연결 정책 : AWSCodeDeployRole
- Name: CodeDeploy
CodeDeploy 에 역할을 부여한다.
- salmal-system-deploy → 배포그룹 생성
- 배포 그룹 이름 : develop
- 서비스 역할 : 위에서 생성한 역할 연결
- 배포 유형 : 현재 위치
- 환경 구성 : Amazon EC2 인스턴스
- AWS CodeDeploy 에이전트 설치 → 안 함
- 배포 설정 : CodeDeployDefault.AllAtOnce
- 로드밸런싱 활성화 끄기
- 배포그룹 생성
우선 VPC 를 생성한다.
서비스 -> 네트워킹 및 콘텐츠 전송 -> VPC -> Virtual Private Cloud -> VPC -> VPC 생성
생성 후 확인해보면 위와 같이 가상 라우터가 생성되어 기본 라우팅 테이블을 보유하게 된다.
Public 서브넷을 생성한다.
서비스 -> 네트워킹 및 콘텐츠 전송 -> VPC -> Virtual Private Cloud -> 서브넷 -> 서브넷 생성
게이트웨이를 생성하고 생성한 VPC 와 연결한다.
서비스 -> 네트워킹 및 콘텐츠 전송 -> VPC -> Virtual Private Cloud -> 인터넷 게이트웨이 -> 인터넷 게이트웨이 생성
작업 -> VPC 연결 -> TriLo-VPC 에 연결
Public 라우팅 테이블을 생성하고, Public 서브넷을 연결한다.
서비스 -> 네트워킹 및 콘텐츠 전송 -> VPC -> Virtual Private Cloud -> 라우팅 테이블 -> 라우팅 테이블 생성
작업 -> VPC 연결 -> TriLo-VPC 에 연결
이전에 작성했던 Github Actions 의 배포 Flow 에 CodeDeploy 관련 Flow 를 추가한다.
- name: CodeDeploy (CodeDeploy를 통해 EC2에 전달)
run: aws deploy create-deployment --application-name salmal-system-deploy --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name develop --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip
이제 Github Actions 에서 배포를 수행하면 다음과 같이 S3 에 올린 파일이 CodeDeploy 를 통해 EC2로 전달되는 것을 확인할 수 있다.
본래는 로드밸런서의 역할을 하기에 별도의 EC2로 분리하는 것이 맞다. 하지만 지금 당장은 프리티어 계정의 여유분이 없고 그렇다고 AWS 로드밸런서를 도입하기엔 (당장 서버도 한개여서 분산환경 구축할 이유도 없고..) 시간이 너무 짧으며
빠르게 배포환경을 구축하여 IOS 분들과의 원활한 협업을 진행하기 위해서
ONLY 한 개의 서버에 몽땅 때려박는다.시간이 남으면 서버 다중화와 DB 다중화, 그리고 제대로된 로드밸런서 및 스케줄링을 설정하여 더 많은 트래픽을 수용할 수 있는 서비스 환경을 구축하기로 한다
.
앞서 EC2에 프로젝트 파일을 전달했으니, 이제 Nginx 를 설치 및 설정하여 외부 API 요청에 대한 프록시를 설정한다.
먼저 설치 스크립트는 다음과 같다.
# 설치
sudo amazon-linux-extras install nginx1
# 버전 확인
sudo nginx -v
이후 Nginx 설정으로 들어가서 지정 URL 로 요청을 보낼 수 있도록 하는 프록시를 설정한다.
EC2에 먼저 외부에서 참조할 수 있는 include 파일을 생성하고, 서비스 url 주소를 정의한다.
sudo vi /home/ec2-user/service_url.inc
# service_url.inc
set $service_url http://127.0.0.1:8081;
그 다음, Nginx 설정 파일을 수정하여 외부 요청에 대한 매핑을 수행한다.
include /home/ec2-user/service_url.inc;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_Host;
proxy_pass $service_url;
}
변경한 설정으로 Nginx 가 동작하려면, 재시작을 해야한다.
# 시작
sudo service nginx start
# 재시작 (서버의 shutdown 없이 설정만 로드한 뒤 재시작)
sudo service nginx reload
# 재시작 (서버의 shutdown 후 재시작)
sudo service nginx restart
# 상태확인
sudo service nginx status
먼저 세 가지 스크립트 파일을 추가한다.
- 새로운 WAS 를 띄우는 스크립트
#!/bin/bash
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Current port of running WAS is ${CURRENT_PORT}."
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
fi
TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
if [ ! -z ${TARGET_PID} ]; then
echo "> Kill WAS running at ${TARGET_PORT}."
sudo kill ${TARGET_PID}
fi
nohup java -jar -Dserver.port=${TARGET_PORT} /home/ec2-user/trilo/build/libs/* > /home/ec2-user/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0
- Health Check 스크립트
#!/bin/bash
# Crawl current connected port of WAS
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
# Toggle port Number
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
exit 1
fi
echo "> Start health check of WAS at 'http://127.0.0.1:${TARGET_PORT}' ..."
for RETRY_COUNT in 1 2 3 4 5 6 7 8 9 10
do
echo "> #${RETRY_COUNT} trying..."
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${TARGET_PORT}/deploy/health)
if [ ${RESPONSE_CODE} -eq 200 ]; then
echo "> New WAS successfully running"
exit 0
elif [ ${RETRY_COUNT} -eq 10 ]; then
echo "> Health check failed."
exit 1
fi
sleep 10
done
- Switching 스크립트
#!/bin/bash
# Crawl current connected port of WAS
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Nginx currently proxies to ${CURRENT_PORT}."
# Toggle port number
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
exit 1
fi
# Change proxying port into target port
echo "set \$service_url http://127.0.0.1:${TARGET_PORT};" | tee /home/ec2-user/service_url.inc
echo "> Now Nginx proxies to ${TARGET_PORT}."
# Reload nginx
sudo service nginx reload
echo "> Nginx reloaded."
appspec.yml 에 앞서 추가했던 세가지 스크립트를 실행하는 명령문을 추가한다.
# 새로 추가되는 부분
hooks:
ApplicationStart:
- location: scripts/run_new_was.sh
timeout: 180
runas: ec2-user
- location: scripts/health_check.sh
timeout: 180
runas: ec2-user
- location: scripts/switch.sh
timeout: 180
runas: ec2-user
현재 아무 WAS 가 떠있지 않은 상태이므로 8081 포트에 새로운 WAS 를 수동으로 띄워준다.
nohup java -jar -Dserver.port=8081 /home/ec2-user/salmal/build/libs/* &