RDS Blue/Green으로 지원 종료 버전 올리기
데이터베이스 메이저 업그레이드는 아무도 먼저 하자고 하지 않는 일입니다. 지금 잘 돌고 있고, 올려서 좋아지는 게 눈에 보이지도 않으니까요.
그러다 표준 지원 종료 안내가 오고, 그때부터는 우리가 시점을 고르는 게 아니라 자동 업그레이드 일정이 시점을 정합니다. 그 전에 계획된 시점에 짧은 전환으로 끝내는 방법을 적어둡니다.
왜 그냥 올리면 안 되는가
RDS 콘솔에는 “엔진 버전 수정” 버튼이 있고, 그걸 누르면 제자리에서 업그레이드가 됩니다. 문제는 그 동안 인스턴스가 내려가 있다는 점입니다. 메이저 버전 업그레이드는 데이터 딕셔너리 변환까지 포함해서 데이터 크기에 비례해 시간이 걸리고, 그 시간을 미리 정확히 알 수도 없습니다.
Multi-AZ라면 낫지 않냐고 생각할 수 있는데, 메이저 업그레이드는 스탠바이도 같이 올라가야 해서 Multi-AZ가 이 중단을 막아주지 않습니다.
더 곤란한 건 되돌리기입니다. 제자리 업그레이드는 스냅샷에서 복원하는 것 외에 돌아갈 길이 없고, 복원하는 사이에 들어온 쓰기는 사라집니다. “올렸다가 문제 생기면 내리자”가 성립하지 않는 작업입니다.
Blue/Green 배포는 이 두 가지를 다르게 만듭니다.
| 제자리 업그레이드 | Blue/Green | |
|---|---|---|
| 중단 시간 | 데이터 크기에 비례, 예측 어려움 | 전환 시점의 짧은 구간 |
| 검증 | 운영 전환 후에야 가능 | 전환 전에 그린에서 |
| 롤백 | 스냅샷 복원 (쓰기 유실) | 블루가 그대로 살아 있음 |
| 비용 | 없음 | 검증 기간 동안 인스턴스 두 벌 |
구조
Blue/Green 배포를 만들면 AWS가 현재 운영 환경(블루)의 복제본을 만들고, 복제를 걸어둔 채 그 복제본(그린)의 엔진 버전을 올립니다.
Blue (운영, 현재 버전)
│ 복제
▼
Green (새 버전, 읽기 전용)
그린은 블루의 변경을 계속 따라오면서도 버전은 다른 상태로 유지됩니다. 그동안 그린에 붙어서 쿼리를 돌려보고, 성능을 재보고, 애플리케이션을 붙여볼 수 있습니다.
전환(switchover)을 실행하면 엔드포인트 이름이 서로 바뀝니다. 애플리케이션이 쓰던 엔드포인트가 그린을 가리키게 되고, 블루는 다른 이름으로 남습니다. 접속 문자열을 고칠 필요가 없다는 뜻이고, 이게 이 방식의 핵심입니다.
사전 조건
바이너리 로그
복제로 동기화하는 구조라 바이너리 로그가 켜져 있어야 합니다.
SHOW VARIABLES LIKE 'log_bin'; -- ON 이어야 함
SHOW VARIABLES LIKE 'binlog_format'; -- ROW 권장
RDS MySQL에서는 자동 백업이 켜져 있어야 바이너리 로그가 활성화됩니다. Aurora MySQL은 클러스터 파라미터 그룹에서 binlog_format을 설정합니다.
이 값을 바꾸면 재부팅이 필요하니, 업그레이드 당일이 아니라 미리 해두는 편이 낫습니다. 여기서 이미 한 번 내려갔다 오면 무중단이라는 말이 무색해집니다.
파라미터 그룹
메이저 버전이 바뀌면 파라미터 그룹 패밀리도 바뀝니다. 기존 그룹에서 기본값과 다르게 설정한 항목을 뽑아 새 패밀리에 옮겨 적어야 합니다.
aws rds describe-db-parameters \
--db-parameter-group-name "$OLD_PG" \
--source user \
--query 'Parameters[].{Name:ParameterName,Value:ParameterValue}' \
--output table
--source user가 중요합니다. 이걸 빼면 수백 개가 쏟아져서 뭘 직접 바꿨는지 알 수 없습니다.
새 버전에서 없어졌거나 이름이 바뀐 파라미터는 여기서 걸러집니다.
호환성
메이저 업그레이드에서 실제로 문제를 일으키는 것들입니다.
- 예약어가 늘어나 컬럼명·테이블명이 충돌하는 경우
- 기본 문자셋/콜레이션 변경 (
utf8mb4_general_ci→utf8mb4_0900_ai_ci등). 정렬 순서와 비교 결과가 달라지고, 서로 다른 콜레이션끼리 조인하면 인덱스를 못 씁니다. sql_mode기본값 변경으로 기존에 통과하던INSERT가 거부되는 경우- 제거된 함수·구문
버전별 변경점은 엔진 공식 문서의 업그레이드 노트에 정리돼 있으니 반드시 훑어보세요. 여기서 걸러내지 못한 건 전환 후에 애플리케이션 오류로 나타납니다.
호환성을 미리 확인하는 쿼리
업그레이드 노트를 읽는 것만으로는 “우리 스키마에 해당되는가”를 알 수 없습니다. 몇 가지는 직접 조회해서 확인할 수 있습니다.
가장 먼저 콜레이션이 섞여 있는지 봅니다. 메이저 업그레이드에서 기본 콜레이션이 바뀌면, 새로 만드는 테이블과 기존 테이블의 콜레이션이 달라집니다. 서로 다른 콜레이션 컬럼을 조인하면 MySQL이 한쪽을 변환하느라 인덱스를 쓰지 못하고, 잘 돌던 쿼리가 갑자기 풀 스캔이 됩니다. 업그레이드 직후 특정 API만 느려진다면 여기를 먼저 봐야 합니다.
-- 스키마 안에 콜레이션이 몇 종류나 섞여 있는지
SELECT TABLE_COLLATION, COUNT(*) AS tables
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
GROUP BY TABLE_COLLATION;
-- 문자열 컬럼 단위로 예외를 찾는다
SELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND COLLATION_NAME IS NOT NULL
AND COLLATION_NAME <> 'utf8mb4_0900_ai_ci'
ORDER BY TABLE_NAME;
한 종류로 통일돼 있다면 신경 쓸 게 없고, 여러 종류가 나오면 업그레이드 전에 정리하는 편이 낫습니다.
전환 후에 ALTER TABLE ... CONVERT TO CHARACTER SET을 돌리면 큰 테이블에서는 그 자체가 또 하나의 작업이 됩니다.
그다음은 예약어입니다. 새 버전에서 추가된 예약어가 기존 컬럼명과 겹치면, 백틱 없이 그 이름을 쓰던 쿼리가 문법 오류를 냅니다. 애플리케이션 코드가 ORM으로 항상 인용부호를 붙인다면 문제가 없지만, 손으로 쓴 SQL이나 리포트 쿼리가 섞여 있으면 거기서 터집니다. 버전별 예약어 목록을 받아 컬럼명·테이블명과 대조해두면 사전에 걸러집니다.
마지막은 sql_mode입니다. 기본값이 엄격해지면서, 예전에는 경고로 넘어가던 값이 오류가 되는 경우가 있습니다.
날짜 컬럼에 0000-00-00이 들어 있거나, 컬럼 길이를 넘는 문자열을 잘라서 저장하던 코드가 대표적입니다.
그린 엔드포인트에 스테이징을 붙여 쓰기 경로까지 한 번 돌려보면 대부분 여기서 드러납니다. 그린 자체는 읽기 전용이라, 이 검증은 전환 리허설을 별도 복제본으로 한 번 더 하는 방법이 확실합니다.
그린 생성과 검증
aws rds create-blue-green-deployment \
--blue-green-deployment-name "prod-mysql-upgrade" \
--source "arn:aws:rds:ap-northeast-2:<account-id>:db:prod-mysql" \
--target-engine-version "8.0.39" \
--target-db-parameter-group-name "prod-mysql80"
생성이 끝나면 그린 인스턴스에 붙을 수 있는 별도 엔드포인트가 생깁니다. 여기서 시간을 충분히 씁니다. 이 검증 구간이 이 방식으로 얻는 가장 큰 이득입니다.
확인할 것은 세 가지입니다.
첫째, 실행 계획이 바뀌지 않았는지. 옵티마이저는 마이너 버전에서도 동작이 달라집니다. 느려지면 곤란한 쿼리 몇 개를 골라 양쪽에서 EXPLAIN을 떠서 비교합니다.
-- 블루와 그린 각각에서 실행해 비교
EXPLAIN FORMAT=JSON
SELECT ... ;
둘째, 애플리케이션을 실제로 붙여보기. 스테이징의 접속 대상을 그린 엔드포인트로 돌려놓고 통합 테스트를 돌리는 게 가장 확실합니다. 그린은 읽기 전용이라 쓰기 경로는 여기서 검증되지 않는다는 점만 유의하세요.
셋째, 복제 지연. 전환은 지연이 0에 가까울 때 해야 짧게 끝납니다.
aws rds describe-blue-green-deployments \
--blue-green-deployment-identifier "$BG_ID" \
--query 'BlueGreenDeployments[0].{Status:Status,Tasks:Tasks}'
지연이 계속 큰 상태로 유지된다면 그린 인스턴스 사양이 블루보다 작거나, 블루의 쓰기량이 그린이 따라올 수 있는 수준을 넘는 경우입니다. 전자는 사양을 키우면 되고, 후자라면 트래픽이 적은 시간대를 골라야 합니다.
전환 시점에 실제로 일어나는 일
전환을 실행하면 대략 이 순서로 진행됩니다.
- 블루에 대한 쓰기가 잠시 차단됩니다.
- 그린이 남은 복제를 따라잡습니다.
- 엔드포인트 이름이 서로 교체됩니다.
- 쓰기 차단이 풀립니다.
즉 중단이 0은 아닙니다. 짧지만 존재하는 구간이고, 그 사이 애플리케이션에서는 커넥션 오류나 타임아웃으로 나타납니다. “무중단”이라는 말보다 “짧고 예측 가능한 중단”이라고 이해하는 편이 정확합니다.
그래서 애플리케이션 쪽 준비가 필요합니다.
여기서 준비가 덜 되면 짧은 중단이 긴 장애가 됩니다. 엔드포인트가 가리키는 대상이 바뀌면 기존 커넥션은 전부 무효가 되는데, 풀이 이걸 감지하지 못하고 붙들고 있으면 전환이 끝난 뒤에도 요청이 계속 실패합니다.
// 예: mysql2 풀 설정
const pool = mysql.createPool({
host: process.env.DB_HOST,
connectionLimit: 20,
maxIdle: 10,
idleTimeout: 30_000, // 유휴 커넥션을 오래 붙들지 않는다
enableKeepAlive: true,
keepAliveInitialDelay: 10_000,
});
짧은 재시도도 넣어둡니다. 전환 구간에 들어온 요청은 몇 초 뒤 재시도하면 대부분 성공합니다. 다만 재시도는 읽기와 멱등한 쓰기에만 걸어야 합니다. 결제 생성 같은 요청을 무턱대고 재시도하면 중복이 생깁니다.
DNS 캐시도 확인합니다. 엔드포인트는 DNS 이름이라 애플리케이션이나 런타임이 결과를 오래 캐시하면 옛 주소를 계속 씁니다. JVM처럼 기본 캐시 정책이 공격적인 환경이라면 TTL 설정을 미리 확인해두세요.
전환은 타임아웃을 지정해 실행합니다.
aws rds switchover-blue-green-deployment \
--blue-green-deployment-identifier "$BG_ID" \
--switchover-timeout 300
이 시간 안에 복제가 따라잡지 못하면 전환은 취소되고 블루가 그대로 운영을 이어갑니다. 반쯤 전환된 상태로 남지 않는다는 뜻이라, 여기서는 실패해도 안전합니다.
되돌릴 수 있는 지점과 없는 지점
전환 직후 블루는 지워지지 않고 이전 버전 그대로 남아 있습니다. 이게 롤백 수단입니다. 문제가 발견되면 애플리케이션을 블루 쪽 엔드포인트로 돌리면 됩니다.
다만 되돌리는 순간 전환 이후에 그린에 들어온 쓰기는 사라집니다. 전환이 끝나면 복제는 끊기고, 블루는 전환 시점에서 멈춘 상태이기 때문입니다.
그래서 실질적인 판단 기준은 이렇게 됩니다.
- 전환 직후 몇 분 안: 유실될 쓰기가 적으니 되돌리는 비용이 낮습니다. 이 구간에 집중해서 봐야 합니다.
- 몇 시간 뒤: 되돌리면 그 사이 데이터가 날아갑니다. 사실상 앞으로 고치는 수밖에 없습니다.
전환 직후에 볼 지표를 미리 정해두면 판단이 빨라집니다.
- 애플리케이션의 DB 관련 에러율
- 느린 쿼리 발생 건수와 상위 쿼리 목록
- 커넥션 수와 대기 시간
- CPU와 버퍼 풀 히트율
특히 버퍼 풀은 새로 시작합니다. 그린은 방금 뜬 인스턴스라 캐시가 비어 있어서, 전환 직후 잠시 디스크 읽기가 늘고 지연 시간이 올라갑니다. 이건 정상이고 시간이 지나면 가라앉습니다. 이 구간을 장애로 오인해 성급하게 롤백하지 않도록 미리 팀에 공유해두면 좋습니다.
남은 정리
전환이 끝났다고 작업이 끝난 게 아닙니다. 며칠에 걸쳐 정리할 것이 남습니다.
블루는 바로 지우지 않습니다. 롤백이 필요 없다는 게 확인될 때까지 두었다가 삭제합니다. 그동안 인스턴스 두 벌 비용이 나가지만, 며칠치 비용과 되돌릴 수단을 잃는 위험을 견주면 대개 남는 장사입니다.
Blue/Green 배포 리소스 자체도 지워야 합니다. 삭제할 때 블루 인스턴스를 같이 지울지 선택하게 되어 있으니, 남길지 여부를 의식적으로 고르세요.
aws rds delete-blue-green-deployment \
--blue-green-deployment-identifier "$BG_ID" \
--delete-target false # 그린(현재 운영)은 당연히 남긴다
딸려 있던 것들이 그린에 그대로 따라왔는지도 봐야 합니다. 리드 리플리카, 이벤트 구독, 파라미터 그룹, 백업 보존 기간, 성능 개선 도우미 설정 같은 것들입니다. 특히 리드 리플리카는 구성에 따라 다시 만들어야 할 수 있는데, 이걸 놓치면 리플리카를 보던 배치나 대시보드가 조용히 옛 데이터를 봅니다.
마지막으로 통계를 갱신합니다. 새 버전에서 옵티마이저 통계가 초기 상태면 실행 계획이 잠시 이상해질 수 있어서, 주요 테이블 통계를 다시 만들어주면 안정화가 빨라집니다.
ANALYZE TABLE orders, payments, users;