Post

blue-green 배포 적용하기

☑️ 왜 필요했나?

기존 배포방식은 다음과 같았다.

GitAction을 사용했다.

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: Deploy to EC2
on:
  push:
    branches:
      - main
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v3
      # JDK Setting
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      # Gradle Caching
      - name: Cache Gradle packages
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: $-gradle-$
          restore-keys: $-gradle-
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      # Create application-prod.properties
      - name: Create application-prod.properties
        run: |
          cd ./src/main/resources
          touch ./application-prod.properties
          echo "$PROPERTIES_PROD" > ./application-prod.properties
        env:
          PROPERTIES_PROD: $
      # Gradle Build
      - name: Build with Gradle
        run: ./gradlew build -x test
      # Image Tagging with Docker Meta
      - name: Docker meta
        id: docker_meta
        uses: crazy-max/ghaction-docker-meta@v1
        with:
          images: teambpm2023/dnd-bpm
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      # DockerHub Login
      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: $
          password: $
      - name: Docker build & push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: true
          tags: $
          labels: $
      - name: create remote directory
        uses: appleboy/ssh-action@master
        with:
          host: $
          username: ubuntu
          key: $
          script: mkdir -p /home/ubuntu/srv/ubuntu
      - name: copy source via ssh key
        uses: burnett01/rsync-deployments@4.1
        with:
          switches: -avzr --delete
          remote_path: /home/ubuntu/srv/ubuntu/
          remote_host: $
          remote_user: ubuntu
          remote_key: $
      - name: executing remote ssh commands using password
        uses: appleboy/ssh-action@master
        with:
          host: $
          username: ubuntu
          key: $
          script: |
            cd /home/ubuntu/srv/ubuntu
            sh ./config/scripts/docker-install.sh
            sudo docker stop $(sudo docker ps -a -q)
            sudo docker rm $(sudo docker ps -a -q)
            sudo docker rmi $(sudo docker images -q)
            sudo docker-compose -f docker-compose.yml pull
            sudo docker-compose -f docker-compose.yml up --build -d

GitAction을 통해 빌드를한 파일을 DockerHub에 올리고, 운영서버에서 명령어로 DockerHub에 올린 빌드된 파일들을 받아서 서버를 띄우고 있었다.

근데, 결국 운영서버에서 빌드하고 다시 띄우는데 시간이 걸렸고 안드로이드 개발자들한테 수정사항이 생기면

“배포 지금 다 했고, 운영서버 뜰 때까지 좀 기다리셔야해요.” 라고 말하는 버릇이 생겼다.

그래서 무중단 배포에 관심이 생겼고 적용해야겠다고 생각했다.

😕 왜 블루-그린 배포를 선택했는가?

블루-그린 배포 방식 말고도 카나리 배포, 롤링 배포가 있다.

그 둘을 적용하지 않고 블루-그린을 배포한 이유는 다음과 같았다.

카나리 배포를 선택하지 않은 이유

  • 카나리 배포 방식의 장점인 조금씩 배포해가며, 위험을 빠르게 감지할 필요가 없다고 생각했다.
  • 카나리 배포 방식의 인프라 구성방식이 복잡하다고 하여, 이는 주어진 일정내에 빠르게 적용할 수 없다고 생각했다.

롤링 배포를 선택하지 않은 이유

  • 롤링 배포 방식의 단점중 하나인 데이터 일관성 문제가 생길 수 있다는 것에 끌리지 않았다.

🔅 해결 과정

로드 밸런서를 두지 않았다.

앱 자체가 서비스를 안 했기에 로드밸런서로 트래픽 관리를 할 필요가 없다고 생각했다. 실제 앱이 출시되고, 트래픽을 감당할 수 없는 상황에 로드 밸런서를 놓을 생각이다.

Flow

내가 생각한 플로우는 기존 코드는 최대한 덜 건드리고 적용하고 싶었기에 다음과 같이 생각했다.

  1. port를 다르게 설정한 docker-compose파일을 blue용, green용으로 둔다.

green

1
2
3
4
5
6
7
8
9
10
version: '3'

services:
  web-green:
    container_name: web-green
    image: teambpm2023/dnd-bpm:main
    expose:
      - "8082"
    ports:
      - "8082:8080"

blue

1
2
3
4
5
6
7
8
9
10
version: '3'

services:
  web-blue:
    container_name: web-blue
    image: teambpm2023/dnd-bpm:main
    expose:
      - "8081"
    ports:
      - "8081:8080"
  1. 배포스크립트를 짜는데, 이 compose파일들에 저장된 정보들을 이용해서 DockerHub에서 이미지를 가져오고 빌드시킨다.
  2. 기존에 존재했던 서버(green이나 blue)와 반대 서버를 띄워서 2포트를 사용하고 있게 한다.(8081, 8082포트)
  3. nginx 설정을 건드려서 green이나 blue의 설정파일을 가리키게 만든다.(포트 변경)
  4. 서버가 가리키는 포트가 변경되었으면, 기존 떠있던 green이나 blue의 컨테이너를 종료시킨다.

