S3 사용자 설정
Github Actions 에서 사용할 IAM 사용자를 추가해주어야 한다.
GitHub Acions 에서 AWS 에 접근을 할때는 당연하게 권한이 필요하다.
이런 권한을 IAM 사용자로 추가하여 생성해줄 것 이다.
1. AWS의 IAM ➝ 사용자 ➝ 사용자 생성
2. 사용자 세부 정보 지정
3. 권한 설정
1. AWSCodeDeployFullAccess
2. AmazonS3FullAccess
4. 사용자 액세스 키 만들기
iam ➝ 사용자 ➝ 사용자 선택 ➝ 보안 자격 증명 ➝ 액세스 키 만들기
1. 액세스키 이동
2. 액세스 키 모범 사례 및 대안 선택
3. 태그 설정
4. 액세스 키 생성 완료
생성된 액세스 키는 잘 보관해야 한다.
Github Actions 세팅
1. 액세스 키 세팅
Settings ➝ Secrets ➝ Actions ➝ New repository secret
2. appspec.yml
github repository의 루트 디렉토리 최상단에 appspec.yml을 위치하게 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| version: 0.0
os: linux
files:
- source: / # 인스턴스에 복사할 디렉터리 경로
destination: /home/ubuntu # 인스턴스에서 파일이 복사되는 위치
overwrite: yes # 복사할 위치에 파일이 있는 경우 대체
permissions:
- object: / # 권한이 지정되는 파일 or 디렉터리
pattern: "**" # 매칭되는 패턴에만 권한 부여
owner: ubuntu # object의 소유자
group: ubuntu # object의 그룹 이름
hooks:
ApplicationStart: # CodeDeploy의 ApplicationStart 단계에서 실행
- location: deploy.sh
timeout: 60
runas: ubuntu
|
3. deploy.sh
deploy.sh 파일은 배포를 위해 실행되는 파일이다.
flow를 살펴보자면 먼저 green 버전이 현재 실행 중이라 가정한다.
- green 버전이 현재 실행 중인지 확인
- green 버전이 현재 실행 중이라면 blue 버전 up
- 30초간 blue, green 버전 동시 실행
- 30초 후 blue 버전 확인 4-1. if blue 버전 실행 O ➝ green 버전 종료 4-2. if blue 버전 실행 X ➝ blue 버전에 런타임 에러 발생했으므로 slack 알람 전송
blue 버전을 실행하려 하다가 런타임 에러가 발생해 blue 버전이 내려가고 기존 green 버전이 남아있는 상황이 발생할 수 있다. 따라서 이 경우에는 slack으로 알림을 전송
git repository의 scripts 디렉터리 안에 작성해주자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
| # 작업 디렉토리를 /home/ubuntu으로 변경
cd /home/ubuntu
# 환경변수 DOCKER_APP_NAME 설정
DOCKER_APP_NAME=ec2-application
# 실행 중인 blue가 있는지 확인
# 프로젝트의 실행 중인 컨테이너를 확인하고, 해당 컨테이너가 실행 중인지를 EXIST_BLUE 변수에 저장
EXIST_BLUE=$(sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml ps | grep Up)
# 배포 시작한 날짜와 시간을 기록
echo "배포 시작 일자 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# green이 실행 중이면 blue up
# EXIST_BLUE 변수가 비어있는지 확인
if [ -z "$EXIST_BLUE" ]; then
# 로그 파일(/home/ubuntu/deploy.log)에 "blue up - blue 배포 : port:8081"이라는 내용을 추가
echo "blue 배포 시작 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# docker-compose.blue.yml 파일을 사용하여 seniors-blue 프로젝트의 컨테이너를 빌드하고 실행
sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml up -d --build
# 30초 동안 대기
sleep 30
# blue가 문제없이 배포되었는지 현재 실행 여부를 확인
BLUE_HEALTH=$(sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml ps | grep Up)
# blue가 현재 실행 중이지 않다면 -> 런타임 에러 또는 다른 이유로 배포가 되지 못한 상태
if [ -z "$BLUE_HEALTH" ]; then
# slack으로 알람을 보낼 수 있는 스크립트를 실행
sudo /home/ubuntu/slack_blue.sh
# blue가 현재 실행되고 있는 경우에만 green을 종료
else
# /home/ubuntu/deploy.log: 로그 파일에 "green 중단 시작"이라는 내용을 추가
echo "green 중단 시작 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# docker-compose.green.yml 파일을 사용하여 seniors-green 프로젝트의 컨테이너를 중지
sudo docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml down
# 사용하지 않는 이미지 삭제
sudo docker image prune -af
echo "green 중단 완료 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
fi
# blue가 실행중이면 green up
else
echo "green 배포 시작 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
sudo docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml up -d --build
sleep 30
GREEN_HEALTH=$(sudo docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml ps | grep Up)
if [ -z "$GREEN_HEALTH" ]; then
sudo ./slack_green.sh
else
# /home/ubuntu/deploy.log: 로그 파일에 "blue 중단 시작"이라는 내용을 추가
echo "blue 중단 시작 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# docker-compose.blue.yml 파일을 사용하여 seniors-green 프로젝트의 컨테이너를 중지
sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml down
# 사용하지 않는 이미지 삭제
sudo docker image prune -af
echo "blue 중단 완료 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
fi
fi
|
4. Java with Gradle (gradle.yml)
Configure를 누른 후 아래와 같이 작성해주자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
| name: Seniors CICD(deploy)
on:
push:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
# JDK를 17 버전으로 세팅
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# Gradle 캐싱-> 빌드 속도 UP
- name: Gradle caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: $-gradle-$
restore-keys: |
$-gradle-
# application.yml 파일 생성
- name: make application.yaml
run: |
cd ./src/main/resources
touch ./application.yml
echo "$" > ./application.yml
shell: bash
# Gradle로 빌드 실행
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: build
# AWS에 연결
- name: Connect to AWS
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: $
aws-secret-access-key: $
aws-region: $
# 빌드파일을 ZIP 파일로 생성
- name: Make zip file
run: |
mkdir deploy
cp ./appspec.yml ./deploy/
cp ./Dockerfile ./deploy/
cp ./docker-compose.blue.yml ./deploy/
cp ./docker-compose.green.yml ./deploy/
cp ./deploy.sh ./deploy/
cp ./build/libs/*.jar ./deploy/
zip -r -qq -j ./seniors-build.zip ./deploy
# S3에 zip 파일 업로드
- name: Upload to S3
run: |
aws s3 cp \
--region ap-northeast-2 \
./seniors-build.zip s3://ci-cd-github-actions-s3-bucket
# CodeDeploy에 배포 요청
- name: Code Deploy Deployment Request
run: |
aws deploy create-deployment --application-name my-cdoedeply-app \
--deployment-config-name CodeDeployDefault.OneAtATime \
--deployment-group-name my-codedeploy-deployment-group \
--s3-location bucket=ci-cd-github-actions-s3-bucket,bundleType=zip,key=seniors-build.zip
# 배포 결과 Slack 알람 전송
- name: Slack 알람 발송
uses: rtCamp/action-slack-notify@v2
env:
SLACK_COLOR: $ # or a specific color like 'good' or '#ff00ff'
SLACK_MESSAGE: 배포 결과 => $
SLACK_TITLE: 배포 결과 알람
SLACK_USERNAME: Yeti-Bot
SLACK_WEBHOOK: $
if: always()
|
여기서 주의 할 것이, 코드를 읽으며 만든 S3 이름과, CodeDeploy 이름, 그룹을 작성해줘야한다.
--application-name
: application name (codedeploy에서 만든 이름)deployment-group-name
: codedeploy 그룹 명--s3-location bucket
: bucket 명s3://ci-cd-github-actions-s3-bucket
: bucket 명 그리고 aws-region: $에는 ap-northeast-2를 작성해주었다.
5. APPLICATION 추가
application.yml에 작성할 코드를 작성해주면 된다. Git Repository ➝ Actions ➝ Secrets and variable
1
2
3
4
5
6
7
8
9
10
| spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: <aws db url>
username: <name>
password: <password>
jpa:
hibernate:
ddl-auto: update
show-sql: true
|
Docker
1. docker, docker compose 설치
1. docker 설치
1
2
| $ sudo apt update
$ sudo apt install docker.io
|
2. docker 시작
1
| $ sudo systemctl start docker
|
3. docker-compose 설치
1
| sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
4. docker-compose 권한 부여
1
| sudo chmod +x /usr/local/bin/docker-compose
|
5. docker-compose 권한 확인
1
2
| $ docker-compose version
Docker Compose version v2.23.3
|
docker-compose.yml 작성
Docker compose는 여러 개의 컨테이너로부터 이루어진 서비스를 구축, 실행하는 순서를 자동으로 해서, 관리를 간단히 하는 기능이다.
blue, green 무중단 배포를 구성하므로 docker-compose.blue.yml와 docker-compose.green.yml 파일을 따로 작성한다.
blue 버전은 8081, green 버전을 8082로 설정한다.
프로젝트의 Dockerfile과 같은 곳에 만들어주면 된다.
docker-compose.blue.yml
1
2
3
4
5
6
7
| version: '3'
services:
backend:
build: .
ports:
- "8081:8080"
container_name: seniors-blue
|
docker-compose.green.yml
1
2
3
4
5
6
7
| version: '3'
services:
backend:
build: .
ports:
- "8082:8080"
container_name: seniors-green
|
Dockerfile
dockerfile은 image를 빌드하기 위한 파일이며 이 image를 기반으로 컨테이너가 실행된다.
아래 코드는 Java 17을 기반으로 하는 애플리케이션을 컨테이너 내에서 실행하기 위한 것으로, 빌드된 애플리케이션 JAR 파일을 Docker 이미지 안에 포함하고 그 JAR 파일을 실행하는 구성을 하고 있다.
1
2
3
4
| FROM openjdk:17
ARG JAR_FILE=<프로젝트명>-0.0.1-SNAPSHOT.jar
COPY $JAR_FILE app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
|
Nginx
1. Nginx 연결
1. ec2 접속
1
| ssh -i [pem 키] username@[ip 주소]
|
2. nginx 설치
1
| $ sudo apt install nginx
|
3. nginx 시작
1
| $ sudo systemctl start nginx
|
4. nginx 상태 확인(초록불 정상)
1
| $ sudo systemctl status nginx
|
5. nginx 설정 파일 수정
1
| $ sudo vi /etc/nginx/nginx.conf
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| # nginx.conf
# Nginx 이벤트 모듈 설정.
events {}
# HTTP 요청과 관련된 설정을 하는 부분.
http {
# upstream 서버 그룹을 정의. 로드 밸런싱을 위해 여러개의 서버를 가리킬 수 있음.
upstream spring-server {
least_conn;
# 로드 밸런싱을 위해 locahost의 8081 포트와 8082 포트에 동작하는 서버를 지정.
server localhost:8081 max_fails=3 fail_timeout=10s;
server localhost:8082 max_fails=3 fail_timeout=10s;
}
# 실제 HTTP 서버를 설정하는 부분.
server {
# listen 지시문은 서버가 80포트에서 들어오는 요청을 수신하도록 설정.
listen 80;
server_name [my domain name]
include /etc/nginx/default.d/*.conf;
# 모든 경로에 대한 처리를 정의. 프록시 서버로의 요청을 설정하는 부분.
location / {
# proxy_set_header는 프록시 서버로 전달되는 요청 헤더를 설정하는 역할.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_pass는 실제 요청을 전달할 upstream 서버 그룹을 지정.
proxy_pass http://spring-server;
# proxy_redirect는 프록시 응답의 리다이렉션을 설정하는 부분.
proxy_redirect off;
}
}
}
|
6. nginx 재시작
1
2
| $ sudo service nginx restart
$ sudo service nginx status
|
Slack
1. 앱 생성
1. slack앱에서 앱 관리로 이동
2. 사용자 지정 통합 앱에서 수신 웹 후크 클릭 후 slack에 추가
3. 원하는 채널 선택 후 작성
발급받은 URL을 Github Actions의 secrets에 추가해준다.
2. 알림 설정
1. Github Actions에 URL 추가
URL을 SLACK_WEBHOOK_URL에 넣어준다.
2. ec2 서버안에 작성
/home/ubuntu(deploy.sh에서 작업 디렉토리를 정해준 곳) 안에 작성해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # slack-web-hook URL 세팅
slack_web_hook="........."
# 배포 중 문제가 발생했다는 내용의 로그를 남겨준다.
echo "blue 배포 중 문제 발생 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
echo "관리자 알람 발송 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# 슬랙으로 보낼 메시지를 변수에 저장해준다.
json="{ \"text\": \"blue 배포 중 문제가 발생하여 배포가 비정상 중단되었으니 확인 부탁드립니다 -> 문제 발생 시각: $(date '+%Y-%m-%d %H:%M:%S')\" }"
# 변수에 메시지가 잘 입력되었는지 콘솔 창에 출력해본다.
echo "json: $json"
# 슬랙으로 메시지를 발송한다.
curl -X POST -H 'Content-type: application/json' --data "$json" "$slack_web_hook"
# 슬랙 알람 발송 이후 배포 비정상종료 로그를 남겨준다.
echo "관리자 알람 발송 완료, 배포 비정상종료 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # slack-web-hook URL 셋팅
slack_web_hook="....."
# 배포 중 문제가 발생했다는 내용의 로그를 남겨준다.
echo "green 배포 중 문제 발생 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
echo "관리자 알람 발송 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
# 슬랙으로 보낼 메시지를 변수에 저장해준다.
json="{ \"text\": \"green 배포 중 문제가 발생하여 배포가 비정상 중단되었으니 확인 부탁드립니다 -> 문제 발생 시각: $(date '+%Y-%m-%d %H:%M:%S')\" }"
# 변수에 메시지가 잘 입력 되었는지 콘솔 창에 출력해본다.
echo "json: $json"
# 슬랙으로 메시지를 발송한다.
curl -X POST -H 'Content-type: application/json' --data "$json" "$slack_web_hook"
# 슬랙 알람 발송 이후 배포 비정상종료 로그를 남겨준다.
echo "관리자 알람 발송 완료, 배포 비정상종료 : $(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" >> /home/ubuntu/deploy.log
|
4. 권한 주기
slack_blue.sh, slack_green.sh 모두 권한을 줘야 한다. sudo chmod +wx slack_blue.sh
sudo chmod +wx slack_green.sh
에러 확인
에러가 나 슬랙으로 에러가 전송되어 온다면 (blue 배포 중 문제가 발생하여 배포가 비정상 중단되었으니 확인 부탁드립니다 -> 문제 발생 시각: 2023-12-04 07:03:46)
1
2
| $ sudo docker ps -a
$ sudo docker logs <container ID>
|
위 명령어로 에러 로그를 확인하면 된다.
참고