17 KiB
o2o-infrakit
aio2o의 분산된 인프라 서비스 및 설정을 단일 프로젝트로 통합하여 구축한 범용 인프라 솔루션입니다.
주요 기능
- FastAPI 애플리케이션 서버: Gunicorn + Uvicorn ASGI 워커 기반의 비동기 Python 웹 서버
- Nginx 리버스 프록시: SSL/TLS 지원, 정적 파일 서빙, WebSocket 지원
- 캐싱: Redis 기반 캐싱 지원
- SSL 인증서 자동화: Let's Encrypt + Certbot 자동 갱신
- 로그 관리: 일별 로그 로테이션 및 30일 보관
기술 스택
| 구분 | 기술 |
|---|---|
| 언어 | Python 3.13 |
| 프레임워크 | FastAPI |
| ASGI 서버 | Gunicorn + Uvicorn |
| 웹 서버 | Nginx 1.26 |
| 캐시 | Redis |
| 컨테이너 | Docker, Docker Compose |
디렉토리 구조
o2o-infrakit/
├── compose/ # Docker Compose 설정
│ ├── docker-compose.yml # 메인 오케스트레이션 파일
│ ├── .env # 환경 변수
│ ├── redis/ # Redis 설정 및 데이터
│ └── ssl/ # SSL 인증서 및 Let's Encrypt
│
├── config/ # 서비스별 설정
│ ├── app-server/ # 애플리케이션 서버 설정
│ │ └── gunicorn_uvicorn.conf.py
│ └── web-server/ # 웹 서버 설정
│ ├── nginx_conf/ # Nginx 메인 설정
│ ├── conf.d/ # 도메인별 Nginx 설정
│ └── proxy_params/ # 프록시 헤더 설정
│
├── docker/ # Docker 이미지
│ ├── gunicorn/ # Python 앱 서버 Dockerfile
│ └── nginx/ # Nginx Dockerfile
│
├── script/ # 유틸리티 스크립트
│ ├── letsencrypt.sh # SSL 인증서 설정
│ ├── crontab_gunicorn_set.sh # Cron 작업 설정
│ └── logrotate/ # 로그 로테이션 설정
│
├── log/ # 로그 디렉토리 (볼륨)
│ ├── nginx/ # Nginx 액세스/에러 로그
│ └── uvicorn/ # Uvicorn/FastAPI 로그
│
└── www/ # 애플리케이션 코드 볼륨
시스템 요구사항
- CPU: 4코어 이상
- RAM: 4GB 이상
- 스토리지: SSD 권장
- OS: Linux (Ubuntu 24.04 권장)
- Docker: 20.10 이상
- Docker Compose: 2.0 이상
빠른 시작
1. 환경 변수 설정
cd compose
cp .env.example .env
# .env 파일을 프로젝트에 맞게 수정
주요 환경 변수:
PROJECT_DIR: 애플리케이션 프로젝트 디렉토리명
2. Redis 볼륨 권한 설정
cd compose
chmod +x set_mysql_permission.sh
./set_mysql_permission.sh
3. SSL 인증서 설정 (선택)
Let's Encrypt를 통해 무료 SSL 인증서를 발급받습니다. 이 스크립트는 Nginx 컨테이너 내부에서 실행해야 합니다.
docker exec -it nginx-uvicorn-webserver bash
cd /script
chmod +x letsencrypt.sh
./letsencrypt.sh
스크립트 실행 시 아래 3가지 정보를 순서대로 입력해야 합니다:
| 입력 항목 | 설명 | 예시 |
|---|---|---|
| webroot_folder | Nginx 웹 루트 디렉토리명 (/www/ 하위 경로) |
o2o-castad-frontend |
| domain | 인증서를 발급할 도메인 (공백으로 구분하여 여러 개 입력 가능, 메인 도메인을 먼저 입력) | example.com www.example.com api.example.com |
| 인증서 만료 알림을 받을 이메일 주소 | admin@example.com |
스크립트 동작 순서:
- Certbot 및 필수 패키지 설치
- OpenSSL DH 파라미터 생성 (4096bit, 최초 1회)
- Certbot을 통한 SSL 인증서 발급 (webroot 방식)
- 자동 갱신 cron 등록 (매주 월요일 오전 5시)
4. Nginx 설정 파일 생성
서비스 도메인에 맞는 Nginx conf 파일을 자동 생성하는 스크립트입니다. 템플릿 기반으로 동작하며, HTTP 또는 HTTPS 용도에 따라 스크립트를 선택합니다.
cd config/web-server
HTTP 설정 파일 생성
chmod +x nginx_conf.sh
./nginx_conf.sh
sample_nginx.conf 템플릿을 기반으로 HTTP conf 파일을 생성합니다.
HTTPS 설정 파일 생성
chmod +x nginx_https_conf.sh
./nginx_https_conf.sh
sample_nginx_https.conf 템플릿을 기반으로 HTTPS conf 파일을 생성합니다. HTTP → HTTPS 리다이렉트, SSL/TLS 설정, 보안 헤더, WebSocket 지원 등이 포함됩니다.
입력 항목
두 스크립트 모두 동일한 항목을 입력받습니다:
| 입력 항목 | 필수 | 설명 | 예시 |
|---|---|---|---|
| webroot | O | 웹 루트 경로 (/www/ 하위). 하위 경로가 있으면 \/로 구분 |
o2o-castad-frontend |
| portnumber | O | Nginx가 수신할 포트 번호 | 80 |
| domain | O | 서비스 도메인 | example.com |
| appname | O | 프록시 대상 컨테이너(서비스)명 | uvicorn-app |
| serviceport | - | 프록시 대상 포트 (빈 값 입력 시 포트 없이 설정) | 8000 |
| filename | O | 생성될 conf 파일의 접두사 | example |
생성 파일 경로
| 스크립트 | 생성 경로 |
|---|---|
nginx_conf.sh |
config/web-server/conf.d/{filename}_gunicorn_ng.conf |
nginx_https_conf.sh |
config/web-server/conf.d/{filename}_gunicorn_https_ng.conf |
생성된 conf 파일은 Nginx 컨테이너의 /etc/nginx/conf.d/ 디렉토리에 볼륨 마운트되어 자동으로 로드됩니다.
5. 컨테이너 실행
cd compose
docker compose up -d
서비스 구성
Docker Compose 서비스
| 서비스 | 설명 | 포트 |
|---|---|---|
| nginx | 리버스 프록시, SSL 종료 | 80, 443 |
| uvicorn-app | FastAPI 애플리케이션 | 8000 (내부) |
| redis | 캐시 | 6379 (내부) |
Gunicorn 설정 (config/app-server/gunicorn_uvicorn.conf.py)
- 워커 수: 4 (4GB RAM 기준 최적화)
- 워커 타입: Uvicorn (비동기 I/O)
- 타임아웃: 300초 (장시간 작업 지원)
- 최대 요청: 1000 (메모리 누수 방지)
- 바인드: 0.0.0.0:8000
Nginx 메인 설정 (config/web-server/nginx_conf/nginx.conf)
Nginx 전역 동작을 제어하는 메인 설정 파일입니다. 컨테이너 내 /etc/nginx/nginx.conf에 마운트되며, 모든 서버 블록에 공통 적용됩니다.
워커 설정
| 항목 | 기본값 | 설명 |
|---|---|---|
worker_processes |
auto |
워커 프로세스 수. auto는 CPU 코어 수에 맞게 자동 설정 |
worker_rlimit_nofile |
8192 |
워커당 열 수 있는 최대 파일 디스크립터 수 |
worker_connections |
1024 |
워커당 동시 연결 수. 최대 동시 연결 = worker_processes × worker_connections |
multi_accept |
off |
off 시 워커 간 균등한 연결 분배로 안정적 성능 확보 |
use |
epoll |
리눅스 환경에서 권장하는 이벤트 처리 방식 |
HTTP 설정
| 항목 | 기본값 | 설명 |
|---|---|---|
keepalive_timeout |
30s |
클라이언트 연결 유지 시간 |
keepalive_requests |
1000 |
하나의 keep-alive 연결에서 처리할 최대 요청 수 |
client_max_body_size |
50M |
최대 업로드 크기. 대용량 파일 업로드 시 증가 필요 |
client_body_buffer_size |
128k |
요청 본문 버퍼 크기 (64KB~256KB 권장) |
client_body_timeout |
15s |
클라이언트 본문 수신 타임아웃 |
client_header_timeout |
15s |
클라이언트 헤더 수신 타임아웃 |
send_timeout |
15s |
클라이언트로 응답 전송 타임아웃 |
프록시 설정
| 항목 | 기본값 | 설명 |
|---|---|---|
proxy_connect_timeout |
300s |
백엔드 연결 타임아웃 |
proxy_send_timeout |
300s |
백엔드로 요청 전송 타임아웃 |
proxy_read_timeout |
300s |
백엔드 응답 수신 타임아웃 |
proxy_request_buffering |
off |
대용량 파일 스트리밍 업로드를 위해 비활성화 |
압축 설정
| 항목 | 기본값 | 설명 |
|---|---|---|
gzip |
off |
실시간 압축 비활성화 (CPU 부하 감소) |
gzip_static |
on |
미리 압축된 .gz 파일 제공 방식 사용 |
gzip_vary |
on |
Vary: Accept-Encoding 헤더 추가 (프록시 캐시 호환성) |
기타
server_tokens off: Nginx 버전 정보 노출 차단resolver 127.0.0.11: Docker 내부 DNS를 사용하여 컨테이너 이름 해석map $http_upgrade: WebSocket과 일반 HTTP 요청을 동적으로 구분하여 Connection 헤더 설정
프록시 헤더 설정 (config/web-server/proxy_params/proxy_params)
모든 서버 블록에서 공통으로 사용하는 프록시 헤더 설정 파일입니다. nginx.conf의 http 블록에서 include되어 전역 적용됩니다.
| 헤더/설정 | 값 | 설명 |
|---|---|---|
Host |
$http_host |
클라이언트가 요청한 원본 호스트 정보를 백엔드에 전달 |
X-Real-IP |
$remote_addr |
클라이언트 실제 IP 주소 전달 |
X-Forwarded-For |
$proxy_add_x_forwarded_for |
프록시 체인을 거친 클라이언트 IP 목록 |
X-Forwarded-Proto |
$scheme |
원본 요청 프로토콜 (http/https) 전달 |
Upgrade |
$http_upgrade |
WebSocket 업그레이드 요청 헤더 전달 |
Connection |
$connection_upgrade |
WebSocket 연결 시 upgrade, 일반 요청 시 빈 값 |
proxy_cache_bypass |
$http_upgrade |
WebSocket 요청은 캐시를 우회 |
proxy_buffering |
off |
응답 버퍼링 비활성화 (실시간 스트리밍/SSE 지원) |
proxy_redirect |
off |
백엔드 리다이렉트 URL 재작성 비활성화 |
proxy_buffers |
32 4k |
프록시 응답 버퍼 개수 및 크기 |
proxy_headers_hash_max_size |
512 |
프록시 헤더 해시 테이블 크기 |
proxy_headers_hash_bucket_size |
64 |
프록시 헤더 해시 버킷 크기 |
도메인별 서버 설정 (conf.d/)
nginx_conf.sh 또는 nginx_https_conf.sh로 생성되는 도메인별 서버 블록 설정입니다.
HTTP conf 설정 가이드 (sample_nginx.conf 기반)
nginx_conf.sh로 생성되는 HTTP 전용 서버 블록의 주요 설정입니다.
| 설정 | 템플릿 변수 | 설명 | 설정 가이드 |
|---|---|---|---|
listen |
portnumber |
수신 포트 | 일반적으로 80. 내부 서비스는 다른 포트 사용 가능 |
server_name |
domain |
서비스 도메인 | example.com www.example.com 형식으로 입력 |
access_log |
filename |
액세스 로그 경로 | /log/nginx/{filename}.com.gunicorn_access.log 형식으로 자동 생성 |
error_log |
filename |
에러 로그 경로 | /log/nginx/{filename}.com.gunicorn_error.log 형식으로 자동 생성 |
location /media |
webroot |
미디어 파일 경로 | /www/{webroot}/media — 사용자 업로드 파일 서빙 경로 |
location /static |
webroot |
정적 파일 경로 | /www/{webroot}/static — CSS, JS, 이미지 등 정적 리소스 경로 |
proxy_pass |
appname, serviceport |
백엔드 프록시 | http://{appname}:{serviceport} — Docker Compose 서비스명과 포트 |
location /.well-known |
webroot |
ACME 챌린지 | Let's Encrypt 인증서 발급/갱신을 위한 경로. 수정 불필요 |
보안 설정 (자동 포함, 수정 불필요):
- 닷 파일 차단 (
.env,.git등) - 민감한 파일 확장자 차단 (
.log,.sql,.key등) - 설정 파일 접근 차단 (
composer.json,package.json등)
HTTPS conf 설정 가이드 (sample_nginx_https.conf 기반)
nginx_https_conf.sh로 생성되는 HTTPS 서버 블록의 주요 설정입니다. HTTP conf의 모든 설정을 포함하며, 추가로 아래 설정이 적용됩니다.
HTTP → HTTPS 리다이렉트 (포트 80)
| 설정 | 설명 | 설정 가이드 |
|---|---|---|
server_name |
리다이렉트 대상 도메인 | HTTPS 서버 블록과 동일한 도메인 설정 |
| 호스트 검증 정규식 | 허용되지 않은 도메인 차단 | 도메인 확장자가 .com, .kr, .net, .org 외라면 정규식에 추가 필요 |
return 301 |
HTTPS로 영구 리다이렉트 | 수정 불필요 |
SSL/TLS 인증서 (포트 443)
| 설정 | 설명 | 설정 가이드 |
|---|---|---|
ssl_certificate |
SSL 인증서 경로 | /etc/letsencrypt/live/{domain}/fullchain.pem — letsencrypt.sh 실행 시 자동 생성 |
ssl_certificate_key |
SSL 개인키 경로 | /etc/letsencrypt/live/{domain}/privkey.pem — 자동 생성 |
ssl_dhparam |
DH 파라미터 경로 | /etc/ssl/certs/{domain}/dhparam.pem — letsencrypt.sh 실행 시 자동 생성 |
ssl_protocols |
허용 TLS 버전 | TLSv1.2 TLSv1.3 — 최신 프로토콜만 사용. TLSv1.0/1.1은 보안 취약 |
ssl_session_cache |
SSL 세션 캐시 | shared:SSL:50m — 트래픽이 많을수록 크기 증가 권장 |
ssl_session_timeout |
SSL 세션 타임아웃 | 10m — 세션 재사용으로 핸드셰이크 오버헤드 감소 |
ssl_session_tickets |
세션 티켓 | off — 보안 강화를 위해 비활성화 |
ssl_stapling |
OCSP 스테이플링 | on — SSL 핸드셰이크 성능 향상 |
ssl_trusted_certificate |
OCSP 신뢰 인증서 | /etc/letsencrypt/live/{domain}/chain.pem — 자동 생성 |
보안 헤더
| 헤더 | 기본값 | 설명 | 설정 가이드 |
|---|---|---|---|
Strict-Transport-Security |
max-age=63072000 |
HSTS — 브라우저가 항상 HTTPS 사용 강제 | 최초 적용 시 max-age를 짧게 설정하여 테스트 후 증가 권장 |
X-Frame-Options |
DENY |
클릭재킹 방지. iframe 삽입 차단 | 외부 iframe 허용이 필요하면 SAMEORIGIN으로 변경 |
X-Content-Type-Options |
nosniff |
MIME 스니핑 방지 | 수정 불필요 |
X-XSS-Protection |
1; mode=block |
레거시 XSS 보호 | 수정 불필요 |
Referrer-Policy |
strict-origin-when-cross-origin |
리퍼러 정보 제어 | 수정 불필요 |
Content-Security-Policy |
default-src 'self' |
XSS 방지를 위한 CSP | 반드시 서비스에 맞게 수정 필요. unsafe-inline은 프로덕션에서 nonce/hash 방식으로 대체 권장 |
프록시 및 성능 설정
| 설정 | 기본값 | 설명 | 설정 가이드 |
|---|---|---|---|
client_max_body_size |
100M |
최대 업로드 크기 | 대용량 파일 업로드 서비스는 증가 필요 |
proxy_connect_timeout |
60s |
백엔드 연결 타임아웃 | 장시간 처리 작업이 있으면 증가 |
proxy_send_timeout |
60s |
백엔드 요청 전송 타임아웃 | 대용량 업로드 시 증가 |
proxy_read_timeout |
60s |
백엔드 응답 수신 타임아웃 | AI 추론 등 오래 걸리는 API가 있으면 증가 |
proxy_buffering |
on |
응답 버퍼링 | WebSocket/SSE 사용 시 off로 변경 |
proxy_request_buffering |
on |
요청 버퍼링 | WebSocket/SSE 사용 시 off로 변경 |
open_file_cache |
max=1000 inactive=20s |
파일 캐시 | 정적 파일이 많으면 max 값 증가 |
limit_req |
zone=general burst=20 |
요청 속도 제한 (DDoS 방지) | burst 값은 순간 트래픽 허용량. nginx.conf의 limit_req_zone 정의 필요 |
limit_conn |
addr 10 |
IP당 동시 연결 제한 | nginx.conf의 limit_conn_zone 정의 필요 |
limit_except |
GET POST HEAD OPTIONS |
허용 HTTP 메서드 | PUT, DELETE 등이 필요하면 허용 목록에 추가 |
정적 리소스 캐싱
| 설정 | 기본값 | 설명 |
|---|---|---|
/media 경로 |
expires 30d |
미디어 파일 30일 캐시 |
/static 경로 |
expires 30d |
정적 파일 30일 캐시 |
| 이미지/폰트/CSS/JS | expires 1y |
정적 리소스 1년 캐시. Cache-Control: public, immutable 적용 |
운영 기능
로그 로테이션
- 주기: 일별
- 보관 기간: 30일
- 최대 파일 크기: 100MB
- 설정 위치:
script/logrotate/
SSL 인증서 자동 갱신
- 주기: 매주 월요일 오전 5시
- 방식: Certbot + Nginx 리로드
- 설정: Nginx Dockerfile 내 crontab
자동 재시작
- 주기: 매주 월요일 오전 6시 (선택적)
- 설정 스크립트:
script/crontab_gunicorn_set.sh
주요 스크립트
| 스크립트 | 설명 |
|---|---|
script/letsencrypt.sh |
Let's Encrypt SSL 인증서 발급 |
compose/set_mysql_permission.sh |
Redis 볼륨 권한 설정 |
script/crontab_gunicorn_set.sh |
컨테이너 자동 재시작 cron 설정 |