- mysql
- 스프링부트
- 스프링
- Git
- 자바
- SSL
- select
- AWS
- spring
- spring mvc
- 데이터베이스
- PYTHON
- Docker
- java
- join
- session
- 프로그래머스
- ORM
- 1차원 배열
- string
- spring boot
- @transactional
- 문자열
- jpa
- spring security 6
- nginx
- sql
- DI
- Django
- springboot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
개발하는 자몽
롤링 무중단 배포 본문
적용 전 준비할 코드
nginx.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
...
# for load balancing
upstream project_server {
server project_server2:8000;
server project_server1:8000;
keepalive 1024;
}
server {
listen 80;
location / {
proxy_pass http://project_server/; # upstream 이름
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
...
}
...
}
}
무중단 배포의 전제조건은 이중화(로드밸런서)로, `upstream` 지시어로 설정할 수 있다.
docker-compose.yml
version: "3.5"
services:
_build_image:
image: project_django:4.2.6
build: .
project_server1:
image: project_django:4.2.6
restart: always
command: gunicorn project_server.backend.wsgi --bind 0.0.0.0:8000 --log-level debug --timeout=120
ports:
- "8000"
depends_on:
- _build_image
project_server2:
image: project_django:4.2.6
restart: always
command: gunicorn project_server.backend.wsgi --bind 0.0.0.0:8000 --log-level debug --timeout=120
ports:
- "8000"
depends_on:
- _build_image
nginx:
image: project-nginx:latest # tag the image with the name "nginx"
container_name: project-nginx
build: ./nginx # Dockerfile location for Nginx
restart: alway
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # read-only
depends_on:
- project_server1
- project_server2
ports:
- "8000:80"
...
수동으로 하는 방법
✅ 전체 과정
- 배포 전 상태로 project_server1, project_server2 모두 로드밸런서(nginx)에 연결되어 있다.
- project_server2를 로드밸런서에서 제거하고, project_server2에 배포를 진행한다.
- project_server2에 배포가 완료되면, 다시 project_server2를 로드밸런서에 연결한다.
- project_server1을 로드밸런서에서 제거하고, project_server1에 배포를 진행한다.
- project_server1에 배포가 완료되면, 다시 project_server1을 로드밸런서에 연결한다.
Step1.
배포가 진행되는 동안 서비스가 멈추지 않는지(무중단) 확인하기 위해서 터미널 하나를 열여서 주기적으로 Nginx에 요청을 보낸다.
# 1초 간격으로 요청 (무한루프)
$ while true; do curl localhost:4100; ehco ""; sleep1; done
Step2.
project_server2를 로드밸런서에서 제거한다. nginx.conf의 `upstream` 서버 그룹에 아래와 같이 `down`을 추가한다.
...
upstream project_server {
server project_server2:8000 down;
server project_Server1:8000;
}
...
Nginx 컨테이너에 아래와 같은 명령어를 전달하여 멈추지 않고 설정을 리로드 한다. 리로드 시 `exec`의 다음 인자는 docker-compose.yml의 `container_name`을 적어야 한다.
$ docker compose exec project-nginx service nginx reload
Step1에서 열어둔 터미널에서 project_server1에서만 응답이 오는 것을 확인하자.
project_server2의 배포를 진행한다.
$ docker compose up --build -d project_server2
Step3.
project_server2의 배포가 완료되면 project_server2를 로드밸런서에 연결하고 Nginx를 리로드 한다.
...
upstream project_server {
server project_server2:8000;
server project_Server1:8000;
}
...
$ docker compose exec project-nginx service nginx reload
리로드 이후, Step1에서 열어둔 터미널에서 project_server1, project_server2로부터 응답이 오는 것을 확인한다.
Step4.
이번에는 project_server1의 배포를 진행한다. nginx.conf의 `upstream` 부분을 다음과 같이 수정하고 Nginx를 리로드 한다.
...
upstream project_server {
server project_server2:8000;
server project_Server1:8000 down;
}
...
마찬가지로 Step1에서 열어둔 터미널에서 project_server2에서만 응답이 오는 것을 확인한다.
project_server1이 로드밸런서에서 제거됐으므로 project_server1의 배포를 진행한다.
$ docker compose up --build -d project_server1
Step5.
project_server1의 배포가 완료되었으므로 다시 로드밸런서에 연결한다.
...
upstream project_server {
server project_server2:8000;
server project_Server1:8000;
}
...
마지막으로 Nginx를 리로드 한다.
$ docker compose exec project-nginx service nginx reload
먼저 열어둔 터미널에서 두 서버로부터 응답이 오는지 확인한다. 응답이 잘 온다면 모든 과정이 끝났다!
Deploy Shell Script
위에서 수동으로 진행한 롤링 무중단 배포 과정을 bash shell script로 작성했다. 수동으로 할 필요 없이 배포해야 할 때 아래와 같이 터미널에 입력하면 된다.
$ bash {쉘 스크립트 위치}/{쉘 스크립트 파일 이름}.sh
$ bash ./deploy.sh
#!/bin/bash
# cut connection to a server from the load-balancer(nginx)
# args: server name, server port, docker container name for nginx
cut_reload_nginx() {
src="$1:$2"
dest=$src
dest+=" down;"
src+=";"
sed -i "s/$src/$dest/g" ./nginx/nginx.conf
docker compose exec $3 nginx -s reload
sleep 10s
}
# connect a server to the load-balancer(nginx)
# args: server name, server port, docker container name for nginx
connect_reload_nginx() {
dest="$1:$2"
src=$dest
src+=" down;"
dest+=";"
sed -i "s/$src/$dest/g" ./nginx/nginx.conf
docker compose exec $3 nginx -s reload
sleep 10s
}
# deploy a server
# args: container name, env file path
deploy_server() {
# 1. stop container
# 2. remove container
# 3. up and build container
echo ">> [deploy_server] Stop & Remove $1..."
docker compose stop $1
sleep 5s
docker compose rm $1
sleep 10s
echo ">> [deploy_server] Up $1..."
sudo docker compose --env-file $2 up $1 --build -d
}
# health check for a container(server)
# args: url for health check(pass a url which is located at after localhost/)
health_check() {
UP_CHECK=""
URL="http://localhost/$1"
echo ">> [health_check] URL=$URL"
echo ">> [health_check] start..."
while [ -z "$UP_CHECK" ]
do
UP_CHECK=$(curl -s $URL)
done
echo ">> [health_check] finish..."
}
echo ""
echo ""
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo ""
echo "Rolling Deployment Start"
echo "Author: Jaamong"
echo "Last Updated Date: 2024-0X-XX"
echo ""
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo ""
echo ""
# ----- for project_server2 container -----
echo "> Nginx: Cut connection to project_server2..."
cut_reload_nginx "project_server2" "8000" "project-nginx"
echo ""
echo "> Deploy only project_server2..."
deploy_server "project_server2" "./project_server/config/.env"
echo ""
echo "> Health Check for project_server2..."
health_check "api/v1/health-check"
echo ""
echo "> Nginx: Connect to project_server2..."
connect_reload_nginx "project_server2" "8000" "project-nginx"
echo ""
sleep 10s
# ----- for project_server1 container -----
echo "> Nginx: Cut connection to project_server1..."
cut_reload_nginx "project_server1" "8000"
echo ""
echo "> Deploy only project_server1..."
deploy_server "project_server1" "./project_server/config/.env"
echo ""
echo "> Health Check for project_server1..."
health_check "api/v1/health-check"
echo ""
echo "> Nginx: Connect to project_server1..."
connect_reload_nginx "project_server1" "8000"
echo ""
sleep 10s
echo ""
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo ""
echo "Rolling Deploy Finish"
echo ""
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo ""
echo ""
주의 사용 시 인자(argument)의 순서를 지켜서 입력해야 한다. 본인의 nginx.conf, docker-compose.yml과 맞지 않는 내용이 있을 수도 있으므로 반드시 스크립트 코드를 확인할 것.
- cut_reload_nginx args
- server name: nginx와 연결을 끊을 서버 이름(컨테이너 명)
- server port: nginx와 연결을 끊을 서버 port
- nginx container name: docker-compose.yml에서 정의된 Nginx용 도커 컨테이너 이름
- connect_reload_nginx args
- server name: nginx와 연결할 서버 이름(컨테이너 명)
- server port: nginx와 연결할 서버 port
- nginx container name: docker-compose.yml에서 정의된 Nginx용 도커 컨테이너 이름
- deploy_server args
- container name: 배포할 컨테이너의 이름
- env file path: `.env` 파일 위치
- health_check args
- health check URL: 헬스체크용 URL
'Architecture & Tool' 카테고리의 다른 글
윈도우에 프로메테우스 설치하기 (0) | 2024.05.03 |
---|---|
블루/그린 무중단 배포 (0) | 2024.02.24 |
서버 부하 테스트 with Locust (0) | 2023.12.16 |
실시간 모니터링 도구 - Glances (0) | 2023.05.29 |
Windows 10에 Tomcat 8 설치 (0) | 2022.01.08 |