콘텐츠 대표 이미지 - 리버스 프록시와 캐시 서버 완전 정복
DB / 서버 · 프로그램개발
🚀 Performance Deep Dive

리버스 프록시와 캐시 서버 완전 정복

Varnish & Squid로 서버 성능을 10배 끌어올리는 실전 가이드 ⚡

야, 솔직히 말해봐. 🤔
서버 응답이 느려서 사용자들이 이탈하는 거 경험해봤지? 트래픽 폭탄 맞고 서버가 뻗어버린 적 있지?
그 해결책이 바로 리버스 프록시와 캐시 서버야. 오늘은 이 두 개념을 완전히 씹어먹어 보자고!


리버스 프록시 아키텍처 개념도 👤 사용자1 👤 사용자2 👤 사용자3 🛡️ 리버스 프록시 Nginx / HAProxy Varnish / Squid ⚡ 캐시 히트! 저장된 응답 즉시 반환 🖥️ 백엔드 서버 1 App Server 🖥️ 백엔드 서버 2 App Server 🖥️ 백엔드 서버 3 App Server 🗄️ 데이터베이스 MySQL / Redis 클라이언트 요청 프록시 → 백엔드 캐시 히트 경로 DB 연결

🤔 리버스 프록시가 뭔데?

먼저 프록시(Proxy)라는 단어 자체가 "대리인"이라는 뜻이야.
우리가 흔히 아는 포워드 프록시(Forward Proxy)는 클라이언트 앞에 서서 클라이언트를 대신해서 인터넷에 요청을 보내는 녀석이야. 회사 방화벽이나 VPN 같은 거 생각하면 돼.

근데 리버스 프록시(Reverse Proxy)는 반대야! 🔄
서버 앞에 서서 클라이언트의 요청을 대신 받아주는 역할을 해. 클라이언트 입장에서는 리버스 프록시가 진짜 서버처럼 보이는 거지. 뒤에 실제 백엔드 서버들이 몇 개가 있는지, 어떤 기술 스택인지 전혀 모르는 거야.

💡 핵심 비유!
리버스 프록시는 마치 호텔 프런트 데스크 같아.
손님(클라이언트)은 프런트에만 얘기하면 되고, 프런트가 알아서 청소부, 룸서비스, 주방 등(백엔드 서버들)에 일을 분배하는 거야. 손님은 뒤에서 어떻게 돌아가는지 알 필요가 없지!

리버스 프록시가 해주는 일들을 정리해보면:

1
로드 밸런싱(Load Balancing) — 여러 백엔드 서버에 트래픽을 골고루 분산시켜줘. 한 서버에 몰리는 걸 방지!
2
SSL 종료(SSL Termination) — HTTPS 암호화/복호화를 리버스 프록시에서 처리해서 백엔드 서버의 부담을 줄여줘.
3
캐싱(Caching) — 자주 요청되는 응답을 저장해뒀다가 백엔드 서버에 물어보지 않고 바로 돌려줘. 속도 UP! 🚀
4
보안(Security) — 백엔드 서버의 실제 IP를 숨기고, DDoS 공격 방어, WAF 역할도 해.
5
압축(Compression) — 응답 데이터를 gzip으로 압축해서 전송 속도를 높여줘.

⚡ 캐시 서버의 마법

캐시 서버의 핵심 원리는 간단해. "한 번 만든 거 다시 만들지 말자!" 🎯
동일한 요청이 들어올 때마다 백엔드 서버에서 DB 조회하고, 로직 처리하고, 응답 생성하는 게 얼마나 비효율적인지 생각해봐.

예를 들어 뉴스 사이트의 메인 페이지를 생각해봐.
1초에 1,000명이 접속한다면? 매번 DB에서 최신 뉴스 목록을 가져오는 쿼리를 1,000번 실행하는 거야. 근데 뉴스가 1초마다 바뀌지는 않잖아? 그럼 캐시에 저장해두고 1,000명 모두에게 저장된 응답을 돌려주면 DB 쿼리는 딱 1번만 실행되는 거지!

📊 캐시 히트율(Cache Hit Rate)의 중요성

캐시 히트율 90%
🔥 매우 우수
캐시 히트율 70%
👍 양호
캐시 히트율 30%
😅 개선 필요

캐시 히트율이 90%라는 건 100개의 요청 중 90개는 백엔드 서버를 거치지 않는다는 뜻이야. 서버 부하가 10분의 1로 줄어드는 거지!

🎯 Varnish Cache — 웹 가속기의 왕

자, 이제 본격적으로 Varnish Cache를 파헤쳐보자! 🕵️
Varnish는 2006년 노르웨이의 Poul-Henning Kamp가 개발한 오픈소스 HTTP 가속기야. "웹 가속기(Web Accelerator)"라고도 불리는데, 그 이유가 있어.

