diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fe4c5ee86..ab27d4b85 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,8 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.7" + cache: "pip" + cache-dependency-path: pyproject.toml - uses: actions/cache@v3 id: cache with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f87be700..ddc43c942 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,11 +21,13 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: pyproject.toml - uses: actions/cache@v3 id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-test-v02 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-test-v03 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -e .[all,dev,doc,test] @@ -52,6 +54,8 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.8' + cache: "pip" + cache-dependency-path: pyproject.toml - name: Get coverage files uses: actions/download-artifact@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e59e05abe..96f097caa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,14 +12,14 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.2 hooks: - id: pyupgrade args: - --py3-plus - --keep-runtime-typing - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.114 + rev: v0.0.138 hooks: - id: ruff args: diff --git a/docs/en/docs/advanced/middleware.md b/docs/en/docs/advanced/middleware.md index ed90f29be..3bf49e392 100644 --- a/docs/en/docs/advanced/middleware.md +++ b/docs/en/docs/advanced/middleware.md @@ -68,7 +68,7 @@ Enforces that all incoming requests have a correctly set `Host` header, in order The following arguments are supported: -* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains to allow any hostname either use `allowed_hosts=["*"]` or omit the middleware. +* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains. To allow any hostname either use `allowed_hosts=["*"]` or omit the middleware. If an incoming request does not validate correctly then a `400` response will be sent. diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 1eb2fad32..4252d68c5 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,32 @@ ## Latest Changes +* 🔧 Update sponsors, disable course bundle. PR [#5713](https://github.com/tiangolo/fastapi/pull/5713) by [@tiangolo](https://github.com/tiangolo). +* ⬆ Update typer[all] requirement from <0.7.0,>=0.6.1 to >=0.6.1,<0.8.0. PR [#5639](https://github.com/tiangolo/fastapi/pull/5639) by [@dependabot[bot]](https://github.com/apps/dependabot). + +## 0.88.0 + +### Upgrades + +* ⬆ Bump Starlette to version `0.22.0` to fix bad encoding for query parameters in new `TestClient`. PR [#5659](https://github.com/tiangolo/fastapi/pull/5659) by [@azogue](https://github.com/azogue). + +### Docs + +* ✏ Fix typo in docs for `docs/en/docs/advanced/middleware.md`. PR [#5376](https://github.com/tiangolo/fastapi/pull/5376) by [@rifatrakib](https://github.com/rifatrakib). + +### Translations + +* 🌐 Add Portuguese translation for `docs/pt/docs/deployment/docker.md`. PR [#5663](https://github.com/tiangolo/fastapi/pull/5663) by [@ayr-ton](https://github.com/ayr-ton). + +### Internal + +* đŸ‘· Tweak build-docs to improve CI performance. PR [#5699](https://github.com/tiangolo/fastapi/pull/5699) by [@tiangolo](https://github.com/tiangolo). +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#5566](https://github.com/tiangolo/fastapi/pull/5566) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). +* âŹ†ïž Upgrade Ruff. PR [#5698](https://github.com/tiangolo/fastapi/pull/5698) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Remove pip cache for Smokeshow as it depends on a requirements.txt. PR [#5700](https://github.com/tiangolo/fastapi/pull/5700) by [@tiangolo](https://github.com/tiangolo). +* 💚 Fix pip cache for Smokeshow. PR [#5697](https://github.com/tiangolo/fastapi/pull/5697) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Fix and tweak CI cache handling. PR [#5696](https://github.com/tiangolo/fastapi/pull/5696) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Update `setup-python` action in tests to use new caching feature. PR [#5680](https://github.com/tiangolo/fastapi/pull/5680) by [@madkinsz](https://github.com/madkinsz). * ⬆ Bump black from 22.8.0 to 22.10.0. PR [#5569](https://github.com/tiangolo/fastapi/pull/5569) by [@dependabot[bot]](https://github.com/apps/dependabot). ## 0.87.0 diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index c5eb94870..e9b9f60eb 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -22,12 +22,6 @@
-
- - - - -
@@ -40,12 +34,6 @@
-
diff --git a/docs/pt/docs/deployment/docker.md b/docs/pt/docs/deployment/docker.md new file mode 100644 index 000000000..42c31db29 --- /dev/null +++ b/docs/pt/docs/deployment/docker.md @@ -0,0 +1,701 @@ +# FastAPI em contĂȘineres - Docker + +Ao fazer o deploy de aplicaçÔes FastAPI uma abordagem comum Ă© construir uma **imagem de contĂȘiner Linux**. Isso normalmente Ă© feito usando o **Docker**. VocĂȘ pode a partir disso fazer o deploy dessa imagem de algumas maneiras. + +Usando contĂȘineres Linux vocĂȘ tem diversas vantagens incluindo **segurança**, **replicabilidade**, **simplicidade**, entre outras. + +!!! Dica + EstĂĄ com pressa e jĂĄ sabe dessas coisas? Pode ir direto para [`Dockerfile` abaixo 👇](#build-a-docker-image-for-fastapi). + + +
+Visualização do Dockerfile 👀 + +```Dockerfile +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +COPY ./app /code/app + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] + +# If running behind a proxy like Nginx or Traefik add --proxy-headers +# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] +``` + +
+ +## O que Ă© um ContĂȘiner + +ContĂȘineres (especificamente contĂȘineres Linux) sĂŁo um jeito muito **leve** de empacotar aplicaçÔes contendo todas as dependĂȘncias e arquivos necessĂĄrios enquanto os mantĂ©m isolados de outros contĂȘineres (outras aplicaçÔes ou componentes) no mesmo sistema. + +ContĂȘineres Linux rodam usando o mesmo kernel Linux do hospedeiro (mĂĄquina, mĂĄquina virtual, servidor na nuvem, etc). Isso simplesmente significa que eles sĂŁo muito leves (comparados com mĂĄquinas virtuais emulando um sistema operacional completo). + +Dessa forma, contĂȘineres consomem **poucos recursos**, uma quantidade comparĂĄvel com rodar os processos diretamente (uma mĂĄquina virtual consumiria muito mais). + +ContĂȘineres tambĂ©m possuem seus prĂłprios processos (comumente um Ășnico processo), sistema de arquivos e rede **isolados** simplificando deploy, segurança, desenvolvimento, etc. + +## O que Ă© uma Imagem de ContĂȘiner + +Um **contĂȘiner** roda a partir de uma **imagem de contĂȘiner**. + +Uma imagem de contĂȘiner Ă© uma versĂŁo **estĂĄtica** de todos os arquivos, variĂĄveis de ambiente e do comando/programa padrĂŁo que deve estar presente num contĂȘiner. **EstĂĄtica** aqui significa que a **imagem** de contĂȘiner nĂŁo estĂĄ rodando, nĂŁo estĂĄ sendo executada, somente contĂ©m os arquivos e metadados empacotados. + +Em contraste com a "**imagem de contĂȘiner**" que contĂ©m os conteĂșdos estĂĄticos armazenados, um "**contĂȘiner**" normalmente se refere Ă  instĂąncia rodando, a coisa que estĂĄ sendo **executada**. + +Quando o **contĂȘiner** Ă© iniciado e estĂĄ rodando (iniciado a partir de uma **imagem de contĂȘiner**), ele pode criar ou modificar arquivos, variĂĄveis de ambiente, etc. Essas mudanças vĂŁo existir somente nesse contĂȘiner, mas nĂŁo persistirĂŁo na imagem subjacente do container (nĂŁo serĂŁo salvas no disco). + +Uma imagem de contĂȘiner Ă© comparĂĄvel ao arquivo de **programa** e seus conteĂșdos, ex.: `python` e algum arquivo `main.py`. + +E o **contĂȘiner** em si (em contraste Ă  **imagem de contĂȘiner**) Ă© a prĂłpria instĂąncia da imagem rodando, comparĂĄvel a um **processo**. Na verdade, um contĂȘiner estĂĄ rodando somente quando hĂĄ um **processo rodando** (e normalmente Ă© somente um processo). O contĂȘiner finaliza quando nĂŁo hĂĄ um processo rodando nele. + +## Imagens de contĂȘiner + +Docker tem sido uma das principais ferramentas para criar e gerenciar **imagens de contĂȘiner** e **contĂȘineres**. + +E existe um Docker Hub pĂșblico com **imagens de contĂȘiner oficiais** prĂ©-prontas para diversas ferramentas, ambientes, bancos de dados e aplicaçÔes. + +Por exemplo, hĂĄ uma Imagem Python oficial. + +E existe muitas outras imagens para diferentes coisas, como bancos de dados, por exemplo: + +* PostgreSQL +* MySQL +* MongoDB +* Redis, etc. + +Usando imagens de contĂȘiner prĂ©-prontas Ă© muito fĂĄcil **combinar** e usar diferentes ferramentas. Por exemplo, para testar um novo banco de dados. Em muitos casos, vocĂȘ pode usar as **imagens oficiais** precisando somente de variĂĄveis de ambiente para configurĂĄ-las. + +Dessa forma, em muitos casos vocĂȘ pode aprender sobre contĂȘineres e Docker e re-usar essa experiĂȘncia com diversos componentes e ferramentas. + +EntĂŁo, vocĂȘ rodaria **vĂĄrios contĂȘineres** com coisas diferentes, como um banco de dados, uma aplicação Python, um servidor web com uma aplicação frontend React, e conectĂĄ-los juntos via sua rede interna. + +Todos os sistemas de gerenciamento de contĂȘineres (como Docker ou Kubernetes) possuem essas funcionalidades de rede integradas a eles. + +## ContĂȘineres e Processos + +Uma **imagem de contĂȘiner** normalmente inclui em seus metadados o programa padrĂŁo ou comando que deve ser executado quando o **contĂȘiner** Ă© iniciado e os parĂąmetros a serem passados para esse programa. Muito similar ao que seria se estivesse na linha de comando. + +Quando um **contĂȘiner** Ă© iniciado, ele irĂĄ rodar esse comando/programa (embora vocĂȘ possa sobrescrevĂȘ-lo e fazer com que ele rode um comando/programa diferente). + +Um contĂȘiner estĂĄ rodando enquanto o **processo principal** (comando ou programa) estiver rodando. + +Um contĂȘiner normalmente tem um **Ășnico processo**, mas tambĂ©m Ă© possĂ­vel iniciar sub-processos a partir do processo principal, e dessa forma vocĂȘ terĂĄ **vĂĄrios processos** no mesmo contĂȘiner. + +Mas nĂŁo Ă© possĂ­vel ter um contĂȘiner rodando sem **pelo menos um processo rodando**. Se o processo principal parar, o contĂȘiner tambĂ©m para. + +## Construindo uma Imagem Docker para FastAPI + +Okay, vamos construir algo agora! 🚀 + +Eu vou mostrar como construir uma **imagem Docker** para FastAPI **do zero**, baseado na **imagem oficial do Python**. + +Isso Ă© o que vocĂȘ quer fazer na **maioria dos casos**, por exemplo: + +* Usando **Kubernetes** ou ferramentas similares +* Quando rodando em uma **Raspberry Pi** +* Usando um serviço em nuvem que irĂĄ rodar uma imagem de contĂȘiner para vocĂȘ, etc. + +### O Pacote Requirements + +VocĂȘ normalmente teria os **requisitos do pacote** para sua aplicação em algum arquivo. + +Isso pode depender principalmente da ferramenta que vocĂȘ usa para **instalar** esses requisitos. + +O caminho mais comum de fazer isso Ă© ter um arquivo `requirements.txt` com os nomes dos pacotes e suas versĂ”es, um por linha. + +VocĂȘ, naturalmente, usaria as mesmas ideias que vocĂȘ leu em [Sobre VersĂ”es do FastAPI](./versions.md){.internal-link target=_blank} para definir os intervalos de versĂ”es. + +Por exemplo, seu `requirements.txt` poderia parecer com: + +``` +fastapi>=0.68.0,<0.69.0 +pydantic>=1.8.0,<2.0.0 +uvicorn>=0.15.0,<0.16.0 +``` + +E vocĂȘ normalmente instalaria essas dependĂȘncias de pacote com `pip`, por exemplo: + +
+ +```console +$ pip install -r requirements.txt +---> 100% +Successfully installed fastapi pydantic uvicorn +``` + +
+ +!!! info + HĂĄ outros formatos e ferramentas para definir e instalar dependĂȘncias de pacote. + + Eu vou mostrar um exemplo depois usando Poetry em uma seção abaixo. 👇 + +### Criando o CĂłdigo do **FastAPI** + +* Crie um diretĂłrio `app` e entre nele. +* Crie um arquivo vazio `__init__.py`. +* Crie um arquivo `main.py` com: + +```Python +from typing import Optional + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} +``` + +### Dockerfile + +Agora, no mesmo diretĂłrio do projeto, crie um arquivo `Dockerfile` com: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 + +# (2) +WORKDIR /code + +# (3) +COPY ./requirements.txt /code/requirements.txt + +# (4) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (5) +COPY ./app /code/app + +# (6) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Inicie a partir da imagem base oficial do Python. + +2. Defina o diretĂłrio de trabalho atual para `/code`. + + Esse Ă© o diretĂłrio onde colocaremos o arquivo `requirements.txt` e o diretĂłrio `app`. + +3. Copie o arquivo com os requisitos para o diretĂłrio `/code`. + + Copie **somente** o arquivo com os requisitos primeiro, nĂŁo o resto do cĂłdigo. + + Como esse arquivo **nĂŁo muda com frequĂȘncia**, o Docker irĂĄ detectĂĄ-lo e usar o **cache** para esse passo, habilitando o cache para o prĂłximo passo tambĂ©m. + +4. Instale as dependĂȘncias de pacote vindas do arquivo de requisitos. + + A opção `--no-cache-dir` diz ao `pip` para nĂŁo salvar os pacotes baixados localmente, pois isso sĂł aconteceria se `pip` fosse executado novamente para instalar os mesmos pacotes, mas esse nĂŁo Ă© o caso quando trabalhamos com contĂȘineres. + + !!! note + `--no-cache-dir` Ă© apenas relacionado ao `pip`, nĂŁo tem nada a ver com Docker ou contĂȘineres. + + A opção `--upgrade` diz ao `pip` para atualizar os pacotes se eles jĂĄ estiverem instalados. + + Por causa do passo anterior de copiar o arquivo, ele pode ser detectado pelo **cache do Docker**, esse passo tambĂ©m **usarĂĄ o cache do Docker** quando disponĂ­vel. + + Usando o cache nesse passo irĂĄ **salvar** muito **tempo** quando vocĂȘ for construir a imagem repetidas vezes durante o desenvolvimento, ao invĂ©s de **baixar e instalar** todas as dependĂȘncias **toda vez**. + +5. Copie o diretĂłrio `./app` dentro do diretĂłrio `/code`. + + Como isso tem todo o cĂłdigo contendo o que **muda com mais frequĂȘncia**, o **cache do Docker** nĂŁo serĂĄ usado para esse passo ou para **qualquer passo seguinte** facilmente. + + EntĂŁo, Ă© importante colocar isso **perto do final** do `Dockerfile`, para otimizar o tempo de construção da imagem do contĂȘiner. + +6. Defina o **comando** para rodar o servidor `uvicorn`. + + `CMD` recebe uma lista de strings, cada uma dessas strings Ă© o que vocĂȘ digitaria na linha de comando separado por espaços. + + Esse comando serĂĄ executado a partir do **diretĂłrio de trabalho atual**, o mesmo diretĂłrio `/code` que vocĂȘ definiu acima com `WORKDIR /code`. + + Porque o programa serĂĄ iniciado em `/code` e dentro dele estĂĄ o diretĂłrio `./app` com seu cĂłdigo, o **Uvicorn** serĂĄ capaz de ver e **importar** `app` de `app.main`. + +!!! tip + Revise o que cada linha faz clicando em cada bolha com o nĂșmero no cĂłdigo. 👆 + +Agora vocĂȘ deve ter uma estrutura de diretĂłrio como: + +``` +. +├── app +│   ├── __init__.py +│ └── main.py +├── Dockerfile +└── requirements.txt +``` + +#### Por TrĂĄs de um Proxy de Terminação TLS + +Se vocĂȘ estĂĄ executando seu contĂȘiner atrĂĄs de um Proxy de Terminação TLS (load balancer) como Nginx ou Traefik, adicione a opção `--proxy-headers`, isso farĂĄ com que o Uvicorn confie nos cabeçalhos enviados por esse proxy, informando que o aplicativo estĂĄ sendo executado atrĂĄs do HTTPS, etc. + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +#### Cache Docker + +Existe um truque importante nesse `Dockerfile`, primeiro copiamos o **arquivo com as dependĂȘncias sozinho**, nĂŁo o resto do cĂłdigo. Deixe-me te contar o porquĂȘ disso. + +```Dockerfile +COPY ./requirements.txt /code/requirements.txt +``` + +Docker e outras ferramentas **constrĂłem** essas imagens de contĂȘiner **incrementalmente**, adicionando **uma camada em cima da outra**, começando do topo do `Dockerfile` e adicionando qualquer arquivo criado por cada uma das instruçÔes do `Dockerfile`. + +Docker e ferramentas similares tambĂ©m usam um **cache interno** ao construir a imagem, se um arquivo nĂŁo mudou desde a Ășltima vez que a imagem do contĂȘiner foi construĂ­da, entĂŁo ele irĂĄ **reutilizar a mesma camada** criada na Ășltima vez, ao invĂ©s de copiar o arquivo novamente e criar uma nova camada do zero. + +Somente evitar a cĂłpia de arquivos nĂŁo melhora muito as coisas, mas porque ele usou o cache para esse passo, ele pode **usar o cache para o prĂłximo passo**. Por exemplo, ele pode usar o cache para a instrução que instala as dependĂȘncias com: + +```Dockerfile +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt +``` + +O arquivo com os requisitos de pacote **nĂŁo muda com frequĂȘncia**. EntĂŁo, ao copiar apenas esse arquivo, o Docker serĂĄ capaz de **usar o cache** para esse passo. + +E entĂŁo, o Docker serĂĄ capaz de **usar o cache para o prĂłximo passo** que baixa e instala essas dependĂȘncias. E Ă© aqui que **salvamos muito tempo**. ✹ ...e evitamos tĂ©dio esperando. đŸ˜Ș😆 + +Baixar e instalar as dependĂȘncias do pacote **pode levar minutos**, mas usando o **cache** leva **segundos** no mĂĄximo. + +E como vocĂȘ estaria construindo a imagem do contĂȘiner novamente e novamente durante o desenvolvimento para verificar se suas alteraçÔes de cĂłdigo estĂŁo funcionando, hĂĄ muito tempo acumulado que isso economizaria. + +A partir daĂ­, perto do final do `Dockerfile`, copiamos todo o cĂłdigo. Como isso Ă© o que **muda com mais frequĂȘncia**, colocamos perto do final, porque quase sempre, qualquer coisa depois desse passo nĂŁo serĂĄ capaz de usar o cache. + +```Dockerfile +COPY ./app /code/app +``` + +### Construindo a Imagem Docker + +Agora que todos os arquivos estĂŁo no lugar, vamos construir a imagem do contĂȘiner. + +* VĂĄ para o diretĂłrio do projeto (onde estĂĄ o seu `Dockerfile`, contendo o diretĂłrio `app`). +* Construa sua imagem FastAPI: + +
+ +```console +$ docker build -t myimage . + +---> 100% +``` + +
+ +!!! tip + Note o `.` no final, Ă© equivalente a `./`, ele diz ao Docker o diretĂłrio a ser usado para construir a imagem do contĂȘiner. + + Nesse caso, Ă© o mesmo diretĂłrio atual (`.`). + +### Inicie o contĂȘiner Docker + +* Execute um contĂȘiner baseado na sua imagem: + +
+ +```console +$ docker run -d --name mycontĂȘiner -p 80:80 myimage +``` + +
+ +## Verifique + +VocĂȘ deve ser capaz de verificar isso no URL do seu contĂȘiner Docker, por exemplo: http://192.168.99.100/items/5?q=somequery ou http://127.0.0.1/items/5?q=somequery (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ algo como: + +```JSON +{"item_id": 5, "q": "somequery"} +``` + +## Documentação interativa da API + +Agora vocĂȘ pode ir para http://192.168.99.100/docs ou http://127.0.0.1/docs (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ a documentação interativa automĂĄtica da API (fornecida pelo Swagger UI): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +## Documentação alternativa da API + +E vocĂȘ tambĂ©m pode ir para http://192.168.99.100/redoc ou http://127.0.0.1/redoc (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ a documentação alternativa automĂĄtica (fornecida pela ReDoc): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +## Construindo uma Imagem Docker com um Arquivo Único FastAPI + +Se seu FastAPI for um Ășnico arquivo, por exemplo, `main.py` sem um diretĂłrio `./app`, sua estrutura de arquivos poderia ser assim: + +``` +. +├── Dockerfile +├── main.py +└── requirements.txt +``` + +EntĂŁo vocĂȘ sĂł teria que alterar os caminhos correspondentes para copiar o arquivo dentro do `Dockerfile`: + +```{ .dockerfile .annotate hl_lines="10 13" } +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (1) +COPY ./main.py /code/ + +# (2) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Copie o arquivo `main.py` para o diretĂłrio `/code` diretamente (sem nenhum diretĂłrio `./app`). + +2. Execute o Uvicorn e diga a ele para importar o objeto `app` de `main` (em vez de importar de `app.main`). + +EntĂŁo ajuste o comando Uvicorn para usar o novo mĂłdulo `main` em vez de `app.main` para importar o objeto FastAPI `app`. + +## Conceitos de Implantação + +Vamos falar novamente sobre alguns dos mesmos [Conceitos de Implantação](./concepts.md){.internal-link target=_blank} em termos de contĂȘineres. + +ContĂȘineres sĂŁo principalmente uma ferramenta para simplificar o processo de **construção e implantação** de um aplicativo, mas eles nĂŁo impĂ”em uma abordagem particular para lidar com esses **conceitos de implantação** e existem vĂĄrias estratĂ©gias possĂ­veis. + +A **boa notĂ­cia** Ă© que com cada estratĂ©gia diferente hĂĄ uma maneira de cobrir todos os conceitos de implantação. 🎉 + +Vamos revisar esses **conceitos de implantação** em termos de contĂȘineres: + +* HTTPS +* Executando na inicialização +* ReinicializaçÔes +* Replicação (nĂșmero de processos rodando) +* MemĂłria +* Passos anteriores antes de começar + +## HTTPS + +Se nos concentrarmos apenas na **imagem do contĂȘiner** para um aplicativo FastAPI (e posteriormente no **contĂȘiner** em execução), o HTTPS normalmente seria tratado **externamente** por outra ferramenta. + +Isso poderia ser outro contĂȘiner, por exemplo, com Traefik, lidando com **HTTPS** e aquisição **automĂĄtica** de **certificados**. + +!!! tip + Traefik tem integraçÔes com Docker, Kubernetes e outros, portanto, Ă© muito fĂĄcil configurar e configurar o HTTPS para seus contĂȘineres com ele. + +Alternativamente, o HTTPS poderia ser tratado por um provedor de nuvem como um de seus serviços (enquanto ainda executasse o aplicativo em um contĂȘiner). + +## Executando na inicialização e reinicializaçÔes + +Normalmente, outra ferramenta Ă© responsĂĄvel por **iniciar e executar** seu contĂȘiner. + +Ela poderia ser o **Docker** diretamente, **Docker Compose**, **Kubernetes**, um **serviço de nuvem**, etc. + +Na maioria (ou em todos) os casos, hĂĄ uma opção simples para habilitar a execução do contĂȘiner na inicialização e habilitar reinicializaçÔes em falhas. Por exemplo, no Docker, Ă© a opção de linha de comando `--restart`. + +Sem usar contĂȘineres, fazer aplicativos executarem na inicialização e com reinicializaçÔes pode ser trabalhoso e difĂ­cil. Mas quando **trabalhando com contĂȘineres** em muitos casos essa funcionalidade Ă© incluĂ­da por padrĂŁo. ✹ + +## Replicação - NĂșmero de Processos + +Se vocĂȘ tiver um cluster de mĂĄquinas com **Kubernetes**, Docker Swarm Mode, Nomad ou outro sistema complexo semelhante para gerenciar contĂȘineres distribuĂ­dos em vĂĄrias mĂĄquinas, entĂŁo provavelmente desejarĂĄ **lidar com a replicação** no **nĂ­vel do cluster** em vez de usar um **gerenciador de processos** (como o Gunicorn com workers) em cada contĂȘiner. + +Um desses sistemas de gerenciamento de contĂȘineres distribuĂ­dos como o Kubernetes normalmente tem alguma maneira integrada de lidar com a **replicação de contĂȘineres** enquanto ainda oferece **balanceamento de carga** para as solicitaçÔes recebidas. Tudo no **nĂ­vel do cluster**. + +Nesses casos, vocĂȘ provavelmente desejarĂĄ criar uma **imagem do contĂȘiner do zero** como [explicado acima](#dockerfile), instalando suas dependĂȘncias e executando **um Ășnico processo Uvicorn** em vez de executar algo como Gunicorn com trabalhadores Uvicorn. + +### Balanceamento de Carga + +Quando usando contĂȘineres, normalmente vocĂȘ terĂĄ algum componente **escutando na porta principal**. Poderia ser outro contĂȘiner que tambĂ©m Ă© um **Proxy de Terminação TLS** para lidar com **HTTPS** ou alguma ferramenta semelhante. + +Como esse componente assumiria a **carga** de solicitaçÔes e distribuiria isso entre os trabalhadores de uma maneira (esperançosamente) **balanceada**, ele tambĂ©m Ă© comumente chamado de **Balanceador de Carga**. + +!!! tip + O mesmo componente **Proxy de Terminação TLS** usado para HTTPS provavelmente tambĂ©m seria um **Balanceador de Carga**. + +E quando trabalhar com contĂȘineres, o mesmo sistema que vocĂȘ usa para iniciar e gerenciĂĄ-los jĂĄ terĂĄ ferramentas internas para transmitir a **comunicação de rede** (por exemplo, solicitaçÔes HTTP) do **balanceador de carga** (que tambĂ©m pode ser um **Proxy de Terminação TLS**) para o(s) contĂȘiner(es) com seu aplicativo. + +### Um Balanceador de Carga - MĂșltiplos ContĂȘineres de Workers + +Quando trabalhando com **Kubernetes** ou sistemas similares de gerenciamento de contĂȘiner distribuĂ­do, usando seus mecanismos de rede internos permitiria que o Ășnico **balanceador de carga** que estivesse escutando na **porta principal** transmitisse comunicação (solicitaçÔes) para possivelmente **mĂșltiplos contĂȘineres** executando seu aplicativo. + +Cada um desses contĂȘineres executando seu aplicativo normalmente teria **apenas um processo** (ex.: um processo Uvicorn executando seu aplicativo FastAPI). Todos seriam **contĂȘineres idĂȘnticos**, executando a mesma coisa, mas cada um com seu prĂłprio processo, memĂłria, etc. Dessa forma, vocĂȘ aproveitaria a **paralelização** em **nĂșcleos diferentes** da CPU, ou atĂ© mesmo em **mĂĄquinas diferentes**. + +E o sistema de contĂȘiner com o **balanceador de carga** iria **distribuir as solicitaçÔes** para cada um dos contĂȘineres com seu aplicativo **em turnos**. Portanto, cada solicitação poderia ser tratada por um dos mĂșltiplos **contĂȘineres replicados** executando seu aplicativo. + +E normalmente esse **balanceador de carga** seria capaz de lidar com solicitaçÔes que vĂŁo para *outros* aplicativos em seu cluster (por exemplo, para um domĂ­nio diferente, ou sob um prefixo de URL diferente), e transmitiria essa comunicação para os contĂȘineres certos para *esse outro* aplicativo em execução em seu cluster. + +### Um Processo por ContĂȘiner + +Nesse tipo de cenĂĄrio, provavelmente vocĂȘ desejarĂĄ ter **um Ășnico processo (Uvicorn) por contĂȘiner**, pois jĂĄ estaria lidando com a replicação no nĂ­vel do cluster. + +EntĂŁo, nesse caso, vocĂȘ **nĂŁo** desejarĂĄ ter um gerenciador de processos como o Gunicorn com trabalhadores Uvicorn, ou o Uvicorn usando seus prĂłprios trabalhadores Uvicorn. VocĂȘ desejarĂĄ ter apenas um **Ășnico processo Uvicorn** por contĂȘiner (mas provavelmente vĂĄrios contĂȘineres). + +Tendo outro gerenciador de processos dentro do contĂȘiner (como seria com o Gunicorn ou o Uvicorn gerenciando trabalhadores Uvicorn) sĂł adicionaria **complexidade desnecessĂĄria** que vocĂȘ provavelmente jĂĄ estĂĄ cuidando com seu sistema de cluster. + +### ContĂȘineres com MĂșltiplos Processos e Casos Especiais + +Claro, existem **casos especiais** em que vocĂȘ pode querer ter um **contĂȘiner** com um **gerenciador de processos Gunicorn** iniciando vĂĄrios **processos trabalhadores Uvicorn** dentro. + +Nesses casos, vocĂȘ pode usar a **imagem oficial do Docker** que inclui o **Gunicorn** como um gerenciador de processos executando vĂĄrios **processos trabalhadores Uvicorn**, e algumas configuraçÔes padrĂŁo para ajustar o nĂșmero de trabalhadores com base nos atuais nĂșcleos da CPU automaticamente. Eu vou te contar mais sobre isso abaixo em [Imagem Oficial do Docker com Gunicorn - Uvicorn](#imagem-oficial-do-docker-com-gunicorn-uvicorn). + +Aqui estĂŁo alguns exemplos de quando isso pode fazer sentido: + +#### Um Aplicativo Simples + +VocĂȘ pode querer um gerenciador de processos no contĂȘiner se seu aplicativo for **simples o suficiente** para que vocĂȘ nĂŁo precise (pelo menos nĂŁo agora) ajustar muito o nĂșmero de processos, e vocĂȘ pode simplesmente usar um padrĂŁo automatizado (com a imagem oficial do Docker), e vocĂȘ estĂĄ executando em um **Ășnico servidor**, nĂŁo em um cluster. + +#### Docker Compose + +VocĂȘ pode estar implantando em um **Ășnico servidor** (nĂŁo em um cluster) com o **Docker Compose**, entĂŁo vocĂȘ nĂŁo teria uma maneira fĂĄcil de gerenciar a replicação de contĂȘineres (com o Docker Compose) enquanto preserva a rede compartilhada e o **balanceamento de carga**. + +EntĂŁo vocĂȘ pode querer ter **um Ășnico contĂȘiner** com um **gerenciador de processos** iniciando **vĂĄrios processos trabalhadores** dentro. + +#### Prometheus and Outros Motivos + +VocĂȘ tambĂ©m pode ter **outros motivos** que tornariam mais fĂĄcil ter um **Ășnico contĂȘiner** com **mĂșltiplos processos** em vez de ter **mĂșltiplos contĂȘineres** com **um Ășnico processo** em cada um deles. + +Por exemplo (dependendo de sua configuração), vocĂȘ poderia ter alguma ferramenta como um exportador do Prometheus no mesmo contĂȘiner que deve ter acesso a **cada uma das solicitaçÔes** que chegam. + +Nesse caso, se vocĂȘ tivesse **mĂșltiplos contĂȘineres**, por padrĂŁo, quando o Prometheus fosse **ler as mĂ©tricas**, ele receberia as mĂ©tricas de **um Ășnico contĂȘiner cada vez** (para o contĂȘiner que tratou essa solicitação especĂ­fica), em vez de receber as **mĂ©tricas acumuladas** de todos os contĂȘineres replicados. + +EntĂŁo, nesse caso, poderia ser mais simples ter **um Ășnico contĂȘiner** com **mĂșltiplos processos**, e uma ferramenta local (por exemplo, um exportador do Prometheus) no mesmo contĂȘiner coletando mĂ©tricas do Prometheus para todos os processos internos e expor essas mĂ©tricas no Ășnico contĂȘiner. + +--- + +O ponto principal Ă© que **nenhum** desses sĂŁo **regras escritas em pedra** que vocĂȘ deve seguir cegamente. VocĂȘ pode usar essas idĂ©ias para **avaliar seu prĂłprio caso de uso** e decidir qual Ă© a melhor abordagem para seu sistema, verificando como gerenciar os conceitos de: + +* Segurança - HTTPS +* Executando na inicialização +* ReinicializaçÔes +* Replicação (o nĂșmero de processos em execução) +* MemĂłria +* Passos anteriores antes de inicializar + +## MemĂłria + +Se vocĂȘ executar **um Ășnico processo por contĂȘiner**, terĂĄ uma quantidade mais ou menos bem definida, estĂĄvel e limitada de memĂłria consumida por cada um desses contĂȘineres (mais de um se eles forem replicados). + +E entĂŁo vocĂȘ pode definir esses mesmos limites e requisitos de memĂłria em suas configuraçÔes para seu sistema de gerenciamento de contĂȘineres (por exemplo, no **Kubernetes**). Dessa forma, ele poderĂĄ **replicar os contĂȘineres** nas **mĂĄquinas disponĂ­veis** levando em consideração a quantidade de memĂłria necessĂĄria por eles e a quantidade disponĂ­vel nas mĂĄquinas no cluster. + +Se sua aplicação for **simples**, isso provavelmente **nĂŁo serĂĄ um problema**, e vocĂȘ pode nĂŁo precisar especificar limites de memĂłria rĂ­gidos. Mas se vocĂȘ estiver **usando muita memĂłria** (por exemplo, com **modelos de aprendizado de mĂĄquina**), deve verificar quanta memĂłria estĂĄ consumindo e ajustar o **nĂșmero de contĂȘineres** que executa em **cada mĂĄquina** (e talvez adicionar mais mĂĄquinas ao seu cluster). + +Se vocĂȘ executar **mĂșltiplos processos por contĂȘiner** (por exemplo, com a imagem oficial do Docker), deve garantir que o nĂșmero de processos iniciados nĂŁo **consuma mais memĂłria** do que o disponĂ­vel. + +## Passos anteriores antes de inicializar e contĂȘineres + +Se vocĂȘ estiver usando contĂȘineres (por exemplo, Docker, Kubernetes), existem duas abordagens principais que vocĂȘ pode usar. + +### ContĂȘineres MĂșltiplos + +Se vocĂȘ tiver **mĂșltiplos contĂȘineres**, provavelmente cada um executando um **Ășnico processo** (por exemplo, em um cluster do **Kubernetes**), entĂŁo provavelmente vocĂȘ gostaria de ter um **contĂȘiner separado** fazendo o trabalho dos **passos anteriores** em um Ășnico contĂȘiner, executando um Ășnico processo, **antes** de executar os contĂȘineres trabalhadores replicados. + +!!! info + Se vocĂȘ estiver usando o Kubernetes, provavelmente serĂĄ um Init Container. + +Se no seu caso de uso nĂŁo houver problema em executar esses passos anteriores **em paralelo vĂĄrias vezes** (por exemplo, se vocĂȘ nĂŁo estiver executando migraçÔes de banco de dados, mas apenas verificando se o banco de dados estĂĄ pronto), entĂŁo vocĂȘ tambĂ©m pode colocĂĄ-los em cada contĂȘiner logo antes de iniciar o processo principal. + +### ContĂȘiner Único + +Se vocĂȘ tiver uma configuração simples, com um **Ășnico contĂȘiner** que entĂŁo inicia vĂĄrios **processos trabalhadores** (ou tambĂ©m apenas um processo), entĂŁo poderia executar esses passos anteriores no mesmo contĂȘiner, logo antes de iniciar o processo com o aplicativo. A imagem oficial do Docker suporta isso internamente. + +## Imagem Oficial do Docker com Gunicorn - Uvicorn + +HĂĄ uma imagem oficial do Docker que inclui o Gunicorn executando com trabalhadores Uvicorn, conforme detalhado em um capĂ­tulo anterior: [Server Workers - Gunicorn com Uvicorn](./server-workers.md){.internal-link target=_blank}. + +Essa imagem seria Ăștil principalmente nas situaçÔes descritas acima em: [ContĂȘineres com MĂșltiplos Processos e Casos Especiais](#contĂȘineres-com-mĂșltiplos-processos-e-casos-Especiais). + +* tiangolo/uvicorn-gunicorn-fastapi. + +!!! warning + Existe uma grande chance de que vocĂȘ **nĂŁo** precise dessa imagem base ou de qualquer outra semelhante, e seria melhor construir a imagem do zero, como [descrito acima em: Construa uma Imagem Docker para o FastAPI](#construa-uma-imagem-docker-para-o-fastapi). + +Essa imagem tem um mecanismo de **auto-ajuste** incluĂ­do para definir o **nĂșmero de processos trabalhadores** com base nos nĂșcleos de CPU disponĂ­veis. + +Isso tem **padrĂ”es sensĂ­veis**, mas vocĂȘ ainda pode alterar e atualizar todas as configuraçÔes com **variĂĄveis de ambiente** ou arquivos de configuração. + +HĂĄ tambĂ©m suporte para executar **passos anteriores antes de iniciar** com um script. + +!!! tip + Para ver todas as configuraçÔes e opçÔes, vĂĄ para a pĂĄgina da imagem Docker: tiangolo/uvicorn-gunicorn-fastapi. + +### NĂșmero de Processos na Imagem Oficial do Docker + +O **nĂșmero de processos** nesta imagem Ă© **calculado automaticamente** a partir dos **nĂșcleos de CPU** disponĂ­veis. + +Isso significa que ele tentarĂĄ **aproveitar** o mĂĄximo de **desempenho** da CPU possĂ­vel. + +VocĂȘ tambĂ©m pode ajustĂĄ-lo com as configuraçÔes usando **variĂĄveis de ambiente**, etc. + +Mas isso tambĂ©m significa que, como o nĂșmero de processos depende da CPU do contĂȘiner em execução, a **quantidade de memĂłria consumida** tambĂ©m dependerĂĄ disso. + +EntĂŁo, se seu aplicativo consumir muito memĂłria (por exemplo, com modelos de aprendizado de mĂĄquina), e seu servidor tiver muitos nĂșcleos de CPU **mas pouca memĂłria**, entĂŁo seu contĂȘiner pode acabar tentando usar mais memĂłria do que estĂĄ disponĂ­vel e degradar o desempenho muito (ou atĂ© mesmo travar). 🚹 + +### Criando um `Dockerfile` + +Aqui estĂĄ como vocĂȘ criaria um `Dockerfile` baseado nessa imagem: + +```Dockerfile +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app +``` + +### AplicaçÔes Maiores + +Se vocĂȘ seguiu a seção sobre a criação de [AplicaçÔes Maiores com MĂșltiplos Arquivos](../tutorial/bigger-applications.md){.internal-link target=_blank}, seu `Dockerfile` pode parecer com isso: + +```Dockerfile + +```Dockerfile hl_lines="7" +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app/app +``` + +### Quando Usar + +VocĂȘ provavelmente **nĂŁo** deve usar essa imagem base oficial (ou qualquer outra semelhante) se estiver usando **Kubernetes** (ou outros) e jĂĄ estiver definindo **replicação** no nĂ­vel do cluster, com vĂĄrios **contĂȘineres**. Nesses casos, Ă© melhor **construir uma imagem do zero** conforme descrito acima: [Construindo uma Imagem Docker para FastAPI](#construindo-uma-imagem-docker-para-fastapi). + +Essa imagem seria Ăștil principalmente nos casos especiais descritos acima em [ContĂȘineres com MĂșltiplos Processos e Casos Especiais](#contĂȘineres-com-mĂșltiplos-processos-e-casos-Especiais). Por exemplo, se sua aplicação for **simples o suficiente** para que a configuração padrĂŁo de nĂșmero de processos com base na CPU funcione bem, vocĂȘ nĂŁo quer se preocupar com a configuração manual da replicação no nĂ­vel do cluster e nĂŁo estĂĄ executando mais de um contĂȘiner com seu aplicativo. Ou se vocĂȘ estiver implantando com **Docker Compose**, executando em um Ășnico servidor, etc. + +## Deploy da Imagem do ContĂȘiner + +Depois de ter uma imagem de contĂȘiner (Docker), existem vĂĄrias maneiras de implantĂĄ-la. + +Por exemplo: + +* Com **Docker Compose** em um Ășnico servidor +* Com um cluster **Kubernetes** +* Com um cluster Docker Swarm Mode +* Com outra ferramenta como o Nomad +* Com um serviço de nuvem que pega sua imagem de contĂȘiner e a implanta + +## Imagem Docker com Poetry + +Se vocĂȘ usa Poetry para gerenciar as dependĂȘncias do seu projeto, pode usar a construção multi-estĂĄgio do Docker: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 as requirements-stage + +# (2) +WORKDIR /tmp + +# (3) +RUN pip install poetry + +# (4) +COPY ./pyproject.toml ./poetry.lock* /tmp/ + +# (5) +RUN poetry export -f requirements.txt --output requirements.txt --without-hashes + +# (6) +FROM python:3.9 + +# (7) +WORKDIR /code + +# (8) +COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt + +# (9) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (10) +COPY ./app /code/app + +# (11) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Esse Ă© o primeiro estĂĄgio, ele Ă© chamado `requirements-stage`. + +2. Defina `/tmp` como o diretĂłrio de trabalho atual. + + Aqui Ă© onde geraremos o arquivo `requirements.txt` + +3. Instale o Poetry nesse estĂĄgio do Docker. + +4. Copie os arquivos `pyproject.toml` e `poetry.lock` para o diretĂłrio `/tmp`. + + Porque estĂĄ usando `./poetry.lock*` (terminando com um `*`), nĂŁo irĂĄ falhar se esse arquivo ainda nĂŁo estiver disponĂ­vel. + +5. Gere o arquivo `requirements.txt`. + +6. Este Ă© o estĂĄgio final, tudo aqui serĂĄ preservado na imagem final do contĂȘiner. + +7. Defina o diretĂłrio de trabalho atual como `/code`. + +8. Copie o arquivo `requirements.txt` para o diretĂłrio `/code`. + + Essse arquivo sĂł existe no estĂĄgio anterior do Docker, Ă© por isso que usamos `--from-requirements-stage` para copiĂĄ-lo. + +9. Instale as dependĂȘncias de pacote do arquivo `requirements.txt` gerado. + +10. Copie o diretĂłrio `app` para o diretĂłrio `/code`. + +11. Execute o comando `uvicorn`, informando-o para usar o objeto `app` importado de `app.main`. + +!!! tip + Clique nos nĂșmeros das bolhas para ver o que cada linha faz. + +Um **estĂĄgio do Docker** Ă© uma parte de um `Dockerfile` que funciona como uma **imagem temporĂĄria do contĂȘiner** que sĂł Ă© usada para gerar alguns arquivos para serem usados posteriormente. + +O primeiro estĂĄgio serĂĄ usado apenas para **instalar Poetry** e para **gerar o `requirements.txt`** com as dependĂȘncias do seu projeto a partir do arquivo `pyproject.toml` do Poetry. + +Esse arquivo `requirements.txt` serĂĄ usado com `pip` mais tarde no **prĂłximo estĂĄgio**. + +Na imagem final do contĂȘiner, **somente o estĂĄgio final** Ă© preservado. Os estĂĄgios anteriores serĂŁo descartados. + +Quando usar Poetry, faz sentido usar **construçÔes multi-estĂĄgio do Docker** porque vocĂȘ realmente nĂŁo precisa ter o Poetry e suas dependĂȘncias instaladas na imagem final do contĂȘiner, vocĂȘ **apenas precisa** ter o arquivo `requirements.txt` gerado para instalar as dependĂȘncias do seu projeto. + +EntĂŁo, no prĂłximo (e Ășltimo) estĂĄgio, vocĂȘ construiria a imagem mais ou menos da mesma maneira descrita anteriormente. + +### Por trĂĄs de um proxy de terminação TLS - Poetry + +Novamente, se vocĂȘ estiver executando seu contĂȘiner atrĂĄs de um proxy de terminação TLS (balanceador de carga) como Nginx ou Traefik, adicione a opção `--proxy-headers` ao comando: + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +## Recapitulando + +Usando sistemas de contĂȘiner (por exemplo, com **Docker** e **Kubernetes**), torna-se bastante simples lidar com todos os **conceitos de implantação**: + +* HTTPS +* Executando na inicialização +* ReinĂ­cios +* Replicação (o nĂșmero de processos rodando) +* MemĂłria +* Passos anteriores antes de inicializar + +Na maioria dos casos, vocĂȘ provavelmente nĂŁo desejarĂĄ usar nenhuma imagem base e, em vez disso, **construir uma imagem de contĂȘiner do zero** baseada na imagem oficial do Docker Python. + +Tendo cuidado com a **ordem** das instruçÔes no `Dockerfile` e o **cache do Docker**, vocĂȘ pode **minimizar os tempos de construção**, para maximizar sua produtividade (e evitar a tĂ©dio). 😎 + +Em alguns casos especiais, vocĂȘ pode querer usar a imagem oficial do Docker para o FastAPI. đŸ€“ diff --git a/docs/pt/mkdocs.yml b/docs/pt/mkdocs.yml index fdef810fa..0858de062 100644 --- a/docs/pt/mkdocs.yml +++ b/docs/pt/mkdocs.yml @@ -87,6 +87,7 @@ nav: - deployment/versions.md - deployment/https.md - deployment/deta.md + - deployment/docker.md - alternatives.md - history-design-future.md - external-links.md diff --git a/fastapi/__init__.py b/fastapi/__init__.py index afdc94874..037d9804b 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.87.0" +__version__ = "0.88.0" from starlette import status as status diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 3df5ccfc8..4c817d5d0 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -105,10 +105,10 @@ def check_file_field(field: ModelField) -> None: assert parse_options_header except ImportError: logger.error(multipart_incorrect_install_error) - raise RuntimeError(multipart_incorrect_install_error) + raise RuntimeError(multipart_incorrect_install_error) from None except ImportError: logger.error(multipart_not_installed_error) - raise RuntimeError(multipart_not_installed_error) + raise RuntimeError(multipart_not_installed_error) from None def get_param_sub_dependant( diff --git a/fastapi/encoders.py b/fastapi/encoders.py index 6bde9f4ab..2f95bcbf6 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -157,7 +157,7 @@ def jsonable_encoder( data = vars(obj) except Exception as e: errors.append(e) - raise ValueError(errors) + raise ValueError(errors) from e return jsonable_encoder( data, include=include, diff --git a/fastapi/utils.py b/fastapi/utils.py index b94dacecc..b15f6a2cf 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -89,7 +89,7 @@ def create_response_field( except RuntimeError: raise fastapi.exceptions.FastAPIError( f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type" - ) + ) from None def create_cloned_field( diff --git a/pyproject.toml b/pyproject.toml index 9549cc47d..be3080ae8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ classifiers = [ "Topic :: Internet :: WWW/HTTP", ] dependencies = [ - "starlette==0.21.0", + "starlette==0.22.0", "pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0", ] dynamic = ["version"] @@ -53,7 +53,7 @@ test = [ "pytest >=7.1.3,<8.0.0", "coverage[toml] >= 6.5.0,<7.0", "mypy ==0.982", - "ruff ==0.0.114", + "ruff ==0.0.138", "black == 22.10.0", "isort >=5.0.6,<6.0.0", "httpx >=0.23.0,<0.24.0", @@ -83,11 +83,11 @@ doc = [ "mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0", # TODO: upgrade and enable typer-cli once it supports Click 8.x.x # "typer-cli >=0.0.12,<0.0.13", - "typer[all] >=0.6.1,<0.7.0", + "typer[all] >=0.6.1,<0.8.0", "pyyaml >=5.3.1,<7.0.0", ] dev = [ - "ruff ==0.0.114", + "ruff ==0.0.138", "uvicorn[standard] >=0.12.0,<0.19.0", "pre-commit >=2.17.0,<3.0.0", ] @@ -168,6 +168,7 @@ select = [ ignore = [ "E501", # line too long, handled by black "B008", # do not perform function calls in argument defaults + "C901", # too complex ] [tool.ruff.per-file-ignores] @@ -178,7 +179,8 @@ ignore = [ "docs_src/dependencies/tutorial010.py" = ["F821"] "docs_src/custom_response/tutorial007.py" = ["B007"] "docs_src/dataclasses/tutorial003.py" = ["I001"] - +"docs_src/path_operation_advanced_configuration/tutorial007.py" = ["B904"] +"docs_src/custom_request_and_route/tutorial002.py" = ["B904"] [tool.ruff.isort] known-third-party = ["fastapi", "pydantic", "starlette"] diff --git a/scripts/docs.py b/scripts/docs.py index 622ba9202..e0953b8ed 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -284,7 +284,9 @@ def build_all(): continue langs.append(lang.name) cpu_count = os.cpu_count() or 1 - with Pool(cpu_count * 2) as p: + process_pool_size = cpu_count * 4 + typer.echo(f"Using process pool size: {process_pool_size}") + with Pool(process_pool_size) as p: p.map(build_lang, langs) diff --git a/tests/test_starlette_urlconvertors.py b/tests/test_starlette_urlconvertors.py index 5a980cbf6..5ef1b819c 100644 --- a/tests/test_starlette_urlconvertors.py +++ b/tests/test_starlette_urlconvertors.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, Path +from fastapi import FastAPI, Path, Query from fastapi.testclient import TestClient app = FastAPI() @@ -19,6 +19,11 @@ def path_convertor(param: str = Path()): return {"path": param} +@app.get("/query/") +def query_convertor(param: str = Query()): + return {"query": param} + + client = TestClient(app) @@ -45,6 +50,13 @@ def test_route_converters_path(): assert response.json() == {"path": "some/example"} +def test_route_converters_query(): + # Test query conversion + response = client.get("/query", params={"param": "QuĂ© tal!"}) + assert response.status_code == 200, response.text + assert response.json() == {"query": "QuĂ© tal!"} + + def test_url_path_for_path_convertor(): assert ( app.url_path_for("path_convertor", param="some/example") == "/path/some/example"