전체 글 (38) 썸네일형 리스트형 [Spring Boot] Spring WebFlux 한계 돌파 - 비동기 논블로킹(Non Blocking) JMeter로 1만 명의 부하 테스트를 때리다 보면, 평범한 Spring MVC(톰캣 기반) 서버가 어느 순간 맥없이 무너지는 것을 보게 됩니다. 이유가 뭘까요? 기존 방식은 '1 요청 = 1 쓰레드(Thread)'라는 치명적인 룰을 가집니다. 유저가 API를 호출하면 쓰레드 하나가 배정되고, 그 쓰레드는 데이터베이스에서 쿼리 결과를 가져오는 2초 동안 '대기(Blocking)' 상태에 빠집니다. 아무 일도 안 하면서 쓰레드를 꽉 쥐고 있는 거죠. 이렇게 200명의 유저가 동시에 접속해 쓰레드 200개가 전부 DB 응답을 기다리며 멈춰버리면, 유저부터는 하얀 화면만 보며 에러를 뱉게 됩니다(Thread Pool 고갈). "아니, DB 기다리는 동안 쓰레드가 놀지 말고, 돌아가서 다른 유저 요청을 받아오면.. [Spring Boot] 힙 덤프(Heap Dump) 분석 및 메모리 누수 자바(Java)의 가장 큰 장점 중 하나는 개발자가 메모리(RAM) 관리에 신경 쓰지 않아도 된다는 것입니다. 객체를 다 쓰고 나면 가비지 컬렉터(Garbage Collector, GC)라는 청소부가 돌아다니며 쓰레기를 싹 치워주기 때문이죠. 그런데 여러분이 만든 쇼핑몰 서버가 일주일만 지나면 서서히 메모리가 차오르더니 java.lang.OutOfMemoryError (OOM)을 뱉으며 장렬히 전사하는 현상이 발생했습니다. 청소부(GC)가 왜 쓰레기를 치우지 못했을까요? 개발자가 코드 어딘가에서 List나 Map 같은 저장소에 객체를 계속 쌓아두기만 하고 빼지 않으면(참조를 유지하면), GC는 "어? 이 객체는 아직 쓰고 있는 중요한 데이터인가 보네? 치우지 말아야지!" 하고 냅두게 됩니다. 이것이 바로.. [Spring Boot] 무중단 배포 전략(Blue-Green) 새벽 3시, 개발자가 버그를 고치고 새로운 스프링 부트(Spring Boot) 버전(V2)을 배포(Deploy)하기 위해 서버에 접속합니다. 기존에 돌고 있던 V1 서버 프로세스를 kill 명령어로 죽이고, 새로운 V2 .jar 파일을 java -jar 명령어로 기동 시킵니다. 스프링의 거대한 프레임워크가 톰캣(Tomcat)을 띄우고 메모리에 빈(Bean)들을 올리는 데 약 10~15초가 소요됩니다. 문제는 이 15초 동안 사이트에 들어온 사용자들은 끔찍한 "502 Bad Gateway" 또는 "연결할 수 없음" 에러 페이지를 마주하게 된다는 것입니다. (다운타임 발생) 과거에는 고객들이 잠든 새벽에 서버를 내리고 작업(점검 시간)하는 것이 당연했지만, 24시간 글로벌로 돌아가는 현대의 서비스(넷플릭스,.. [Jenkins] 파이프라인 나누기(Upstream / Downstream) 지금까지 우리는 하나의 거대한 Jenkinsfile 안에 빌드, 테스트, 소나큐브 검사, 도커 이미지 푸시, K8s 배포까지 모든 로직을 한곳에 집중시키는 단일 파이프라인(Monolithic Pipeline) 방식을 배웠습니다. 프로젝트 규모가 작을 때는 이것이 편합니다. 하지만 회사가 성장하여 수십 개의 마이크로서비스(MSA)가 얽히고, "A 서비스와 B 서비스가 모두 성공적으로 빌드된 후에야 C라는 통합 결제 서버를 빌드하고 배포하라"는 극악의 워크플로우(Workflow) 요구사항이 떨어지면 어떻게 될까요? 이 모든 것을 파일 하나에 욱여넣으면 코드는 2천 줄이 넘어가고, 중간에 작은 에러 하나만 터져도 2천 줄짜리 거대 공장이 통째로 멈춰버리는 끔찍한 사태가 발생합니다.이번 글에서는 "크고 뚱뚱한 .. [Jenkins] Node.js 다중 버전 환경 완벽 통제 가이드 엔터프라이즈 환경에서 젠킨스(Jenkins) 서버 단 1대로 수십 개의 파이프라인을 운영하다 보면 필연적으로 '버전 충돌(Version Conflict)'이라는 거대한 장벽에 부딪히게 됩니다. 가장 대표적인 생태계가 바로 프론트엔드의 Node.js 입니다. 5년 전에 만들어진 구형 레거시 어드민 프로젝트는 반드시 Node.js v14 환경에서만 빌드되며, 오늘 새로 런칭한 최신 React 프로젝트는 Node.js v20 환경을 요구합니다. 만약 젠킨스 호스트 서버 리눅스에 apt-get install nodejs로 14 버전을 깔아버리면 새 프로젝트가 빌드 에러를 뿜고, 20 버전으로 올려버리면 레거시 프로젝트가 죽어버립니다. 이번엔 인프라 엔지니어를 골머리 앓게 만드는 "하나의 젠킨스 서버에서 수십 가.. [Jenkins] Trivy로 컨테이너 보안 취약점 차단하기 우리는 에서 코드를 도커 이미지(Docker Image)로 굽는 화려한 기술을 배웠습니다. FROM node:14-alpine 같은 베이스 이미지를 가져와서 무심코 컨테이너를 말아 올렸죠. 그런데 만약 그 베이스 이미지 안에 해커가 악용할 수 있는 치명적인 OpenSSL 해킹 취약점(예: Heartbleed)이나 탈취 가능한 루트 권한 버그가 심어져 있다면 어떨까요? 젠킨스가 아무리 배포를 잘해도, 결과적으로는 '보안 폭탄'을 운영 서버 한가운데에 아주 빠르고 정확하게 배달해 주는 꼴이 됩니다. 바야흐로 배포 파이프라인 내부에 보안(Security)을 욱여넣어 강제화하는 DevSecOps(개발-보안-운영)의 시대입니다. 이번에는 전 세계에서 가장 사랑받는 초경량 오픈소스 컨테이너 보안 스캐너인 'Triv.. [Jenkins] Retry 블록 네트워크 장애를 극복하는 파이프라인 CI/CD 자동화를 구축해 놓으면 퇴근 후에도 마음이 편할 줄 알았습니다. 하지만 밤 11시에 슬랙으로 [빌드 실패] 빨간 알람이 울려 부랴부랴 랩탑을 열어보면, 내 코드의 버그가 원인이 아닙니다. "Github에서 소스코드를 다운로드하다가 DNS를 못 찾음", "NPM Registry 서버가 잠시 응답하지 않음", "Docker Hub Rate Limit(속도 제한)에 걸려 이미지 pull 실패" 인프라 엔지니어가 통제할 수 없는 '외부 요인(네트워크 지연 및 타임아웃)' 때문에 파이프라인이 터지는 억울한 경우가 실무 빌드 실패의 절반 이상을 차지합니다. 억울하게 실패한 빌드를 데브옵스 담당자가 젠킨스 화면에 접속해 수동으로 'Rebuild' 버튼을 눌러주면 언제 그랬냐는 듯 쌩쌩하게 잘 돌아갑니다. .. [Spring Boot] Apache Kafka 연동 MSA의 이벤트 주도 비동기 통신 배달 앱에서 고객이 치킨을 '결제'했다고 상상해 봅시다. 백엔드 시스템 내부에서는 어마어마한 일들이 순차적으로 벌어집니다.1. 주문 시스템에 접수2. 라이더 배차 시스템 호출3. 고객에게 카카오톡 알림 발송4. 쿠폰 소진 처리만약 결제 서버가 이 4개의 서버에게 일일이 HTTP(REST API)로 전화를 걸어 "야, 결제됐어! 일해!"라고 동기식(Synchronous)으로 통신하면 어떻게 될까요? 만약 카카오톡 알림 서버가 장애로 3초간 멈춰있다면, 결제 서버도 그 3초 동안 응답을 기다리며 멈춰있게 되고 결국 사용자의 화면은 하얗게 굳어버립니다. 하나의 서버 장애가 전체 시스템의 장애로 퍼지는 '도미노 붕괴(강한 결합)' 현상입니다. 이 끔찍한 의존성을 칼같이 끊어내기 위해 도입된 구원자가 바로 아파치.. [Spring Boot] 액추에이터 실시간 모니터링(Actuator) 열심히 개발한 스프링 부트(Spring Boot) 서버를 무사히 라이브(Live) 환경에 배포했습니다. 그런데 배포 후 3일째 되는 날, 유저 한 명이 "홈페이지가 엄청 느려요!"라고 게시판에 불평을 남겼습니다. 이 말을 들은 개발자는 등골이 오싹해집니다. "서버 메모리가 다 차서 느려진 건가? 아니면 DB 커넥션이 부족한가? 혹시 특정 API가 유독 늦게 응답하는 건가?" 원인을 찾기 위해 리눅스 서버에 SSH로 접속해서 top 명령어를 치거나, 끝도 없이 올라가는 로그 텍스트를 눈알이 빠지도록 쳐다보는 것은 원시 시대의 방식입니다. 서버 스스로가 자신의 건강 상태(Health), 메모리, CPU 사용량, DB 연결 상태, 최근 100개의 HTTP 요청 내역 등을 완벽한 JSON 데이터로 뱉어내 주면 .. [Spring Boot] 파일 업로드와 AWS S3 클라우드 사용하기 게시판에 사용자가 10MB짜리 고화질 사진을 첨부하여 글을 작성합니다. 초보 시절에는 MultipartFile 객체를 받아 스프링 부트(Spring Boot)가 구동되고 있는 로컬 서버의 C:\upload 디렉토리나 리눅스 /var/www/images 폴더에 file.transferTo() 코드로 저장(Save)하는 방식을 썼습니다. 서버가 딱 1대일 때는 이 방식이 기가 막히게 잘 돌아갑니다. 하지만 대박이 터져서 서버를 3대(A, B, C)로 스케일 아웃(Scale-out)하는 순간 지옥문이 열립니다. 사용자가 A서버에 접속해 프로필 사진을 올렸는데, 내일 다시 접속할 땐 로드밸런서가 B서버로 연결해 주면? B서버 디스크에는 사진이 없으므로 화면에 끔찍한 '엑스박스(이미지 깨짐)'가 뜹니다. 게다가 .. ← 이전 1 2 3 4 다음 →