플래티어의 X2bee 솔루션은 develop 부터 production 레벨까지, 최적의 개발 환경을 위해 무중단 배포를 지원합니다.
무중단 배포
무중단 배포란 말 그대로 신규 배포시 서비스가 중단되지 않도록 배포하는 기술이다.
다시 말하면 서비스하는 application을 내리지 않고, 새로운 application을 올리는 것이다.
이와 관련해 두 가지 종류가 있다.
하나는 배포 전략에 관련된 것이다.
신규로 배포를 하고 그 pod가 정상적으로 동작을 하면 서서히 트래픽을 이 신규 pod로 흘려보내는 측면에서 보는 무중단 배포인데, 이것은 Blue/Green 배포, Canary 배포, 우리가 선택 중인 Rolling Update가 여기에 해당한다. (배포 전략에 대해 정리가 잘 된 블로그: https://onlywis.tistory.com/10)
두번째는 SSR을 지원하는 Frontend framework의 application을 배포하는 문제이다.
강조하고 싶은 것은, 이 문제는 방금 위에서 말한 배포 전략과 관련은 있지만 다른 이야기다.
(흔하게 발생되는 문제일 것 같은데 구글링해보면 누가 정리해놓은 글이 없다.)
밑에 더 자세히 설명하겠지만, 이는 FO의 정적 파일을 storage (CDN 혹은 다른 외부저장소)에서 제공해주어야하기 때문에 배포전략과 무관하다.
1. Rolling Update
Rolling update는 health check를 마친 pod를 하나씩 생성하면서 기존의 pod를 하나씩 소멸시키는 방법이다.
이 방법의 장점 : 시스템을 무중단으로 업데이트 할 수 있다.
이 방법의 단점 : 새 버전의 pod들로 트래픽이 이전되기 전까지 이전 버전과 새 버전의 pod가 동시에 존재할 수 있다.
health check 종류
헬스체크 종류에 대해서 알아보자.
공식 문서: https://kubernetes.io/docs/concepts/configuration/liveness-readiness-startup-probes/
Startup Probe
- 시작시에만 확인
- pod container가 완전히 start up (load data, cache) 되었는지 확인한다. 이 시간동안 liveness와 readiness는 체크를 잠시 중단한다. X2bee MSA의 경우 평균 2분 정도 소요된다.
Liveness Probe
- container에 deadlock이 있으면 container를 재시작할 것인지 점검하고 결정
Readiness probe
- container가 traffic을 받을 수 있는 지 체크 → readiness가 실패한다면 K8s는
svc
의 load balancing에서 연결을 끊는다. → 즉, 실패시 liveness처럼 재시작하지는 않고 연결만 해제한다.
즉, Rolling update는 3가지 health check를 마친 신규 pod가 생성되고나서, 기존 pod 하나가 제거된다.
2. Next.js static files
npm build
Next.js application에서
> npm run build
를 하게 되면 아래 그림처럼 .next
폴더가 생기게 된다.
이 폴더에는 여러가지 파일들이 존재한다.
- SSG를 위한 pre-rendered HTML
- ISR을 위한 동적으로 재생성되는 HTML 파일들
- SSR를 위해 pre-compile된 번들
- 미들웨어와 API route는
.next/server
에 저장 - RSC 번들 파일들
그리고 가장 문제가 되는 - 정적 파일들 :
.css
파일,.js
chunks, 그리고 이미지 파일들이 최적화 되어.next/static
폴더에 저장된다. (public 폴더에 있는 것도 원래는 여기 저장된다.
hashed file names
이 .next/static
폴더 안을 보면 파일 이름들이 전부 hashed 되어있다.
소스 코드 수정이 없어도 다시 npm run build
를 하게 되면 이 hashed 파일명은 또 바뀌게 된다. (hash를 위해 build timestamp도 쓰이기 때문)
파일명 해시의 이유
- 브라우저는 기본적으로 사용자에게 성능을 보강하기 위하여 pre-rendering된
.js
.css
파일을 브라우저에 저장하기 위해 서버에 요청한다. - 이 파일명이 만약 바뀌지 않는다면 브라우저는 캐시가 된 stale version의 파일들을 사용할 수 있으므로, 대부분 frontend framework에서는 이렇게 새로 생성된 파일 이름을 겹치지 않게 hashed된 이름으로 저장하는 전략을 사용하고 있다. 다시 빌드가 될 때 새로운 업데이트된 최신 파일들의 fetch를 보장하는 프레임워크의 전략이다.
_next
npm run build
를 하면.next
라는 폴더가 생기지만, 이것을 browser가 액세스할 때는_next
라는 virtual public-facing URL로 접근하도록 Next.js의 라우팅 메카니즘이 정해져있다.- 즉 browser가
_next/
로 요청을 하면 서버는.next
로 응답한다.
문제점
- AS-IS pod가 2개라고 가정할 때, (Rolling Update를 위해 MaxSurge를 50%로 설정하면) 신규 pod가 한 개씩 교체가 된다.
- 이렇게 신규 배포가 하나씩 생성될 때 기존 pod와 신규 pod가 동시에 존재하는 시간이 생김.
- 이미 로그인하고 있던 사용자는 브라우저에서는 새로운 페이지로 이동시 서버에
aaaa.js
와bbbb.css
를 요청 - 쿠버네티스의 load balancer의 역할을 하고 있는 svc는 이를 각 pod에 분산시킨다. 하나의 브라우저에서의 요청이라도 svc는
aaaa.js
는 기존 살아있는 pod에 요청,bbbb.css
는 신규 pod에 요청을 한다. - 이 때 신규pod는 다른 파일명을 가지고 있어서
bbbb.css
의 요청을 받아들일 수 없고, 이 때 백화현상이나 404에러가 브라우저에서 발생하게 된다.
위에서 보았듯이 이 문제는 단지 Blue/Green방법으로 트래픽을 조절한다고 해결되는 문제가 아니고, 기존 정적파일과 새로 생성된 정적파일을 CDN에 전부 밀어넣던지, 혹은 위에서 보이는 쿠버네티스보다 더 앞단에서 정적파일 요청은 외부 스토리지로 트래픽을 보내는 방법을 써야한다.
해결방법 1
Jenkins에서 Next.js를 빌드할 때 정적인 파일을 CDN으로 올리는 방법이 있다.
이는 Tech구씨의 글 https://x2bee.tistory.com/459 에 자세히 설명되어있다.
해결방법 2
PV(Persistent Volume), PVC(Persistent Volume Claim), 그리고 SC(Storage Class)를 사용하는 방법이 있다. 그러나 이는 CDN보다 느려서 몇 초간 오류가 나서 추천하지 않는다.
해결방법 3
이건 chatGPT도 제안하지 않은 방법이고 내가 고민해낸 방법인데, 어차피 Next.js의 pod container에서 파일들을 배포하는 것이다. 그래서 Dockerfile에서 빌드를 할 때 기존 정적인 파일들은 VM에 임시 저장을 하고, 이전 빌드에서 저장해놓은 정적인 파일들을 저장해서 배포하는 방법이 있다.
'DevOps와 Infra > DevOps 일반' 카테고리의 다른 글
K8s에 Next.js CDN 구성과 무중단 배포 전략 (3) (0) | 2025.02.18 |
---|---|
WhaTap 모니터링 적용해보기 (1) (1) | 2025.02.13 |
K8s에 Next.js CDN 구성과 무중단 배포 전략 (2) (0) | 2025.02.04 |
Kickstart를 활용한 Linux 자동설치 템플릿 작성 및 자동화 (0) | 2025.01.20 |
SMTP 메일 전송 프로토콜 확인과정 (0) | 2025.01.16 |