使用docker-compose构建FastAPI uvicorn nginx


目标

  • 使用docker-compose通过Nginx反向代理在http上发布FastAPI(由uvicorn启动)应用

    • 首次发布在http(80端口)上,未经身份验证

文件组织

外壳(重击等)

1
2
3
4
5
6
7
8
9
10
11
12
13
$ tree  
.
├── app
│   ├── Dockerfile
│   ├── app
│   │   ├── __init__.py
│   │   └── main.py
│   ├── poetry.lock
│   └── pyproject.toml
├── docker-compose.yml
└── web
    └── conf.d
        └── app.conf

  • FastAPI应用程序使用app/Dockerfile创建图像

    • 源代码存储在app/app
    • Python软件包管理在必要时使用pyproject.tomlpoetry.lock(使用Poetry)

      • (没有深层的原因,因此我认为有多种方法可以轻松实现此目的,例如使用pip + requirements.txt或使用Pipenvminiconda。)
  • Nginx使用web/conf.d/app.conf设置

docker-compose.yml

  • 关于系统配置概述

docker-compose.yml

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
version: '3'

services:
  web:
    container_name: web
    image: nginx:alpine
    depends_on:
      # `app`サービス(コンテナ)が先に起動している必要があるので`depends_on`を指定しておく
      - app
    ports:
      # ポートマッピング: "ホストOSのポート:コンテナ(Nginx)のポート"
      - "80:80"
    volumes:
      # volumeマウント: "ホストOSのパス:コンテナにおけるパス"
      - ./web/conf.d:/etc/nginx/conf.d
    networks:
      - nginx_network

  app:
    container_name: app
    image: test_fastapi_app # ビルドされるDockerイメージ名を指定
    build:
      context: ./app
      dockerfile: Dockerfile
    expose:
      - 8000
    networks:
      - nginx_network
    # 例えばソースコードをリアルタイムに編集したいときは`volumes`でマウントすると便利
    # volumes:
    #   - ./app/app:/app/app
    # appコンテナの`CMD`を上書きするには`command`を使う
    # command: "uvicorn app.main:app --reload --host 0.0.0.0"

networks:
  nginx_network:
    driver: bridge
  • 分别创建Nginx容器web和FastAPI应用容器app,并将它们与docker-compose网络:nginx_network连接。
  • Nginx图像使用轻量级高山版本

稍后将描述

appweb的内容的细节。

app(快速API应用程序主体)

Dockerfile

Docker文件

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
46
47
48
49
50
51
52
53
54
ARG BASE_IMAGE=python:3.8-slim-buster                                                                                
FROM $BASE_IMAGE                                                                                                    

