본문 바로가기

Programmer/PostgreSQL

AWS Aurora PostgreSQL 14 를 17.7 LTS 로 업그레이드 하기 #1

반응형

AWS에서 "Aurora PostgreSQL 14 표준 지원이 2027년 2월 28일에 끝난다"는 안내를 받았습니다. 그 뒤로는 RDS Extended Support 요금이 붙고 올리지 않은 클러스터는 유지보수 창에서 AWS가 강제로 올립니다. 올려야 한다는 건 정해졌고 문제는 어느 버전으로, 앱 코드를 얼마나 고치고, 데이터를 어떻게 옮기며, 서비스를 얼마나 세울 것인가였습니다.

이 글은 그 판단 과정을 근거와 함께 정리한 1부입니다. 실제 업그레이드를 마친 뒤 실측 다운타임과 겪은 문제를 2부로 씁니다. 수치와 절차는 AWS 공식 문서와 우리 운영 DB에서 직접 확인한 것만 적었고, 회사 고유 정보는 일반화했습니다.

📌 한 줄 요약
17.7 LTS로 14에서 바로 올린다 · 앱 코드 필수 변경 0건 · 데이터는 in-place pg_upgrade · 운영 전환은 Blue/Green으로 단절 두 번(각 1분 안팎) · 전환 전 행 수·해시 diff로 데이터 동일성을 직접 확인한다.

1. 어느 버전으로 올릴까: LTS를 고른 이유

후보는 15, 16, 17, 18이었습니다. 기준은 하나였습니다. 다음 메이저 업그레이드까지 얼마나 오래 버티는가. Aurora는 특정 마이너 버전을 LTS(long-term support)로 지정해 표준 지원을 길게 주는데, LTS가 아닌 최신 마이너는 표준 지원이 1년 남짓이라 금방 또 올려야 합니다.

후보 Aurora 표준 지원 종료 판단
15.10 LTS 커뮤니티 EOL 2027-11 1년 뒤 또 메이저 업그레이드
16.8 LTS 커뮤니티 EOL 2028-11 17보다 1년 손해
17.7 LTS채택 2030-02-28 채택
17.10 (최신 마이너) 2027-12-31 LTS가 아니라 1년 안에 마이너 업그레이드 필요
18.x 2027-11~12 LTS 미지정, 우리 드라이버 스택과 조합 사례 부족

14에서 17로 바로 올릴 수 있습니다. Aurora는 두 단계 건너뛰는 메이저 업그레이드 경로를 제공하므로 15, 16을 거칠 이유가 없습니다. 콘솔의 "수정 → 엔진 버전" 목록이나 아래 명령으로 자기 버전의 타깃 목록을 확인하면 됩니다.

aws rds describe-db-engine-versions --engine aurora-postgresql --engine-version 14.20 \
  --query 'DBEngineVersions[0].ValidUpgradeTarget[?IsMajorVersionUpgrade].EngineVersion'

2. 앱 코드는 얼마나 바뀌나: 점검 방법

결론부터 쓰면 필수 변경은 0건이었습니다. 하지만 "아마 괜찮을 것"이 아니라 아래 네 가지를 확인한 결과입니다. 같은 순서로 보면 어느 스택이든 반나절이면 끝납니다.

  1. 드라이버. JDBC, R2DBC, psycopg, node-postgres는 모두 PostgreSQL wire protocol v3를 쓰고, 이 프로토콜은 메이저 버전이 올라가도 바뀌지 않습니다. 인증 방식만 확인합니다. password_encryption이 이미 scram-sha-256이면 손댈 것이 없습니다.
  2. 마이그레이션 도구. Flyway나 Liquibase는 자기가 아는 것보다 높은 DB 버전을 만나면 어떻게 하는지 봐야 합니다. Flyway 8.x는 "newer than this version of Flyway and support has not been tested" 경고만 남기고 정상 동작합니다. 라이브러리 jar 안의 문자열로 확인했습니다. 실패가 아니라 경고입니다.
  3. PG 전용 SQL 기능. 코드베이스에서 jsonb, ON CONFLICT, RETURNING, LATERAL, :: 캐스트, interval, pg_trgm 같은 기능을 grep으로 세어 목록을 만들고 각각이 15·16·17 릴리즈 노트의 "Migration to Version N" 절에 걸리는지 대조합니다. 우리가 쓰는 기능은 전부 변경이 없었습니다.
  4. 비호환 목록 대조. 실제로 걸릴 뻔한 항목은 셋이었습니다. 15의 public 스키마 CREATE 권한 제거는 새로 만드는 DB에만 적용되고 업그레이드는 기존 권한을 유지합니다. 16의 plpgsql 바운드 커서 이름 규칙 변경은 우리 함수가 커서를 안 써서 무관했습니다. 17의 old_snapshot·adminpack 확장 제거는 둘 다 미사용이었습니다.

필수는 아니지만 권장한 것이 둘 있습니다. 테스트에서 쓰는 Testcontainers의 PostgreSQL 이미지 태그를 올릴 버전으로 고정해서 전체 테스트를 한 번 돌리는 것, 그리고 테라폼의 엔진 버전과 파라미터 그룹 family를 실제와 맞추는 것입니다. 테라폼이 운영과 어긋나 있으면 업그레이드 뒤 apply가 엉뚱한 변경을 만듭니다.

3. 데이터는 어떻게 넘어가나

Aurora 메이저 업그레이드는 스토리지를 그대로 두고 카탈로그만 변환하는 pg_upgrade 방식입니다. 데이터를 복사하거나 덤프·복원하지 않습니다. 대신 pg_upgrade가 거부하는 조건이 있어서 미리 점검합니다. 전부 읽기 전용 쿼리입니다.

-- reg* 타입 컬럼 (regtype·regclass 제외) — 있으면 업그레이드 불가
SELECT n.nspname, c.relname, a.attname
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_type t ON t.oid = a.atttypid
WHERE t.typname IN ('regproc','regprocedure','regoper','regoperator','regconfig','regdictionary')
  AND c.relkind IN ('r','m') AND a.attnum > 0 AND NOT a.attisdropped
  AND n.nspname NOT IN ('pg_catalog','information_schema');

SELECT count(*) FROM pg_prepared_xacts;                     -- prepared transaction
SELECT datname FROM pg_database WHERE datconnlimit = -2;   -- 삭제 중 끊긴 DB
SELECT count(*) FROM pg_replication_slots;                  -- 논리 복제 슬롯
SELECT extname, extversion FROM pg_extension ORDER BY 1;    -- 확장 → 대상 버전 지원표와 대조

우리는 전부 0건이었고 확장 6개도 모두 17에서 지원됐습니다. 그런데 차단 조건이 없다고 끝이 아닙니다. 업그레이드 뒤에 손봐야 하는 것이 셋 있습니다.

  • 옵티마이저 통계 소실. pg_upgrade는 pg_statistic을 옮기지 않습니다. 업그레이드 직후 모든 테이블이 통계 없는 상태라 큰 테이블의 실행 계획이 무너질 수 있습니다. vacuumdb --analyze-in-stages를 바로 돌려야 합니다.
  • 콜레이션 인덱스. glibc 기반 콜레이션은 OS 라이브러리 버전이 바뀌면 정렬 규칙이 바뀔 수 있고, 그러면 텍스트 컬럼 btree 인덱스가 어긋납니다. 15 이상은 이때 "collation version mismatch" 경고를 냅니다. 텍스트 인덱스 크기를 미리 재 두고(우리는 수 GB) 경고가 나면 REINDEX 합니다.
  • 확장 버전. 엔진이 올라가도 확장은 자동으로 올라가지 않습니다. ALTER EXTENSION ... UPDATE를 돌립니다.

4. 왜 "reader만 먼저" 올릴 수 없나

여기서 처음 막혔습니다. 운영은 writer 하나에 reader 하나인 흔한 구조였고, 자연스럽게 "reader를 17로 만들어 승격하면 되지 않나"라고 생각했습니다. 안 됩니다. Aurora의 writer와 reader는 하나의 공유 스토리지를 같이 쓰는 한 클러스터라서 엔진 버전이 클러스터 단위로만 바뀝니다.

⚠️ 왜 in-place가 아닌가
클러스터를 통째로 in-place 업그레이드하면 writer와 reader가 동시에 멈추고 10~30분 뒤에 함께 올라옵니다. 그 뒤엔 통계가 없는 상태로 서비스가 열립니다.

그래서 선택지는 둘입니다. 클러스터를 통째로 in-place 업그레이드하면 writer와 reader가 동시에 멈추고 10~30분 뒤에 함께 올라옵니다. 이걸 피하는 관리형 방법이 Blue/Green 배포입니다.

5. Blue/Green: 버전이 달라도 데이터가 따라가는 원리

Blue/Green은 AWS 콘솔의 클러스터 "작업 → Blue/Green 배포 생성"으로 만드는 정식 기능입니다. 현재 클러스터(Blue)의 토폴로지 전체를 복사해 새 버전의 쌍둥이 클러스터(Green)를 만들고 준비가 끝나면 엔드포인트 이름만 바꿔 끼웁니다. 우리 버전(14.20)이 지원 범위(14.9 이상)에 들어가는지는 AWS의 지원 버전 표에서 확인했습니다.

핵심은 논리 복제입니다. writer와 reader의 물리 복제는 같은 버전끼리만 되지만, 논리 복제는 WAL을 "어느 테이블에 어떤 행이 INSERT/UPDATE/DELETE 됐다"라는 행 단위 변경으로 풀어서 보내고 받는 쪽이 자기 버전의 SQL로 다시 실행합니다. 그래서 14와 17 사이에서 동작합니다. PostgreSQL 커뮤니티가 "무중단에 가까운 메이저 업그레이드" 방법으로 안내하는 것도 이 논리 복제이고, Blue/Green은 그 절차를 AWS가 자동화한 것입니다.

Blue (운영, 엔진 14) writerreader스토리지 Green (검증용, 엔진 17) writerreader스토리지 (복제본) 논리 복제서비스 트래픽은 전환 전까지 Blue로만 간다. Green에서는 ANALYZE·REINDEX·확장 갱신·스모크 테스트를 미리 끝낸다. 전환 시 클러스터·reader 엔드포인트 이름을 Green이 이어받는다. 앱 설정 변경 없음. Blue는 이름 뒤에 -old1이 붙어 남는다 (되돌리기용).

전환(switchover)은 AWS 문서 Switching a blue/green deployment의 "Switchover actions" 절 그대로 다섯 단계입니다.

💡 데이터 무손실을 지키는 장치 = 가드레일
전환 시작 전에 AWS가 Green의 복제 상태·복제 지연·활성 쓰기·외부 복제 여부를 검사하고, 하나라도 걸리면 전환을 시작하지 않습니다. 문서는 이 검사의 목적을 "예상보다 긴 다운타임과 데이터 손실 방지"라고 적습니다.

논리 복제라서 생기는 제한

이 부분이 실제 운영 규칙이 됩니다. Limitations and considerations에서 그대로 가져왔습니다.

  • DDL은 복제되지 않습니다. Blue에서 CREATE TABLE 같은 스키마 변경이 감지되면 Green이 "Replication degraded" 상태가 되고 배포를 지우고 다시 만들어야 합니다. Green을 만든 뒤 전환까지는 마이그레이션이 포함된 배포를 멈춥니다.
  • GRANT/REVOKE도 복제되지 않습니다(경고만).
  • 시퀀스의 NEXTVAL은 실시간으로 안 맞고, 전환 시 AWS가 Green 시퀀스를 앞으로 맞춥니다.
  • PK(또는 replica identity)가 없는 테이블은 UPDATE/DELETE를 옮기지 못합니다. 아래 쿼리로 미리 찾아 PK를 붙이거나 REPLICA IDENTITY FULL로 바꿉니다. 우리는 6개가 나왔고 4개는 백업 테이블이라 지웠습니다.
SELECT n.nspname||'.'||c.relname, c.relreplident, pg_size_pretty(pg_total_relation_size(c.oid))
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname NOT LIKE 'pg_%' AND n.nspname <> 'information_schema'
  AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE i.indrelid = c.oid AND i.indisprimary)
ORDER BY pg_total_relation_size(c.oid) DESC;

사전 조건과 "단절 두 번"

