티스토리 뷰
DB 마이그레이션을 진행하며 동시에 데이터의 일부를 개발 서버의 인메모리로 데이터를 관리할 일이 생겼다.
DB와 웹 서버의 변경이 동시에 일어나야 하는 상황에서 다운타임이 없는 무중단 배포를 하는 것은 많이 까다롭다.
변경된 데이터를 다루는 API들에 대해서 정교하게 요청을 구분하고 프록시 서버로 라우팅을 해줘야 한다.
다행히(?) 변경하려는 서버는 운영 서버가 아닌 개발 서버이고, 웹서버도 한 대만 올라간 상태여서 비교적 간단한 중단 배포를 하기로 결정했다.
이 예시는 Jenkins, MySQL과 SpringBoot 서버 환경에서 진행되었으며 예약 명령어들을 사용해 밤 시간에 DB 및 웹 서버를 안전하게 예약하여 재배포하는 방법을 다룬다.
DB 스키마 변경 예약하기
DB의 스키마 변경은 MySQL의 이벤트 스케줄러를 사용한다.
다음과 같이 event_scheduler 사용에 대한 전역 설정이 켜져있음을 확인한다.
꺼져있다면 다음의 명령어로 설정을 켠다.
SET GLOBAL event_scheduler = ON ;
mysql> show variables like 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.00 sec)
아래와 같이 원하는 시간을 지정하여 이벤트를 생성한다.
BEGIN ~ END 사이에 원하는 내용을 작성한다.
구분자와 자동 커밋 설정에 유의하며 주석이 적힌 부분에 실행을 원하는 스크립트를 작성한다.
# 구분자를 ;(세미콜론)에서 &&로 바꿔 두어 지정할 이벤트 스크립트 내부의
# 세미콜론을 이벤트의 종료로 인식하지 않도록 한다.
delimiter &&
create event if not exists new_event
ON SCHEDULE AT '2023-09-01 04:00:00'
DO
BEGIN
set autocommit = false;
# SQL
commit;
set autocommit = true;
END &&
delimiter ;
정상적으로 설정되었다면 아래의 테이블에서 예약된 이벤트를 볼 수 있다.
SELECT * FROM information_schema.events;
dump로 만든 별도의 DB에서 마이그레이션 스크립트를 꼼꼼히 검증해본 뒤 예약을 걸자.
Jenkins로 웹 서버 배포 예약하기
DB 스키마가 변경되면 기존의 동작 중인 웹 서버 또한 정상적인 동작을 할 수 없으므로 그에 맞게 수정된 버전을 같은 시간에 배포해야 한다. 현재는 CD에 Jenkins를 사용 중이며, Jenkins는 예약 배포 기능 또한 포함한다.
자신이 실행할 빌드 파이프라인의 구성 -> Build Triggers로 이동한다.
Build Periodically 부분에 자신이 원하는 빌드 시간을 크론 표현식 을 사용하여 명시한다.
이를 사용하면 지정된 시간 혹은 주기적인 자동 빌드가 가능하며 위와 같이 예약 작업을 한 번만 수행하도록 할 수도 있다.
기재한 0 4 1 9 5의 의미는 다음과 같다
0: 0분
4: 4시
1: 1일
9: 9월
5: 5번째 요일 == 금요일 (0과 7은 일요일을 의미)
즉 9월 1일 4시 0분에 작업이 수행된다.
Nginx로 작업 중 요청 막아두기
기존의 Jenkins를 통해 웹 서버가 배포되는 데 걸리는 시간은 2분 남짓,
개발 서버의 데이터 양이 크지 않아 스키마 변경이 이뤄지는 시간은 그보다 짧았다.
웹 서버와 스키마 변경이 이뤄지는 시간의 차이가 있고 그 시간을 경험적으로 예측 정도만 가능하다.
경우에 따라 웹 서버가 DB보다 먼저 배포되어 운영되는 경우 아직 스키마가 변경되지 않은 DB에 요청을 날리며 런타임 예외가 계속 발생하는 끔찍한 일이 벌어질 수 있다.
이를 방지하고자 MySQL의 이벤트 스케줄, 그리고 Jenkins의 재배포가 수행되기 앞 뒤로 Reverse Proxy 서버인 Nginx가 웹 서버로의 요청을 충분한 시간 동안 막아두어야 한다.
중단 시작과 끝에 사용할 스크립트 두 개 만들기
요청을 막는 방법은 무중단 배포 방식인 Blue Green 배포를 할 때와 유사한 방식으로 Nginx를 사용하면 된다.
Nginx는 /etc/nginx/conf.d 의 설정 파일을 기준으로 동작한다.
점검이 시작되면 Nginx가 참조할 설정 파일을 웹서버 요청을 막는 버전으로 교체해야 한다.
이후 점검이 끝나면 다시 웹서버 요청을 허용하는 버전으로 교체해야 한다.
점검 시작
먼저 점검 시작 시점에 사용할 스크립트(. sh) 파일을 만든다.
최대한 간단한 방법으로, /nginx에 /script라는 폴더를 만들고 스크립트가 실행 시에
- 운영 설정 파일을 다른 폴더인 /script로 옮긴다
- 점검 중 설정 파일을 Nginx가 사용하는 설정 폴더 위치인 /nginx로 옮긴다
- Nginx를 리로드한다.
#!/bin/bash
mv /etc/nginx/conf.d/(운엉 셜정).conf /etc/nginx/script
mv /etc/nginx/script/(점검 중 설정).conf /etc/nginx/conf.d
nginx -s reload
이때 Nginx를 다시 켜는 게 아닌 reload를 하여 Nginx가 아예 중단되는 시점이 없도록 한다!
reload를 하면 설정 파일이 변경되는 동안에도 요청을 받을 수 있으며, 설정 파일에 문법적 오류가 있어도 Nginx가 다운되지 않고 기존의 설정 기반으로 운영된다.
점검 중 설정 파일은 아래와 같다.
임시로 사용할 정적 문서를 만들어 보여주도록 하였다.
점검 종료
점검 종료 시에는 다시 기존의 운영 설정 파일을 돌려놓고 점검 설정 파일을 외부로 빼는 작업을 수행한다.
#!/bin/bash
mv /etc/nginx/conf.d/(점검 중 설정).conf /etc/nginx/script
mv /etc/nginx/script/(운영 설정).conf /etc/nginx/conf.d
nginx -s reload
같은 방법을 사용, 운영 설정을 돌려놓은 뒤 reload를 한다.
작업 예약
실행 파일을 두 개 만들었으니 이제 점검 시작 시의 스크립트와 종료 시의 스크립트 두 개의 실행 예약을 걸어야 한다.
유닉스 계열에서 공통적으로 제공하는 cron을 사용하여 스케줄을 설정한다.
명령어는 Jenkins 작업 예약과 같은 크론 표현식을 사용한다.
sudo crontab -e
cron의 작업 예약을 할 때는 Nginx폴더 접근 및 설정 변경 등에 관리자 권한이 필요하므로 sudo 명령어를 사용해야 함에 유의하자.
내부의 예약 스크립트에는 sudo를 따로 명시하지 않아도 된다.
점검 작업이 발생하는 시간 앞 뒤로 Nginx 설정 변경이 일어나도록 했다.
서버 재배포 및 스키마 변경 시간에 맞게 짧은 다운타임을 설정하자.
만약 지정한 작업이 정상적으로 수행되었는지 로그를 보고 싶다면 아래와 같이 전체 syslog에서 작업 기록을 볼 수 있다.
cat /var/log/syslog | grep CRON
grep은 대소문자를 구분함에 유의하자.
위와 같은 설정을 통해 DB 스키마 변경 및 웹 서버 재배포,
그리고 해당 작업이 일어나기 전 Nginx를 통해 적절한 다운타임을 주어 변경 도중이 데이터 정합성 문제의 발생을 방지하였다.
정상적으로 사용자가 적은 새벽 시간에 점검이 수행되었다.
이후 웹 서버만 버전 업하는 경우 매우 유사한 방법으로 Blue Green 배포를 진행할 예정이다.
로컬에서 연습을 여러 번 진행한 뒤 사용하길 권장한다.
'하루스터디' 카테고리의 다른 글
인증 관련 플로우 및 기능 점검 (0) | 2023.10.14 |
---|---|
우리 서버는 어느 정도의 부하를 견딜 수 있을까 - 부하 테스트 계획 & 실행 (1) | 2023.10.10 |
SpringBoot Application과 Grafana 기반의 Metric & Log 모니터링 (4) | 2023.08.13 |
언제 JPA를 통해 슈퍼/서브타입을 사용해야 할까? (0) | 2023.07.30 |
RDB에 JPA로 변경 가능성이 높은 데이터를 JSON으로 저장하기 (0) | 2023.07.16 |
- Total
- Today
- Yesterday
- 함수형 인터페이스
- MySQL
- MySQL 이벤트 스케줄
- Spring
- GitHub Discussion Template
- 스프링
- Payload 암호화
- springboottest
- RandomPort
- multiplebagsfetchexception
- Spring Boot Monitoring
- 생성자 주입
- 우테코
- GitHub Discussion 템플릿
- stubbing
- 의존성 주입
- 우테코 프리코스
- invokedynamic
- 우테코 5기
- Spring 테스트
- Fromtail
- JPA
- logback-spring.xml
- 람다식
- Jenkins 예약 배포
- java switch case
- 자바
- Java
- JPA JSON
- GitHub Discussion
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |