프론트개발자가 인프라와 씨름하는 법

Eamon·2025년 2월 2일
post-thumbnail

최근에 프론트 배포 파이프라인에서 문제를 발견하고 해결 과정을 복기 하면서 웹이 서빙되고 배포할때 필요한 개념들을 다시 공부해 보았습니다. 여기서 다루는 것은 웹 프론트엔드 서비스 (특히 nextjs 서비스)를 어떤 과정을 통해 서빙하고 있고 그 과정들의 개념들이 왜 필요한지를 짚고 넘어가기 위해서 쓰여진 글입니다. (키워드들을 알면 기억했다가 디버깅하거나 추가 공부하기 쉬우니 참고하길 바라는 마음에서 적고 있습니다.)

문제 상황

한 번 배포하면 정상적으로 동작하지만, 다시 배포하면 서버에서 503 오류가 발생하는 심각한 현상이 발견되었습니다.

사실, 이 문제는 예전부터 존재하던 문제였지만, 다행히 운영 환경이 아닌 dev 서버에서만 발생하는 상황이어서 바쁜 기능을 개발하는 기간엔 흐린 눈 하며 참고 개발을 하고 있었습니다. 그렇지만 개발이 활발하게 이루어지는 스프린트 QA 기간에 dev 서버에 개선 사항을 적용하기 위해서 배포를 두 번 해야 하는 과정은 너무 이상하고 불편하여 다음 스프린트 전에는 꼭 개선해야겠다고 느꼈습니다.

문제 파악

503 Error (Service Unavailable) 의 파악

HTTP 503 오류(Service Unavailable)는 서버가 요청을 처리할 수 없을 때 발생하는 오류입니다. 클라이언트(웹 브라우저 또는 API 요청)가 서버에 요청을 보내지만, 서버가 응답할 수 없는 상태일 때 503 상태 코드를 반환합니다.

503 에러는 아래와 같은 상황에서 주로 발생합니다.

원인설명해결 방법
서버 유지보수 중서비스 점검 중유지보수 종료 후 정상화
백엔드 서비스 문제데이터베이스, API 연결 끊김백엔드 서비스 점검 및 재시작
로드 밸런서 설정 오류트래픽을 잘못된 서버로 전달로드 밸런서 설정 확인
애플리케이션 충돌코드 오류 또는 프로세스 다운애플리케이션 로그 확인 후 재시작

프로덕트의 구조 파악

먼저 파악한 프로덕트의 도식도 입니다.

사용자가 서버로 (기본 80포트) 요청을 보내면 로드 밸런서의 대상 등록(타겟 등록)을 통해 EC2 인스턴스 안 내부에 있는 Docker 컨테이너(3000포트)로 요청을 보냅니다. Docker 컨테이너 내부에서는 Next.js 서버가 실행되고 있습니다.

무중단 배포를 위해 Docker 컨테이너는 Blue 컨테이너와 Green 컨테이너가 번갈아 가며 실행되는 방식을 따릅니다. 배포가 이루어지면 Blue 컨테이너가 실행되며, 이후 Green 컨테이너가 새 버전으로 교체되어 운영됩니다.

문제 좁히기

저는 "Blue 컨테이너와 Green 컨테이너가 번갈아 가며 실행되는 방식"에 집중하여 문제를 더 좁게 정의하기로 하였습니다. 그 결과 Blue 컨테이너인 경우에는 배포가 올바르게 되고 Green 컨테이너일 때는 503을 내보낸다는 것을 알게 되었습니다. 그 둘의 차이는 열린 포트의 차이였습니다. Green 컨테이너는 4000 포트를 향해 열려 있었고 Blue 컨테이너는 3000 포트를 향해 열려 있었습니다.

그렇기 때문에 4000 포트의 통신만 열려진 Green 컨테이너가 실행되어 있을 때는 안에 있는 서버에 요청이 도착하지 않고 503 에러가 발생하는 것이었습니다. 정확히는 AWS의 로드 밸런서에서 보내는 /health-check 요청을 받지 못해서 503 에러라고 판단하는 것입니다.

