Django WSGI 최적화 - Granian 과 ARM64(Graviton)
·
탐구 생활/개발 탐구
이 글은 최근 Django WSGI 를 다루게 된 백엔드 개발자가 문제를 인식하는 과정, 실험을 통해 데이터를 확보하고 실제 성능과 비용을 최적화한 경험을 다룹니다. TL;DR공식문서를 따르는게 아닌 실제 운영 환경에 맞춰 파라미터를 튜닝하고, linux/amd64 를 linux/arm64 로 교체하여 Req/s 지표를 2배 높일 수 있었습니다.Gunicorn, gthread 기반 HTTP 서버를 Granian 으로 교체하여 부하 상황에서 발생하던 502 에러를 방지했습니다.1. 문제 정의 및 가설 수립"왜 우리 서비스만 Task가 70%나 더 많을까?" AWS 콘솔을 모니터링하던 중, 타 프로덕트 대비 비정상적으로 높은 ECS Task 수와 일일 최대 6만 건에 달하는 502 Bad Gateway 에러..
ORM의 편리함 속 함정 - SELECT *와 수직 분할의 중요성
·
탐구 생활
현대의 백엔드 개발자들(심지어 DB를 다루는 프론트엔드 개발까지)은 기본적으로 ORM(Object-Relational Mapper) 사용을 널리 받아들이고 있습니다. Python 생태계에서도 금쪽같은 Django의 ORM, 혹은 SQLAlchemy 가 있어서 Python 개발자들에게 정말 많은 편리함을 제공하고 있습니다. 하지만 "지옥으로 가는 길은 선의로 포장되어 있다" 라는 유명한 격언처럼, ORM 이 제공하는 편리함 속에는 함정이 숨어있습니다. 대표적인 문제인 N+1 현상은 널리 알려져있는 반면, 무심코 날리고 있는 SELECT * 쿼리와 Fat Model 문제가 성능에도 영향을 끼친다는 사실은 종종 무시 당하는것 같습니다. 성능에 집착하는 개발자 중 한명으로서 ORM 의 또다른 함정을 명확히 정리..
Python 과 zstd - middleware 적용하기
·
탐구 생활/Python 과 zstd
이 시리즈 첫글에서 Python 3.14 정식 배포 버전에서 zstd 알고리즘 native 지원에 더 눈길이 갔던 이유가 I/O Bounded API Server 의 응답속도 개선에 주의를 기울이고 있기 때문이라고 말했었습니다. 특히 지금 맡은 업무가 웹 브라우저와 직접적으로 상호작용하는 그렇다면 Python 으로 작성된 Web Server (FastAPI, Django) 에서 zstd 를 어떻게 사용할 수 있을지 공유하고자 합니다.전체 흐름 점검하기우선 가장먼저 드는 의문점은 누가 언제 무엇을 인코딩하고 디코딩하냐는 겁니다. 정말 다양한 기준이 있겟지만 웹서버를 기준으로만 정리해보겠습니다. 이제부터 말하는 클라이언트는 웹브라우저(PC / Mobile) 이며 서버는 웹 서버입니다. 그리고 주된 "흐름"..
Python 과 zstd - LZ77 과 허프만 코딩
·
탐구 생활/Python 과 zstd
25년 10월 7일, 추석 연휴가 한창일때, Python 3.14 정식 버전 (3.14.0) 이 배포되었습니다. 많은 Python 사용자들이 Free-Threaded 에 대해 이야기했습니다. 하지만 저는 Python Native zstd 압축이 지원된다는 사실에 더 관심이 갔습니다. 최근 I/O Bounded API Server 의 응답속도 개선에 관심을 기울이고 있었기 때문입니다. 이번 글에서는 본격적으로 zstd 압축 알고리즘을 알아보고 활용법을 탐구하기 전에 대표적인 무손실 압축 알고리즘인 LZ77과 허프만 코딩을 알아보겠습니다.대표적인 무손실 압축 알고리즘zstd 압축에 대해서 이야기하기에 앞서 현대 컴퓨터 시스템에서 압축 알고리즘의 근간이 되는 두 알고리즘을 살펴보겠습니다. 앞으로 설명할 gzi..
하나의 컨테이너에 하나의 프로세스
·
탐구 생활/개발 탐구
종종 하나의 Docker 컨테이너에 2개 이상의 프로세스를 Entrypoint의 Script 를 통해 실행하는 경우를 봅니다. 그리고 이러한 행태가 "전혀 문제 없는 방식" 이라고 말하는 사람도 만납니다. 이런 방식이 본질적으로 잘못된게 맞습니다. 어떤 점에서 잘못됐는지 알아보겠습니다.문제점gRPC 와 FastAPI 를 하나의 컨테이너에서 운영하는 경우를 예시로 들겠습니다.1. 운영 리스크를 키운다.서로 다른 프로세스 두 개를 하나의 컨테이너에서 운영하는 것은 운영 측면에서 리스크를 키웁니다.프로세스 생명주기: Docker는 PID 1 하나만 관리합니다. 추가로 띄운 자식 프로세스가 신호(SIGTERM/SIGINT)를 못 받아 좀비 프로세스가 남거나, graceful-shutdown 없이 강제 종료될 수..
가시성 (1) - 그 개념에 대하여
·
탐구 생활/개발 탐구
백엔드 개발자들끼리 이야기하다보면 "어떻게 모니터링하냐"는 주제는 반드시 등장하는 것 같습니다. 특정 기능을 만들어서 배포하면 마치 자식을 학교에 보내놓은 부모의 마음처럼 가서 친구들하고 잘 지내는지, 급식은 잘 먹었는지 궁금하지 않을 수 없기 때문입니다. 결국 대화는 가시성에 대한 내용으로 옮겨갑니다. 그러다 문득 내가 가시성을 과연 제대로 이해하고 있는게 맞나 궁금해졌습니다. 호기심이 동한김에 가시성에 대한 시리즈를 시작합니다.가시성가시성(observability)이란?가시성(Observability) 은 시스템이 스스로 내보내는 신호만으로 내부 상태를 재구성하고 특정 현상의 원인을 추론할 수 있는 능력을 뜻합니다. 처음엔 제어공학 용어였지만, 오늘날 소프트웨어, 클라우드 세계에서는 그 아래와 같은 ..
Python Monorepo - uv Docker 다이어트하기
·
탐구 생활/Python Monorepo
문제 제기교적 간단한 기능만 하는 마이크로서비스의 Docker 이미지가 180MB에 달하고 Runtime 에는 Idle 상태에서 메모리를 120MB 를 사용하고 있었습니다. Python alpine 도 아니고 slim 이미지 기반인데 왜 이렇게 메모리를 비효율적으로 쓸까? 호기심이 생겼고 문제를 분석했습니다.맥락 요약 (이전 글 안본 분들을 위해)프로젝트 구조현재 프로젝트의 디렉터리 구조는 아래와 같이 packages 에 공통모듈을 정의하고 services 에서는 해당 공통모듈을 끌어다가 FastAPI 와 gRPC 서버를 돌리는 코드가 위치해 있습니다. 그리고 프로젝트 root 에 pyproject.toml, packges 안의 모든 모듈에 pyproject.toml 이, services 안의 모든 서비..
Python Monorepo - namespace 활용하기
·
탐구 생활/Python Monorepo
문제점: 잘못된 이름으로 만들어진 공통모듈직전 글에서 uv 를 이용해서 Python Monorepo 를 구성하였습니다. 대략 이런 모습이었죠. shared 에 있는 모듈을 services 에서 끌어다 쓰는 구조입니다.이 구조 자체는 괜찮은데, 저의 미숙함때문에 치명적인 문제가 생겼습니다. 바로 너무 일반적인 이름을 사용했다는 점입니다.services 하위의 app에서도 언제든지 exception, logger 와 같은 파일을 정의해서 사용할 수 있기에 충돌 가능성이 있습니다. 즉, 공통모듈을 사용하는 개발자 개개인이 조심해야하는 문제점이 생깁니다. 실무에 집중해야하는 개발자에게 큰 스트레스겠죠? 이 문제를 해결했던 과정을 공유하겠습니다.원하는 결과물가장 일반적으로 문제를 해결하는 방식은 shared 밑에..