Varnish의 가장 큰 특징은 VCL(Varnish Configuration Language)이라는 자체 설정 언어를 사용한다는 거야. 처음엔 좀 낯설 수 있는데, 익숙해지면 엄청 강력해!

🌟 Varnish가 선택받는 이유

HTTP 전용 최적화 메모리 기반 초고속 VCL 유연한 설정 ESI 지원 Grace Mode

Varnish는 HTTP 트래픽만을 위해 설계된 특화 솔루션이야. 범용 프록시가 아니라 HTTP 캐싱에 모든 걸 쏟아부은 녀석이지.

📝 Varnish 기본 설치 및 설정

Ubuntu/Debian 기준으로 설치해보자:

# Varnish 설치 (Ubuntu 22.04 기준)
sudo apt-get update
sudo apt-get install varnish

# 버전 확인
varnishd -V

# 서비스 시작
sudo systemctl start varnish
sudo systemctl enable varnish

기본 설정 파일은 /etc/varnish/default.vcl에 있어. 이게 바로 VCL 파일이야!

# /etc/varnish/default.vcl 기본 구조
vcl 4.1;

# 백엔드 서버 정의
backend default {
    .host = "127.0.0.1";  # 백엔드 서버 IP
    .port = "8080";        # 백엔드 서버 포트
    .connect_timeout = 5s;
    .first_byte_timeout = 30s;
    .between_bytes_timeout = 60s;
}

# 요청 수신 시 처리 로직
sub vcl_recv {
    # 쿠키가 있는 요청은 캐시하지 않음 (기본 동작)
    if (req.http.Cookie) {
        return (pass);
    }
    
    # POST 요청은 캐시하지 않음
    if (req.method == "POST") {
        return (pass);
    }
    
    return (hash);  # 캐시 조회
}

# 백엔드 응답 처리
sub vcl_backend_response {
    # 캐시 TTL 설정 (1시간)
    set beresp.ttl = 1h;
    
    # 압축 활성화
    set beresp.do_gzip = true;
}

# 캐시 히트 시 처리
sub vcl_hit {
    return (deliver);
}

# 캐시 미스 시 처리
sub vcl_miss {
    return (fetch);  # 백엔드에서 가져옴
}

# 응답 전달 시 처리
sub vcl_deliver {
    # 캐시 히트/미스 헤더 추가 (디버깅용)
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    return (deliver);
}

🔥 VCL의 핵심 — 요청 흐름 이해하기

Varnish의 요청 처리 흐름을 이해하는 게 핵심이야. VCL에는 여러 개의 서브루틴(subroutine)이 있고, 각 단계에서 요청을 어떻게 처리할지 결정해:

🔄 Varnish 요청 처리 흐름

1
vcl_recv — 클라이언트 요청 수신. 캐시할지(hash), 그냥 통과시킬지(pass), 파이프할지(pipe) 결정
2
vcl_hash — 캐시 키 생성. URL + Host 헤더 조합이 기본값
3
vcl_hit / vcl_miss — 캐시 히트면 바로 deliver, 미스면 백엔드에 fetch
4
vcl_backend_fetch — 백엔드로 요청 보내기 전 처리
5
vcl_backend_response — 백엔드 응답 받은 후 처리. TTL, 캐시 여부 결정
6
vcl_deliver — 클라이언트에게 응답 전달 직전 처리

💎 Varnish 고급 기능들

1. Grace Mode — 서버가 죽어도 캐시는 살아있어!

# Grace Mode 설정
# 백엔드가 다운되어도 오래된 캐시를 일정 시간 제공
sub vcl_backend_response {
    set beresp.ttl = 10m;      # 정상 캐시 유효시간: 10분
    set beresp.grace = 1h;     # 백엔드 장애 시 1시간 더 제공
}

sub vcl_recv {
    set req.grace = 30s;       # 30초 동안 오래된 캐시 허용
}

Grace Mode는 진짜 꿀 기능이야! 🍯 백엔드 서버가 잠깐 다운되거나 응답이 느릴 때, 만료된 캐시라도 사용자에게 제공해서 서비스 중단을 막아줘. 사용자 입장에서는 서버 장애를 눈치채지 못하는 거지!

2. ESI(Edge Side Includes) — 페이지 조각 캐싱

# ESI 활성화
sub vcl_backend_response {
    if (beresp.http.content-type ~ "text/html") {
        set beresp.do_esi = true;  # ESI 처리 활성화
    }
}

# HTML 파일에서 ESI 태그 사용 예시
# <esi:include src="/header" />  ← 헤더 부분만 별도 캐시
# <esi:include src="/sidebar" /> ← 사이드바 별도 캐시
# <p>사용자별 개인화 콘텐츠는 ESI 밖에</p>

ESI는 페이지를 여러 조각으로 나눠서 각각 다른 TTL로 캐시할 수 있게 해줘. 예를 들어 헤더는 1시간, 뉴스 목록은 5분, 사용자 정보는 캐시 안 함 — 이런 식으로 세밀하게 제어 가능!