# system update & package install                                                                                    
RUN apt-get -y update && \                                                                                          
    apt-get install -y --no-install-recommends \                                                                    
    build-essential \                                                                                                
    openssl libssl-dev \                                                                                            
    && apt-get clean \                                                                                              
    && rm -rf /var/lib/apt/lists/*                                                                                  

# Add Tini                                                                                                          
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# 一般ユーザーアカウントを追加
ARG USER_NAME=app
ARG USER_UID=1000
ARG PASSWD=password

RUN useradd -m -s /bin/bash -u $USER_UID $USER_NAME && \
    gpasswd -a $USER_NAME sudo && \
    echo "${USER_NAME}:${PASSWD}" | chpasswd && \
    echo "${USER_NAME} ALL=(ALL) ALL" >> /etc/sudoers

# FastAPIのソースコードなどをコンテナ内へCOPY
COPY ./app /app/app
COPY ./pyproject.toml /app/pyproject.toml
# local環境で使用しているpoetry.lockファイルがあればそれも追加する
# COPY ./poetry.lock /app/poetry.lock
RUN chown -R ${USER_NAME}:${USER_NAME} /app


USER $USER_NAME
WORKDIR /app
# pip & poetry
ENV PATH $PATH:/home/${USER_NAME}/.local/bin
RUN python3 -m pip install --user --upgrade pip && \
    python3 -m pip install poetry --user && \
    poetry config virtualenvs.in-project true && \
    poetry install && \
    rm -rf ~/.cache/pip/* && \
    rm -rf ~/.cache/pypoetry/*

# Poetryで作った仮想環境にPATHを通しておく(予めpoetryのconfigを設定して仮想環境のPATHを一意にしておく)
ENV PATH=/app/.venv/bin:$PATH

# Configration
EXPOSE 8000

# Execute
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
  • 暂时使用Python版本3.8(使用官方图像的slim-buster系列(基于debian的)标签)
  • 由于apt是基于Debian的,因此需要安装它
  • 我个人不想以root用户身份启动它,所以我创建了一个用户来启动应用程序
  • tini可以在没有它的情况下工作,但是启动容器时似乎最好使用它,因此请根据GitHub上的说明进行放置。

  • FastAPI应用程序的一组文件存储在容器的/app目录中,并用作工作空间。

    • 源代码的主体为/app/app

  • 您正在使用具有本地用户特权的Poetry来安装所需的软件包

    • poetry config virtualenvs.in-project true/app/.venv中创建虚拟环境

    • 如果您有poetry.lock文件要在本地环境中进行开发,则可以通过取消注释# COPY ./poetry.lock /app/poetry.lock来修复要安装的软件包。

pyproject.toml

  • 有关要安装的软件包等的信息

    • tool.poetry.dependencies中描述了所需的最少软件包信息,而其他信息则适当。

    • (根据需要参考正式文件等进行补充或更正)

pyproject.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[tool.poetry]
name = "test_fastapi_app"
version = "0.1.0"
description = "just for test"
authors = ["Your Name <[email protected]>"]

[tool.poetry.dependencies]
python = "^3.8"
uvicorn = "*"
fastapi = "*"

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

FastAPI应用程序

  • 这次只有最低限度的准备

app/app/main.py看起来像这样:

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
"""
app main
"""

from fastapi import FastAPI

app = FastAPI()


@app.get('/')
async def site_root():
    """root"""
    return {"message": "Hello, WORLD!"}
  • (app/app/__init__.py是一个空文件)

app/app/以下是每种场合的目的?将来我们会根据需要创建它

uvicorn

使用uvicorn(官方文档)启动FastAPI应用

1
uvicorn app.main:app --host 0.0.0.0

。 (请注意,如果目录名或文件名更改,则app.main:app部分也应更改。)
可选:--host 0.0.0.0是必需的,以便可以从外部看到容器上运行的uvicorn服务器。
uvicorn服务器默认在端口8000上启动,Dockerfiledocker-compose.yml上的EXPOSE 8000等也是如此。 (也许我不需要它)

如果添加--reload选项,则uvicorn服务器将检测到文件何时更改并重新启动,这对于开发等操作很方便。

web(Nginx的Web服务器)

  • app在容器中运行的uvicorn服务器的反向代理

  • 通过在/etc/nginx/conf.d/***.conf中写入设置可以进行各种设置

    • 看来***.conf的名称可以是任何东西(这次是app.conf)。

app.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
upstream backend {
    server app:8000;
}

server {
    listen 80;
    # server_name  localhost;

    location / {
        proxy_pass http://backend;
    }

    # log
    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
}
  • app:8000的反向代理设置为upstream

    • 我觉得这里的app对应于docker-compose.yml上面写的服务名称(如果不一样,我很抱歉)
    • location /app:8000的内容发布在Nginx的/下。

      • 结果,如果访问Nginx的端口80,则可以看到FastAPI(uvicorn)的端口8000发布的内容。
  • 如果需要server_name或日志输出,请适当地注释掉相关部分。

  • 常用物品:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# `app`のイメージをビルド
docker-compose build

# サービス全体の起動
docker-compose up -d

# 終了時
docker-compose down

# コンソール出力の確認
docker-compose logs
docker-compose logs app # appサービスのログのみ確認

# appコンテナの中に入る
docker-compose run app bash

如果设置正确,则执行docker-compose up -d访问http://本地主机和http:// localhost / docs,

  • http://本地主机(在本地环境中运行时)

image.png

  • http://本地主机/ docs(在本地环境中运行时)

image.png

您会看到类似

的屏幕。
在AWS上的EC2之类的远程环境中运行时,如果将其替换为运行localhost的计算机的地址(如果合适),应该可以。

概要

我制作了一个标题类似的结构,内容尽可能少。
这次我还没有完成,但是您可以将Nginx用于SSL(HTTPS通信)和BASIC身份验证。
→另外,使用Jinja2的模板功能时,我非常沉迷于SSL转换(https变为http,使用默认端口以外的端口时端口号信息丢失,并且链接功能不可用。效果很好),所以我将分别计划。 (↓以下)

  • 显示带有FastAPI uvicorn Nginx的网页(Jinja2的模板功能)
  • 使用FastAPI uvicorn Nginx(SSL / HTTPS)显示网页