update for hwang

main
Dohyun Lim 2026-03-13 17:25:25 +09:00
parent e2742f2169
commit feda78c1d3
18 changed files with 261 additions and 1827 deletions

34
.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
# OS
.DS_Store
Thumbs.db
# Python
__pycache__/
*.py[cod]
*.egg-info/
*.egg
dist/
build/
# Logs
log/**/*.log
# Redis data
compose/redis/data/
# SSL certificates
compose/ssl/certs/
compose/ssl/letsencrypt/
# Application code (mounted volume)
www/*/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Claude
.claude/

251
README.md
View File

@ -6,8 +6,7 @@ aio2o의 분산된 인프라 서비스 및 설정을 단일 프로젝트로 통
- **FastAPI 애플리케이션 서버**: Gunicorn + Uvicorn ASGI 워커 기반의 비동기 Python 웹 서버
- **Nginx 리버스 프록시**: SSL/TLS 지원, 정적 파일 서빙, WebSocket 지원
- **데이터베이스**: PostgreSQL 및 MySQL(Percona) 지원
- **캐싱 및 메시지 브로커**: Redis 기반 캐싱 및 Celery 태스크 큐 지원
- **캐싱**: Redis 기반 캐싱 지원
- **SSL 인증서 자동화**: Let's Encrypt + Certbot 자동 갱신
- **로그 관리**: 일별 로그 로테이션 및 30일 보관
@ -19,9 +18,7 @@ aio2o의 분산된 인프라 서비스 및 설정을 단일 프로젝트로 통
| 프레임워크 | FastAPI |
| ASGI 서버 | Gunicorn + Uvicorn |
| 웹 서버 | Nginx 1.26 |
| 데이터베이스 | PostgreSQL, MySQL (Percona) |
| 캐시/브로커 | Redis |
| 태스크 큐 | Celery + Celery Beat |
| 캐시 | Redis |
| 컨테이너 | Docker, Docker Compose |
## 디렉토리 구조
@ -31,17 +28,12 @@ o2o-infrakit/
├── compose/ # Docker Compose 설정
│ ├── docker-compose.yml # 메인 오케스트레이션 파일
│ ├── .env # 환경 변수
│ ├── mysql/ # MySQL 데이터 볼륨
│ ├── pgdata/ # PostgreSQL 데이터 볼륨
│ ├── redis/ # Redis 설정 및 데이터
│ └── ssl/ # SSL 인증서 및 Let's Encrypt
├── config/ # 서비스별 설정
│ ├── app-server/ # 애플리케이션 서버 설정
│ │ └── gunicorn_uvicorn.conf.py
│ ├── database/ # 데이터베이스 설정
│ │ ├── mysql/ # MySQL 설정 (my.cnf, init.sql)
│ │ └── postgresql/ # PostgreSQL 설정
│ └── web-server/ # 웹 서버 설정
│ ├── nginx_conf/ # Nginx 메인 설정
│ ├── conf.d/ # 도메인별 Nginx 설정
@ -57,9 +49,7 @@ o2o-infrakit/
│ └── logrotate/ # 로그 로테이션 설정
├── log/ # 로그 디렉토리 (볼륨)
│ ├── mysql/ # MySQL 로그
│ ├── nginx/ # Nginx 액세스/에러 로그
│ ├── postgresql/ # PostgreSQL 로그
│ └── uvicorn/ # Uvicorn/FastAPI 로그
└── www/ # 애플리케이션 코드 볼륨
@ -86,10 +76,8 @@ cp .env.example .env
주요 환경 변수:
- `PROJECT_DIR`: 애플리케이션 프로젝트 디렉토리명
- `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`: PostgreSQL 접속 정보
- `CELERY_BROKER_URL`: Celery 브로커 URL
### 2. 데이터베이스 볼륨 권한 설정
### 2. Redis 볼륨 권한 설정
```bash
cd compose
@ -99,13 +87,78 @@ chmod +x set_mysql_permission.sh
### 3. SSL 인증서 설정 (선택)
Let's Encrypt를 통해 무료 SSL 인증서를 발급받습니다. 이 스크립트는 **Nginx 컨테이너 내부**에서 실행해야 합니다.
```bash
cd script
docker exec -it nginx-uvicorn-webserver bash
cd /script
chmod +x letsencrypt.sh
./letsencrypt.sh
```
### 4. 컨테이너 실행
스크립트 실행 시 아래 3가지 정보를 순서대로 입력해야 합니다:
| 입력 항목 | 설명 | 예시 |
|-----------|------|------|
| **webroot_folder** | Nginx 웹 루트 디렉토리명 (`/www/` 하위 경로) | `o2o-castad-frontend` |
| **domain** | 인증서를 발급할 도메인 (공백으로 구분하여 여러 개 입력 가능, 메인 도메인을 먼저 입력) | `example.com www.example.com api.example.com` |
| **e-mail** | 인증서 만료 알림을 받을 이메일 주소 | `admin@example.com` |
스크립트 동작 순서:
1. Certbot 및 필수 패키지 설치
2. OpenSSL DH 파라미터 생성 (4096bit, 최초 1회)
3. Certbot을 통한 SSL 인증서 발급 (webroot 방식)
4. 자동 갱신 cron 등록 (매주 월요일 오전 5시)
### 4. Nginx 설정 파일 생성
서비스 도메인에 맞는 Nginx conf 파일을 자동 생성하는 스크립트입니다. 템플릿 기반으로 동작하며, HTTP 또는 HTTPS 용도에 따라 스크립트를 선택합니다.
```bash
cd config/web-server
```
#### HTTP 설정 파일 생성
```bash
chmod +x nginx_conf.sh
./nginx_conf.sh
```
`sample_nginx.conf` 템플릿을 기반으로 HTTP conf 파일을 생성합니다.
#### HTTPS 설정 파일 생성
```bash
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. 컨테이너 실행
```bash
cd compose
@ -120,9 +173,7 @@ docker compose up -d
|--------|------|------|
| nginx | 리버스 프록시, SSL 종료 | 80, 443 |
| uvicorn-app | FastAPI 애플리케이션 | 8000 (내부) |
| redis | 캐시 및 메시지 브로커 | 6379 (내부) |
| postgresql | 데이터베이스 | 5432 (내부) |
| mysql | 데이터베이스 (선택) | 3306 (내부) |
| redis | 캐시 | 6379 (내부) |
### Gunicorn 설정 (config/app-server/gunicorn_uvicorn.conf.py)
@ -132,13 +183,157 @@ docker compose up -d
- **최대 요청**: 1000 (메모리 누수 방지)
- **바인드**: 0.0.0.0:8000
### Nginx 설정 (config/web-server/nginx_conf/nginx.conf)
### Nginx 메인 설정 (config/web-server/nginx_conf/nginx.conf)
- **워커 프로세스**: auto (CPU 코어 수에 맞게 자동 조정)
- **워커 연결**: 1024/프로세스
- **Keep-alive**: 30초
- **최대 요청 본문**: 50MB
- **프록시 타임아웃**: 300초
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` 적용 |
## 운영 기능
@ -165,7 +360,5 @@ docker compose up -d
| 스크립트 | 설명 |
|----------|------|
| `script/letsencrypt.sh` | Let's Encrypt SSL 인증서 발급 |
| `compose/set_mysql_permission.sh` | 데이터베이스 볼륨 권한 설정 |
| `compose/set_mysql_permission.sh` | Redis 볼륨 권한 설정 |
| `script/crontab_gunicorn_set.sh` | 컨테이너 자동 재시작 cron 설정 |

View File

@ -2,10 +2,3 @@ PROJECT_DIR=o2o-castad-backend
LOG_DRIVER=json-file
LOG_OPT_MAXF=5
LOG_OPT_MAXS=100m
CELERY_BROKER_URL=redis://redis:6379/3
FLOWER_ID=admin
FLOWER_PWD=admin
POSTGRES_DB=ado3_dev
POSTGRES_USER=ado3_dev_admin
POSTGRES_PASSWORD=ado31324
MYSQL_PASSWORD=ado31324

View File

@ -21,7 +21,6 @@ services:
ports:
- 80:80
- 443:443
# - 8000:8000
environment:
TZ: "Asia/Seoul"
restart: always
@ -70,25 +69,3 @@ services:
TZ: "Asia/Seoul"
restart: always
command: redis-server /usr/local/etc/redis/redis.conf
# mysql:
# image: percona/percona-server:latest
# logging:
# driver: "${LOG_DRIVER}"
# options:
# max-file: "${LOG_OPT_MAXF}"
# max-size: "${LOG_OPT_MAXS}"
# container_name: mysql
# environment:
# MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
# TZ: Asia/Seoul
# ports:
# - "3306:3306"
# volumes:
# - ./mysql:/var/lib/mysql
# - ../config/database/mysql/my.cnf:/etc/my.cnf
# # - ../config/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
# - ../log/mysql:/var/log/mysql
# restart: always
# profiles:
# - mysql

View File

Binary file not shown.

View File

@ -1,67 +1,15 @@
#!/usr/bin/env bash
# ============================================================
# set_mysql_permission.sh
# MySQL, PostgreSQL, Redis 데이터 디렉터리 권한 설정 스크립트
# set_permission.sh
# Redis 데이터 디렉터리 권한 설정 스크립트
# ============================================================
# 스크립트 실행 경로 기준
MYSQL_DIR="./mysql"
PGDATA_DIR="./pgdata"
REDIS_DIR="./redis"
# ============================================================
# MySQL (Percona) 권한 설정
# ============================================================
echo "[MySQL] 데이터 디렉터리 권한 설정을 시작합니다..."
echo "[MySQL] 대상 디렉터리: $MYSQL_DIR"
if [ ! -d "$MYSQL_DIR" ]; then
echo "[MySQL] $MYSQL_DIR 디렉터리가 존재하지 않아 새로 생성합니다."
sudo mkdir -p "$MYSQL_DIR"
fi
# Percona의 mysql 유저 UID는 보통 1001
MYSQL_UID=1001
MYSQL_GID=1001
echo "[MySQL] 소유권을 $MYSQL_UID:$MYSQL_GID 로 변경합니다..."
sudo chown -R ${MYSQL_UID}:${MYSQL_GID} "$MYSQL_DIR"
echo "[MySQL] 권한을 750 (rwxr-x---) 으로 설정합니다..."
sudo chmod -R 750 "$MYSQL_DIR"
echo "[MySQL] 적용 결과:"
ls -ld "$MYSQL_DIR"
# ============================================================
# PostgreSQL 권한 설정
# ============================================================
echo ""
echo "[PostgreSQL] 데이터 디렉터리 권한 설정을 시작합니다..."
echo "[PostgreSQL] 대상 디렉터리: $PGDATA_DIR"
if [ ! -d "$PGDATA_DIR" ]; then
echo "[PostgreSQL] $PGDATA_DIR 디렉터리가 존재하지 않아 새로 생성합니다."
sudo mkdir -p "$PGDATA_DIR"
fi
# PostgreSQL의 postgres 유저 UID는 보통 999 (공식 Docker 이미지 기준)
POSTGRES_UID=999
POSTGRES_GID=999
echo "[PostgreSQL] 소유권을 $POSTGRES_UID:$POSTGRES_GID 로 변경합니다..."
sudo chown -R ${POSTGRES_UID}:${POSTGRES_GID} "$PGDATA_DIR"
echo "[PostgreSQL] 권한을 700 (rwx------) 으로 설정합니다..."
sudo chmod -R 700 "$PGDATA_DIR"
echo "[PostgreSQL] 적용 결과:"
ls -ld "$PGDATA_DIR"
# ============================================================
# Redis 권한 설정
# ============================================================
echo ""
echo "[Redis] 데이터 디렉터리 권한 설정을 시작합니다..."
echo "[Redis] 대상 디렉터리: $REDIS_DIR"

View File

@ -1,5 +0,0 @@
-- 1. admin 권한 계정 생성
CREATE USER IF NOT EXISTS 'devadmin'@'%' IDENTIFIED BY 'test!';
-- 2. 전체 권한 부여
GRANT ALL PRIVILEGES ON *.* TO 'devadmin'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

View File

@ -1,546 +0,0 @@
# ========================================================================
# Percona Server / MySQL 8.0 최적화 설정
# 하드웨어 사양: 4코어 CPU, 4GB RAM, SSD, 1GB LAN
# ========================================================================
[mysqld]
# ------------------------------------------------------------------------
# 기본 경로 설정
# ------------------------------------------------------------------------
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
pid-file=/var/run/mysqld/mysqld.pid
# ------------------------------------------------------------------------
# 네트워크 및 연결 설정
# ------------------------------------------------------------------------
# 바인드 주소
bind-address = 0.0.0.0
# 기본값: 127.0.0.1 (로컬만)
# 변경값: 0.0.0.0 (모든 IP)
# 목적: 원격 접속 허용
# 보안: 방화벽 설정 필수
# 포트
port = 3306
# 기본값: 3306
# MySQL 표준 포트
# 최대 연결 수
max_connections = 200
# 기본값: 151
# 변경값: 200
# 목적: 4GB RAM 환경에서 충분한 연결 수 제공
# 계산: 각 연결당 약 4-8MB 메모리 사용
# 200 연결 = 최대 1.6GB 메모리 (버퍼 포함)
# 참고: 연결 풀링(ProxySQL, MaxScale) 사용 시 더 효율적
max_connect_errors = 1000000
# 기본값: 100
# 변경값: 1000000
# 목적: 연결 오류로 인한 호스트 차단 방지
# 성능: 네트워크 이슈로 인한 불필요한 차단 감소
# 대기 시간 설정
wait_timeout = 600
# 기본값: 28800 (8시간)
# 변경값: 600 (10분)
# 목적: 유휴 연결 자동 정리
# 성능: 불필요한 연결 점유 방지
interactive_timeout = 600
# 기본값: 28800
# 변경값: 600
# 목적: 대화형 클라이언트 타임아웃
# 성능: 리소스 효율적 관리
connect_timeout = 10
# 기본값: 10
# 유지 이유: 연결 시도 타임아웃
# 스레드 캐시
thread_cache_size = 50
# 기본값: 8
# 변경값: 50
# 목적: 스레드 재사용으로 연결 생성 오버헤드 감소
# 성능: 연결 빈도가 높은 환경에서 효과적
# 계산: max_connections의 약 25%
# 백로그 큐
back_log = 512
# 기본값: 80
# 변경값: 512
# 목적: 대기 중인 연결 요청 큐 크기
# 성능: 트래픽 버스트 시 연결 손실 방지
# ------------------------------------------------------------------------
# InnoDB 버퍼 풀 설정 (가장 중요!)
# ------------------------------------------------------------------------
innodb_buffer_pool_size = 2G
# 기본값: 128MB
# 변경값: 2GB (전체 RAM 4GB의 50%)
# 목적: 데이터와 인덱스를 메모리에 캐싱
# 성능: 디스크 I/O를 크게 감소시키는 가장 중요한 설정
# 권장: 전용 서버는 RAM의 70-80%, 혼합 환경은 50-60%
# 계산: 2GB buffer pool + 1GB 연결/쿼리 + 1GB OS/기타
innodb_buffer_pool_instances = 4
# 기본값: 1 (또는 자동)
# 변경값: 4
# 목적: 버퍼 풀을 여러 인스턴스로 분할하여 동시성 향상
# 성능: 멀티 코어 환경에서 잠금 경합 감소
# 권장: CPU 코어 수와 동일하게 설정
# 참고: buffer_pool_size >= 1GB일 때만 효과적
innodb_buffer_pool_chunk_size = 128M
# 기본값: 128MB
# 유지 이유: buffer_pool_size가 instances × chunk_size의 배수여야 함
# 계산: 2GB = 4 instances × 4 chunks × 128MB
# ------------------------------------------------------------------------
# InnoDB 로그 설정
# ------------------------------------------------------------------------
innodb_log_file_size = 512M
# 기본값: 48MB
# 변경값: 512MB
# 목적: Redo 로그 파일 크기
# 성능: 쓰기 집약적 워크로드에서 체크포인트 빈도 감소
# 권장: buffer_pool_size의 25% 정도
# 주의: 너무 크면 크래시 복구 시간 증가
innodb_log_buffer_size = 32M
# 기본값: 16MB
# 변경값: 32MB
# 목적: Redo 로그 버퍼
# 성능: 디스크 쓰기 전 로그를 메모리에 버퍼링
# 권장: 대용량 트랜잭션이 많으면 증가
innodb_flush_log_at_trx_commit = 1
# 기본값: 1
# 유지 이유: ACID 보장 (데이터 무결성)
# 옵션:
# 0: 로그를 메모리에만 (속도↑, 안정성↓↓)
# 1: 매 커밋마다 디스크에 flush (속도↓, 안정성↑↑) ← 권장
# 2: OS 캐시까지만 (속도↑, 안정성↑)
# 주의: 성능을 위해 2로 변경 가능하나 크래시 시 1초 데이터 손실
innodb_flush_method = O_DIRECT
# 기본값: fsync (Linux)
# 변경값: O_DIRECT
# 목적: OS 파일 시스템 캐시 우회
# 성능: 이중 버퍼링 방지, SSD 환경에서 효과적
# 권장: SSD 사용 시 필수 설정
# ------------------------------------------------------------------------
# InnoDB I/O 설정 (SSD 최적화)
# ------------------------------------------------------------------------
innodb_io_capacity = 2000
# 기본값: 200 (HDD 기준)
# 변경값: 2000
# 목적: InnoDB가 초당 수행할 수 있는 I/O 작업 수
# 성능: SSD의 높은 IOPS 활용
# 권장: SSD는 2000-5000, NVMe는 10000+
# 측정: fio 벤치마크로 실제 IOPS 측정 후 70% 수준으로 설정
innodb_io_capacity_max = 4000
# 기본값: 2000
# 변경값: 4000 (io_capacity의 2배)
# 목적: 긴급 상황(체크포인트 등)에서 최대 I/O
# 성능: 버스트 상황에서 더 많은 I/O 허용
innodb_read_io_threads = 4
# 기본값: 4
# 유지 이유: CPU 코어 수와 일치
# 목적: 읽기 작업을 위한 I/O 스레드
innodb_write_io_threads = 4
# 기본값: 4
# 유지 이유: CPU 코어 수와 일치
# 목적: 쓰기 작업을 위한 I/O 스레드
innodb_flush_neighbors = 0
# 기본값: 1 (HDD 최적화)
# 변경값: 0
# 목적: 인접 페이지 flush 비활성화
# 성능: SSD는 랜덤 쓰기가 빠르므로 불필요
# 권장: SSD 환경에서는 반드시 0으로 설정
# ------------------------------------------------------------------------
# InnoDB 동시성 설정
# ------------------------------------------------------------------------
innodb_thread_concurrency = 0
# 기본값: 0 (무제한)
# 유지 이유: MySQL이 자동으로 최적화
# 목적: 동시 실행 스레드 수 제한
# 참고: 특정 워크로드에서 제한이 필요한 경우 CPU 코어 수 × 2
innodb_lock_wait_timeout = 50
# 기본값: 50
# 유지 이유: 락 대기 타임아웃 (초)
# 성능: 데드락 상황 빠른 감지
# ------------------------------------------------------------------------
# 테이블 및 파일 설정
# ------------------------------------------------------------------------
innodb_file_per_table = ON
# 기본값: ON (MySQL 5.6.6+)
# 유지 이유: 테이블별로 별도 파일 생성
# 성능: 테이블 삭제 시 공간 즉시 반환, 관리 용이
innodb_open_files = 2000
# 기본값: 300
# 변경값: 2000
# 목적: InnoDB가 동시에 열 수 있는 파일 수
# 성능: 많은 테이블이 있을 때 파일 열기 오버헤드 감소
table_open_cache = 4000
# 기본값: 2000
# 변경값: 4000
# 목적: 열린 테이블 캐시
# 성능: 테이블 열기/닫기 오버헤드 감소
# 계산: max_connections × 평균 조인 테이블 수
table_open_cache_instances = 16
# 기본값: 16
# 유지 이유: 캐시를 여러 인스턴스로 분할
# 성능: 동시성 향상
table_definition_cache = 2000
# 기본값: 400
# 변경값: 2000
# 목적: 테이블 정의 캐시 (.frm 파일)
# 성능: 테이블 메타데이터 접근 속도 향상
# ------------------------------------------------------------------------
# 쿼리 캐시 (MySQL 8.0에서는 제거됨)
# ------------------------------------------------------------------------
# MySQL 8.0에서는 쿼리 캐시가 제거되었습니다.
# 대신 애플리케이션 레벨 캐싱(Redis, Memcached) 사용 권장
# ------------------------------------------------------------------------
# 임시 테이블 설정
# ------------------------------------------------------------------------
tmp_table_size = 64M
# 기본값: 16MB
# 변경값: 64MB
# 목적: 메모리 내 임시 테이블 최대 크기
# 성능: 복잡한 쿼리의 임시 테이블을 메모리에 유지
# 주의: max_heap_table_size와 함께 설정
max_heap_table_size = 64M
# 기본값: 16MB
# 변경값: 64MB
# 목적: MEMORY 테이블 최대 크기
# 성능: tmp_table_size와 동일하게 설정
# ------------------------------------------------------------------------
# 정렬 및 조인 버퍼
# ------------------------------------------------------------------------
sort_buffer_size = 4M
# 기본값: 256KB
# 변경값: 4MB
# 목적: 정렬 작업에 사용되는 버퍼
# 성능: ORDER BY, GROUP BY 성능 향상
# 주의: 세션별로 할당되므로 너무 크면 메모리 부족
# 계산: 200 연결 × 4MB = 최대 800MB
read_buffer_size = 2M
# 기본값: 128KB
# 변경값: 2MB
# 목적: 순차 스캔 버퍼
# 성능: 전체 테이블 스캔 시 성능 향상
read_rnd_buffer_size = 4M
# 기본값: 256KB
# 변경값: 4MB
# 목적: 정렬 후 행 읽기 버퍼
# 성능: ORDER BY 후 행 검색 속도 향상
join_buffer_size = 4M
# 기본값: 256KB
# 변경값: 4MB
# 목적: 인덱스를 사용하지 않는 조인 버퍼
# 성능: 조인 성능 향상
# ------------------------------------------------------------------------
# 바이너리 로그 설정
# ------------------------------------------------------------------------
# 바이너리 로그 활성화 (복제 및 Point-in-Time 복구에 필수)
# log_bin = /var/lib/mysql/mysql-bin
# 목적: 데이터 변경 사항 기록
# 용도: 복제(Replication), 백업, 복구
# 참고: 복제를 사용하지 않으면 disable_log_bin 설정 가능
server_id = 1
# 기본값: 1
# 목적: 복제 환경에서 서버 식별자
# 참고: 각 서버마다 고유한 값 필요
binlog_format = ROW
# 기본값: ROW (MySQL 8.0+)
# 옵션:
# STATEMENT: SQL 문 저장 (크기↓, 안정성↓)
# ROW: 실제 행 변경 저장 (크기↑, 안정성↑) ← 권장
# MIXED: 자동 선택
# 권장: ROW (가장 안전하고 일관성 있음)
binlog_expire_logs_seconds = 604800
# 기본값: 2592000 (30일)
# 변경값: 604800 (7일)
# 목적: 오래된 바이너리 로그 자동 삭제
# 성능: 디스크 공간 관리
# 참고: 백업 주기에 따라 조정
max_binlog_size = 100M
# 기본값: 1GB
# 변경값: 100MB
# 목적: 단일 바이너리 로그 파일 최대 크기
# 성능: 작은 파일로 관리 용이성 향상
sync_binlog = 1
# 기본값: 1
# 유지 이유: 매 커밋마다 바이너리 로그를 디스크에 동기화
# 성능: 안정성 최우선 (크래시 시 데이터 손실 방지)
# 참고: 성능이 중요하면 0으로 설정 가능하나 권장하지 않음
# ------------------------------------------------------------------------
# 에러 로그 설정
# ------------------------------------------------------------------------
# log_error = /var/log/mysql/error.log
log_error = /var/log/mysqld.log
# 목적: 에러 로그 파일 위치
# 참고: 디렉토리가 존재하고 mysql 사용자가 쓰기 권한 필요
log_error_verbosity = 2
# 기본값: 2
# 옵션: 1 (오류만), 2 (오류+경고), 3 (오류+경고+정보)
# 권장: 2 (운영 환경)
# ------------------------------------------------------------------------
# 슬로우 쿼리 로그
# ------------------------------------------------------------------------
# slow_query_log = 1
# 기본값: 0 (비활성화)
# 변경값: 1 (활성화)
# 목적: 느린 쿼리 기록
# 성능: 쿼리 최적화에 필수
slow_query_log_file = /var/log/mysql/slow-query.log
# 목적: 슬로우 쿼리 로그 파일 위치
long_query_time = 2
# 기본값: 10
# 변경값: 2
# 목적: 2초 이상 걸리는 쿼리 기록
# 권장: 1-2초 (애플리케이션 특성에 따라 조정)
log_queries_not_using_indexes = 1
# 기본값: 0
# 변경값: 1
# 목적: 인덱스를 사용하지 않는 쿼리도 기록
# 성능: 인덱스 누락 쿼리 발견
# ------------------------------------------------------------------------
# 일반 쿼리 로그 (개발 환경에서만 사용)
# ------------------------------------------------------------------------
# 운영 환경에서는 비활성화 권장 (과도한 로그 생성)
# general_log = 0
# general_log_file = /var/log/mysql/general.log
# ------------------------------------------------------------------------
# 문자셋 및 콜레이션
# ------------------------------------------------------------------------
character_set_server = utf8mb4
# 기본값: utf8mb4 (MySQL 8.0+)
# 목적: 서버 기본 문자셋
# 참고: 이모지 등 4바이트 문자 지원
collation_server = utf8mb4_unicode_ci
# 기본값: utf8mb4_0900_ai_ci (MySQL 8.0+)
# 변경값: utf8mb4_unicode_ci
# 목적: 다국어 정렬 규칙
# 참고: 호환성을 위해 unicode_ci 사용
# ------------------------------------------------------------------------
# SQL 모드
# ------------------------------------------------------------------------
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
# 기본값: STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,... (MySQL 8.0+)
# 목적: SQL 엄격 모드 설정
# 권장: STRICT_TRANS_TABLES (데이터 무결성)
# 참고: 레거시 애플리케이션은 모드 조정 필요
# ------------------------------------------------------------------------
# 타임존
# ------------------------------------------------------------------------
# default_time_zone = '+09:00'
# 기본값: SYSTEM
# 변경값: '+09:00' (한국 시간)
# 목적: 서버 타임존 설정
# 참고: 글로벌 서비스는 '+00:00' (UTC) 권장
# ------------------------------------------------------------------------
# 성능 스키마 (Performance Schema)
# ------------------------------------------------------------------------
performance_schema = ON
# 기본값: ON
# 유지 이유: 성능 모니터링 및 진단
# 참고: 약간의 오버헤드 있지만 필수 모니터링 도구
# ------------------------------------------------------------------------
# 보안 설정
# ------------------------------------------------------------------------
# 로컬 파일 로드 비활성화 (보안)
local_infile = 0
# 기본값: 0 (MySQL 8.0+)
# 목적: LOAD DATA LOCAL INFILE 비활성화
# 보안: 로컬 파일 접근 방지
# 심볼릭 링크 비활성화
symbolic_links = 0
# 기본값: 0
# 목적: 심볼릭 링크 사용 비활성화
# 보안: 디렉토리 탐색 공격 방지
# ------------------------------------------------------------------------
# 기타 최적화
# ------------------------------------------------------------------------
# 쿼리 결과 캐시 (애플리케이션 레벨 권장)
# MySQL 8.0에서는 쿼리 캐시 제거됨
# 오픈 파일 제한
open_files_limit = 65535
# 기본값: 5000
# 변경값: 65535
# 목적: 동시에 열 수 있는 파일 수
# 성능: 많은 테이블과 연결을 처리할 때 필요
# 최대 허용 패킷 크기
max_allowed_packet = 64M
# 기본값: 64MB (MySQL 8.0+)
# 유지 이유: 대용량 데이터 처리
# 참고: 필요시 증가 가능 (최대 1GB)
# 그룹 커밋 최적화
binlog_group_commit_sync_delay = 0
# 기본값: 0
# 목적: 바이너리 로그 그룹 커밋 지연 (마이크로초)
# 성능: 0보다 크면 처리량 증가, 지연 약간 증가
# 참고: 초당 수천 개 트랜잭션 환경에서 1000-10000 설정
binlog_group_commit_sync_no_delay_count = 0
# 기본값: 0
# 목적: 지연 없이 커밋할 트랜잭션 수
# ------------------------------------------------------------------------
# 설정 파일 추가 포함
# ------------------------------------------------------------------------
!includedir /etc/my.cnf.d
# ========================================================================
# 주요 변경사항 요약
# ========================================================================
#
# 1. 메모리 설정 (4GB RAM 기준)
# - innodb_buffer_pool_size: 128MB → 2GB (50% of RAM)
# - tmp_table_size / max_heap_table_size: 16MB → 64MB
# - sort_buffer_size: 256KB → 4MB
# - read_buffer_size: 128KB → 2MB
# - join_buffer_size: 256KB → 4MB
#
# 2. 연결 설정
# - max_connections: 151 → 200
# - thread_cache_size: 8 → 50
# - wait_timeout: 28800 → 600 (10분)
#
# 3. InnoDB 최적화 (SSD 특화)
# - innodb_io_capacity: 200 → 2000
# - innodb_io_capacity_max: 2000 → 4000
# - innodb_flush_neighbors: 1 → 0 (SSD 최적화)
# - innodb_flush_method: fsync → O_DIRECT
#
# 4. 로그 설정
# - innodb_log_file_size: 48MB → 512MB
# - innodb_log_buffer_size: 16MB → 32MB
# - slow_query_log: 활성화 (2초 이상 쿼리)
#
# 5. 버퍼 풀 설정
# - innodb_buffer_pool_instances: 1 → 4 (CPU 코어 수)
#
# ========================================================================
# 예상 성능 향상
# ========================================================================
#
# - 읽기 성능: 40-60% 향상 (innodb_buffer_pool_size 증가)
# - 쓰기 성능: 30-50% 향상 (SSD 최적화, 로그 버퍼 증가)
# - 복잡한 쿼리: 50-100% 향상 (정렬/조인 버퍼 증가)
# - 동시 연결: 연결 처리 능력 향상 (thread_cache, max_connections)
# - 전체 처리량: 30-50% 향상
#
# ========================================================================
# 적용 방법
# ========================================================================
#
# 1. 이 파일을 /etc/my.cnf로 저장 (기존 파일 백업)
# sudo cp /etc/my.cnf /etc/my.cnf.backup
# sudo vi /etc/my.cnf
#
# 2. 로그 디렉토리 생성 및 권한 설정
# sudo mkdir -p /var/log/mysql
# sudo chown mysql:mysql /var/log/mysql
# sudo chmod 755 /var/log/mysql
#
# 3. MySQL 재시작
# sudo systemctl restart mysql
# 또는
# sudo service mysql restart
#
# 4. 설정 확인
# mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"
#
# 5. 슬로우 쿼리 로그 분석 (정기적으로)
# mysqldumpslow /var/log/mysql/slow-query.log
#
# 6. 성능 모니터링
# mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G"
# mysql -u root -p -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
#
# ========================================================================
# Docker 환경 주의사항
# ========================================================================
#
# Docker 환경에서 사용 시:
# 1. 로그 디렉토리를 볼륨 마운트
# volumes:
# - ./logs/mysql:/var/log/mysql
#
# 2. 권한 문제 방지
# - 컨테이너 시작 전 호스트에서 디렉토리 생성
# mkdir -p ./logs/mysql
# chmod 777 ./logs/mysql # 또는 적절한 권한
#
# 3. 메모리 제한 확인
# - Docker 컨테이너에 최소 4GB RAM 할당
#
# ========================================================================

View File

@ -1,4 +0,0 @@
CREATE ROLE IF NOT EXISTS devadmin WITH
LOGIN
PASSWORD 'test!'
SUPERUSER;

View File

@ -1,128 +0,0 @@
# PostgreSQL Client Authentication Configuration File
# ===================================================
#
# Refer to the "Client Authentication" section in the PostgreSQL
# documentation for a complete description of this file. A short
# synopsis follows.
#
# ----------------------
# Authentication Records
# ----------------------
#
# This file controls: which hosts are allowed to connect, how clients
# are authenticated, which PostgreSQL user names they can use, which
# databases they can access. Records take one of these forms:
#
# local DATABASE USER METHOD [OPTIONS]
# host DATABASE USER ADDRESS METHOD [OPTIONS]
# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
# hostgssenc DATABASE USER ADDRESS METHOD [OPTIONS]
# hostnogssenc DATABASE USER ADDRESS METHOD [OPTIONS]
#
# (The uppercase items must be replaced by actual values.)
#
# The first field is the connection type:
# - "local" is a Unix-domain socket
# - "host" is a TCP/IP socket (encrypted or not)
# - "hostssl" is a TCP/IP socket that is SSL-encrypted
# - "hostnossl" is a TCP/IP socket that is not SSL-encrypted
# - "hostgssenc" is a TCP/IP socket that is GSSAPI-encrypted
# - "hostnogssenc" is a TCP/IP socket that is not GSSAPI-encrypted
#
# DATABASE can be "all", "sameuser", "samerole", "replication", a
# database name, a regular expression (if it starts with a slash (/))
# or a comma-separated list thereof. The "all" keyword does not match
# "replication". Access to replication must be enabled in a separate
# record (see example below).
#
# USER can be "all", a user name, a group name prefixed with "+", a
# regular expression (if it starts with a slash (/)) or a comma-separated
# list thereof. In both the DATABASE and USER fields you can also write
# a file name prefixed with "@" to include names from a separate file.
#
# ADDRESS specifies the set of hosts the record matches. It can be a
# host name, or it is made up of an IP address and a CIDR mask that is
# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
# specifies the number of significant bits in the mask. A host name
# that starts with a dot (.) matches a suffix of the actual host name.
# Alternatively, you can write an IP address and netmask in separate
# columns to specify the set of hosts. Instead of a CIDR-address, you
# can write "samehost" to match any of the server's own IP addresses,
# or "samenet" to match any address in any subnet that the server is
# directly connected to.
#
# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
# "gss", "sspi", "ident", "peer", "pam", "oauth", "ldap", "radius" or
# "cert". Note that "password" sends passwords in clear text; "md5" or
# "scram-sha-256" are preferred since they send encrypted passwords.
#
# OPTIONS are a set of options for the authentication in the format
# NAME=VALUE. The available options depend on the different
# authentication methods -- refer to the "Client Authentication"
# section in the documentation for a list of which options are
# available for which authentication methods.
#
# Database and user names containing spaces, commas, quotes and other
# special characters must be quoted. Quoting one of the keywords
# "all", "sameuser", "samerole" or "replication" makes the name lose
# its special character, and just match a database or username with
# that name.
#
# ---------------
# Include Records
# ---------------
#
# This file allows the inclusion of external files or directories holding
# more records, using the following keywords:
#
# include FILE
# include_if_exists FILE
# include_dir DIRECTORY
#
# FILE is the file name to include, and DIR is the directory name containing
# the file(s) to include. Any file in a directory will be loaded if suffixed
# with ".conf". The files of a directory are ordered by name.
# include_if_exists ignores missing files. FILE and DIRECTORY can be
# specified as a relative or an absolute path, and can be double-quoted if
# they contain spaces.
#
# -------------
# Miscellaneous
# -------------
#
# This file is read on server startup and when the server receives a
# SIGHUP signal. If you edit the file on a running system, you have to
# SIGHUP the server for the changes to take effect, run "pg_ctl reload",
# or execute "SELECT pg_reload_conf()".
#
# ----------------------------------
# Put your actual configuration here
# ----------------------------------
#
# If you want to allow non-local connections, you need to add more
# "host" records. In that case you will also need to make PostgreSQL
# listen on a non-local interface via the listen_addresses
# configuration parameter, or via the -i or -h command line switches.
# CAUTION: Configuring the system for local "trust" authentication
# allows any local user to connect as any PostgreSQL user, including
# the database superuser. If you do not trust all your local users,
# use another authentication method.
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# IPv6 local connections:
host all all ::1/128 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all trust
host replication all 127.0.0.1/32 trust
host replication all ::1/128 trust
host all all all scram-sha-256

View File

@ -1,766 +0,0 @@
# -----------------------------
# PostgreSQL 18 최적화 설정
# 하드웨어 사양: 4코어 CPU, 4GB RAM, SSD, 1GB LAN
# -----------------------------
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
#data_directory = 'ConfigDir'
#hba_file = 'ConfigDir/pg_hba.conf'
#ident_file = 'ConfigDir/pg_ident.conf'
#external_pid_file = ''
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*'
# 기본값: 'localhost'
# 변경값: '*' (모든 IP에서 접근 허용)
# 목적: 네트워크를 통한 원격 접속 허용
#port = 5432
max_connections = 100
# 기본값: 100
# 유지 이유: 4GB RAM 환경에서 적절한 연결 수
# 성능: 각 연결은 약 10MB의 메모리를 사용하므로 100개 연결 = 약 1GB
# 참고: 연결 풀링(pgBouncer 등) 사용 시 더 효율적
#reserved_connections = 0
#superuser_reserved_connections = 3
#unix_socket_directories = '/var/run/postgresql'
#unix_socket_group = ''
#unix_socket_permissions = 0777
#bonjour = off
#bonjour_name = ''
# - TCP settings -
tcp_keepalives_idle = 60
# 기본값: 0 (시스템 기본값 사용, 보통 7200초)
# 변경값: 60초
# 목적: 유휴 연결을 60초마다 체크하여 죽은 연결을 빠르게 감지
# 성능: 네트워크 장애 시 빠른 연결 정리로 리소스 확보
tcp_keepalives_interval = 10
# 기본값: 0 (시스템 기본값 사용, 보통 75초)
# 변경값: 10초
# 목적: keepalive 재전송 간격
# 성능: 연결 문제를 빠르게 탐지
tcp_keepalives_count = 3
# 기본값: 0 (시스템 기본값 사용, 보통 9회)
# 변경값: 3회
# 목적: 연결 실패 판정까지의 재시도 횟수
# 성능: 60초 + (10초 × 3) = 최대 90초 내에 죽은 연결 정리
#tcp_user_timeout = 0
#client_connection_check_interval = 0
# - Authentication -
#authentication_timeout = 1min
#password_encryption = scram-sha-256
#scram_iterations = 4096
# - SSL -
#ssl = off
#ssl_ca_file = ''
#ssl_cert_file = 'server.crt'
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
# - Memory -
shared_buffers = 1GB
# 기본값: 128MB
# 변경값: 1GB (전체 RAM 4GB의 25%)
# 목적: 데이터베이스가 디스크에서 읽은 데이터를 캐시하는 메모리
# 성능: 자주 사용되는 데이터를 메모리에 유지하여 디스크 I/O 크게 감소
# 참고: PostgreSQL에서 가장 중요한 메모리 설정 중 하나
huge_pages = try
# 기본값: try
# 유지 이유: 가능한 경우 huge pages 사용으로 메모리 관리 효율 향상
# 성능: TLB 미스 감소, 대용량 shared_buffers 사용 시 특히 효과적
#huge_page_size = 0
#temp_buffers = 8MB
#max_prepared_transactions = 0
work_mem = 16MB
# 기본값: 4MB
# 변경값: 16MB
# 목적: 정렬, 해시 테이블 등 쿼리 작업에 사용되는 메모리
# 성능: 복잡한 쿼리의 정렬/조인 성능 향상, 디스크 임시 파일 사용 감소
# 주의: (max_connections × work_mem)이 너무 크면 OOM 위험
# 계산: 100 연결 × 16MB = 최대 1.6GB (복잡한 쿼리가 동시 실행될 경우)
#hash_mem_multiplier = 2.0
maintenance_work_mem = 256MB
# 기본값: 64MB
# 변경값: 256MB (RAM의 약 6%)
# 목적: VACUUM, CREATE INDEX, ALTER TABLE 등 유지보수 작업에 사용
# 성능: 인덱스 생성 및 VACUUM 작업 속도 대폭 향상
# 참고: 유지보수 작업은 동시에 많이 실행되지 않으므로 크게 설정 가능
autovacuum_work_mem = 256MB
# 기본값: -1 (maintenance_work_mem 사용)
# 변경값: 256MB
# 목적: autovacuum 전용 메모리 할당
# 성능: autovacuum 성능 향상으로 테이블 bloat 감소
#logical_decoding_work_mem = 64MB
max_stack_depth = 2MB
# 기본값: 2MB
# 유지 이유: 기본값이 대부분의 경우에 적절
#shared_memory_type = mmap
dynamic_shared_memory_type = posix
# 기본값: posix (Linux에서)
# 유지 이유: Linux에서 가장 효율적인 방식
#min_dynamic_shared_memory = 0MB
# - Disk -
#temp_file_limit = -1
#file_copy_method = copy
# - Kernel Resources -
#max_files_per_process = 1000
# - Background Writer -
bgwriter_delay = 200ms
# 기본값: 200ms
# 유지 이유: SSD 환경에서도 기본값이 적절
bgwriter_lru_maxpages = 100
# 기본값: 100
# 유지 이유: 백그라운드 쓰기 작업의 균형 유지
#bgwriter_lru_multiplier = 2.0
bgwriter_flush_after = 512kB
# 기본값: 512kB
# 유지 이유: SSD에서 적절한 flush 크기
# - I/O -
#backend_flush_after = 0
effective_io_concurrency = 200
# 기본값: 1 (HDD), 16 (SSD 감지 시)
# 변경값: 200
# 목적: SSD의 높은 IOPS를 활용한 병렬 I/O 요청 수
# 성능: bitmap heap scan 등에서 여러 페이지를 동시에 prefetch
# 참고: SSD는 동시 I/O 처리 능력이 뛰어나므로 높게 설정
maintenance_io_concurrency = 200
# 기본값: 10
# 변경값: 200
# 목적: VACUUM, CREATE INDEX 등 유지보수 작업의 병렬 I/O
# 성능: 유지보수 작업 속도 향상
#io_max_combine_limit = 128kB
#io_combine_limit = 128kB
#io_method = worker
#io_max_concurrency = -1
#io_workers = 3
# - Worker Processes -
max_worker_processes = 8
# 기본값: 8
# 유지 이유: 4코어 환경에서 적절 (코어 수 × 2)
# 성능: 병렬 쿼리, autovacuum 등 다양한 백그라운드 작업 처리
max_parallel_workers_per_gather = 2
# 기본값: 2
# 변경값: 2 (4코어 환경에서 적절)
# 목적: 단일 쿼리가 사용할 수 있는 최대 병렬 worker 수
# 성능: 대용량 테이블 스캔 시 쿼리 속도 향상
# 참고: 너무 높으면 다른 쿼리의 리소스 부족 발생 가능
max_parallel_maintenance_workers = 2
# 기본값: 2
# 유지 이유: CREATE INDEX 등 유지보수 작업의 병렬화
# 성능: 인덱스 생성 속도 향상
max_parallel_workers = 4
# 기본값: 8
# 변경값: 4 (CPU 코어 수)
# 목적: 시스템 전체에서 동시 실행 가능한 병렬 worker 총 수
# 성능: CPU 코어 수에 맞춰 과도한 컨텍스트 스위칭 방지
#parallel_leader_participation = on
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------
# - Settings -
#wal_level = replica
#fsync = on
#synchronous_commit = on
#wal_sync_method = fsync
#full_page_writes = on
#wal_log_hints = off
wal_compression = lz4
# 기본값: off
# 변경값: lz4
# 목적: WAL 파일 압축으로 I/O 및 스토리지 사용량 감소
# 성능: 네트워크를 통한 복제 시 대역폭 절약, 아카이빙 효율 향상
# 참고: CPU 사용량은 약간 증가하지만 4코어 환경에서 무리 없음
#wal_init_zero = on
#wal_recycle = on
wal_buffers = 16MB
# 기본값: -1 (shared_buffers의 3%, 최소 64kB, 최대 약 16MB)
# 변경값: 16MB
# 목적: WAL 데이터를 디스크에 쓰기 전 버퍼링
# 성능: 쓰기 집약적 워크로드에서 WAL 쓰기 성능 향상
#wal_writer_delay = 200ms
wal_writer_flush_after = 1MB
# 기본값: 1MB
# 유지 이유: SSD에서 적절한 flush 크기
#wal_skip_threshold = 2MB
#commit_delay = 0
#commit_siblings = 5
# - Checkpoints -
checkpoint_timeout = 15min
# 기본값: 5min
# 변경값: 15min
# 목적: 체크포인트 발생 간격 조정
# 성능: 체크포인트 빈도 감소로 I/O spike 완화, 전체 성능 향상
# 참고: 크래시 복구 시간은 약간 증가하지만 일반적으로 허용 가능
checkpoint_completion_target = 0.9
# 기본값: 0.9
# 유지 이유: 체크포인트를 시간에 걸쳐 분산하여 I/O spike 방지
# 성능: 90%의 시간에 걸쳐 checkpoint 완료하여 부하 분산
#checkpoint_flush_after = 256kB
#checkpoint_warning = 30s
max_wal_size = 2GB
# 기본값: 1GB
# 변경값: 2GB
# 목적: 체크포인트 간 생성 가능한 최대 WAL 크기
# 성능: 쓰기 집약적 워크로드에서 체크포인트 빈도 감소
# 참고: SSD 환경에서 더 큰 WAL 크기는 성능에 유리
min_wal_size = 1GB
# 기본값: 80MB
# 변경값: 1GB
# 목적: 항상 유지할 최소 WAL 크기
# 성능: WAL 파일 재사용으로 파일 생성/삭제 오버헤드 감소
# - Prefetching during recovery -
#recovery_prefetch = try
#wal_decode_buffer_size = 512kB
# - Archiving -
#archive_mode = off
#archive_library = ''
#archive_command = ''
#archive_timeout = 0
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Sending Servers -
#max_wal_senders = 10
#max_replication_slots = 10
#wal_keep_size = 0
#max_slot_wal_keep_size = -1
#idle_replication_slot_timeout = 0
#wal_sender_timeout = 60s
#track_commit_timestamp = off
# - Primary Server -
#synchronous_standby_names = ''
#synchronized_standby_slots = ''
# - Standby Servers -
#primary_conninfo = ''
#primary_slot_name = ''
#hot_standby = on
#max_standby_archive_delay = 30s
#max_standby_streaming_delay = 30s
#wal_receiver_create_temp_slot = off
#wal_receiver_status_interval = 10s
#hot_standby_feedback = off
#wal_receiver_timeout = 60s
#wal_retrieve_retry_interval = 5s
#recovery_min_apply_delay = 0
#sync_replication_slots = off
# - Subscribers -
#max_active_replication_origins = 10
#max_logical_replication_workers = 4
#max_sync_workers_per_subscription = 2
#max_parallel_apply_workers_per_subscription = 2
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
# - Planner Method Configuration -
#enable_async_append = on
#enable_bitmapscan = on
#enable_gathermerge = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_incremental_sort = on
#enable_indexscan = on
#enable_indexonlyscan = on
#enable_material = on
#enable_memoize = on
#enable_mergejoin = on
#enable_nestloop = on
#enable_parallel_append = on
#enable_parallel_hash = on
#enable_partition_pruning = on
#enable_partitionwise_join = off
#enable_partitionwise_aggregate = off
#enable_presorted_aggregate = on
#enable_seqscan = on
#enable_sort = on
#enable_tidscan = on
# - Planner Cost Constants -
#seq_page_cost = 1.0
random_page_cost = 1.1
# 기본값: 4.0 (HDD), 1.1 (SSD 자동 감지 시)
# 변경값: 1.1
# 목적: SSD의 랜덤 액세스 특성을 반영
# 성능: 인덱스 스캔 선호도 증가, 쿼리 플래너의 더 나은 결정
# 참고: HDD는 4.0, SSD는 1.1-1.5가 적절
#cpu_tuple_cost = 0.01
#cpu_index_tuple_cost = 0.005
#cpu_operator_cost = 0.0025
#parallel_setup_cost = 1000.0
#parallel_tuple_cost = 0.1
#min_parallel_table_scan_size = 8MB
#min_parallel_index_scan_size = 512kB
effective_cache_size = 3GB
# 기본값: 4GB (시스템에 따라 다름)
# 변경값: 3GB (전체 RAM 4GB의 75%)
# 목적: OS와 PostgreSQL이 파일 캐싱에 사용 가능한 메모리 추정
# 성능: 쿼리 플래너가 인덱스 스캔 비용을 더 정확히 계산
# 참고: shared_buffers + OS 파일 캐시 = 약 3GB
#jit_above_cost = 100000
#jit_inline_above_cost = 500000
#jit_optimize_above_cost = 500000
# - Genetic Query Optimizer -
#geqo = on
#geqo_threshold = 12
# - Other Planner Options -
default_statistics_target = 100
# 기본값: 100
# 유지 이유: 통계 정확도와 ANALYZE 시간의 균형
# 성능: 쿼리 플래너의 정확한 비용 추정
# 참고: 특정 컬럼에 대해 개별적으로 높일 수 있음
#constraint_exclusion = partition
#cursor_tuple_fraction = 0.1
#from_collapse_limit = 8
#jit = on
#join_collapse_limit = 8
#plan_cache_mode = auto
#recursive_worktable_factor = 10.0
#------------------------------------------------------------------------------
# REPORTING AND LOGGING
#------------------------------------------------------------------------------
# 로깅 설정은 기본값 유지 (사용자 요청사항)
#log_destination = 'stderr'
#logging_collector = off
#log_directory = 'log'
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
#log_file_mode = 0600
#log_rotation_age = 1d
#log_rotation_size = 10MB
#log_truncate_on_rotation = off
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
#syslog_sequence_numbers = on
#syslog_split_messages = on
#event_source = 'PostgreSQL'
#log_min_messages = warning
#log_min_error_statement = error
#log_min_duration_statement = -1
#log_min_duration_sample = -1
#log_statement_sample_rate = 1.0
#log_transaction_sample_rate = 0.0
#log_startup_progress_interval = 10s
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_autovacuum_min_duration = 10min
#log_checkpoints = on
#log_connections = ''
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default
#log_hostname = off
#log_line_prefix = '%m [%p] '
#log_lock_waits = off
#log_lock_failures = off
#log_recovery_conflict_waits = off
#log_parameter_max_length = -1
#log_parameter_max_length_on_error = 0
#log_statement = 'none'
#log_replication_commands = off
#log_temp_files = -1
log_timezone = 'Etc/UTC'
#cluster_name = ''
#update_process_title = on
#------------------------------------------------------------------------------
# STATISTICS
#------------------------------------------------------------------------------
#track_activities = on
#track_activity_query_size = 1024
#track_counts = on
#track_cost_delay_timing = off
track_io_timing = on
# 기본값: off
# 변경값: on
# 목적: I/O 작업의 시간 추적으로 성능 병목 지점 파악
# 성능: EXPLAIN ANALYZE 등으로 I/O 병목 진단 가능
# 참고: 약간의 오버헤드 있지만 성능 튜닝에 매우 유용
#track_wal_io_timing = off
#track_functions = none
#stats_fetch_consistency = cache
# - Monitoring -
#compute_query_id = auto
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
#------------------------------------------------------------------------------
# VACUUMING
#------------------------------------------------------------------------------
# - Automatic Vacuuming -
#autovacuum = on
autovacuum_worker_slots = 8
# 기본값: 16 (PostgreSQL 18 신규 파라미터)
# 변경값: 8
# 목적: autovacuum worker 슬롯 수 (동시 실행 가능한 worker 총량)
# 성능: 4코어 환경에서 적절한 슬롯 수로 CPU 리소스 균형
# 참고: max_workers와 별개로 동적으로 worker 생성 가능
autovacuum_max_workers = 3
# 기본값: 3
# 변경값: 3
# 목적: 동시에 실행 가능한 autovacuum worker 프로세스 수
# 성능: 여러 테이블을 동시에 vacuum 처리
# 참고: 4코어 환경에서 적절한 수준
autovacuum_naptime = 30s
# 기본값: 1min
# 변경값: 30s
# 목적: autovacuum이 데이터베이스를 체크하는 주기
# 성능: 더 빈번한 체크로 테이블 bloat 감소, 성능 유지
# 참고: 쓰기가 많은 환경에서 효과적
#autovacuum_vacuum_threshold = 50
#autovacuum_vacuum_insert_threshold = 1000
#autovacuum_analyze_threshold = 50
autovacuum_vacuum_scale_factor = 0.1
# 기본값: 0.2 (테이블의 20%)
# 변경값: 0.1 (테이블의 10%)
# 목적: vacuum 실행 trigger 조건 (dead tuple 비율)
# 성능: 더 자주 vacuum 실행으로 테이블 bloat 최소화
# 참고: 대형 테이블에서 특히 효과적
#autovacuum_vacuum_insert_scale_factor = 0.2
autovacuum_analyze_scale_factor = 0.05
# 기본값: 0.1 (테이블의 10%)
# 변경값: 0.05 (테이블의 5%)
# 목적: analyze 실행 trigger 조건
# 성능: 더 빈번한 통계 업데이트로 쿼리 플래너의 정확도 향상
#autovacuum_vacuum_max_threshold = 100000000
#autovacuum_freeze_max_age = 200000000
#autovacuum_multixact_freeze_max_age = 400000000
#autovacuum_vacuum_cost_delay = 2ms
#autovacuum_vacuum_cost_limit = -1
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0
#vacuum_cost_page_hit = 1
#vacuum_cost_page_miss = 2
#vacuum_cost_page_dirty = 20
#vacuum_cost_limit = 200
# - Default Behavior -
#vacuum_truncate = on
# - Freezing -
#vacuum_freeze_table_age = 150000000
#vacuum_freeze_min_age = 50000000
#vacuum_failsafe_age = 1600000000
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_failsafe_age = 1600000000
#vacuum_max_eager_freeze_failure_rate = 0.03
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
# - Statement Behavior -
#client_min_messages = notice
#search_path = '"$user", public'
#row_security = on
#default_table_access_method = 'heap'
#default_tablespace = ''
#default_toast_compression = 'pglz'
#temp_tablespaces = ''
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'
statement_timeout = 30000
# 기본값: 0 (무제한)
# 변경값: 30000ms (30초)
# 목적: 장시간 실행되는 쿼리 자동 종료
# 성능: 문제있는 쿼리로 인한 리소스 점유 방지
# 참고: 애플리케이션 특성에 따라 조정 필요, 0으로 비활성화 가능
#transaction_timeout = 0
lock_timeout = 5000
# 기본값: 0 (무제한)
# 변경값: 5000ms (5초)
# 목적: 락 대기 시간 제한
# 성능: 데드락 상황 빠른 감지, 애플리케이션 응답성 향상
#idle_in_transaction_session_timeout = 0
idle_session_timeout = 300000
# 기본값: 0 (무제한)
# 변경값: 300000ms (5분)
# 목적: 유휴 세션 자동 종료
# 성능: 불필요한 연결로 인한 리소스 낭비 방지
# 참고: 연결 풀 사용 시 조정 필요
#bytea_output = 'hex'
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = ''
#event_triggers = on
# - Locale and Formatting -
datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
timezone = 'Etc/UTC'
#timezone_abbreviations = 'Default'
#extra_float_digits = 1
#client_encoding = sql_ascii
lc_messages = 'en_US.utf8'
lc_monetary = 'en_US.utf8'
lc_numeric = 'en_US.utf8'
lc_time = 'en_US.utf8'
#icu_validation_level = warning
default_text_search_config = 'pg_catalog.english'
# - Shared Library Preloading -
#local_preload_libraries = ''
#session_preload_libraries = ''
#shared_preload_libraries = ''
#jit_provider = 'llvmjit'
# - Other Defaults -
#dynamic_library_path = '$libdir'
#extension_control_path = '$system'
#gin_fuzzy_search_limit = 0
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
deadlock_timeout = 1s
# 기본값: 1s
# 유지 이유: 데드락 감지를 위한 적절한 대기 시간
#max_locks_per_transaction = 64
#max_pred_locks_per_transaction = 64
#max_pred_locks_per_relation = -2
#max_pred_locks_per_page = 2
#------------------------------------------------------------------------------
# VERSION AND PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
#array_nulls = on
#backslash_quote = safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
#transform_null_equals = off
#allow_alter_system = on
#------------------------------------------------------------------------------
# ERROR HANDLING
#------------------------------------------------------------------------------
#exit_on_error = off
#restart_after_crash = on
#data_sync_retry = off
#recovery_init_sync_method = fsync
#------------------------------------------------------------------------------
# CONFIG FILE INCLUDES
#------------------------------------------------------------------------------
#include_dir = '...'
#include_if_exists = '...'
#include = '...'
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# =============================================================================
# 주요 변경사항 요약
# =============================================================================
#
# 1. 메모리 설정 (4GB RAM 기준)
# - shared_buffers: 128MB → 1GB (25% of RAM)
# - effective_cache_size: 4GB → 3GB (75% of RAM)
# - work_mem: 4MB → 16MB (쿼리 성능 향상)
# - maintenance_work_mem: 64MB → 256MB (유지보수 작업 가속)
#
# 2. 연결 관리
# - tcp_keepalives 설정: 죽은 연결 빠른 감지 (90초 이내)
# - statement_timeout: 30초 (장시간 쿼리 방지)
# - lock_timeout: 5초 (락 대기 제한)
# - idle_session_timeout: 5분 (유휴 세션 정리)
#
# 3. 병렬 처리 (4코어 최적화)
# - max_parallel_workers: 4 (CPU 코어 수)
# - max_parallel_workers_per_gather: 2
# - max_worker_processes: 8
#
# 4. I/O 최적화 (SSD 특화)
# - random_page_cost: 4.0 → 1.1
# - effective_io_concurrency: 1 → 200
# - maintenance_io_concurrency: 10 → 200
#
# 5. WAL 및 체크포인트
# - wal_compression: off → lz4 (I/O 감소)
# - wal_buffers: 자동 → 16MB
# - checkpoint_timeout: 5min → 15min
# - max_wal_size: 1GB → 2GB
# - min_wal_size: 80MB → 1GB
#
# 6. Autovacuum 튜닝
# - autovacuum_worker_slots: 16 → 8
# - autovacuum_naptime: 1min → 30s (더 빈번한 체크)
# - autovacuum_vacuum_scale_factor: 0.2 → 0.1 (더 자주 실행)
# - autovacuum_analyze_scale_factor: 0.1 → 0.05
#
# 7. 모니터링
# - track_io_timing: off → on (I/O 성능 진단)
#
# =============================================================================
# 예상 성능 향상
# =============================================================================
#
# - 읽기 성능: 30-50% 향상 (shared_buffers, effective_cache_size)
# - 쓰기 성능: 20-40% 향상 (WAL 설정, checkpoint 최적화)
# - 복잡한 쿼리: 40-100% 향상 (work_mem, 병렬 처리)
# - 유지보수 작업: 100-300% 향상 (maintenance_work_mem, I/O 동시성)
# - 전체 처리량: 25-50% 향상 (모든 최적화의 시너지)
#
# =============================================================================
# 적용 방법
# =============================================================================
#
# 1. 이 파일을 postgresql.conf로 저장 (또는 기존 파일 백업 후 교체)
# 2. PostgreSQL 재시작:
# sudo systemctl restart postgresql
# 또는
# sudo pg_ctl restart -D /var/lib/postgresql/data
#
# 3. 설정 확인:
# SHOW shared_buffers;
# SHOW effective_cache_size;
# SHOW work_mem;
#
# 4. 모니터링 (첫 며칠간):
# - 메모리 사용량: free -h, htop
# - 체크포인트 빈도: 로그 확인
# - 쿼리 성능: pg_stat_statements 활용
#
# =============================================================================

View File

@ -1,6 +1,6 @@
server {
listen 80;
server_name demo.castad.net;
server_name localhost;
if ($bad_bot) {
return 403;

View File

@ -1,245 +0,0 @@
# ===============================================
# Production Level Nginx Configuration (Sample Template)
# 서버 사양: 쿼드코어 CPU, 4GB RAM, ~50 req/s
# 백엔드: FastAPI REST API Server
# ===============================================
# HTTP 서버 블록 (포트 80) - HTTPS로 리다이렉트
server {
listen 80;
server_name demo.castad.net www.demo.castad.net;
# 보안을 위한 호스트 검증 - 허용되지 않은 도메인 차단
# 도메인 확장자가 다르다면 추가해줘야함
# if ($host !~* ^(www\.)?demo.castad.net\.(com|kr|net|org)$) {
# return 444;
# }
# Let's Encrypt 도메인 검증 프로그램 허용 (리다이렉트 전에 처리)
# SSL 인증서 갱신을 위해 필수
location ^~ /.well-known/acme-challenge/ {
allow all;
root /www/certbot;
try_files $uri =404; # 디렉토리 순회 공격 방지
}
# HTTP를 HTTPS로 리다이렉트 (acme-challenge 제외)
# return 301은 rewrite보다 효율적이며 $server_name이 $host보다 안전함
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS 서버 블록 (포트 443) - 메인 애플리케이션
server {
listen 443 ssl;
http2 on;
server_name demo.castad.net www.demo.castad.net;
# 악성 봇 차단 (nginx.conf의 http 블록에서 $bad_bot 맵 정의 필요)
if ($bad_bot) {
return 403;
}
# SSL/TLS 인증서 설정
ssl_certificate /etc/letsencrypt/live/demo.castad.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/demo.castad.net/privkey.pem;
ssl_dhparam /etc/ssl/certs/demo.castad.net/dhparam.pem; # openssl dhparam -out /etc/ssl/certs/demo.castad.net/dhparam.pem 2048
# 최신 SSL/TLS 설정 - SSL Labs A+ 등급 달성 가능
ssl_session_cache shared:SSL:50m; # SSL 세션 캐시 크기 증가 (트래픽 많을 시 유용)
ssl_session_timeout 10m; # SSL 세션 타임아웃
ssl_session_tickets off; # 보안 향상을 위해 세션 티켓 비활성화 (nginx >= 1.5.9)
ssl_protocols TLSv1.2 TLSv1.3; # 최신 TLS 프로토콜만 사용
ssl_prefer_server_ciphers off; # TLSv1.3에서는 클라이언트 선호 암호화 사용 (모범 사례)
# 최신 암호화 스위트 - CHACHA20-POLY1305 포함 (모바일 최적화)
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
ssl_ecdh_curve secp384r1; # ECDH 곡선 설정 (nginx >= 1.1.0)
# OCSP 스테이플링 - SSL 핸드셰이크 성능 향상
# 인증서에 OCSP URL이 없으면 자동으로 비활성화됨 (경고는 정상)
#ssl_stapling on;
#ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/demo.castad.net/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s; # Cloudflare와 Google DNS 사용
resolver_timeout 5s;
# 보안 헤더 - 다양한 공격으로부터 보호
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # HSTS - 다운그레이드 공격 방지
add_header X-Frame-Options "DENY" always; # 클릭재킹 방지
add_header X-Content-Type-Options "nosniff" always; # MIME 스니핑 방지
add_header X-XSS-Protection "1; mode=block" always; # 레거시 XSS 보호
add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 리퍼러 정보 제어
#add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' ''; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss: ws:;" always;
#add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com https://www.youtube.com https://s.ytimg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.tailwindcss.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: https: https://i.ytimg.com https://img.youtube.com; connect-src 'self' wss: ws: https://cdn.tailwindcss.com; frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com; media-src 'self' https://www.youtube.com;" always;
add_header Content-Security-Policy "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;" always;
# XSS 방지를 위한 CSP
# 'unsafe-inline'은 범용 설정이 아니며, 보안상 위험한 설정입니다. CSP의 핵심 보호 기능을 무력화시키므로, 개발 환경이나 레거시 코드 마이그레이션 과정에서만 임시로 사용하고, 프로덕션 환경에서는 nonce, hash, 또는 외부 파일 분리 방식으로 대체해야 합니다.
# 클라이언트가 업로드할 수 있는 전체 요청 본문(body) 의 최대 허용 크기
# 요청 바디(파일, 폼 데이터 등)가 이 값을 초과하면 Nginx는 즉시 413 Request Entity Too Large 에러를 반환
# 업로드 제한선.
client_max_body_size 100M; # 최대 업로드 크기 제한 (애플리케이션에 맞게 조정)
# 파일 캐시 - I/O 성능 향상
open_file_cache max=1000 inactive=20s; # 최대 1000개 파일 캐시, 20초 비활성 시 제거
open_file_cache_valid 30s; # 캐시 유효성 검사 주기
open_file_cache_min_uses 2; # 최소 2회 사용 시 캐시
open_file_cache_errors on; # 파일 오류도 캐시
# 로깅 설정 - 버퍼링으로 I/O 감소
access_log /log/nginx/demo.castad.net.com.gunicorn_access.log main buffer=32k flush=5s;
error_log /log/nginx/demo.castad.net.com.gunicorn_error.log warn;
# frontend에 오류 페이지 정의가 되어 있는 경우 사용 가능
# # 커스텀 오류 페이지 - 더 나은 사용자 경험 및 정보 노출 방지
# error_page 404 /404.html;
# error_page 500 502 503 504 /50x.html;
# location = /404.html {
# internal; # 내부 리다이렉트만 허용
# root /www/error_pages;
# }
# location = /50x.html {
# internal; # 내부 리다이렉트만 허용
# root /www/error_pages;
# }
# 프론트엔드 정적 파일 루트
root /www/o2o-castad-frontend/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Fastapi 미디어 파일 - 사용자 업로드 파일
location /media {
autoindex off; # 디렉토리 목록 비활성화
# gzip_static on; # 사전 압축된 .gz 파일 사용
expires 30d; # 브라우저 캐시 30일 후 브라우저가 다시 요청할 때 재검증
alias /www/o2o-castad-backend/media; # Fastapi 프로젝트의 미디어 파일 경로
# 정적 파일 캐싱 - 브라우저 캐시 최적화
add_header Cache-Control "public, immutable";
access_log off; # 액세스 로그 비활성화로 성능 향상
}
# Fastapi 정적 파일 - CSS, JS, 이미지 등
location /static {
autoindex off; # 디렉토리 목록 비활성화
# gzip_static on; # 사전 압축된 .gz 파일 사용 압축된 파일이 없다면 설정 무의미
expires 30d; # 브라우저 캐시 30일 후 브라우저가 다시 요청할 때 재검증
alias /www/o2o-castad-backend/static; # Fastapi 프로젝트의 정적 파일 경로
# 정적 파일 캐싱 - 브라우저 캐시 최적화
add_header Cache-Control "public, immutable";
access_log off; # 액세스 로그 비활성화로 성능 향상
}
# 메인 애플리케이션 - 백엔드로 프록시
location /api/ {
autoindex off; # 디렉토리 목록 비활성화
# 속도 제한 - DDoS 및 무차별 대입 공격 방지
# (nginx.conf의 http 블록에서 limit_req_zone과 limit_conn_zone 정의 필요)
#limit_req zone=general burst=20 nodelay; # 초당 요청 제한
#limit_conn addr 10; # IP당 동시 연결 제한
# HTTP 메서드 제한 - HTTP 동사 변조 공격 방지
limit_except GET POST HEAD OPTIONS DELETE {
deny all;
}
# 백엔드 애플리케이션으로 프록시
proxy_pass http://uvicorn-app:8000/;
# WebSocket 지원 - 실시간 통신 애플리케이션에 필수
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; # nginx.conf에서 $connection_upgrade 맵 정의 필요
# 프록시 헤더 - 클라이언트 정보 전달
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# 타임아웃 설정 - 애플리케이션에 맞게 조정
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 버퍼 설정 - 대화형 애플리케이션에 최적화 - Websocket 사용시 설정
# proxy_buffering off; # 즉시 응답 전달
# proxy_request_buffering off; # 즉시 요청 전달
# 버퍼링 활성화 (기본값) - 기본 fastapi 사용시 설정
proxy_buffering on;
proxy_request_buffering on;
# 버퍼 크기 설정 - 기본 fastapi 사용시 설정
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_set_header Accept-Encoding gzip;
}
# Let's Encrypt 도메인 검증 프로그램 허용 (HTTPS에서도 필요시)
location ^~ /.well-known/acme-challenge/ {
allow all;
root /www/certbot;
try_files $uri =404; # 디렉토리 순회 공격 방지
}
# 정적 리소스 캐싱 - 이미지, 폰트, CSS, JS 등
# 브라우저 캐시로 로드 시간 단축 및 서버 부하 감소
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
expires 1y; # 1년 캐시
add_header Cache-Control "public, immutable";
access_log off; # 액세스 로그 비활성화로 성능 향상
}
# 닷 파일 차단 - .htaccess, .htpasswd, .svn, .git, .env 등
# 민감한 설정 파일 노출 방지
location ~ /\. {
deny all;
access_log off; # 차단된 시도 로그 비활성화
log_not_found off;
}
# 민감한 파일 확장자 차단 - 로그, 인증서, 스크립트, SQL 등
# 보안을 위해 직접 접근 차단
location ~* \.(log|binary|pem|enc|crt|conf|cnf|sql|sh|key|yml|lock)$ {
deny all;
access_log off; # 차단된 시도 로그 비활성화
log_not_found off;
}
# 민감한 설정 파일 차단 - composer, package.json, phpunit 등
# 프로젝트 메타데이터 및 설정 파일 노출 방지
location ~* (composer\.json|composer\.lock|composer\.phar|contributing\.md|license\.txt|readme\.rst|readme\.md|readme\.txt|copyright|artisan|gulpfile\.js|package\.json|phpunit\.xml|access_log|error_log|gruntfile\.js)$ {
deny all;
access_log off; # 차단된 시도 로그 비활성화
log_not_found off;
}
# 파비콘 - 로그 노이즈 제거
location = /favicon.ico {
log_not_found off;
access_log off;
}
# robots.txt - 검색 엔진 크롤러 제어
location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}
}

View File

@ -15,7 +15,7 @@ RUN apt-get update && \
curl wget git tar gnupg2 lsb-release lz4 zstd vim \
build-essential zlib1g-dev libncurses5-dev libgdbm-dev \
libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev \
python3-dev libmysqlclient-dev pkg-config ca-certificates
python3-dev pkg-config ca-certificates
# ========================================
# 2. Python 3.13 build & install
@ -47,28 +47,11 @@ RUN rm -f /usr/bin/python /usr/bin/python3 && \
RUN pip install --upgrade pip && \
pip install wheel && \
pip install sqlalchemy alembic pydantic && \
pip install psycopg2-binary asyncpg && \
pip install mysqlclient asyncmy && \
pip install gunicorn uvicorn[standard] && \
pip install fastapi uv poetry
# ========================================
# 5. Percona XtraBackup (mysql backup)
# ========================================
RUN curl -O https://repo.percona.com/apt/percona-release_latest.generic_all.deb && \
apt-get install -y ./percona-release_latest.generic_all.deb && \
rm -f percona-release_latest.generic_all.deb && \
apt-get update && \
percona-release enable pxb-84-lts && \
apt-get install -y percona-xtrabackup-84
# ========================================
# 6. PostgreSQL backup (pgbackrest)
# ========================================
RUN apt-get install -y pgbackrest
# ========================================
# 7. Cleanup
# 5. Cleanup
# ========================================
RUN apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \

View File