3. 다중 백엔드 & 로드 밸런싱

import directors;  # directors VMOD 임포트

backend server1 {
    .host = "192.168.1.10";
    .port = "8080";
    .weight = 2;  # 가중치 2 (더 많은 트래픽 처리)
}

backend server2 {
    .host = "192.168.1.11";
    .port = "8080";
    .weight = 1;
}

backend server3 {
    .host = "192.168.1.12";
    .port = "8080";
    .weight = 1;
}

sub vcl_init {
    # 라운드 로빈 로드 밸런서 생성
    new lb = directors.round_robin();
    lb.add_backend(server1);
    lb.add_backend(server2);
    lb.add_backend(server3);
    
    # 또는 랜덤 방식
    # new lb = directors.random();
    # lb.add_backend(server1, 2.0);  # 가중치 2.0
}

sub vcl_recv {
    set req.backend_hint = lb.backend();
}

🔧 Varnish 모니터링 도구

Varnish는 강력한 내장 모니터링 도구를 제공해:

# 실시간 통계 확인
varnishstat

# 실시간 로그 스트리밍
varnishlog

# 캐시 히트율 확인
varnishstat -1 -f MAIN.cache_hit,MAIN.cache_miss

# 특정 URL 캐시 무효화 (Purge)
varnishadm "ban req.url ~ /news"

# 특정 오브젝트 즉시 삭제
curl -X PURGE http://localhost/path/to/resource
⚠️ Varnish 주의사항!

1. HTTPS 직접 처리 불가 — Varnish 자체는 SSL/TLS를 처리 못해. Nginx나 HAProxy를 앞에 두고 SSL 종료 후 Varnish로 넘겨야 해.

2. 기본적으로 쿠키가 있으면 캐시 안 함 — 로그인 사용자 요청은 기본적으로 캐시를 건너뛰어. VCL에서 명시적으로 설정해야 해.

3. 재시작 시 캐시 초기화 — Varnish를 재시작하면 메모리 캐시가 모두 날아가. 이를 방지하려면 Persistent Storage 설정 필요.

Varnish vs Squid 완전 비교 🚀 Varnish Cache "HTTP 전용 웹 가속기" 📌 주요 특징 • HTTP/HTTPS 트래픽 전용 최적화 • VCL(Varnish Config Language) 사용 • 메모리 기반 초고속 캐싱 • ESI(Edge Side Includes) 지원 • Grace Mode (장애 시 오래된 캐시 제공) ⚡ 성능 지표 응답 속도 매우 빠름 처리량(RPS) 수만 RPS 메모리 효율 높음 ✅ 적합한 사용 사례 • 고트래픽 웹사이트 / 미디어 서비스 • CDN 엣지 서버 구성 • 뉴스, 커머스 사이트 가속 • API 응답 캐싱 🦑 Squid Cache "범용 프록시 캐시 서버" 📌 주요 특징 • HTTP, HTTPS, FTP 등 다양한 프로토콜 • 포워드/리버스 프록시 모두 지원 • 디스크 기반 대용량 캐싱 • ACL(접근 제어 목록) 강력 지원 • 콘텐츠 필터링 / 인터넷 제어 ⚡ 성능 지표 응답 속도 빠름 처리량(RPS) 수천 RPS 저장 용량 매우 큼 ✅ 적합한 사용 사례 • 기업 내부 인터넷 게이트웨이 • ISP 투명 프록시 • 콘텐츠 필터링 / 보안 정책 • 대용량 파일 캐싱 (이미지, 동영상) VS

🦑 Squid Cache — 범용 프록시의 강자

이번엔 Squid를 알아보자! Squid는 1996년부터 시작된 오래된 오픈소스 프록시 서버야. 역사가 길다 보니 안정성이 검증되어 있고, 전 세계 수많은 기업과 ISP에서 사용 중이야.

Squid의 가장 큰 차별점은 범용성이야. Varnish가 HTTP 전용 스포츠카라면, Squid는 뭐든 다 실을 수 있는 트럭 같은 존재야! 🚛

🚀 Varnish

HTTP 전용, 메모리 캐시
초고속, VCL 설정
웹 가속에 특화

🦑 Squid

HTTP/FTP/HTTPS 등 다양
디스크 캐시, ACL 강력
포워드+리버스 모두 가능

📝 Squid 설치 및 기본 설정

# Squid 설치
sudo apt-get update
sudo apt-get install squid

# 설정 파일 위치
# /etc/squid/squid.conf

# 서비스 시작
sudo systemctl start squid
sudo systemctl enable squid

# 설정 파일 문법 검사
sudo squid -k parse

Squid의 설정 파일은 squid.conf야. Varnish의 VCL보다는 좀 더 직관적인 키-값 형태야:

# /etc/squid/squid.conf 핵심 설정

