首先廢話一下,FastAPI是一種現代,快速(高性能)的Web框架,用於基於標準Python類型提示使用Python 3.6+構建API。據說是go+nodejs的競爭對手。
這裏主要講的是uvicorn-gunicorn-fastapi 這個Fastapi的官方鏡像,主要的技術細節:
Uvicorn
Uvicorn是一款閃電般的“ ASGI”服務器。
它在單個過程中運行異步Python Web代碼。
Gunicorn
您可以使用Gunicorn管理Uvicorn和運行多個這些併發進程。
這樣,您將獲得最佳的併發性和並行性。
FastAPI
FastAPI是一種現代,快速(高性能)的Web框架,用於使用Python 3.6+構建API。
反正主要使用的技術就是Uvicorn和Gunicorn,官網介紹說其是站在巨人肩膀上的框架,也確實有其流弊之處吧。
這裏主要介紹 tiangolo/uvicorn-gunicorn-fastapi ,適用於生產環境,官網的其他鏡像也至少改變的操作系統的版本爲了縮減體積。
git 傳送 ☞ uvicorn-gunicorn-fastapi
用法:
#Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
COPY ./app /app
dockerfile的意思就是把你的代碼(./app)複製到 /app文件夾中。
當然前提是至少需要一個main.py的配置文件下面是鏡像能讀取的兩個默認位置,選擇一個去放就好了。
/app/app/main.py
/app/main.py
main.py類似於這樣:
#這個其實就是鏡像中自帶的main.py 位置/app/main.py
import sys
from fastapi import FastAPI
version = f"{sys.version_info.major}.{sys.version_info.minor}"
app = FastAPI()
@app.get("/")
async def read_root():
message = f"Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python {version}"
return {"message": message}
你自己的目錄結構大概就是這樣:
.
├── app
│ └── main.py
└── Dockerfile
然後就是構建鏡像:
# 在dockerfile的路徑下執行 myimage 替換成自己的起的名字作爲鏡像名
docker build -t myimage ./
然後你可以試着啓動一下了:
docker run -d --name mycontainer -p 80:80 myimage
當然,上面講的情況是把你自己代碼放進去部署,如果只是想驗證以下效果不用去看上面的直接執行:
docker pull tiangolo/uvicorn-gunicorn-fastapi:python3.7
docker run -d --name fastapidemo -p 80:80 tiangolo/uvicorn-gunicorn-fastapi:python3.7
然後用瀏覽器打開以下鏈接測試以下:
http://127.0.0.1/
#返回{"message":"Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7"}
http://127.0.0.1/docs
#API文檔
http://127.0.0.1/redoc
# 備用API文檔
API文檔類似:
不用驚訝,FastApi內置了swgger文檔 ,你只要正常寫方法就可以了,但是類型還是要注意一下像下面這樣是完全沒用問題的:
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
當然肯定也有更復雜的需求,比如:安裝一個依賴管理工具 Poetry,你可以這樣組織你的dockerfile:
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
# Install Poetry
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \
cd /usr/local/bin && \
ln -s /opt/poetry/bin/poetry && \
poetry config virtualenvs.create false
# Copy using poetry.lock* in case it doesn't exist yet
COPY ./app/pyproject.toml ./app/poetry.lock* /app/
RUN poetry install --no-root --no-dev
COPY ./app /app
用法挺簡單,但是裏面包含的功能一點也不簡單。
我們上面使用的鏡像 tiangolo/uvicorn-gunicorn-fastapi:python3.7 如果追溯它的源鏡像那就是下面這個:
# DockerFile tiangolo/uvicorn-gunicorn-fastapi:python3.7
FROM python:3.7
LABEL maintainer="Sebastian Ramirez <[email protected]>"
RUN pip install --no-cache-dir uvicorn gunicorn
COPY ./start.sh /start.sh
RUN chmod +x /start.sh
COPY ./gunicorn_conf.py /gunicorn_conf.py
COPY ./start-reload.sh /start-reload.sh
RUN chmod +x /start-reload.sh
COPY ./app /app
WORKDIR /app/
ENV PYTHONPATH=/app
EXPOSE 80
# Run the start script, it will check for an /app/prestart.sh script (e.g. for migrations)
# And then will start Gunicorn with Uvicorn
CMD ["/start.sh"]
git 地址我也貼一下 ☞ tiangolo/uvicorn-gunicorn-fastapi:python3.7 構建代碼
下面重點來了 ,如何使用 環境變量 去更改默認的配置:
(1)MODULE_NAME:
由Gunicorn導入的Python“模塊”(文件),該模塊將在變量中包含實際的應用程序,就是上面說的main.lpy
默認值:
如果你的main.py 是這個路徑 /app/app/main.py 那麼 值爲 app.main
路徑是 /app/main.py 值爲main
在運行的時候這樣就改變默認的值:
docker run -d -p 80:80 -e MODULE_NAME="custom_app.custom_main" myimage
(2)VARIABLE_NAME
默認: app
如果你的main文件是這樣寫的 你需要把它改成api
from fastapi import FastAPI
api = FastAPI()
@api.get("/")
def read_root():
return {"Hello": "World"}
docker run -d -p 80:80 -e VARIABLE_NAME="api" myimage
# 運行時修改VARIABLE_NAME
(3)APP_MODULE
把上面兩個變量結合起來就是這個變量
docker run -d -p 80:80 -e APP_MODULE="custom_app.custom_main:api" myimage
(4)GUNICORN_CONF
Gunicorn Python配置文件的路徑。
默認:
/app/gunicorn_conf.py
如果存在/app/app/gunicorn_conf.py
如果存在/gunicorn_conf.py
(包含的默認值)
設置方法:
docker run -d -p 80:80 -e GUNICORN_CONF="/app/custom_gunicorn_conf.py" myimage
(4)常用的 HOST(容器內使用)PORT BIND
默認就是0.0.0.0:80 略。
(5)LOG_LEVEL
Gunicorn日誌級別
debug
info
warning
error
critical
默認:info
docker run -d -p 80:8080 -e LOG_LEVEL="warning" myimage
(6)TIMEOUT
沉默(silent )了多少秒的Workers 被殺死並重新啓動。
默認:120
像FastAPI這樣的Uvicorn和ASGI框架是異步的,而不是同步的。因此,與同步工作器相比,擁有更高的超時可能是安全的。
您可以將其設置爲:
docker run -d -p 80:8080 -e TIMEOUT="20" myimage
(7)GRACEFUL_TIMEOUT
正常工作人員的超時重新啓動。在Gunicorn文檔中瞭解更多有關此內容的信息:graceful-timeout。
默認:120
docker run -d -p 80:8080 -e GRACEFUL_TIMEOUT="20" myimage
(8)KEEP_ALIVE
等待“保持活動”連接上的請求的秒數。
在Gunicorn docs:keepalive中閱讀更多有關它的信息。
默認情況下,設置爲2
。
您可以將其設置爲:
docker run -d -p 80:8080 -e KEEP_ALIVE="20" myimage
(9)ACCESS_LOG
要寫入的訪問日誌文件。
默認情況下"-"
,表示stdout(在Docker日誌中打印)。
如果要禁用ACCESS_LOG
,請將其設置爲空值。
例如,您可以通過以下方式禁用它:
docker run -d -p 80:8080 -e ACCESS_LOG= myimage
#注意這裏表示設置爲空值
(10)ERROR_LOG
要寫入的錯誤日誌文件。
默認情況下"-"
,表示stderr(在Docker日誌中打印)。
如果要禁用ERROR_LOG
,請將其設置爲空值。
例如,您可以通過以下方式禁用它:
docker run -d -p 80:8080 -e ERROR_LOG = myimage
(11)GUNICORN_CMD_ARGS
Gunicorn的任何其他命令行設置都可以在GUNICORN_CMD_ARGS
環境變量中傳遞。
在Gunicorn文檔:設置中閱讀更多相關信息。
這些設置將優先於其他環境變量和任何Gunicorn配置文件。
例如,如果您具有要使用的自定義TLS / SSL證書,則可以將其複製到Docker映像或將其安裝在容器中,然後設置--keyfile
和--certfile
文件的位置,例如:
docker run -d -p 80:8080 -e GUNICORN_CMD_ARGS = “” --keyfile = / secrets / key.pem --certfile = / secrets / cert.pem “ -e PORT = 443
注意:建議不要使用Traefik之類的“ TLS終止代理”,而是自己處理TLS / SSL並在容器中進行配置。您可以在有關HTTPS的FastAPI文檔中閱讀有關它的更多信息。
(12)PRE_START_PATH
預啓動腳本的路徑
默認:/app/prestart.sh
設置方法:
docker run -d -p 80:8080 -e PRE_START_PATH="/custom/script.sh" myimage
這個重點說一下默認的文件如下:
#! /usr/bin/env sh
# 文件路徑:/app/prestart.sh
echo "Running inside /app/prestart.sh, you could add migrations to this file, e.g.:"
echo "
#! /usr/bin/env bash
# Let the DB start
sleep 10;
# Run migrations
alembic upgrade head
"
啓動時就是這樣的,在這裏加載一些需要的腳本就好,一般寫等待10s是給數據庫啓動留下的時間。
像 /app/prestart.sh 還有gunicorn_conf.py這種開發常用的配置文件推薦的方法:
是寫你自己的dockerfile的時候,編寫命令把他們的從默認位置替換掉就好了。
/app/gunicorn_conf.py
/app/app/gunicorn_conf.py
/gunicorn_conf.py
當然,環境變量也不是必須在啓動時從docker run 後面指定,使用dockerfile的ENV設置能獲得更好的體驗
其他功能 :開發時重載
基於 : /start-reload.sh
生產環境是默認使用的是 /start.sh
使用方法:
docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh
-v $(pwd):/app
:表示該目錄$(pwd)
應作爲卷掛載到位於的容器內/app
。$(pwd)
:運行pwd
(“打印工作目錄”),並將其作爲字符串的一部分。
/start-reload.sh
:/start-reload.sh
在命令末尾添加一些內容(如),用此命令替換默認的“命令”。在這種情況下,它將/start.sh
開發替代項替換爲default()/start-reload.sh
。
由於/start-reload.sh
不與Gunicorn一起運行,因此您放入gunicorn_conf.py
文件中的任何配置都將不適用。
但是這些環境變量的工作原理與上述相同:
MODULE_NAME
VARIABLE_NAME
APP_MODULE
HOST
PORT
LOG_LEVEL
貼一下這倆文件代碼:
# /start.sh
#! /usr/bin/env sh
set -e
if [ -f /app/app/main.py ]; then
DEFAULT_MODULE_NAME=app.main
elif [ -f /app/main.py ]; then
DEFAULT_MODULE_NAME=main
fi
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
VARIABLE_NAME=${VARIABLE_NAME:-app}
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}
if [ -f /app/gunicorn_conf.py ]; then
DEFAULT_GUNICORN_CONF=/app/gunicorn_conf.py
elif [ -f /app/app/gunicorn_conf.py ]; then
DEFAULT_GUNICORN_CONF=/app/app/gunicorn_conf.py
else
DEFAULT_GUNICORN_CONF=/gunicorn_conf.py
fi
export GUNICORN_CONF=${GUNICORN_CONF:-$DEFAULT_GUNICORN_CONF}
export WORKER_CLASS=${WORKER_CLASS:-"uvicorn.workers.UvicornWorker"}
# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
echo "Checking for script in $PRE_START_PATH"
if [ -f $PRE_START_PATH ] ; then
echo "Running script $PRE_START_PATH"
. "$PRE_START_PATH"
else
echo "There is no script $PRE_START_PATH"
fi
# Start Gunicorn
exec gunicorn -k "$WORKER_CLASS" -c "$GUNICORN_CONF" "$APP_MODULE"
# /start-reload.sh
#! /usr/bin/env sh
set -e
if [ -f /app/app/main.py ]; then
DEFAULT_MODULE_NAME=app.main
elif [ -f /app/main.py ]; then
DEFAULT_MODULE_NAME=main
fi
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
VARIABLE_NAME=${VARIABLE_NAME:-app}
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}
HOST=${HOST:-0.0.0.0}
PORT=${PORT:-80}
LOG_LEVEL=${LOG_LEVEL:-info}
# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
echo "Checking for script in $PRE_START_PATH"
if [ -f $PRE_START_PATH ] ; then
echo "Running script $PRE_START_PATH"
. "$PRE_START_PATH"
else
echo "There is no script $PRE_START_PATH"
fi
# Start Uvicorn with live reload
exec uvicorn --reload --host $HOST --port $PORT --log-level $LOG_LEVEL "$APP_MODULE"