Docker部署FastApi詳解,這一篇就夠了

首先廢話一下,FastAPI是一種現代,快速(高性能)的Web框架,用於基於標準Python類型提示使用Python 3.6+構建API。據說是go+nodejs的競爭對手。

☞  FastAPI 官方文檔

這裏主要講的是uvicorn-gunicorn-fastapi 這個Fastapi的官方鏡像,主要的技術細節:

Uvicorn

Uvicorn是一款閃電般的“ ASGI”服務器。

它在單個過程中運行異步Python Web代碼。

Gunicorn

您可以使用Gunicorn管理Uvicorn和運行多個這些併發進程。

這樣,您將獲得最佳的併發性和並行性。

FastAPI

FastAPI是一種現代,快速(高性能)的Web框架,用於使用Python 3.6+構建API。

反正主要使用的技術就是UvicornGunicorn官網介紹說其是站在巨人肩膀上的框架,也確實有其流弊之處吧。

這裏主要介紹 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並在容器中進行配置。您可以在有關HTTPSFastAPI文檔中閱讀有關它的更多信息。

 

(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"

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章