# 포트 설정 (기본 3128)
http_port 3128

# 리버스 프록시 모드 설정
http_port 80 accel vhost defaultsite=example.com

# 백엔드 서버 정의
cache_peer 192.168.1.10 parent 8080 0 no-query originserver name=myapp

# ACL 정의
acl localnet src 192.168.0.0/16    # 내부 네트워크
acl SSL_ports port 443
acl Safe_ports port 80 443 8080
acl CONNECT method CONNECT

# 접근 제어
http_access allow localnet
http_access allow localhost
http_access deny all

# 캐시 디렉토리 설정 (디스크 캐시)
# 형식: cache_dir [타입] [경로] [크기MB] [1단계 디렉토리수] [2단계 디렉토리수]
cache_dir ufs /var/spool/squid 10000 16 256
# 10GB 캐시, 16개 1단계 디렉토리, 256개 2단계 디렉토리

# 메모리 캐시 크기
cache_mem 512 MB

# 최대 오브젝트 크기
maximum_object_size 100 MB
maximum_object_size_in_memory 8 MB

# 로그 설정
access_log /var/log/squid/access.log squid
cache_log /var/log/squid/cache.log

# 워커 프로세스 수 (CPU 코어 수에 맞게)
workers 4

🔐 Squid ACL — 강력한 접근 제어

Squid의 ACL(Access Control List)은 정말 강력해. 다양한 기준으로 트래픽을 제어할 수 있어:

# 다양한 ACL 타입 예시

# IP 기반 ACL
acl office_network src 10.0.0.0/8
acl blocked_ips src "/etc/squid/blocked_ips.txt"

# 도메인 기반 ACL
acl social_media dstdomain .facebook.com .twitter.com .instagram.com
acl allowed_sites dstdomain "/etc/squid/allowed_sites.txt"

# 시간 기반 ACL
acl work_hours time MTWHF 09:00-18:00
acl lunch_break time MTWHF 12:00-13:00

# URL 패턴 ACL
acl streaming_urls url_regex -i youtube netflix

# 파일 타입 ACL
acl media_files urlpath_regex -i \.(mp4|avi|mkv|mov)$

# 접근 제어 규칙 적용
http_access deny social_media work_hours  # 업무시간 SNS 차단
http_access allow social_media lunch_break  # 점심시간 SNS 허용
http_access deny streaming_urls  # 스트리밍 차단
http_access allow office_network  # 사무실 네트워크 허용
http_access deny all  # 나머지 모두 차단
💡 Squid ACL 활용 팁!
기업 환경에서 Squid를 쓰는 가장 큰 이유 중 하나가 바로 이 ACL이야. 업무 시간에 SNS 차단, 특정 사이트만 허용, 대용량 파일 다운로드 제한 등 세밀한 인터넷 정책을 구현할 수 있거든. IT 관리자들의 필수 도구!

🗄️ Squid 캐시 스토리지 전략

Squid는 여러 가지 캐시 스토리지 타입을 지원해:

# 1. UFS (Unix File System) - 가장 기본적인 방식
cache_dir ufs /var/spool/squid 10000 16 256

# 2. AUFS (Async UFS) - 비동기 I/O로 성능 향상
cache_dir aufs /var/spool/squid 10000 16 256

# 3. COSS (Cyclic Object Storage System) - 순환 저장
cache_dir coss /var/spool/squid/coss 2000 max-size=65536

# 4. Rock - SSD에 최적화된 고성능 스토리지
cache_dir rock /var/spool/squid/rock 5000 \
    slot-size=32768 max-size=32768

# 여러 캐시 디렉토리 조합 (SSD + HDD)
cache_dir rock /ssd/squid 2000 slot-size=32768  # SSD: 빠른 접근
cache_dir aufs /hdd/squid 50000 16 256          # HDD: 대용량

# 캐시 교체 정책
cache_replacement_policy heap LFUDA  # Least Frequently Used with Dynamic Aging
# 또는
cache_replacement_policy heap GDSF   # Greedy Dual Size Frequency

🔍 Squid 투명 프록시 설정

투명 프록시(Transparent Proxy)는 클라이언트가 프록시 설정을 하지 않아도 자동으로 트래픽이 Squid를 통과하게 만드는 방식이야. 기업 네트워크에서 많이 써:

# 투명 프록시 설정
http_port 3128 intercept  # intercept 모드

# iptables로 트래픽 리다이렉트 (Linux)
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp \
    --dport 80 -j REDIRECT --to-port 3128

# IPv6도 처리
sudo ip6tables -t nat -A PREROUTING -i eth0 -p tcp \
    --dport 80 -j REDIRECT --to-port 3128

# HTTPS 투명 프록시 (SSL Bump)
https_port 3129 intercept ssl-bump \
    cert=/etc/squid/ssl_cert/myCA.pem \
    key=/etc/squid/ssl_cert/myCA.key

ssl_bump server-first all