이를 그림으로 잘 표현한것은 https://jay-ji.tistory.com/m/99 이 블로그이고, 실제로 여기있는 코드들을 잘 적용해서 사용하고 있다.

그러므로 잘 이해가 안된다면 참고하시길 바랍니다.

nginx blue버전의 설정은 다음과 같이 구성했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
events {}

http{
    upstream web-blue {
        server localhost:8081;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://web-blue;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_redirect off;
        }
    }
}

nginx green버전의 설정이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
events {}

http{
    upstream web-green{
        server localhost:8082;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://web-green;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_redirect off;

            proxy_buffer_size          128k;
            proxy_buffers              4 256k;
            proxy_busy_buffers_size    256k;
        }
    }
}

그리고, 배포 스크립트는 다음과 같이 작성했다.

EC2환경에서 실행되는거라 sudo코드를 이용해서 작성했다.

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
#!/bin/bash

# Blue 를 기준으로 현재 떠있는 컨테이너를 체크한다.
EXIST_BLUE=$(sudo docker-compose -p web-blue -f docker-compose-blue.yml ps | grep Up)
echo "EXIST_BLUE value is: $EXIST_BLUE"

if [ -z "$EXIST_BLUE" ]; then
    echo "blue up"
    sudo docker-compose -f docker-compose-blue.yml pull
    sudo docker-compose -p web-blue -f docker-compose-blue.yml up --build -d
    BEFORE_COMPOSE_COLOR="green"
    AFTER_COMPOSE_COLOR="blue"
else
    echo "green up"
    sudo docker-compose -f docker-compose-green.yml pull
    sudo docker-compose -p web-green -f docker-compose-green.yml up --build -d
    BEFORE_COMPOSE_COLOR="blue"
    AFTER_COMPOSE_COLOR="green"
fi

sleep 10

# 새로운 컨테이너가 제대로 떴는지 확인
EXIST_AFTER=$(sudo docker-compose -p web-${AFTER_COMPOSE_COLOR} -f docker-compose-${AFTER_COMPOSE_COLOR}.yml ps | grep Up)
if [ -n "$EXIST_AFTER" ]; then
    # nginx.config를 컨테이너에 맞게 변경해주고 reload 한다 -> 필요없을듯?
    sudo cp ./config/nginx/nginx.${AFTER_COMPOSE_COLOR}.conf /etc/nginx/nginx.conf
    sudo nginx -s reload
    # 이전 컨테이너 종료
    sudo docker-compose -p web-${BEFORE_COMPOSE_COLOR} -f docker-compose-${BEFORE_COMPOSE_COLOR}.yml down
    echo "$BEFORE_COMPOSE_COLOR down"
fi

그리고 기존 gitAction에서 배포방식의 스크립트도 변경해줘야했다. 배포되고 deploy.sh파일을 실행하게끔 말이다.

그러므로 chmod를 통해 권한을 주고 실행시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
~~~

script: |
    cd /home/ubuntu/srv/ubuntu
    sh ./config/scripts/docker-install.sh
    - sudo docker stop $(sudo docker ps -a -q)
    - sudo docker rm $(sudo docker ps -a -q)
    - sudo docker rmi $(sudo docker images -q)
    - sudo docker-compose -f docker-compose.yml pull
    - sudo docker-compose -f docker-compose.yml up --build -d
    + sh chmod +x ./deploy.sh
    + sh ./deploy.sh

🤔 어려웠던 점, 아쉬운 점

어려웠던 점으로는 환경부분이 컸다.

내 노트북은 mac이고, 데스크탑은 window11 운영체제를 가지고 있어서 로컬에서 테스트하기 좀 어려웠다.

왜냐하면 Dockerhub에 올라간 파일은 arm/64운영체제에서 돌아가게끔 되어있었기 때문이다.. 운영환경인 ubuntu와 셋 다 운영체제가 달라서 도커허브에 이미지를 올리거나 받아오는 작업을 운영환경속에서 확인해야했다.

그 외에는 어려웠던건 없었다.

아쉬웠던 점으로는 nginx추가 설정에 있다.

일단 지금 운영환경에 nginx가 무조건 떠 있는걸 가정하고 배포를 진행하였는데, 이에 대한 체크도 스크립트에서 작성이 필요하다고 느꼈다.

이는 추후 작업할 것이다.

😀 배운 점

확실히 직접 구성해보니, 왜 필요하고 왜 이런 배포방식을 구성했는지에 대해 깊게 생각하는 계기가 되었다.

그리고, 기존 배포방식에서 많은 코드를 건들지 않도록 바꾸니 어떻게 하면 배포방식들을 내 프로젝트에 자연스럽게 스며들게 할 지 고민할 수도 있었다.

blue-green 배포 쉬운데?

This post is licensed under CC BY 4.0 by the author.