FastAPI тез ёзиш ва тез ишлаш учун яратилган фреймворк, шунинг учун у мобил иловалар, Telegram ботлар ва ички хизматлар учун API сифатида тобора кўп танланмоқда. Локал компьютерда uvicorn main:app --reload буйруғи билан ҳаммаси бир зумда ишга тушади. Аммо бу режим ишлаб чиқиш учун: --reload файлларни кузатади, битта жараёнда ишлайди ва сервер қайта юкланганда ўзи тикланмайди. Ушбу мақолада FastAPI иловасини VPS'га тўғри жойлаш, уни доимий ишлайдиган хизматга айлантириш ва Nginx орқали HTTPS билан очишни кўриб чиқамиз.
ASGI нима ва нега Uvicorn керак
FastAPI ASGI стандартига асосланган, яъни у асинхрон сўровларни, WebSocket уланишларини ва узоқ давом этадиган алоқаларни қўллаб-қувватлайди. Бундай иловани ишга тушириш учун ASGI сервер керак, энг кенг тарқалгани Uvicorn. У бир нечта ишчи жараён очиши мумкин, ҳар бир жараён эса ичида кўплаб асинхрон сўровни параллел равишда қайта ишлайди. Nginx эса олдинда туриб, HTTPS шифрлаш, секин мижозлар ва сўров ҳажми чекловларини ўз зиммасига олади, шунда Python жараёнлари фақат илова мантиғи билан банд бўлади.
Мисол: Тошкентдаги миллий таомлар ошхонаси етказиб бериш учун мобил илова буюртма қилди. Илова api.misol.uz манзилидаги FastAPI хизматига мурожаат қилади: меню рўйхатини олади, буюртма юборади ва курер жойлашувини WebSocket орқали кузатади. Тушлик пайтида сўровлар кескин кўпаяди, шунинг учун хизмат бир нечта ишчи билан, автоматик қайта тикланиш билан ва тўғри созланган прокси орқали ишлаши керак.
Лойиҳани серверга тайёрлаш
Илова учун алоҳида тизим фойдаланувчиси ва виртуал муҳит яратинг. Боғлиқликларни requirements.txt файлидан ўрнатинг; Uvicorn'нинг standard варианти тезроқ ҳодиса сикли ва WebSocket кутубхоналарини бирга олиб келади.
sudo apt update
sudo apt install python3-venv nginx
sudo adduser --system --group --home /srv/api api
sudo -u api python3 -m venv /srv/api/venv
sudo -u api /srv/api/venv/bin/pip install -r /srv/api/requirements.txt "uvicorn[standard]"
Маълумотлар базаси пароли, JWT калити ва ташқи хизматлар токенлари кодда эмас, /srv/api/.env файлида сақланади ва илова уларни муҳит ўзгарувчилари сифатида ўқийди. Бу файлга фақат api фойдаланувчиси кира олиши учун chmod 600 қўйинг. Аввал иловани қўлда ишга тушириб, жавоб беришини текшириб олинг:
cd /srv/api
sudo -u api venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
curl http://127.0.0.1:8000/health
Ишлаб чиқариш учун илова созламалари
FastAPI стандарт ҳолатда /docs ва /redoc саҳифаларида API'нинг тўлиқ тавсифини очиқ кўрсатади. Ички ёки тижорат API учун бу ортиқча маълумот, шунинг учун ишлаб чиқаришда уларни ўчириб қўйиш ёки фақат маълум IP манзилларга очиш маъқул. Мониторинг учун эса оддий соғлиқ текшируви нуқтасини қўшинг.
import os
from fastapi import FastAPI
is_prod = os.getenv("APP_ENV") == "production"
app = FastAPI(
docs_url=None if is_prod else "/docs",
redoc_url=None if is_prod else "/redoc",
)
@app.get("/health")
def health():
return {"status": "ok"}
Яна бир муҳим жиҳат бор: async def билан ёзилган эндпоинт ичида блокловчи код, масалан синхрон HTTP сўров ёки синхрон маълумотлар базаси драйвери ишлатилса, бутун ишчи жараён кутиб қолади ва бошқа сўровлар ҳам секинлашади. Бундай ҳолларда ё асинхрон кутубхонадан фойдаланинг, ё эндпоинтни оддий def қилиб ёзинг, шунда FastAPI уни алоҳида оқимлар ҳовузида бажаради.
Uvicorn'ни systemd хизмати қилиш
Хизмат сервер қайта юкланганда автоматик кўтарилиши ва хато билан тўхтаганда қайта ишга тушиши керак. /etc/systemd/system/api.service файлини яратинг:
[Unit]
Description=FastAPI (Uvicorn) for api.misol.uz
After=network.target
[Service]
User=api
Group=api
WorkingDirectory=/srv/api
EnvironmentFile=/srv/api/.env
ExecStart=/srv/api/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2 --proxy-headers --forwarded-allow-ips=127.0.0.1
Restart=on-failure
[Install]
WantedBy=multi-user.target
--host 127.0.0.1 хизмат фақат сервернинг ичидан очиқ бўлишини таъминлайди, ташқаридан унга фақат Nginx орқали кириш мумкин. --proxy-headers ва --forwarded-allow-ips эса Nginx юборган X-Forwarded-For ва X-Forwarded-Proto сарлавҳаларига ишонишни буюради, шунда илова ҳақиқий мижоз IP манзилини ва HTTPS эканини кўради. Ишчилар сони сервердаги процессор ядроларига қараб танланади. Хизматни ёқинг:
sudo systemctl daemon-reload
sudo systemctl enable --now api
sudo journalctl -u api -f
Nginx прокси ва WebSocket
WebSocket уланиши оддий HTTP сўровдан протоколни янгилаш орқали бошланади, шунинг учун Nginx Upgrade ва Connection сарлавҳаларини ўтказиши керак. Аввал /etc/nginx/conf.d/upgrade.conf файлида ёрдамчи ўзгарувчи эълон қилинади:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
Сўнг сайт конфигурацияси /etc/nginx/sites-available/api ёзилади:
server {
listen 80;
server_name api.misol.uz;
client_max_body_size 10M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 300s;
}
}
sudo ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d api.misol.uz
proxy_read_timeout курер кузатуви каби узоқ очиқ турадиган WebSocket уланишларининг Nginx томонидан бир дақиқада узиб қўйилишининг олдини олади. Сертификат олишдан олдин api субдомени учун A ёзуви VPS манзилига кўрсатилган бўлиши керак; буни sayt.uz кабинетидаги DNS бўлимида қўшиш мумкин.
Янгилаш ва муаммоларни аниқлаш
Янги версияни чиқариш тартиби оддий: кодни янгилаш, боғлиқликларни ўрнатиш, керак бўлса маълумотлар базаси миграциясини бажариш ва sudo systemctl restart api. Агар Nginx 502 қайтарса, аввал journalctl -u api логларига қаранг: кўпинча модул йўли нотўғри, .env даги ўзгарувчи етишмайди ёки порт бошқа жараён томонидан банд қилинган бўлади. Илова ичида мижознинг IP манзили доим 127.0.0.1 бўлиб кўринаётган бўлса, --proxy-headers параметрини текширинг. Ошхона мисолида бу тартиб тушлик олдидан янгиланишни хавфсиз қилиш имконини беради: хизмат бир неча сонияда қайта кўтарилади, мобил илова эса буни деярли сезмайди.