1. Nginx 개요 및 HTTP 프로토콜
1.1 Nginx 특성
Nginx는 고성능 경량 웹 서버 및 리버스 프록시 소프트웨어로, 다음과 같은 장점을 제공합니다:
- 높은 안정성과 낮은 리소스 소비
- HTTP 동시 연결 처리 능력 우수 (단일 물리 서버에서 30,000~50,000개 동시 요청 처리 가능)
단점으로는 동적 콘텐츠 처리 성능이 상대적으로 떨어지며, Apache에 비해 rewrite 기능이 제한적입니다.
1.2 HTTP 프로토콜 발전
| 버전 | 특징 |
|---|---|
| HTTP/0.9 | 텍스트 전용 단순 프로토콜, 콘텐츠 협상 불가 |
| HTTP/1.0 | 요청/응답마다 연결 생성 및 종료, 트랜잭션 중심 |
| HTTP/1.1 | 지속 연결(Persistent Connection) 지원, 파이프라이닝 |
| HTTP/2 | 바이너리 프레이밍, 헤더 압축(HPACK), 서버 푸시 |
1.3 프록시 방식 비교
포워드 프록시(Forward Proxy): 클라이언트와 대상 서버 사이에 위치하여 클라이언트를 대신해 요청을 전달합니다. 클라이언트는 프록시 설정이 필요하고, 실제 서버 IP는 숨겨집니다.
리버스 프록시(Reverse Proxy): 클이언트 입장에서는 목표 서버처럼 동작합니다. 클라이언트 설정 없이 프록시 서버를 통해 내부 서버에 접근하며, 실제 서버 정보가 클라이언트에 노출되지 않습니다.
2. 설치 및 기본 설정
2.1 소스 컴파일 설치 스크립트
#!/bin/bash
set -e
# 방화벽 초기화 및 의존성 설치
iptables -F
yum install -y epel-release
yum clean all && yum makecache
yum install -y pcre-devel zlib-devel gcc gcc-c++ make wget openssl-devel
# 전용 계정 생성
id -u webuser &>/dev/null || useradd -M -s /sbin/nologin webuser
# 소스 다운로드 및 압축 해제
NGINX_VER="1.24.0"
wget "https://nginx.org/download/nginx-${NGINX_VER}.tar.gz" -P /tmp
tar -zxf "/tmp/nginx-${NGINX_VER}.tar.gz" -C /opt
cd "/opt/nginx-${NGINX_VER}"
# 컴파일 설정
./configure \
--prefix=/apps/nginx \
--user=webuser \
--group=webuser \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream \
--with-stream_ssl_module
make -j$(nproc) && make install
# 심볼릭 링크 생성
ln -sf /apps/nginx/sbin/nginx /usr/local/bin/
# systemd 서비스 등록
cat > /etc/systemd/system/web-nginx.service <<'EOF'
[Unit]
Description=Nginx Web Server
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/apps/nginx/logs/nginx.pid
ExecStartPre=/apps/nginx/sbin/nginx -t
ExecStart=/apps/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now web-nginx
# 설치 확인
if pgrep -x "nginx" > /dev/null; then
echo -e "\e[32mNginx 정상 실행 중\e[0m"
else
echo -e "\e[31mNginx 실행 실패\e[0m"
fi
2.2 기본 설정 파일 구조
webuser webuser;
worker_processes auto;
events {
worker_connections 2048;
use epoll;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server {
listen 80;
server_name _;
location / {
root /apps/nginx/html;
index index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /apps/nginx/html;
}
}
}
3. 핵 기능 구현
3.1 포워드 프록시 설정
server {
resolver 8.8.8.8 8.8.4.4;
listen 8080;
location / {
proxy_pass http://$host$request_uri;
proxy_set_header Host $host;
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffers 256 4k;
}
}
server {
resolver 8.8.8.8 8.8.4.4;
listen 8443 ssl;
ssl_certificate /apps/nginx/certs/proxy.crt;
ssl_certificate_key /apps/nginx/certs/proxy.key;
location / {
proxy_pass https://$host$request_uri;
proxy_connect_timeout 30s;
}
}
3.2 리버스 프록시 및 로드밸싱
upstream backend_pool {
least_conn; # 최소 연결 알고리즘
server 10.0.1.10:8080 weight=5;
server 10.0.1.11:8080 weight=3;
server 10.0.1.12:8080 backup; # 백업 서버
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_pool;
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;
}
}
3.3 동적/정적 콘텐츠 분리
upstream app_servers {
server 127.0.0.1:9000;
server 127.0.0.1:9001;
}
server {
listen 80;
server_name www.example.com;
root /data/web/static;
# 정적 파일 직접 처리
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# 동적 요청 애플리케이션 서버로 전달
location ~ \.(php|jsp|do)$ {
proxy_pass http://app_servers;
proxy_hide_header X-Powered-By;
}
location / {
try_files $uri $uri/ =404;
}
}
4. 로드밸런싱 알고리즘
| 알고리즘 | 지시어 | 설명 |
|---|---|---|
| 라운드 로빈 | 기본값 | 요청을 순차적으로 분배 |
| 가중치 라운드 로빈 | weight | 서버 성능에 따른 가중치 적용 |
| IP 해시 | ip_hash | 클라이언트 IP 기반 세션 고정 |
| 최소 연결 | least_conn | 현재 연결 수가 가장 적은 서버 선택 |
| 최소 응답 시간 | fair | 응답 시간이 빠른 서버 우선 (타사 모듈) |
| URL 해시 | hash $request_uri | 요청 URI 기반 캐시 최적화 (타사 모듈) |
5. 로그 포맷 및 분석
5.1 사용자 정의 로그 포맷
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'$upstream_addr $upstream_status';
access_log /apps/nginx/logs/access.log detailed;
5.2 로그 필드 설명
| 변수 | 의미 |
|---|---|
| $remote_addr | 클라이언트 IP 주소 |
| $request_time | 전체 요청 처리 시간 |
| $upstream_response_time | 백엔드 서버 응답 시간 |
| $upstream_addr | 실제 요청 처리한 백엔드 서버 |
| $http_x_forwarded_for | 프록시 체인을 통한 원본 클라이언트 IP |
6. curl 명령어 활용
| 옵션 | 기능 | 예시 |
|---|---|---|
| -X, --request | HTTP 메서드 지정 | curl -X POST URL |
| -H, --header | 요청 헤더 추가 | curl -H "Accept: application/json" |
| -d, --data | POST 데이터 전송 | curl -d "key=value" |
| -i, --include | 응답 헤더 포함 출력 | curl -i URL |
| -I, --head | 헤더만 조회 | curl -I URL |
| -v, --verbose | 상세 통신 과정 출력 | curl -v URL |
| -k, --insecure | SSL 인증서 검증 무시 | curl -k https://... |
| -o, --output | 출력 파일 지정 | curl -o file.html URL |
| -L, --location | 리다이렉트 따라가기 | curl -L URL |
7. HTTP 메서드 비교
| 메서드 | 목적 | 특성 |
|---|---|---|
| GET | 리소스 조회 | URL 파라미터, 캐시 가능, 브라우저 히스토리 저장 |
| POST | 리소스 생성 | 요청 본문 전송, 데이터 크기 제한 없음 |
| PUT | 리소스 전체 수정 | 멱등성 보장, 전체 교체 |
| PATCH | 리소스 부분 수정 | 부분 업데이트 |
| DELETE | 리소스 삭제 | 멱등성 보장 |
| HEAD | 메타데이터 조회 | 본문 없이 헤더만 반환 |
| OPTIONS | 지원 메서드 확인 | CORS 프리플라이트에 사용 |
8. Tcpdump를 활용한 패킷 캡처
8.1 기본 사용법
# 특정 포트 패킷 캡처
tcpdump -n -i any port 80
# 상세 정보 출력
tcpdump -nn -v -i eth0 port 443
# 파일로 저장 (Wireshark 분석용)
tcpdump -nn -i any port 80 -w /tmp/capture.pcap
# 호스트 기반 필터링
tcpdump -nn host 192.168.1.100 and port 8080
# TCP 플래그 필터링 (SYN 패킷만)
tcpdump -nn "tcp[tcpflags] & tcp-syn != 0"
8.2 출력 해석
13:30:31.068361 IP 192.168.10.65.51238 > 192.168.10.20.80: Flags [S], seq 2461555369, win 29200, options [mss 1460,sackOK,TS val 187463682 ecr 0,nop,wscale 7], length 0
타임스탬프, 프로토콜, 출발지.포트 > 목적지.포트, TCP 플래그, 시퀀스 번호, 윈도우 크기, 옵션 순으로 표시됩니다.
9. Wireshark 분석
9.1 캡처 파일 분석 절차
- tcpdump로 생성한 .pcap 파일을 Wireshark로 열기
- 필터 적용:
ip.addr==192.168.10.20 && tcp.port==80 - 특정 TCP 스트림 팔로우: 마우스 우클릭 → "Follow" → "TCP Stream"
- HTTP 요청/응답 분석: "Statistics" → "HTTP" → "Packet Counter"
9.2 유용한 디스플레이 필터
http.request.method == "GET"
http.response.code == 200
tcp.analysis.retransmission # 재전송 패킷
tcp.flags.syn == 1 # SYN 패킷만
ssl.handshake.type == 1 # Client Hello
10. TLS/SSL 인증서 설정
10.1 OpenSSL 자체 서명 인증서 생성
# CA 개인키 및 인증서 생성
mkdir -p /apps/nginx/certs && cd /apps/nginx/certs
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \
-subj "/C=KR/ST=Seoul/L=Seoul/O=MyOrg/OU=IT/CN=RootCA"
# 서버 인증서 생성
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server-req.pem \
-subj "/C=KR/ST=Seoul/L=Seoul/O=MyOrg/OU=IT/CN=*.example.com"
openssl x509 -req -days 365 -in server-req.pem \
-CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem
10.2 HTTPS 서버 설정
server {
listen 443 ssl http2;
server_name secure.example.com;
ssl_certificate /apps/nginx/certs/server-cert.pem;
ssl_certificate_key /apps/nginx/certs/server-key.pem;
ssl_trusted_certificate /apps/nginx/certs/ca-cert.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
location / {
root /data/web/secure;
index index.html;
}
}
11. TCP/UDP 스트림 프록시
11.1 TCP 프록시 설정
stream {
upstream db_cluster {
least_conn;
server 10.0.2.10:3306;
server 10.0.2.11:3306;
}
upstream mongo_cluster {
server 10.0.2.20:27017;
server 10.0.2.21:27017;
}
server {
listen 3306;
proxy_pass db_cluster;
proxy_connect_timeout 5s;
proxy_timeout 300s;
}
server {
listen 27017;
proxy_pass mongo_cluster;
}
}
11.2 UDP 프록시 설정
stream {
server {
listen 53 udp;
proxy_pass 8.8.8.8:53;
proxy_timeout 1s;
proxy_responses 1;
error_log logs/dns-proxy.log;
}
server {
listen 514 udp;
proxy_pass syslog_backend;
proxy_buffer_size 16k;
}
upstream syslog_backend {
server 10.0.3.10:514;
server 10.0.3.11:514;
}
}
stream 블록은 http 블록과 동일한 계층에 위치하며, events 블록 이후에 선언합니다. TCP와 UDP 프록시 모두 --with-stream 컴파일 옵션이 필요합니다.