문제 해결

이 문제는 prod에서는 발생하지 않지만, dev 환경에서는 발생하는 문제였으므로 그 둘의 인프라 환경에서의 차이가 있을 것이라 판단하여 그 차이를 찾아내는 데 집중해서 문제를 해결해 나갔습니다.

가장 큰 차이점은 대상 인스턴스에서 어떤 포트를 수신 대상으로 하는지를 지정하는 타깃 그룹의 포트 번호가 달랐습니다. prod는 80으로 열려 있었지만 dev는 3000으로 열려 있었던 것을 발견했습니다.

먼저, AWS 로드 밸런서의 타깃 그룹에 4000 포트를 추가로 등록하여 임시로 문제를 해결하였습니다. 이로 인해 Green 컨테이너에서도 요청을 정상적으로 받을 수 있게 되었습니다. 이후, prod 환경에서는 이미 Nginx가 설정되어 있다는 것을 확인하고, dev 환경에서도 동일하게 Nginx를 설정하였습니다. 이를 통해 내부적으로 4000 포트로 접근하는 요청을 3000 포트로 라우팅하여, Blue-Green 컨테이너 간의 포트 차이로 인해 발생하는 문제를 해결하였습니다.

그렇다면 prod EC2 인스턴스에서 Nginx가 리버스 프록시 역할을 하고 있다는 것을 확인하고 그 이후에 dev 환경에서도 Nginx를 구성하여 클라이언트(3000 or 4000)와 로드 밸런서 사이에서 포트를 변경해 주는 역할을 하도록 변경하여 해결했습니다.

공부한 개념

로드 밸런서

로드 밸런서는 서버 간 트래픽을 분산하여 부하를 줄이고 서비스의 가용성을 높이는 역할을 합니다. 단일 서버가 과부하로 인해 다운되는 것을 방지하며, 장애 발생 시 정상적인 서버로 트래픽을 자동으로 우회합니다.

로드 밸런서의 기능

  • 트래픽 분산: 여러 서버에 요청을 균등하게 분배하여 부하를 줄임
  • 헬스 체크: 서버의 정상 동작 여부를 주기적으로 확인하고, 비정상적인 서버를 제외함
  • SSL 종료: HTTPS 보안을 위한 SSL 인증서를 적용하고 해제하여 보안성을 강화함

무중단 배포

무중단 배포는 서비스 중단 없이 새로운 버전으로 업데이트할 수 있는 배포 전략입니다. 무중단 배포에는 크게 세 가지 방법이 있는데 Rolling 배포, Blue-Green 배포, Canary 배포가 있습니다.

Rolling 배포

Rolling 배포는 사용 중인 인스턴스에 새로운 버전의 서비스를 점진적으로 배포하는 방식으로, 무중단 배포의 가장 기본적인 방법입니다. 기존에 운영 중인 인스턴스를 하나씩 로드 밸런서에서 제외한 뒤, 새로운 버전의 서비스를 적용하고 다시 라우팅하도록 합니다. 이 과정을 반복하여 모든 인스턴스에 새로운 버전이 배포됩니다.

🔹 장점

인스턴스마다 차례로 배포가 진행되므로 에러 발생 시 쉽게 롤백 가능합니다.
기존 인스턴스에 추가적인 인스턴스를 늘리지 않아도 되므로 비용 부담이 적습니다.
관리가 용이하여 운영 부담이 적습니다.

🔸 단점
새로운 버전을 배포하는 동안 운영 중인 인스턴스의 수가 줄어들어 정상 동작 중인 인스턴스에 트래픽이 집중될 가능성이 있습니다.
배포가 진행될 때 기존 버전과 새로운 버전이 함께 존재하므로 호환성 문제가 발생할 수 있습니다.

Blue-Green 배포

Blue-Green 배포는 운영 중인 기존 버전(Blue)과 새로운 버전(Green)을 동시에 유지하면서 배포하는 방식입니다. 새로운 버전을 별도로 배포한 뒤, 배포가 완료되면 모든 트래픽을 Green으로 일괄 전환합니다.