실전 고성능 웹 서버 아키텍처 (Nginx + Varnish + App Server + Redis + DB 조합) 🌐 인터넷 트래픽 🛡️ Nginx SSL 종료 · 로드밸런서 · 정적파일 서빙 ⚡ Varnish Cache HTTP 캐싱 · ESI · Grace Mode · 캐시 히트 90%+ ⚡ 캐시 HIT 즉시 응답! 🖥️ App Server 1 Node.js / Python 포트: 8001 🖥️ App Server 2 Node.js / Python 포트: 8002 🖥️ App Server 3 Node.js / Python 포트: 8003 🔴 Redis 세션 · 캐시 🗄️ MySQL / PostgreSQL Primary DB + Replica Layer 1 SSL/LB Layer 2 캐시 Layer 3 앱 Layer 4 데이터

🏗️ 실전 아키텍처 구성

자, 이제 실제로 어떻게 조합해서 쓰는지 알아보자! 위 SVG에서 보여준 것처럼 Nginx + Varnish + App Server 조합이 가장 일반적인 고성능 웹 서버 구성이야.

🔧 Nginx + Varnish 연동 설정

Nginx가 80/443 포트를 받아서 SSL 처리 후 Varnish(6081 포트)로 넘기고, Varnish가 캐시 처리 후 백엔드 앱 서버로 전달하는 구조야:

# /etc/nginx/sites-available/example.com
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    
    # SSL 설정
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    
    # Varnish로 프록시 (HTTP로 전달)
    location / {
        proxy_pass http://127.0.0.1:6081;  # Varnish 포트
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 타임아웃 설정
        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
    }
    
    # 정적 파일은 Nginx에서 직접 서빙 (Varnish 우회)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        root /var/www/example.com/public;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

# HTTP → HTTPS 리다이렉트
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
# /etc/varnish/default.vcl - Nginx와 연동
vcl 4.1;

