Uvicorn#
Uvicorn은 Python용 ASGI(Asynchronous Server Gateway Interface) 웹 서버 구현체이다.
Uvicorn은 비동기 Python 웹 애플리케이션을 위한 고성능 서버이다.
ASGI 프로토콜을 지원하여 HTTP, HTTP2, WebSocket 등의 프로토콜을 처리할 수 있다.
주요 특징#
- 비동기 처리: asyncio를 기반으로 하여 비동기 코드를 효율적으로 실행한다.
- 고성능: uvloop와 httptools를 사용하여 빠른 속도를 제공한다.
- 경량화: 최소한의 의존성으로 설치 가능하다.
- 개발 편의성: 자동 리로드 기능을 제공하여 개발 시 편리하다.
- ASGI 호환성: ASGI 표준을 준수하여 다양한 ASGI 프레임워크와 호환된다.
FastAPI와의 통합#
Uvicorn은 FastAPI의 기본 웹 서버로 사용된다.
FastAPI는 Uvicorn의 비동기 처리 능력을 활용하여 고성능 API를 구현할 수 있다.
최적화#
Uvicorn은 uvloop를 사용하여 asyncio의 성능을 2-4배 향상시킬 수 있다.
또한 httptools를 사용하여 HTTP 파싱 속도를 개선한다.
Uvloop#
uvloop는 Cython으로 작성된 매우 빠른 Python 이벤트 루프 구현체이다.
libuv(Node.js가 사용하는 동일한 이벤트 루프 엔진)를 기반으로 하여 Python의 기본 asyncio 이벤트 루프를 대체한다.
기본적인 사용방법:
1
2
3
4
5
6
7
8
9
10
11
12
13
| import uvloop
import asyncio
# uvloop를 기본 이벤트 루프로 설정
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
async def main():
# 비동기 작업 수행
await asyncio.sleep(1)
print("Hello, uvloop!")
# 이벤트 루프 실행
asyncio.run(main())
|
주요 특징:
- 성능: uvloop는 Node.js의 libuv를 기반으로 하며, 기본 asyncio 이벤트 루프보다 2-4배 빠른 성능을 제공한다.
- 구현: Cython으로 작성되어 C 수준의 성능을 제공한다.
- 호환성: asyncio와 완벽하게 호환되며, 기존 asyncio 코드를 수정하지 않고도 사용할 수 있다.
- 플랫폼 지원: Linux, macOS, FreeBSD에서 사용 가능하지만, Windows에서는 지원되지 않는다.
uvloop가 제공하는 주요 성능 향상 요소:
향상된 I/O 처리:
1
2
3
4
5
6
| async def handle_connection(reader, writer):
# uvloop는 이러한 I/O 작업을 매우 효율적으로 처리합니다
data = await reader.read(100)
writer.write(data)
await writer.drain()
writer.close()
|
타이머 최적화:
1
2
3
4
5
6
| async def timed_operation():
# uvloop는 타이머 처리도 더 효율적으로 수행합니다
start = asyncio.get_event_loop().time()
await asyncio.sleep(0.1)
end = asyncio.get_event_loop().time()
return end - start
|
시스템 콜 최적화:
1
2
3
4
5
| async def file_operations():
# 파일 시스템 작업도 더 빠르게 처리됩니다
async with aiofiles.open('file.txt', mode='r') as f:
contents = await f.read()
return contents
|
httptools는 Node.js의 http-parser를 Python에 바인딩한 고성능 HTTP 파싱 라이브러리.
이는 Python의 기본 HTTP 파서보다 훨씬 빠른 성능을 제공한다.
httptools의 기본 사용법:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| from httptools import HttpRequestParser
class RequestParser:
def __init__(self):
self.parser = HttpRequestParser(self)
self.headers = {}
self.body = bytearray()
def on_header(self, name: bytes, value: bytes):
# 헤더를 파싱할 때 호출됩니다
self.headers[name.decode()] = value.decode()
def on_body(self, body: bytes):
# 요청 본문을 파싱할 때 호출됩니다
self.body.extend(body)
|
httptools의 주요 특징과 장점들:
성능: Nginx의 HTTP 파서를 기반으로 하여 매우 빠른 파싱 속도를 제공한다.
구현: C로 작성되어 있어 높은 성능을 보장한다.
비동기 지원: 비동기 프로그래밍 모델과 잘 맞아 Uvicorn과 같은 비동기 서버에서 효과적으로 사용된다.
메모리 효율성: 스트리밍 파싱을 지원하여 메모리 사용을 최적화한다.
스트리밍 파싱:
1
2
3
4
5
6
7
| class HTTPProtocol:
def data_received(self, data):
# 데이터가 도착하는 대로 점진적으로 파싱
try:
self.parser.feed_data(data)
except Exception as exc:
print(f"파싱 에러: {exc}")
|
고성능 URL 파싱:
1
2
3
4
5
6
7
8
9
10
11
| from httptools import parse_url
def parse_request_url(url: bytes):
parsed = parse_url(url)
return {
'schema': parsed.schema,
'host': parsed.host,
'port': parsed.port,
'path': parsed.path,
'query': parsed.query
}
|
Uvicorn에서 이 두 구성요소가 어떻게 함께 작동하는지 살펴보자:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import uvicorn
from fastapi import FastAPI
app = FastAPI()
if __name__ == "__main__":
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
loop="uvloop", # uvloop 사용
http="httptools", # httptools 사용
workers=4 # 워커 프로세스 수
)
|
이러한 구성요소들의 조합은 다음과 같은 이점을 제공한다:
- 향상된 처리량: uvloop와 httptools의 조합은 기존 Python 웹 서버보다 훨씬 높은 요청 처리량을 달성할 수 있다.
- 낮은 지연 시간: 최적화된 이벤트 루프와 HTTP 파싱으로 인해 각 요청의 처리 시간이 단축된다.
- 메모리 효율성: C 기반의 구현으로 인해 메모리 사용이 더 효율적이다.
- 안정성: 널리 검증된 libuv와 http-parser를 기반으로 하므로 안정적인 성능을 제공한다.
이러한 특성들은 Uvicorn이 고성능 Python 웹 애플리케이션을 구축하는 데 이상적인 선택이 되게 한다.
특히 FastAPI나 Starlette와 같은 현대적인 비동기 웹 프레임워크와 함께 사용할 때 그 장점이 더욱 두드러진다.
설치 및 사용#
Uvicorn은 pip를 통해 쉽게 설치할 수 있다:
기본 사용 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import uvicorn
async def app(scope, receive, send):
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, log_level="info")
|
Uvicorn 환경 설정#
다양한 설정 옵션#
실제 운영 환경에서는 설정들을 서버의 특성과 요구사항에 맞게 조정해야 한다.
특히 다음과 같은 점들을 고려해야 한다:
- 서버의 하드웨어 리소스 (CPU, 메모리)에 맞춘 워커 수와 동시성 제한 설정
- 예상되는 트래픽 패턴에 따른 타임아웃과 버퍼 크기 조정
- 보안 요구사항에 맞는 SSL/TLS 설정
- 모니터링과 디버깅을 위한 적절한 로깅 레벨 설정
이러한 설정들은 실제 부하 테스트를 통해 최적화되어야 하며, 운영 중에도 지속적인 모니터링과 튜닝이 필요하다.
uvicorn의 설정 옵션들을 체계적으로 설명해드리겠습니다. 각 카테고리별로 설정의 목적과 영향을 자세히 살펴보며, 실제 운영 환경에서 어떻게 활용할 수 있는지도 함께 알아보겠습니다.
서버 기본 설정#
서버의 가장 기본적인 동작을 제어하는 핵심 설정.
이러한 설정들은 uvicorn이 어떻게 요청을 받고 처리할지를 결정한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import uvicorn
config = {
# 애플리케이션 설정
"app": "main:app", # 실행할 애플리케이션의 경로
"host": "0.0.0.0", # 바인딩할 호스트 주소
"port": 8000, # 리스닝할 포트 번호
"uds": None, # Unix 도메인 소켓 경로 (선택적)
"fd": None, # 파일 디스크립터 (선택적)
"loop": "auto", # 이벤트 루프 선택 (auto, asyncio, uvloop)
"http": "auto", # HTTP 프로토콜 구현체 (auto, h11, httptools)
"ws": "auto", # WebSocket 프로토콜 구현체 (auto, none, websockets)
"lifespan": "auto", # 수명 주기 이벤트 처리 (auto, on, off)
"env_file": ".env" # 환경 변수 파일 경로
}
|
최적화 설정#
서버의 성능과 리소스 사용에 직접적인 영향을 미치는 설정.
이러한 설정들은 실제 운영 환경에서 매우 중요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| performance_config = {
# 프로세스 설정
"workers": 4, # 워커 프로세스 수
"limit_concurrency": 1000, # 동시 연결 제한
"limit_max_requests": 10000, # 워커 재시작 전 최대 요청 수
"backlog": 2048, # 연결 대기열 크기
# 타임아웃 설정
"timeout_keep_alive": 5, # keep-alive 연결 타임아웃 (초)
"timeout_notify": 30, # 종료 전 알림 대기 시간 (초)
# 버퍼 설정
"buffer_size": 16384, # 요청/응답 버퍼 크기 (바이트)
}
|
보안 설정#
서버의 보안을 강화하기 위한 설정.
SSL/TLS 설정과 프록시 관련 설정이 포함된다.
1
2
3
4
5
6
7
8
9
10
11
| security_config = {
# SSL/TLS 설정
"ssl_keyfile": "/path/to/key.pem", # SSL 키 파일 경로
"ssl_certfile": "/path/to/cert.pem", # SSL 인증서 파일 경로
"ssl_keyfile_password": None, # SSL 키 파일 암호
"ssl_version": 2, # SSL/TLS 버전
# 프록시 설정
"proxy_headers": True, # X-Forwarded-* 헤더 처리
"forwarded_allow_ips": "*", # 신뢰할 수 있는 프록시 IP
}
|
로깅 설정#
서버의 동작을 모니터링하고 문제를 진단하기 위한 로깅 관련 설정.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| logging_config = {
# 로그 레벨 설정
"log_level": "info", # 로그 레벨 (critical, error, warning, info, debug, trace)
"access_log": True, # 접근 로그 활성화
"use_colors": True, # 컬러 로그 사용
# 로그 포맷 설정
"log_config": {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"uvicorn": {
"handlers": ["default"],
"level": "INFO"
}
}
}
}
|
개발 설정#
개발 환경에서 유용한 설정.
코드 변경 감지와 디버깅을 위한 옵션들이 포함된다.
1
2
3
4
5
6
7
8
9
10
| development_config = {
# 리로드 설정
"reload": True, # 코드 변경 감지 및 자동 리로드
"reload_dirs": ["app", "config"], # 리로드를 감시할 디렉토리
"reload_delay": 0.25, # 리로드 지연 시간 (초)
# 디버깅 설정
"debug": True, # 디버그 모드 활성화
"log_level": "debug", # 상세한 로깅
}
|
리소스 관리 설정#
서버의 리소스 사용을 제어하는 설정.
메모리와 CPU 사용량을 관리하는 데 도움이 된다.
1
2
3
4
5
6
7
8
| resource_config = {
# 프로세스 제한 설정
"limit_max_requests": 10000, # 워커 재시작 전 최대 요청 수
"limit_concurrency": 1000, # 동시 연결 제한
# 메모리 관리
"buffer_size": 16384, # 버퍼 크기 (바이트)
}
|
시스템 서비스 등록#
Systemd 서비스 파일을 작성#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| [Unit]
Description=Uvicorn instance to serve FastAPI application
After=network.target
# 데이터베이스 의존성이 있다면 다음과 같이 추가할 수 있습니다
# Requires=postgresql.service
# After=postgresql.service
[Service]
# 서비스를 실행할 사용자와 그룹을 지정합니다
User=www-data
Group=www-data
# 작업 디렉토리를 지정합니다
WorkingDirectory=/path/to/your/fastapi/app
# 환경 변수 설정
Environment="PATH=/path/to/your/virtualenv/bin"
Environment="PYTHONPATH=/path/to/your/fastapi/app"
Environment="LOG_LEVEL=info"
# 실행 명령을 지정합니다
ExecStart=/path/to/your/virtualenv/bin/uvicorn \
main:app \
--host 0.0.0.0 \
--port 8000 \
--workers 4 \
--loop uvloop \
--http httptools \
--log-level info \
--access-log \
--use-colors \
--proxy-headers \
--forwarded-allow-ips='*'
# 프로세스 관리 설정
Restart=always
RestartSec=10
StartLimitInterval=0
# 보안 관련 설정
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
|
서비스 파일을 시스템에 등록하고 활성화하는 방법#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 서비스 파일 복사
sudo cp uvicorn.service /etc/systemd/system/
# systemd 데몬 리로드
sudo systemctl daemon-reload
# 서비스 시작
sudo systemctl start uvicorn
# 부팅 시 자동 시작 설정
sudo systemctl enable uvicorn
# 서비스 상태 확인
sudo systemctl status uvicorn
|
프로덕션 환경에서는 Gunicorn과 함께 Uvicorn을 사용하는 것이 권장된다.
Gunicorn은 프로세스 관리와 로드 밸런싱을 제공하며, Uvicorn은 워커로 동작하여 높은 성능을 발휘한다.
장점들을 살펴보면:
- Nginx는 리버스 프록시로서 SSL 종료, 정적 파일 서빙, 로드 밸런싱을 처리한다.
- Gunicorn은 프로세스 관리자로서 워커 프로세스의 생명주기를 관리한다.
- Uvicorn은 ASGI 서버로서 비동기 Python 애플리케이션을 효율적으로 실행한다.
이 구성은 높은 성능과 안정성을 제공하며, 각 구성 요소의 장점을 최대한 활용할 수 있다.
실제 운영 환경에서는 서버의 리소스와 트래픽 패턴에 따라 워커 수와 타임아웃 설정 등을 적절히 조정해야 한다.
서비스 아키텍처는 다음과 같다:
Nginx(리버스 프록시) → Gunicorn(프로세스 관리자) → Uvicorn(ASGI 서버) → FastAPI/Django 애플리케이션
먼저 Nginx 설정 파일을 작성한다.
/etc/nginx/sites-available/fastapi_app
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| server {
listen 80;
server_name example.com;
access_log /var/log/nginx/app-access.log;
error_log /var/log/nginx/app-error.log;
location / {
proxy_pass http://unix:/run/gunicorn.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header 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;
# WebSocket 지원을 위한 설정
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
}
|
Gunicorn 설정 파일을 작성한다.
`` /etc/gunicorn/conf.py`:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Gunicorn 설정 파일
import multiprocessing
from uvicorn.workers import UvicornWorker
# 기본 설정
bind = "unix:/run/gunicorn.sock"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
max_requests = 1000
max_requests_jitter = 50
# Uvicorn 워커 설정
class CustomUvicornWorker(UvicornWorker):
CONFIG_KWARGS = {
"loop": "uvloop",
"http": "httptools",
"lifespan": "on",
"access_log": True,
"use_colors": False,
"timeout_keep_alive": 5,
}
# 로깅 설정
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
|
systemd 서비스 파일을 작성한다.
`` /etc/systemd/system/gunicorn.service`:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| [Unit]
Description=Gunicorn with Uvicorn workers for FastAPI application
After=network.target
Requires=nginx.service
After=nginx.service
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/app
Environment="PATH=/path/to/your/virtualenv/bin"
Environment="PYTHONPATH=/path/to/your/app"
Environment="PYTHONUNBUFFERED=1"
# 환경 변수 파일 포함
EnvironmentFile=/etc/default/gunicorn
# Gunicorn 실행 명령
ExecStart=/path/to/your/virtualenv/bin/gunicorn \
--config /etc/gunicorn/conf.py \
main:app
# 프로세스 관리
Restart=always
RestartSec=10
StartLimitInterval=0
# 보안 설정
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
# 리소스 제한
LimitNOFILE=65535
TimeoutStopSec=5
[Install]
WantedBy=multi-user.target
|
환경 변수를 관리하기 위한 파일을 생성한다.
/etc/default/gunicorn
:
1
2
3
4
5
| # Gunicorn 환경 변수
PYTHONPATH=/path/to/your/app
DATABASE_URL=postgresql://user:password@localhost/dbname
REDIS_URL=redis://localhost:6379/0
LOG_LEVEL=info
|
필요한 디렉토리와 권한을 설정한다
1
2
3
4
5
6
| # 로그 디렉토리 생성
sudo mkdir -p /var/log/gunicorn
sudo chown -R www-data:www-data /var/log/gunicorn
# 소켓 디렉토리 권한 설정
sudo chown www-data:www-data /run
|
서비스를 활성화하고 시작한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Nginx 설정 심볼릭 링크 생성
sudo ln -s /etc/nginx/sites-available/fastapi_app /etc/nginx/sites-enabled/
# Nginx 설정 테스트
sudo nginx -t
# systemd 데몬 리로드
sudo systemctl daemon-reload
# 서비스 시작
sudo systemctl start gunicorn
sudo systemctl start nginx
# 부팅 시 자동 시작 설정
sudo systemctl enable gunicorn
sudo systemctl enable nginx
|
참고 및 출처#
Uvicorn