무중단 배포 구현하기 - (4)
- 도커 컴포즈 파일 설정 -blue, green
- 도커파일 작성(Dockerfile)
- GitHub ACTIONS secrets 주입
- GitHub ACTIONS 워크플로우 생성
- GitHub ACTIONS 워크플로우 작성
[10] 도커 컴포즈 파일 설정 - blue, green
1. docker-compose-blue 파일 설정
version: '3.8'
services:
blue:
image: eunchaelyu/eroom-prod:latest
container_name: blue
ports:
- "8080:8080"
environment:
- PROFILES=blue
- ENV=blue
2. docker-compose-green 파일 설정
version: '3.8'
services:
green:
image: eunchaelyu/eroom-prod:latest
container_name: green
ports:
- "8081:8081"
environment:
- PROFILES=green
- ENV=green
3. docker-compose 확인시 명령어
- 로그 확인
docker-compose -f docker-compose-blue.yml logs
- 실행 확인
docker-compose -f docker-compose-blue.yml ps
[11] 도커파일 작성(Dockerfile)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # open jdk 17 버전의 환경을 구성
FROM openjdk:17-alpine
# build가 되는 시점에 JAR_FILE이라는 변수 명에 build/libs/Eroom-Project-BE-0.0.1-SNAPSHOT.jar 선언
# build/libs - gradle로 빌드했을 때 jar 파일이 생성되는 경로
ARG JAR_FILE=build/libs/Eroom-Project-BE-0.0.1-SNAPSHOT.jar
ARG PROFILES
ARG ENV
# JAR_FILE을 app.jar로 복사
COPY ${JAR_FILE} Eroom-Project-BE-0.0.1-SNAPSHOT.jar
# 운영 및 개발에서 사용되는 환경 설정을 분리
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILES}" , "-Dserver.env=${ENV}", "-jar" , "/Eroom-Project-BE-0.0.1-SNAPSHOT.jar"]
|
- dockerfile 작성 필요(이미지를 만들기 위해서)
- 프로젝트들을 그대로 가지고 DOCKER 이미지가 하나 만들어진다
서버에서 DOCKER-COMPOSE 설정했던 profiles,env 를 넣어준다
"-Dspring.profiles.active=${PROFILES}"
의 의미는 “application.yml에서 작성해준 spring의 profiles 의 active”가 blue or green인지에 따라“docker-compose에 설정된 profiles”가 달라지기 때문에 blue or green 서버가 열리게 된다는 뜻이다
"-Dserver.env=${ENV}"
의 의미는 “application.yml에서 작성해준 spring의 profiles 의 active”가 blue or green인지에 따라“docker-compose에 설정된 env”가 달라지기 때문에 blue or green 서버가 열리게 된다는 뜻이다
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILES}" , "-Dserver.env=${ENV}", "-jar" , "/Eroom-Project-BE-0.0.1-SNAPSHOT.jar"]
- 이 코드는 우리가 ubuntu에서 jar 파일을 실행할 때 사용하는
java -jar Eroom-Project-BE-0.0.1-SNAPSHOT.jar
- 이 명령어를 띄어쓰기별로 적어준 것이고 추가로 적어준 profiles와 env는 옵션이라고 할 수 있다
[12] GitHub ACTIONS secrets 주입
- Github에 public 레포지토리 생성 > Setting > Secrets and variables > Actions 탭
- New repository secret에 각각 추가 (Settings > Secrets and variables > Actions)
[14] GitHub ACTIONS 워크플로우 작성
STEP 1
1
2
3
4
5
6
7
8
9
10
11
| # github repository actions 페이지에 나타날 이름
name: CI/CD
# event trigger
# master브랜치에 push가 되었을 때 실행
on:
push:
branches: [ "master" ]
permissions:
contents: read
|
- master에 push하면 actions 활성화가 된다
- permissions는 읽기에 권한을 준다는 뜻!
STEP 2
1
2
3
4
5
6
7
8
9
10
11
| jobs:
build:
runs-on: ubuntu-latest
steps:
# JDK setting
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
|
STEP 3
1
2
3
4
5
6
7
| - name: Build with Gradle
run: |
mkdir -p ./src/main/resources
echo $ | base64 --decode > ./src/main/resources/application.yml
cat ./src/main/resources/application.yml
chmod +x ./gradlew
./gradlew build -x test
|
- /src/main/resources 디렉토리를 생성하여 YML 파일을 디코딩하여 가지고 온다
- application.yml은 Secrets에 저장된 값으로 Base 64로 인코딩된 값이 들어가 있기 때문에 디코딩 작업을 한다
- gradlew 스크립트에 실행 권한 부여 후 build 된다
- x test 옵션은 테스트를 실행하지 않도록 설정
- jar 파일이 만들어진다
STEP 4
1
2
3
4
5
| - name: Login to DockerHub
uses: docker/login-action@v1
with:
username: $
password: $
|
- jar 파일을 ubuntu에서 만들었기 때문에 도커 로그인을 ubuntu에서 한다
STEP 5
1
2
3
4
| - name: Build Docker
run: docker build --platform linux/amd64 -t $/eroom-prod .
- name: Push Docker
run: docker push $/eroom-prod:latest
|
- jar 파일을 스냅샷을 찍어서 이미지로 만든다
-> eroom-prod:latest라는 레포지토리로 도커 허브에 보낸다(push)
STEP 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Set target IP
run: |
STATUS=$(curl -o /dev/null -w "%{http_code}" "http://$/env")
echo $STATUS
if [ $STATUS = 200 ]; then
CURRENT_UPSTREAM=$(curl -s "http://$/env")
else
CURRENT_UPSTREAM=green
fi
echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV
if [ $CURRENT_UPSTREAM = blue ]; then
echo "CURRENT_PORT=8080" >> $GITHUB_ENV
echo "STOPPED_PORT=8081" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
elif [ $CURRENT_UPSTREAM = green ]; then
echo "CURRENT_PORT=8081" >> $GITHUB_ENV
echo "STOPPED_PORT=8080" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
fi
|
현재 상황
- Nginx 서버로 접속은 되지만 env 요청은 처리가 안된 상태
- 아직 프록시 서버에서 스프링 부트 서버를 배포하지 않았기 때문에 404에러가 뜨는 것
http://localhost:8080/env
로는 요청이 잘 가는 상태
STEP 7
1
2
3
4
5
6
7
8
9
10
11
| - name: Docker compose
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: $
port: 22
key: $
script_stop: true
script: |
sudo docker pull $/eroom-prod:latest
sudo docker-compose -f docker-compose-$.yml up -d
|
STEP 8
1
2
3
4
5
6
| - name: Check deploy server URL
uses: jtalk/url-health-check-action@v3
with:
url: http://$:$/env
max-attempts: 5
retry-delay: 10s
|
- 10초마다 1번씩 최대 5번 요청하고 응답이 없다면 배포가 실패하게 됨
- 이 다음이 blue를 green으로 바꿔주는 작업인데 요청이 없다면 나중에 둘다 실행이 안되기 때문에 먼저 정상적으로 돌아간다는 체크 작업이 필요하다
STEP 9
1
2
3
4
5
6
7
8
9
| - name: Change nginx upstream
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: $
key: $
script_stop: true
script: |
sudo docker exec -i nginxserver bash -c 'echo "set \$service_url $;" > /etc/nginx/conf.d/service-env.inc && nginx -s reload'
|
- 다시 ssh 로 EC2 에 접속하고
sudo docker exec -i nginxserver bash
는 nginxserver라는 서버에 접속한다- 여기에 -c 를 같이 쓰면 접속한 것처럼 command만 사용할 수 있다
'echo "set \$service_url $;"
- service_url을 현재
env.TARGET_UPSTREAM
로 바꾼다 - (현재 위의 사진처럼 green으로 돼있는 것을 blue로 바꾸는 것, 아직 배포가 안됐기 때문에 status가 200이 아니라 green으로 돼있음)
STEP 10
1
2
3
4
5
6
7
8
9
10
11
12
13
| - name: Stop current server
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: $
key: $
script_stop: true
script: |
sudo docker stop $
sudo docker rm $
- name: Prune unused Docker images
run: sudo docker image prune -a
|
- 다시 EC2로 SSH 사용해서 접속 후
- 컨테이너 green을 중지 시키고 삭제한다
- 처음 배포할 때 이 과정에서 기존 실행 서버가 없어서 오류난다(첫 시도에서는 이 과정 에러 무시 해도 됨)