Creating a blue/green deployment의 PostgreSQL 준비 절에 따르면 커스텀 클러스터 파라미터 그룹에서 rds.logical_replication = 1, synchronous_commit = on이어야 하고 파라미터가 writer에 반영돼 있지 않으면 생성이 실패합니다. 이 파라미터는 static이라 writer 재기동이 필요합니다. 그래서 단절은 두 번입니다. 파라미터 반영을 위한 재기동 한 번(미리, 한가한 시간에), 전환 한 번(검증이 끝난 뒤). 각각 1분 안팎입니다. 우리는 전환을 새벽에 하기로 했고 그 1분 사이의 쓰기 요청은 앱 재시도 없이 실패를 감수하기로 했습니다. 새벽 쓰기는 거의 없기 때문입니다.

 

6. 데이터가 깨지지 않았는지 직접 확인하기

가드레일은 "복제가 따라잡았는가"를 보장하지만 "따라잡은 내용이 같은가"까지 보여주지는 않습니다. 그래서 전환 전에 Blue와 Green에서 같은 시각에 아래를 돌려 diff 합니다. 틀리면 안 되는 테이블(결제·정산 같은 것)은 행 수와 함께 내용 해시를 비교합니다.

-- 1) 테이블별 행 수
SELECT n.nspname||'.'||c.relname AS tbl,
       (xpath('/row/c/text()', query_to_xml(format('SELECT count(*) AS c FROM %I.%I', n.nspname, c.relname), false, true, '')))[1]::text::bigint AS rows
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname NOT LIKE 'pg_%' AND n.nspname <> 'information_schema'
ORDER BY 1;

-- 2) 핵심 테이블 내용 해시 (예시)
SELECT md5(string_agg(t::text, '|' ORDER BY id)) FROM payments t;

-- 3) 시퀀스 현재값 (전환 후 Green이 Blue 이상이어야 함)
SELECT schemaname||'.'||sequencename, last_value FROM pg_sequences ORDER BY 1;

전환 후에는 시퀀스 값이 Blue의 마지막 값 이상인지, 복제 슬롯·subscription 잔여물이 없는지, 그리고 앱에서 로그인·읽기·쓰기 1건씩의 실사용을 확인합니다.

7. 되돌리기

시점 되돌리기
전환 전 Green을 지우면 끝. Blue는 아무 영향 없음
전환 후 Blue가 -old1로 남아 있으므로 앱 엔드포인트를 Blue로 돌리거나 Blue를 재승격. 단, 전환 뒤 Green에 쓰인 데이터는 Blue에 없음
in-place를 택했을 때 업그레이드 직전 자동 스냅샷에서 복원. 표준 지원 종료 뒤 14 스냅샷을 복원하면 Extended Support 버전으로 변환되고 과금됨

8. 체크리스트

단계 할 일 근거
버전 LTS 마이너 선택, 직접 업그레이드 경로 확인 Aurora 릴리즈 캘린더
코드 드라이버·마이그레이션 도구 동작 확인, SQL 기능 목록 ↔ 릴리즈 노트 대조, 테스트 이미지 버전 고정 PostgreSQL 15/16/17 릴리즈 노트
데이터 pg_upgrade 차단 조건 0건 확인, 확장 지원표 대조 Aurora 메이저 업그레이드 문서
Blue/Green 준비 logical_replication 켜고 writer 재기동, PK 없는 테이블 정리, 기존 복제 슬롯 없음 확인 Blue/Green 생성·제한 문서
Green 검증 확장 UPDATE, ANALYZE, 콜레이션 확인·REINDEX, 행 수·해시 diff 본문 3·6절
전환 DDL 배포 동결 유지, 저트래픽 시간, 가드레일 통과 확인 Blue/Green 전환 문서
사후 시퀀스·잔여물·실사용 확인, Blue 보관 후 삭제 본문 6·7절

마치며

"버전이 다른데 데이터가 실시간으로 같이 유지된다"는 말이 처음엔 믿기지 않았습니다. 논리 복제가 행 단위 변경을 보내는 것이라는 점, 그리고 전환 직전에 AWS가 복제 상태를 검사해 실패하면 아예 시작하지 않는다는 점을 문서에서 확인하고 나서야 납득이 됐습니다. 2부에서는 실제 전환의 실측 단절 시간과, 계획과 달랐던 것들을 씁니다.

반응형