From 7822839a93d4489473512756269b57c0cb96054d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:40:06 +0200 Subject: [PATCH 1/8] :construction_worker: Move from Travis to GitHub actions --- .github/workflows/deploy.yml | 44 +++++++++++++++++++ .../workflows/{main.yml => issue-manager.yml} | 2 + .github/workflows/test.yml | 42 ++++++++++++++++++ .travis.yml | 33 -------------- backup..travis.yml | 35 +++++++++++++++ 5 files changed, 123 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/deploy.yml rename .github/workflows/{main.yml => issue-manager.yml} (96%) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml create mode 100644 backup..travis.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..8ca6f68 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,44 @@ +name: Deploy + +on: + push: + branches: + - master + +jobs: + build: + strategy: + matrix: + image: + - name: latest + python_version: "3.8" + - name: python3.8 + python_version: "3.8" + - name: python3.7 + python_version: "3.7" + - name: python3.6 + python_version: "3.6" + - name: python3.8-alpine3.10 + python_version: "3.8" + - name: python3.7-alpine3.8 + python_version: "3.7" + - name: python3.6-alpine3.8 + python_version: "3.6" + fail-fast: true + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: "3.7" + - name: Install Dependencies + run: python3.7 -m pip install docker pytest + - name: Deploy Image + run: bash scripts/build-push.sh + env: + NAME: ${{ matrix.image.name }} + DOCKERFILE: ${{ matrix.image.dockerfile }} + PYTHON_VERSION: ${{ matrix.image.python_version }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/main.yml b/.github/workflows/issue-manager.yml similarity index 96% rename from .github/workflows/main.yml rename to .github/workflows/issue-manager.yml index 0e9450c..cd5d7a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/issue-manager.yml @@ -1,3 +1,5 @@ +name: Issue Manager + on: schedule: - cron: "0 0 * * *" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4d7923f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,42 @@ +name: Test + +on: + push: + pull_request: + types: [opened, synchronize] + +jobs: + build: + strategy: + matrix: + image: + - name: latest + python_version: "3.8" + - name: python3.8 + python_version: "3.8" + - name: python3.7 + python_version: "3.7" + - name: python3.6 + python_version: "3.6" + - name: python3.8-alpine3.10 + python_version: "3.8" + - name: python3.7-alpine3.8 + python_version: "3.7" + - name: python3.6-alpine3.8 + python_version: "3.6" + fail-fast: true + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: "3.7" + - name: Install Dependencies + run: python3.7 -m pip install docker pytest + - name: Test Image + run: bash scripts/test.sh + env: + NAME: ${{ matrix.image.name }} + DOCKERFILE: ${{ matrix.image.dockerfile }} + PYTHON_VERSION: ${{ matrix.image.python_version }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b9cd7b0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -dist: xenial - -language: python - -python: - - "3.7" - -install: - - pip install docker pytest - -services: - - docker - -env: - - NAME='latest' BUILD_PATH='python3.7' TEST_STR1='Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7' - - NAME='python3.7' BUILD_PATH='python3.7' TEST_STR1='Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7' - - NAME='python3.6' BUILD_PATH='python3.6' TEST_STR1='Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.6' - - NAME='python3.7-alpine3.8' BUILD_PATH='python3.7-alpine3.8' TEST_STR1='Hello world! From FastAPI running on Uvicorn with Gunicorn in Alpine. Using Python 3.7' - - NAME='python3.6-alpine3.8' BUILD_PATH='python3.6-alpine3.8' TEST_STR1='Hello world! From FastAPI running on Uvicorn with Gunicorn in Alpine. Using Python 3.6' - -script: - - bash scripts/test.sh - -jobs: - include: - - script: bash scripts/test.sh - - stage: deploy - script: skip - deploy: - provider: script - script: bash scripts/build-push-all.sh - on: - branch: master diff --git a/backup..travis.yml b/backup..travis.yml new file mode 100644 index 0000000..8146cab --- /dev/null +++ b/backup..travis.yml @@ -0,0 +1,35 @@ +dist: xenial + +language: python + +python: + - "3.7" + +install: + - pip install docker pytest + +services: + - docker + +env: + - NAME='latest' PYTHON_VERSION='3.8' + - NAME='python3.8' PYTHON_VERSION='3.8' + - NAME='python3.7' PYTHON_VERSION='3.7' + - NAME='python3.6' PYTHON_VERSION='3.6' + - NAME='python3.8-alpine3.10' PYTHON_VERSION='3.8' + - NAME='python3.7-alpine3.8' PYTHON_VERSION='3.7' + - NAME='python3.6-alpine3.8' PYTHON_VERSION='3.6' + +script: + - bash scripts/test.sh + +jobs: + include: + - script: bash scripts/test.sh + - stage: deploy + script: skip + deploy: + provider: script + script: bash scripts/build-push-all.sh + on: + branch: master From c34cbec1cdcff5c82ac055d0b47aacd4a340799a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:40:38 +0200 Subject: [PATCH 2/8] :fire: Deduplicate code, centralize configs --- {python3.6 => docker-images}/app/main.py | 0 .../python3.6-alpine3.8.dockerfile | 0 .../python3.6.dockerfile | 2 +- .../python3.7-alpine3.8.dockerfile | 0 .../python3.7.dockerfile | 0 docker-images/python3.8-alpine3.10.dockerfile | 7 +++++++ docker-images/python3.8.dockerfile | 7 +++++++ python3.6-alpine3.8/app/main.py | 13 ------------- python3.7-alpine3.8/app/main.py | 13 ------------- python3.7/app/main.py | 13 ------------- 10 files changed, 15 insertions(+), 40 deletions(-) rename {python3.6 => docker-images}/app/main.py (100%) rename python3.6-alpine3.8/Dockerfile => docker-images/python3.6-alpine3.8.dockerfile (100%) rename python3.6/Dockerfile => docker-images/python3.6.dockerfile (75%) rename python3.7-alpine3.8/Dockerfile => docker-images/python3.7-alpine3.8.dockerfile (100%) rename python3.7/Dockerfile => docker-images/python3.7.dockerfile (100%) create mode 100644 docker-images/python3.8-alpine3.10.dockerfile create mode 100644 docker-images/python3.8.dockerfile delete mode 100755 python3.6-alpine3.8/app/main.py delete mode 100755 python3.7-alpine3.8/app/main.py delete mode 100755 python3.7/app/main.py diff --git a/python3.6/app/main.py b/docker-images/app/main.py similarity index 100% rename from python3.6/app/main.py rename to docker-images/app/main.py diff --git a/python3.6-alpine3.8/Dockerfile b/docker-images/python3.6-alpine3.8.dockerfile similarity index 100% rename from python3.6-alpine3.8/Dockerfile rename to docker-images/python3.6-alpine3.8.dockerfile diff --git a/python3.6/Dockerfile b/docker-images/python3.6.dockerfile similarity index 75% rename from python3.6/Dockerfile rename to docker-images/python3.6.dockerfile index ce0abb4..db40a36 100644 --- a/python3.6/Dockerfile +++ b/docker-images/python3.6.dockerfile @@ -2,6 +2,6 @@ FROM tiangolo/uvicorn-gunicorn:python3.6 LABEL maintainer="Sebastian Ramirez " -RUN pip --no-cache-dir install fastapi +RUN pip install --no-cache-dir fastapi COPY ./app /app diff --git a/python3.7-alpine3.8/Dockerfile b/docker-images/python3.7-alpine3.8.dockerfile similarity index 100% rename from python3.7-alpine3.8/Dockerfile rename to docker-images/python3.7-alpine3.8.dockerfile diff --git a/python3.7/Dockerfile b/docker-images/python3.7.dockerfile similarity index 100% rename from python3.7/Dockerfile rename to docker-images/python3.7.dockerfile diff --git a/docker-images/python3.8-alpine3.10.dockerfile b/docker-images/python3.8-alpine3.10.dockerfile new file mode 100644 index 0000000..312d9dc --- /dev/null +++ b/docker-images/python3.8-alpine3.10.dockerfile @@ -0,0 +1,7 @@ +FROM tiangolo/uvicorn-gunicorn:python3.8-alpine3.10 + +LABEL maintainer="Sebastian Ramirez " + +RUN pip install --no-cache-dir fastapi + +COPY ./app /app diff --git a/docker-images/python3.8.dockerfile b/docker-images/python3.8.dockerfile new file mode 100644 index 0000000..978c293 --- /dev/null +++ b/docker-images/python3.8.dockerfile @@ -0,0 +1,7 @@ +FROM tiangolo/uvicorn-gunicorn:python3.8 + +LABEL maintainer="Sebastian Ramirez " + +RUN pip install --no-cache-dir fastapi + +COPY ./app /app diff --git a/python3.6-alpine3.8/app/main.py b/python3.6-alpine3.8/app/main.py deleted file mode 100755 index 33410e5..0000000 --- a/python3.6-alpine3.8/app/main.py +++ /dev/null @@ -1,13 +0,0 @@ -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 in Alpine. Using Python {version}" - return {"message": message} diff --git a/python3.7-alpine3.8/app/main.py b/python3.7-alpine3.8/app/main.py deleted file mode 100755 index 33410e5..0000000 --- a/python3.7-alpine3.8/app/main.py +++ /dev/null @@ -1,13 +0,0 @@ -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 in Alpine. Using Python {version}" - return {"message": message} diff --git a/python3.7/app/main.py b/python3.7/app/main.py deleted file mode 100755 index 2ca579c..0000000 --- a/python3.7/app/main.py +++ /dev/null @@ -1,13 +0,0 @@ -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} From 09caaacc1f63ac8cca04426203ea584fcc153708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:41:40 +0200 Subject: [PATCH 3/8] :wrench: Update configs and setup, simplify env vars --- scripts/build-push-all.sh | 2 +- scripts/build-push.sh | 4 +++- scripts/build.sh | 12 ++++++++++++ scripts/docker-login.sh | 5 +++++ scripts/format-imports.sh | 6 ++++++ scripts/format.sh | 7 +++++++ scripts/lint.sh | 7 ++++--- scripts/process_all.py | 32 +++++++------------------------- scripts/test.sh | 4 +--- 9 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 scripts/build.sh create mode 100644 scripts/docker-login.sh create mode 100755 scripts/format-imports.sh create mode 100644 scripts/format.sh mode change 100644 => 100755 scripts/lint.sh diff --git a/scripts/build-push-all.sh b/scripts/build-push-all.sh index e54d3f1..a18cab7 100644 --- a/scripts/build-push-all.sh +++ b/scripts/build-push-all.sh @@ -2,6 +2,6 @@ set -e -echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin +bash scripts/docker-login.sh BUILD_PUSH=1 python scripts/process_all.py diff --git a/scripts/build-push.sh b/scripts/build-push.sh index dcd63d3..acb5319 100644 --- a/scripts/build-push.sh +++ b/scripts/build-push.sh @@ -5,9 +5,11 @@ set -e use_tag="tiangolo/uvicorn-gunicorn-fastapi:$NAME" use_dated_tag="${use_tag}-$(date -I)" -docker build -t "$use_tag" "$BUILD_PATH" +bash scripts/build.sh docker tag "$use_tag" "$use_dated_tag" +bash scripts/docker-login.sh + docker push "$use_tag" docker push "$use_dated_tag" diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..203d085 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e + +use_tag="tiangolo/uvicorn-gunicorn-fastapi:$NAME" + +DOCKERFILE="$NAME" + +if [ "$NAME" == "latest" ] ; then + DOCKERFILE="python3.8" +fi + +docker build -t "$use_tag" --file "./docker-images/${DOCKERFILE}.dockerfile" "./docker-images/" diff --git a/scripts/docker-login.sh b/scripts/docker-login.sh new file mode 100644 index 0000000..d872c89 --- /dev/null +++ b/scripts/docker-login.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -e + +echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin diff --git a/scripts/format-imports.sh b/scripts/format-imports.sh new file mode 100755 index 0000000..8710c79 --- /dev/null +++ b/scripts/format-imports.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -x + +# Sort imports one per line, so autoflake can remove unused imports +isort --recursive --force-single-line-imports --apply ./ +sh ./scripts/format.sh diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100644 index 0000000..a38f39a --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -x + +autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place ./ --exclude=__init__.py +isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --apply ./ +black ./ diff --git a/scripts/lint.sh b/scripts/lint.sh old mode 100644 new mode 100755 index a38f39a..8198da0 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash +set -e set -x -autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place ./ --exclude=__init__.py -isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --apply ./ -black ./ +mypy ./ +black ./ --check +isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only diff --git a/scripts/process_all.py b/scripts/process_all.py index 32a9f27..12b084d 100644 --- a/scripts/process_all.py +++ b/scripts/process_all.py @@ -3,31 +3,13 @@ import sys environments = [ - { - "NAME": "latest", - "BUILD_PATH": "python3.7", - "TEST_STR1": "Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7", - }, - { - "NAME": "python3.7", - "BUILD_PATH": "python3.7", - "TEST_STR1": "Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7", - }, - { - "NAME": "python3.6", - "BUILD_PATH": "python3.6", - "TEST_STR1": "Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.6", - }, - { - "NAME": "python3.7-alpine3.8", - "BUILD_PATH": "python3.7-alpine3.8", - "TEST_STR1": "Hello world! From FastAPI running on Uvicorn with Gunicorn in Alpine. Using Python 3.7", - }, - { - "NAME": "python3.6-alpine3.8", - "BUILD_PATH": "python3.6-alpine3.8", - "TEST_STR1": "Hello world! From FastAPI running on Uvicorn with Gunicorn in Alpine. Using Python 3.6", - }, + {"NAME": "latest", "PYTHON_VERSION": "3.8"}, + {"NAME": "python3.8", "PYTHON_VERSION": "3.8"}, + {"NAME": "python3.7", "PYTHON_VERSION": "3.7"}, + {"NAME": "python3.6", "PYTHON_VERSION": "3.6"}, + {"NAME": "python3.8-alpine3.10", "PYTHON_VERSION": "3.8"}, + {"NAME": "python3.7-alpine3.8", "PYTHON_VERSION": "3.7"}, + {"NAME": "python3.6-alpine3.8", "PYTHON_VERSION": "3.6"}, ] start_with = os.environ.get("START_WITH") diff --git a/scripts/test.sh b/scripts/test.sh index 54ff464..693467a 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash set -e -use_tag="tiangolo/uvicorn-gunicorn-fastapi:$NAME" - -docker build -t "$use_tag" "$BUILD_PATH" +bash scripts/build.sh pytest tests From 83e21a8f902b54dad24044e60c9ae06e45c58742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:42:00 +0200 Subject: [PATCH 4/8] :white_check_mark: Update tests --- tests/test_01_main/test_defaults.py | 31 +++++++++++++++++++++-------- tests/utils.py | 21 +++++++++++++------ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/tests/test_01_main/test_defaults.py b/tests/test_01_main/test_defaults.py index 58b4c24..6b9e6f9 100644 --- a/tests/test_01_main/test_defaults.py +++ b/tests/test_01_main/test_defaults.py @@ -2,37 +2,52 @@ import time import docker -import pytest import requests +from docker.client import DockerClient -from ..utils import CONTAINER_NAME, get_config, get_logs, remove_previous_container +from ..utils import ( + CONTAINER_NAME, + get_config, + get_logs, + get_response_text1, + remove_previous_container, +) client = docker.from_env() -def verify_container(container, response_text): +def verify_container(container: DockerClient, response_text: str) -> None: + response = requests.get("http://127.0.0.1:8000") + data = response.json() + assert data["message"] == response_text config_data = get_config(container) assert config_data["workers_per_core"] == 1 + assert config_data["use_max_workers"] is None assert config_data["host"] == "0.0.0.0" assert config_data["port"] == "80" assert config_data["loglevel"] == "info" assert config_data["workers"] >= 2 assert config_data["bind"] == "0.0.0.0:80" + assert config_data["graceful_timeout"] == 120 + assert config_data["timeout"] == 120 + assert config_data["keepalive"] == 5 + assert config_data["errorlog"] == "-" + assert config_data["accesslog"] == "-" logs = get_logs(container) assert "Checking for script in /app/prestart.sh" in logs assert "Running script /app/prestart.sh" in logs assert ( "Running inside /app/prestart.sh, you could add migrations to this file" in logs ) - response = requests.get("http://127.0.0.1:8000") - data = response.json() - assert data["message"] == response_text + assert '"GET / HTTP/1.1" 200' in logs + assert "[INFO] Application startup complete." in logs + assert "Using worker: uvicorn.workers.UvicornWorker" in logs -def test_defaults(): +def test_defaults() -> None: name = os.getenv("NAME") image = f"tiangolo/uvicorn-gunicorn-fastapi:{name}" - response_text = os.getenv("TEST_STR1") + response_text = get_response_text1() sleep_time = int(os.getenv("SLEEP_TIME", 1)) remove_previous_container(client) container = client.containers.run( diff --git a/tests/utils.py b/tests/utils.py index 8dda3e4..42f4518 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,18 +1,22 @@ import json +import os +from typing import Any, Dict, List +from docker.client import DockerClient from docker.errors import NotFound +from docker.models.containers import Container CONTAINER_NAME = "uvicorn-gunicorn-fastapi-test" -def get_process_names(container): +def get_process_names(container: Container) -> List[str]: top = container.top() process_commands = [p[7] for p in top["Processes"]] gunicorn_processes = [p for p in process_commands if "gunicorn" in p] return gunicorn_processes -def get_gunicorn_conf_path(container): +def get_gunicorn_conf_path(container: Container) -> str: gunicorn_processes = get_process_names(container) first_process = gunicorn_processes[0] first_part, partition, last_part = first_process.partition("-c") @@ -20,13 +24,13 @@ def get_gunicorn_conf_path(container): return gunicorn_conf -def get_config(container): +def get_config(container: Container) -> Dict[str, Any]: gunicorn_conf = get_gunicorn_conf_path(container) result = container.exec_run(f"python {gunicorn_conf}") return json.loads(result.output.decode()) -def remove_previous_container(client): +def remove_previous_container(client: DockerClient) -> None: try: previous = client.containers.get(CONTAINER_NAME) previous.stop() @@ -35,6 +39,11 @@ def remove_previous_container(client): return None -def get_logs(container): - logs: str = container.logs() +def get_logs(container: DockerClient) -> str: + logs = container.logs() return logs.decode("utf-8") + + +def get_response_text1() -> str: + python_version = os.getenv("PYTHON_VERSION") + return f"Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python {python_version}" From 81e8205a2ae80856588f291f29f36c64b5b1fdb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:42:15 +0200 Subject: [PATCH 5/8] :heavy_plus_sign: Add dev dependencies --- mypy.ini | 3 +++ pyproject.toml | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..4ff4483 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +disallow_untyped_defs = True +ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index 83cb2f0..019175d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,10 @@ docker = "^4.2.0" pytest = "^5.4.1" [tool.poetry.dev-dependencies] +black = "^19.10b0" +isort = "^4.3.21" +autoflake = "^1.3.1" +mypy = "^0.770" [build-system] requires = ["poetry>=0.12"] From dfab369d236c7164fafc7c62094b569fbd08d5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:42:35 +0200 Subject: [PATCH 6/8] :memo: Update docs with latest changes from base image --- README.md | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 99bdf30..fc2a440 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,19 @@ ## Supported tags and respective `Dockerfile` links -* [`python3.7`, `latest` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/python3.7/Dockerfile) -* [`python3.6` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/python3.6/Dockerfile) -* [`python3.6-alpine3.8` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/python3.6-alpine3.8/Dockerfile) -* [`python3.7-alpine3.8` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/python3.7-alpine3.8/Dockerfile) +* [`python3.8`, `latest` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.8.dockerfile) +* [`python3.7`, _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.7.dockerfile) +* [`python3.6` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.6.dockerfile) +* [`python3.8-alpine3.10` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.8-alpine3.10.dockerfile) +* [`python3.7-alpine3.8` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.7-alpine3.8.dockerfile) +* [`python3.6-alpine3.8` _(Dockerfile)_](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.6-alpine3.8.dockerfile) + **Note**: Note: There are [tags for each build date](https://hub.docker.com/r/tiangolo/uvicorn-gunicorn-fastapi/tags). If you need to "pin" the Docker image version you use, you can select one of those tags. E.g. `tiangolo/uvicorn-gunicorn-fastapi:python3.7-2019-10-15`. # uvicorn-gunicorn-fastapi -[**Docker**](https://www.docker.com/) image with [**Uvicorn**](https://www.uvicorn.org/) managed by [**Gunicorn**](https://gunicorn.org/) for high-performance [**FastAPI**](https://fastapi.tiangolo.com/) web applications in **[Python](https://www.python.org/) 3.7** and **3.6** with performance auto-tuning. Optionally with Alpine Linux. +[**Docker**](https://www.docker.com/) image with [**Uvicorn**](https://www.uvicorn.org/) managed by [**Gunicorn**](https://gunicorn.org/) for high-performance [**FastAPI**](https://fastapi.tiangolo.com/) web applications in **[Python](https://www.python.org/) 3.6 and above** with performance auto-tuning. Optionally with Alpine Linux. **GitHub repo**: [https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker) @@ -265,6 +268,8 @@ You can set it like: docker run -d -p 80:80 -e GUNICORN_CONF="/app/custom_gunicorn_conf.py" myimage ``` +You can use the [config file from the base image](https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/docker-images/gunicorn_conf.py) as a starting point for yours. + #### `WORKERS_PER_CORE` This image will check how many CPU cores are available in the current server running your container. @@ -295,6 +300,24 @@ In a server with 8 CPU cores, this would make it start only 4 worker processes. **Note**: By default, if `WORKERS_PER_CORE` is `1` and the server has only 1 CPU core, instead of starting 1 single worker, it will start 2. This is to avoid bad performance and blocking applications (server application) on small machines (server machine/cloud/etc). This can be overridden using `WEB_CONCURRENCY`. +#### `MAX_WORKERS` + +Set the maximum number of workers to use. + +You can use it to let the image compute the number of workers automatically but making sure it's limited to a maximum. + +This can be useful, for example, if each worker uses a database connection and your database has a maximum limit of open connections. + +By default it's not set, meaning that it's unlimited. + +You can set it like: + +```bash +docker run -d -p 80:80 -e MAX_WORKERS="24" myimage +``` + +This would make the image start at most 24 workers, independent of how many CPU cores are available in the server. + #### `WEB_CONCURRENCY` Override the automatic definition of number of workers. @@ -379,6 +402,112 @@ You can set it like: docker run -d -p 80:8080 -e LOG_LEVEL="warning" myimage ``` +#### `WORKER_CLASS` + +The class to be used by Gunicorn for the workers. + +By default, set to `uvicorn.workers.UvicornWorker`. + +The fact that it uses Uvicorn is what allows using ASGI frameworks like FastAPI, and that is also what provides the maximum performance. + +You probably shouldn't change it. + +But if for some reason you need to use the alternative Uvicorn worker: `uvicorn.workers.UvicornH11Worker` you can set it with this environment variable. + +You can set it like: + +```bash +docker run -d -p 80:8080 -e WORKER_CLASS="uvicorn.workers.UvicornH11Worker" myimage +``` + +#### `TIMEOUT` + +Workers silent for more than this many seconds are killed and restarted. + +Read more about it in the [Gunicorn docs: timeout](https://docs.gunicorn.org/en/stable/settings.html#timeout). + +By default, set to `120`. + +Notice that Uvicorn and ASGI frameworks like FastAPI are async, not sync. So it's probably safe to have higher timeouts than for sync workers. + +You can set it like: + +```bash +docker run -d -p 80:8080 -e TIMEOUT="20" myimage +``` + +#### `KEEP_ALIVE` + +The number of seconds to wait for requests on a Keep-Alive connection. + +Read more about it in the [Gunicorn docs: keepalive](https://docs.gunicorn.org/en/stable/settings.html#keepalive). + +By default, set to `2`. + +You can set it like: + +```bash +docker run -d -p 80:8080 -e KEEP_ALIVE="20" myimage +``` + +#### `GRACEFUL_TIMEOUT` + +Timeout for graceful workers restart. + +Read more about it in the [Gunicorn docs: graceful-timeout](https://docs.gunicorn.org/en/stable/settings.html#graceful-timeout). + +By default, set to `120`. + +You can set it like: + +```bash +docker run -d -p 80:8080 -e GRACEFUL_TIMEOUT="20" myimage +``` + +#### `ACCESS_LOG` + +The access log file to write to. + +By default `"-"`, which means stdout (print in the Docker logs). + +If you want to disable `ACCESS_LOG`, set it to an empty value. + +For example, you could disable it with: + +```bash +docker run -d -p 80:8080 -e ACCESS_LOG= myimage +``` + +#### `ERROR_LOG` + +The error log file to write to. + +By default `"-"`, which means stderr (print in the Docker logs). + +If you want to disable `ERROR_LOG`, set it to an empty value. + +For example, you could disable it with: + +```bash +docker run -d -p 80:8080 -e ERROR_LOG= myimage +``` + +#### `GUNICORN_CMD_ARGS` + +Any additional command line settings for Gunicorn can be passed in the `GUNICORN_CMD_ARGS` environment variable. + +Read more about it in the [Gunicorn docs: Settings](https://docs.gunicorn.org/en/stable/settings.html#settings). + +These settings will have precedence over the other environment variables and any Gunicorn config file. + +For example, if you have a custom TLS/SSL certificate that you want to use, you could copy them to the Docker image or mount them in the container, and set [`--keyfile` and `--certfile`](http://docs.gunicorn.org/en/latest/settings.html#ssl) to the location of the files, for example: + +```bash +docker run -d -p 80:8080 -e GUNICORN_CMD_ARGS="--keyfile=/secrets/key.pem --certfile=/secrets/cert.pem" -e PORT=443 myimage +``` + +**Note**: instead of handling TLS/SSL yourself and configuring it in the container, it's recommended to use a "TLS Termination Proxy" like [Traefik](https://docs.traefik.io/). You can read more about it in the [FastAPI documentation about HTTPS](https://fastapi.tiangolo.com/deployment/#https). + #### `PRE_START_PATH` The path where to find the pre-start script. @@ -469,7 +598,7 @@ docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh * `$(pwd)`: runs `pwd` ("print working directory") and puts it as part of the string. * `/start-reload.sh`: adding something (like `/start-reload.sh`) at the end of the command, replaces the default "command" with this one. In this case, it replaces the default (`/start.sh`) with the development alternative `/start-reload.sh`. -#### Technical Details +#### Development live reload - Technical Details As `/start-reload.sh` doesn't run with Gunicorn, any of the configurations you put in a `gunicorn_conf.py` file won't apply. From 65be03d104d0024cd541e76780ac455adf408ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:46:33 +0200 Subject: [PATCH 7/8] :memo: Update build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc2a440..0b0fb76 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.com/tiangolo/uvicorn-gunicorn-fastapi-docker.svg?branch=master)](https://travis-ci.com/tiangolo/uvicorn-gunicorn-fastapi-docker) +[![Test](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/workflows/Test/badge.svg)](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/actions?query=workflow%3ATest) [![Deploy](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/workflows/Deploy/badge.svg)](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/actions?query=workflow%3ADeploy) ## Supported tags and respective `Dockerfile` links From 860f4ae8725a375d757857302b287640d2150fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 27 Apr 2020 18:49:22 +0200 Subject: [PATCH 8/8] :truck: Rename Travis backup file --- backup..travis.yml => backup.travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backup..travis.yml => backup.travis.yml (100%) diff --git a/backup..travis.yml b/backup.travis.yml similarity index 100% rename from backup..travis.yml rename to backup.travis.yml