backend app1 {
    .host = "127.0.0.1";
    .port = "8001";
    .probe = {
        .url = "/health";
        .timeout = 2s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

backend app2 {
    .host = "127.0.0.1";
    .port = "8002";
    .probe = {
        .url = "/health";
        .timeout = 2s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

import directors;

sub vcl_init {
    new cluster = directors.round_robin();
    cluster.add_backend(app1);
    cluster.add_backend(app2);
}

sub vcl_recv {
    set req.backend_hint = cluster.backend();
    
    # X-Forwarded-For 헤더 처리
    if (req.http.X-Forwarded-For) {
        set req.http.X-Real-IP = req.http.X-Forwarded-For;
    }
    
    # 정적 파일 캐시 (쿠키 무시)
    if (req.url ~ "\.(jpg|jpeg|png|gif|ico|css|js|woff2)(\?.*)?$") {
        unset req.http.Cookie;
        return (hash);
    }
    
    # 로그인 사용자 캐시 처리
    if (req.http.Cookie ~ "session_id") {
        return (pass);  # 로그인 사용자는 캐시 우회
    }
    
    # PURGE 요청 처리 (캐시 무효화)
    if (req.method == "PURGE") {
        if (req.http.X-Purge-Token != "your-secret-token") {
            return (synth(403, "Forbidden"));
        }
        return (purge);
    }
    
    return (hash);
}

sub vcl_hash {
    hash_data(req.url);
    
    # 모바일/데스크톱 별도 캐시
    if (req.http.User-Agent ~ "Mobile|Android|iPhone") {
        hash_data("mobile");
    } else {
        hash_data("desktop");
    }
    
    hash_data(req.http.host);
    return (lookup);
}

sub vcl_backend_response {
    # 404, 500 에러는 짧게 캐시
    if (beresp.status == 404) {
        set beresp.ttl = 30s;
    } else if (beresp.status >= 500) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    } else {
        set beresp.ttl = 10m;  # 기본 10분 캐시
        set beresp.grace = 1h;  # Grace 1시간
    }
    
    # 정적 파일은 더 오래 캐시
    if (bereq.url ~ "\.(jpg|jpeg|png|gif|ico|css|js)$") {
        set beresp.ttl = 24h;
    }
    
    return (deliver);
}

sub vcl_deliver {
    # 디버깅 헤더 추가
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        set resp.http.X-Cache = "MISS";
    }
    
    # 불필요한 헤더 제거
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    
    return (deliver);
}

🔄 캐시 무효화(Cache Invalidation) 전략

캐시의 가장 어려운 문제 중 하나가 바로 캐시 무효화야. 데이터가 업데이트됐을 때 오래된 캐시를 어떻게 처리할 것인가!

🤣 유명한 개발자 격언
"컴퓨터 과학에서 어려운 문제는 딱 두 가지다. 캐시 무효화와 이름 짓기."
— Phil Karlton

그만큼 캐시 무효화는 까다로운 문제야. 전략을 잘 세워야 해!
# 1. TTL 기반 자동 만료 (가장 단순)
set beresp.ttl = 5m;  # 5분 후 자동 만료

# 2. PURGE 요청으로 즉시 삭제
# Varnish에 PURGE 요청 보내기
curl -X PURGE -H "X-Purge-Token: your-secret-token" \
     http://localhost:6081/news/article/123

# 3. BAN을 이용한 패턴 기반 무효화
# 특정 패턴의 URL 모두 무효화
varnishadm "ban req.url ~ ^/news/"
varnishadm "ban req.http.host == example.com && req.url ~ /product/"

# 4. 태그 기반 무효화 (Surrogate-Key / Cache-Tag)
# 백엔드에서 응답 헤더에 태그 추가
# Response Header: Surrogate-Key: product-123 category-electronics

sub vcl_backend_response {
    # 태그 헤더를 Xkey로 저장
    if (beresp.http.Surrogate-Key) {
        set beresp.http.xkey = beresp.http.Surrogate-Key;
    }
}

# 특정 태그의 캐시 모두 무효화
varnishadm "xkey.purge product-123"

📊 성능 벤치마크 & 튜닝

실제로 얼마나 빨라지는지 궁금하지? 🤩 실제 벤치마크 결과들을 보면 놀라울 거야!

⚡
10x
Varnish 적용 후
평균 응답속도 향상
📉
90%
백엔드 서버
부하 감소
🚀
100k+
Varnish 초당
처리 요청 수

🔧 Varnish 성능 튜닝 핵심 파라미터

# /etc/varnish/varnish.params 또는 systemd 서비스 파일

# 메모리 캐시 크기 (RAM의 50-70% 권장)
VARNISH_STORAGE="malloc,4G"  # 4GB 메모리 캐시

# 또는 파일 기반 스토리지 (대용량)
VARNISH_STORAGE="file,/var/lib/varnish/varnish_storage.bin,20G"

# 워커 스레드 설정
VARNISH_NCSA_LOG=1

# varnishd 직접 실행 시 파라미터
varnishd \
    -a 0.0.0.0:6081 \          # 리스닝 포트
    -T localhost:6082 \         # 관리 포트
    -f /etc/varnish/default.vcl \
    -s malloc,4G \              # 4GB 메모리 캐시
    -p thread_pools=2 \         # CPU 코어 수에 맞게
    -p thread_pool_min=100 \    # 최소 스레드 수
    -p thread_pool_max=5000 \   # 최대 스레드 수
    -p thread_pool_add_delay=2ms \
    -p connect_timeout=5s \
    -p first_byte_timeout=30s \
    -p between_bytes_timeout=60s \
    -p workspace_backend=256k \ # 백엔드 워크스페이스
    -p workspace_client=256k    # 클라이언트 워크스페이스

🔧 Squid 성능 튜닝

# /etc/squid/squid.conf 성능 튜닝

# 메모리 설정
cache_mem 1024 MB                    # 메모리 캐시 1GB
memory_replacement_policy heap GDSF  # 메모리 교체 정책

# 디스크 캐시 최적화
cache_dir aufs /var/spool/squid 20000 16 256  # 20GB
cache_replacement_policy heap LFUDA           # 디스크 교체 정책

# 연결 설정
max_filedescriptors 65536  # 최대 파일 디스크립터
client_persistent_connections on
server_persistent_connections on

# 요청 처리 최적화
pipeline_prefetch 1        # 파이프라인 프리페치
collapsed_forwarding on    # 동일 요청 병합 (캐시 스탬피드 방지)

# DNS 캐싱
dns_nameservers 8.8.8.8 8.8.4.4
positive_dns_ttl 6 hours
negative_dns_ttl 1 minute

# 로그 최적화 (성능 중요 시 로그 최소화)
access_log none
cache_log /var/log/squid/cache.log
cache_store_log none

# 워커 프로세스 (멀티코어 활용)
workers 8  # CPU 코어 수에 맞게

🛠️ 실전 트러블슈팅

실제로 운영하다 보면 이런 문제들을 자주 만나게 돼. 미리 알아두면 당황하지 않을 수 있어! 😎

🚨 문제 1: 캐시가 전혀 안 되는 경우

원인들:
• Set-Cookie 헤더가 응답에 포함되어 있음
• Cache-Control: no-cache, no-store 헤더
• Authorization 헤더가 요청에 포함됨
• POST/PUT/DELETE 메서드 요청

해결:
# Varnish에서 불필요한 Set-Cookie 제거
sub vcl_backend_response {
    # 정적 파일의 Set-Cookie 헤더 제거
    if (bereq.url ~ "\.(jpg|png|css|js)$") {
        unset beresp.http.Set-Cookie;
    }
    
    # Cache-Control 헤더 강제 설정
    if (beresp.http.Cache-Control ~ "no-cache") {
        unset beresp.http.Cache-Control;
        set beresp.ttl = 5m;
    }
}

# 요청에서 불필요한 쿠키 제거
sub vcl_recv {
    # 트래킹 쿠키 제거 (캐시 히트율 향상)
    set req.http.Cookie = regsuball(req.http.Cookie,
        "(^|;\s*)(_ga|_gid|_fbp|__utm[a-z]+)=[^;]*", "");
    
    # 쿠키가 비어있으면 헤더 제거
    if (req.http.Cookie ~ "^\s*$") {
        unset req.http.Cookie;
    }
}
🚨 문제 2: 캐시 스탬피드(Cache Stampede) / 썬더링 허드(Thundering Herd)

캐시가 만료되는 순간 수천 개의 요청이 동시에 백엔드로 쏟아지는 현상이야. 서버가 순간적으로 과부하 상태가 되지.
# Varnish에서 캐시 스탬피드 방지
sub vcl_recv {
    # 캐시 만료 전에 미리 갱신 (Saint Mode)
    set req.grace = 30s;  # 30초 동안 오래된 캐시 허용
}

sub vcl_backend_response {
    set beresp.ttl = 10m;
    set beresp.grace = 1h;  # 1시간 Grace 기간
    
    # 백그라운드 갱신 활성화
    set beresp.keep = 8h;  # 8시간 동안 백그라운드 갱신 가능
}

# Squid에서 캐시 스탬피드 방지
# squid.conf
collapsed_forwarding on  # 동일 URL 요청 병합
# 동일한 캐시 미스 요청이 여러 개 들어오면 하나만 백엔드로 전달
🚨 문제 3: 메모리 부족 (OOM)

Varnish가 메모리를 너무 많이 사용해서 시스템이 OOM Killer에 의해 프로세스를 죽이는 경우야.
# 메모리 사용량 모니터링
varnishstat -1 -f MAIN.n_object,MAIN.n_expired,SMA.s0.g_bytes

# 메모리 제한 설정
# /etc/systemd/system/varnish.service
[Service]
MemoryLimit=6G  # 최대 6GB 메모리 사용 제한

# 오브젝트 크기 제한
# VCL에서 큰 오브젝트 캐시 제외
sub vcl_backend_response {
    # 10MB 이상 오브젝트는 캐시하지 않음
    if (beresp.http.Content-Length ~ "[0-9]{8,}") {
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    }
}

🎯 언제 뭘 써야 할까?

자, 이제 Varnish와 Squid를 모두 알았으니 어떤 상황에서 뭘 선택해야 하는지 정리해보자!

✅ Varnish를 선택해야 할 때

고트래픽 웹사이트 미디어/뉴스 서비스 API 게이트웨이 CDN 구성

• 초당 수만~수십만 HTTP 요청을 처리해야 할 때
• 세밀한 캐시 제어 로직이 필요할 때 (VCL 활용)
• 웹 콘텐츠 가속이 주 목적일 때
• ESI로 페이지 조각 캐싱이 필요할 때
• 메모리가 충분하고 디스크 캐시가 필요 없을 때
✅ Squid를 선택해야 할 때

기업 네트워크 ISP 프록시 콘텐츠 필터링 대용량 파일 캐싱

• HTTP 외 FTP, HTTPS 투명 프록시가 필요할 때
• 강력한 ACL 기반 접근 제어가 필요할 때
• 대용량 파일(이미지, 동영상)을 디스크에 캐시할 때
• 포워드 프록시 + 리버스 프록시 모두 필요할 때
• 기업 인터넷 게이트웨이로 사용할 때
🤝 Varnish + Squid 함께 쓰는 경우도 있어!

대형 미디어 회사나 CDN 업체에서는 두 가지를 함께 쓰기도 해.
예를 들어 Squid를 포워드 프록시로 내부 직원들의 인터넷 접근 제어에 쓰고,
Varnish를 리버스 프록시로 외부 사용자들에게 서비스를 제공하는 식으로!

캐시 요청 처리 플로우차트 👤 클라이언트 HTTP 요청 GET /news/article/123 🔑 캐시 키 생성 URL + Host + 기타 조건 캐시에 있나요? ✅ HIT ⚡ 캐시 히트! 저장된 응답 반환 ✅ 응답 전달 ~1ms 이내 ❌ MISS 🔄 캐시 미스 백엔드 요청 필요 🖥️ 백엔드 처리 DB 조회 + 로직 실행 💾 캐시 저장 다음 요청을 위해 📤 클라이언트에게 응답 X-Cache: MISS 헤더 포함

🌐 실제 서비스 적용 사례

이론은 충분히 알았으니, 실제로 어떤 서비스들이 이걸 쓰는지 알아보자!

1
위키피디아(Wikipedia) — Varnish를 대규모로 사용하는 대표적인 사례야. 전 세계 수억 명이 접속하는 위키피디아는 Varnish 클러스터를 통해 엄청난 트래픽을 처리해. 캐시 히트율이 90% 이상이라고 알려져 있어.
2
Fastly CDN — 글로벌 CDN 서비스인 Fastly는 Varnish를 기반으로 구축됐어. Varnish의 공동 창업자들이 만든 회사이기도 해. GitHub, Twitter, Spotify 등이 Fastly를 사용하지.
3
대형 ISP들 — KT, SK브로드밴드 같은 인터넷 서비스 제공업체들은 Squid를 투명 프록시로 사용해서 자주 요청되는 콘텐츠를 캐시해 대역폭을 절약해.
4
국내 대형 포털/커머스 — 네이버, 카카오, 쿠팡 같은 대형 서비스들도 자체 CDN과 캐시 레이어를 구축해서 사용해. 물론 Varnish 외에도 자체 개발 솔루션을 쓰기도 하지.
💡 재능넷에서도 활용 가능!
재능넷처럼 다양한 재능을 거래하는 플랫폼에서도 리버스 프록시와 캐시 서버는 필수야. 재능 목록 페이지, 인기 재능 카테고리, 검색 결과 등 자주 조회되는 콘텐츠를 Varnish로 캐시하면 서버 부하를 크게 줄이고 사용자 경험을 향상시킬 수 있어. 특히 트래픽이 몰리는 이벤트 기간에 진가를 발휘하지!

🔮 최신 트렌드 & 대안들

Varnish와 Squid 외에도 최근에는 다양한 대안들이 등장했어. 알아두면 좋아!

🟢 Nginx (캐시 기능)

proxy_cache 모듈로 기본적인 캐싱 가능. 이미 Nginx를 쓰고 있다면 별도 서버 없이 캐시 추가 가능. 단, Varnish보다는 기능이 제한적.

🔴 Redis / Memcached

애플리케이션 레벨 캐시. HTTP 레이어가 아닌 코드 레벨에서 캐시. DB 쿼리 결과, 세션 등을 캐시하는 데 특화.

🔵 Cloudflare / AWS CloudFront

클라우드 기반 CDN/캐시. 인프라 관리 없이 글로벌 캐시 가능. 비용이 발생하지만 운영 부담이 적음.

🟣 Traefik

쿠버네티스 환경에 특화된 현대적 리버스 프록시. 자동 서비스 디스커버리, Let's Encrypt 자동 갱신 등 DevOps 친화적.

🎯 2024년 기준 추천 스택

소규모 서비스: Nginx (캐시 기능 내장) + Redis
중규모 서비스: Nginx (SSL) + Varnish (캐시) + App Server
대규모 서비스: Cloudflare/CDN + Nginx + Varnish + App Cluster + Redis
기업 내부망: Squid (포워드 프록시 + 필터링)
쿠버네티스: Traefik/Nginx Ingress + Redis

📝 마무리 정리

자, 오늘 엄청 많은 내용을 다뤘는데 핵심만 다시 정리해볼게! 🎯

✓
리버스 프록시는 서버 앞에 서서 클라이언트 요청을 대신 받아주는 "대리인"이야. 로드 밸런싱, SSL 종료, 캐싱, 보안 등 다양한 역할을 해.
✓
Varnish는 HTTP 전용 웹 가속기야. VCL로 세밀한 캐시 제어, Grace Mode, ESI 등 강력한 기능을 제공해. 고트래픽 웹 서비스에 최적!
✓
Squid는 범용 프록시 서버야. HTTP/FTP/HTTPS 등 다양한 프로토콜, 강력한 ACL, 대용량 디스크 캐시를 지원해. 기업 네트워크 게이트웨이에 최적!
✓
실전 구성은 Nginx(SSL 종료) + Varnish(캐시) + App Server 조합이 가장 일반적이야. 캐시 히트율 90% 이상이면 서버 부하가 10분의 1로 줄어들어!
✓
캐시 무효화는 TTL, PURGE, BAN, 태그 기반 방식을 상황에 맞게 조합해서 사용해야 해. 캐시 스탬피드 방지도 잊지 말고!
🚀 ⚡ 🛡️ 🎯 💪
🎓 더 깊이 공부하고 싶다면?

• Varnish 공식 문서: varnish-cache.org/docs
• Squid 공식 문서: squid-cache.org/Doc
• The Varnish Book: Varnish Software에서 무료 제공하는 공식 가이드
• High Performance Browser Networking (O'Reilly): 웹 성능 최적화 바이블

그리고 재능넷에서 서버 최적화 전문가를 찾아서 직접 컨설팅을 받는 것도 좋은 방법이야! 이론을 실제 프로젝트에 적용하는 건 또 다른 차원의 경험이거든. 😉

이 글이 도움이 됐다면 주변 개발자 친구들에게도 공유해줘! 🙌
서버 성능 최적화는 혼자 하는 것보다 팀 전체가 이해하고 함께 적용할 때 진짜 빛을 발하거든.

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.