🔹 장점
기존의 Blue 환경이 그대로 유지되므로 빠른 롤백이 가능합니다.
운영 환경과 분리된 상태에서 새로운 버전을 테스트할 수 있어 안정성이 높습니다.
기존 환경(Blue)을 유지하면서 다음 배포에 재사용할 수 있습니다.

🔸 단점
운영 중인 환경과 동일한 새로운 환경을 추가로 구성해야 하므로 시스템 자원이 두 배로 필요하며 비용 부담이 큽니다.

Canary 배포

Canary 배포는 새로운 버전이 배포될 때 일부 사용자 또는 특정 서버에서 먼저 테스트한 후, 이상이 없으면 점진적으로 전체 배포를 진행하는 방식입니다. 블루-그린 배포와 다르게 한 번에 모든 트래픽을 전환하지 않고, 단계적으로 트래픽을 조정하며 새로운 버전을 확장해 나갑니다.

🔹 장점
문제 상황을 빠르게 감지할 수 있어 성능 모니터링에 유용합니다.
특정 사용자 그룹을 대상으로 A/B 테스트가 가능하여 새로운 기능을 안전하게 실험할 수 있습니다.

🔸 단점
트래픽을 점진적으로 전환해야 하므로 네트워크 트래픽 제어에 대한 부담이 증가할 수 있습니다.

리버스 프록시

리버스 프록시는 클라이언트(사용자)의 요청을 받아 실제 서버(백엔드)로 전달하고, 응답을 다시 클라이언트로 반환하는 중간 서버입니다. 쉽게 말해, 사용자의 요청을 대신 처리해주는 서버라고 할 수 있습니다.

일반적으로 Nginx, Apache, HAProxy와 같은 소프트웨어를 사용하여 리버스 프록시를 설정합니다.

리버스 프록시의 역할

  • 보안 강화
    클라이언트가 직접 백엔드 서버와 통신하지 않도록 하여 백엔드 서버를 보호합니다.
    DDoS(분산 서비스 거부) 공격을 방어하고, SSL/TLS 암호화를 적용할 수 있습니다.

  • 로드 밸런싱
    여러 개의 백엔드 서버가 있을 경우, 리버스 프록시가 트래픽을 분산하여 특정 서버에 과부하가 걸리지 않도록 합니다.

  • 캐싱을 통한 성능 향상
    자주 요청되는 정적 리소스(이미지, CSS, JavaScript)를 캐싱하여 백엔드 서버의 부담을 줄이고 응답 속도를 빠르게 합니다.

  • 무중단 배포 지원
    Blue-Green 배포, Canary 배포 등을 지원하며, 특정 트래픽을 새로운 버전의 서버로 전환할 수 있습니다.

  • HTTPS 적용 (SSL 종료)
    리버스 프록시에서 SSL 인증서를 적용하여, 클라이언트와의 통신을 암호화할 수 있습니다.

이번 해결과정에서 Nginx의 역할은 위의 역할 중에 특정 트래픽을 새로운 버전의 서버로 전환하기위해 새로운 버전의 서버로 전환하는 역할을 위해 사용했다고 할수있다.

마무리

사실 위의 개념들은 인프라를 구축해보신 분들이라면 굉장히 익숙한 개념들이라고 생각합니다. 그러나 저는 이번 디버깅 과정에서 처음 겪었던 환경이였고 공부를 하면서 웹 생태계에 대한 이해가 더 깊어진 것 같아서 좋았습니다. 너무 가볍게 공부한게 아닌가 라는 생각도 들지만 나중에 하나씩 더 깊게 파서 공부해보는 시간을 따로 가질 수 있는 발판을 마련했다고 생각하며 글을 마칩니다.

참고

Elastic Load Balancing이란 무엇인가요?
무중단 배포 아키텍처(Zero Downtime Deployment)- 글로벌 서비스 운영의 필수 요소
Reverse Proxy.. 과연 무엇일까요?

profile
Steadily , Daily, Academically, Socially semi-nerd Front Engineer.

0개의 댓글