Nextjs 로 만든 서비스를 k8s를 사용하여 잘 배포하고 있었다. k8s는 replica 3 으로 세팅하여 pod 3개를 사용하고 있었고 재배포를 하면 RollingUpdate 방식으로 pod 이 25% 씩 꺼지고 생성 되도록 되어있었다.
그런데 어느 순간부터 재배포 도중에 우리 서비스 사이트에서 새로고침을 계속 하니 js 파일을 찾을 수 없다는 404 에러가 나오는 것이었다. (사실 엄청 예전부터 계속 그랬을 것 같다...) build시 js 파일 hash 값이 바뀌기 때문에 다른 js 파일의 파일명이 달라지는 것은 당연하다고 생각했는데, 왜 못 찾는 것일까? 고민했다. 팀원들도 다 의아해했다. 배포 시마다 js를 찾을 수 없어 특정 시간 동안 흰화면이 떠버리니 모두가 스트레스를 받았다.
이것을 해결하기 위해 밤을 새겠다는 생각으로 10시간 동안 열심히 해결했다. build시 buildId를 주어 hash 값을 사용하지 않고 항상 같은 파일명으로 나오면 어떨까 싶었지만 그러면 브라우저 cache 문제 때문에 최신 배포버전이 바로 배포되지 않을 문제가 있을 것 같았다. 그래서 다른 방법을 chatGPT와 구글링으로 이것저것 찾아보다가 겨우 해결을 했는데 문제점은 k8s에서의 HealthCheck 였다.
Healthcheck 란 컨테이너의 상태를 체크하는 것인데, 2가지 방법이 있다. Liveness Probe 와 Readiness Probe 방법이다. Liveness Probe는 컨테이너가 살아있는지 확인하는 것으로, 컨테이너가 죽었다고 생각이 되면 자동으로 다시 실행시킨다. Readiness Probe 은 컨테이너가 서비스가 가능한 상태인지 체크하는 것이다.
Liveness probe와 차이점은 Liveness probe는 컨테이너의 상태가 비정상이라고 판단하면, 해당 Pod를 재시작을 하는 것이고, Readiness probe는 컨테이너가 비정상일 경우에는 해당 Pod를 사용할 수 없음으로 표시 후, 서비스에서 제외하는 것이 차이이다.
우리의 경우는 Readiness check
하는 HTTP probe path를 '/' 로 해뒀었는데 이걸로하여 문제가 있는 것 같았다. Nginx 쪽에서든 서버에서 아직 제대로 켜지지 않았을 때든 어떤식으로 200~300 status가 내려갔고, 재생성되고 있는 pod이 준비되었다고 판단하여 ingress가 아직 완벽하게 실행되지 않은 pod으로 요청을 보내고, index.html은 제대로 내려오는데, 몇몇 js 파일들이 제대로 내려오지 않은 일들이 생긴 것으로 판단된다.
이 문제를 해결하기 위해 Nextjs의 api routes를 활용하여, health check용 api를 추가하였다.
예를 들어 api/test.ts
를 만들어 아래와 같은 코드를 추가하고, Readiness check
의 path를 /
에서 /api/test
로 수정하였다.
import { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ message: 'Server is running' });
}
그 후 다시 재배포를 하였을 때는 아직 NextJS
서버가 제대로 실행되지 않았을 때에는 /api/test
에서 status 200을 내려주지 않을 것이기 때문에 이전 버전으로 서비스 되고 있다가, pod에서 NextJS 서버가 다 실행되고 나면 status 200을 내려주면서 제대로 JS 파일 404 에러가 나지 않고 제대로 동작하는 것을 볼 수 있었다. pod이 이전 버전과 최신 버전이 동시에 실행되고 있기 때문에, 새로고침할 때마다 이전 pod으로 접속 될 때도 있고, 최신 버전의 pod으로 접속 될 때도 있었지만 아무 문제 없었고, 이전 버전 pod이 다 없어지고 나면 항상 최신 버전의 pod으로 접속할 수 있었다.
팀원들의 불안과 스트레스, 귀찮음을 없앨 수 있어서 너무 좋고 뿌듯한 결과였다.
SSR을 지원하는 프론트엔드는 모두 겪는 현상이죠. 특히 롤링 업데이트 순간에 브라우저가 그린 파드에서 HTML 수신 후 js/css 등 정적 파일을 요청이 이루어 지면, 인그레스 통과 후 서비스에서 로드 밸런싱 될 때 블루 파드로 요청이 들어가면 js/css/image 를 못 찾게 되고 백화현상이나 404가 발생합니다.
롤링으로 하지 않고 블루그린배포로 하면 해결 될 것 같지만 오래전에 블루에서 제공된 html 을 열고 있는 브라우저가 있는데 이미 그린으로 다 교체가 되었을 때, CSR 네비게이션 하면 블루에 있던 청크js 를 못찾는 현상도 있구요.
그래서 이런 순단이나 백화 방지를 위해서 스태틱 파일들을 CDN에 모두 푸시 하거나(폴링은 접근 되지 않으면 캐싱이 안되니..) 별도 스토리지에 정적파일을 넣고 쿠버네티스 보다 더 앞에서 정적 파일 요청은 해당 스토리지로 보내는 방법을 씁니다.
안녕하세요! 본문 내용 관련해서 여쭤보고 싶은 내용이 있는데 혹시 이메일 주소를 알려주실 수 있을까요?
감사합니다.