You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+276-6Lines changed: 276 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,16 +9,33 @@ _Docker images to power your Python APIs and help you ship faster. With support
9
9
10
10
Brendon Smith ([br3ndonland](https://github.com/br3ndonland/))
11
11
12
+
## Table of Contents <!-- omit in toc -->
13
+
14
+
-[Description](#description)
15
+
-[Instructions](#instructions)
16
+
-[Configure Docker for GitHub Packages](#configure-docker-for-github-packages)
17
+
-[Pull images](#pull-images)
18
+
-[Use images in a _Dockerfile_](#use-images-in-a-dockerfile)
19
+
-[Run containers](#run-containers)
20
+
-[Configuration](#configuration)
21
+
-[General](#general)
22
+
-[Gunicorn and Uvicorn](#gunicorn-and-uvicorn)
23
+
-[Development](#development)
24
+
-[Code style](#code-style)
25
+
-[Building development images](#building-development-images)
26
+
-[Running development containers](#running-development-containers)
27
+
12
28
## Description
13
29
14
30
This is a refactor of [tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker) with the following advantages:
15
31
16
-
-**One repo**. The tiangolo/uvicorn-gunicorn images are in at least three separate repos, [tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker), [tiangolo/uvicorn-gunicorn-starlette-docker](https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker), and [tiangolo/uvicorn-gunicorn-starlette-docker](https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker), with large amounts of code duplication, making maintenance difficult for an already-busy maintainer. This repo combines three into one.
17
-
-**One _Dockerfile_.** This repository leverages [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) to produce multiple Docker images from one _Dockerfile_.
18
-
-**One Python requirements file.** This project leverages Poetry with Poetry Extras for dependency management with the _pyproject.toml_.
19
-
-**One platform.** Docker Hub is superfluous. You're already on GitHub. Why not [pull Docker images from GitHub Packages](https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages)?
32
+
-**One repo**. The tiangolo/uvicorn-gunicorn images are in at least three separate repos ([tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker), [tiangolo/uvicorn-gunicorn-fastapi-docker](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker), and [tiangolo/uvicorn-gunicorn-starlette-docker](https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker)), with large amounts of code duplication, making maintenance difficult for an already-busy maintainer. This repo combines three into one.
33
+
-**One _Dockerfile_.** This repo leverages [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) to produce multiple Docker images from one _Dockerfile_.
34
+
-**One Python requirements file.** This repo uses [Poetry](https://github.com/python-poetry/poetry) with Poetry Extras for dependency management with a single _pyproject.toml_.
35
+
-**One programming language.** Pure Python with no shell scripts.
36
+
-**One platform.** You're already on GitHub. Why not [pull Docker images from GitHub Packages](https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages)?
For a [Poetry](https://github.com/python-poetry/poetry) project with the following directory structure:
83
+
84
+
-`repo`
85
+
-`package`
86
+
-`main.py`
87
+
-`prestart.py`
88
+
-`Dockerfile`
89
+
-`poetry.lock`
90
+
-`pyproject.toml`
91
+
92
+
The _Dockerfile_ could look like this:
93
+
94
+
```dockerfile
95
+
FROM docker.pkg.github.com/br3ndonland/inboard/fastapi
96
+
97
+
# Install Python requirements
98
+
COPY poetry.lock pyproject.toml /
99
+
RUN poetry install --no-dev --no-interaction --no-root
100
+
101
+
# Install Python app
102
+
COPY package /app/
103
+
104
+
# RUN command already included in base image
105
+
```
106
+
107
+
Organizing the _Dockerfile_ this way helps [leverage the Docker build cache](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache). Files and commands that change most frequently are added last to the _Dockerfile_. Next time the image is built, Docker will skip any layers that didn't change, speeding up builds.
108
+
109
+
For a standard `pip` install:
110
+
111
+
-`repo`
112
+
-`package`
113
+
-`main.py`
114
+
-`prestart.py`
115
+
-`Dockerfile`
116
+
-`requirements.txt`
117
+
65
118
```dockerfile
66
119
FROM docker.pkg.github.com/br3ndonland/inboard/fastapi
120
+
121
+
# Install Python requirements
122
+
COPY requirements.txt /
123
+
RUN pip install -r requirements.txt
124
+
125
+
# Install Python app
126
+
COPY package /app/
127
+
128
+
# RUN command already included in base image
129
+
```
130
+
131
+
The image could then be built with:
132
+
133
+
```sh
134
+
cd /path/to/repo
135
+
docker build . -t imagename:latest
67
136
```
68
137
69
138
### Run containers
70
139
71
140
Run container:
72
141
73
142
```sh
74
-
docker run -d -p 80:80 br3ndonland/inboard/fastapi
143
+
docker run -d -p 80:80 imagename
144
+
```
145
+
146
+
Run container with mounted volume and Uvicorn reloading for development:
-`WITH_RELOAD=true`: `start.py` will run Uvicorn with reloading and without Gunicorn. The Gunicorn configuration won't apply, but these environment variables will still work as [described](#configuration):
154
+
-`MODULE_NAME`
155
+
-`VARIABLE_NAME`
156
+
-`APP_MODULE`
157
+
-`HOST`
158
+
-`PORT`
159
+
-`LOG_LEVEL`
160
+
-`-v $(pwd)/package:/app`: the specified directory (`/path/to/repo/package` in this example) will be [mounted as a volume](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) inside of the container at `/app`. When files in the working directory change, Docker and Uvicorn will sync the files to the running Docker container.
161
+
- The final argument is the Docker image name (`imagename` in this example). Replace with your image name.
- Custom: For a module at `/app/custom/module.py`and variable `api`, `APP_MODULE="custom.module:api"`
222
+
-`PRE_START_PATH`: Path to a pre-start script. Add a file`prestart.py`or`prestart.sh` to the application directory, and copy the directory into the Docker image as described (for a project with the Python application in`repo/package`, `COPY package /app/`). The container will automatically detect and run the prestart script before starting the web server.
223
+
224
+
- Default: `/app/prestart.py`
225
+
- Custom: `PRE_START_PATH="/custom/script.sh"`
226
+
227
+
### Gunicorn and Uvicorn
228
+
229
+
-`GUNICORN_CONF`: Path to a [Gunicorn configuration file](https://docs.gunicorn.org/en/latest/settings.html#config-file).
230
+
- Default:
231
+
-`/app/gunicorn_conf.py`if exists
232
+
- Else `/app/app/gunicorn_conf.py`if exists
233
+
- Else `/gunicorn_conf.py` (the default file provided with the Docker image)
234
+
- Custom:
235
+
-`GUNICORN_CONF="/app/custom_gunicorn_conf.py"`
236
+
- Feel free to use the [`gunicorn_conf.py`](./inboard/gunicorn_conf.py) from this repo as a starting point for your own custom configuration.
237
+
-`HOST`: Host IP address (inside of the container) where Gunicorn will listen for requests.
238
+
- Default: `0.0.0.0`
239
+
- Custom: _TODO_
240
+
-`PORT`: Port the container should listen on.
241
+
- Default: `80`
242
+
- Custom: `PORT="8080"`
243
+
- [`BIND`](https://docs.gunicorn.org/en/latest/settings.html#server-socket): The actual host and port passed to Gunicorn.
244
+
- Default: `HOST:PORT` (`0.0.0.0:80`)
245
+
- Custom: `BIND="0.0.0.0:8080"`
246
+
- [`WORKER_CLASS`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): The class to be used by Gunicorn for the workers.
247
+
- Default: `uvicorn.workers.UvicornWorker`
248
+
- Custom: For the alternate Uvicorn worker, `WORKER_CLASS="uvicorn.workers.UvicornH11Worker"`
249
+
- [`WORKERS_PER_CORE`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): Number of Gunicorn workers per CPU core.
250
+
- Default: `1`
251
+
- Custom: `WORKERS_PER_CORE="2"`
252
+
- Notes:
253
+
- This image will check how many CPU cores are available in the current server running your container. It will set the number of workers to the number of CPU cores multiplied by this value.
254
+
- On a server with2CPU cores, `WORKERS_PER_CORE="3"` will run 6 worker processes.
255
+
- Floating point values are permitted. If you have a powerful server (let's say, with 8 CPU cores) running several applications, including an ASGI application that won't need high performance, but you don't want to waste server resources, you could set the environment variable to `WORKERS_PER_CORE="0.5"`. A server with 8 CPU cores would start only 4 worker processes.
256
+
- By default, if`WORKERS_PER_CORE`is`1`and the server has only 1CPU core, 2 workers will be started instead of 1, to avoid poor performance and blocking applications. This behavior can be overridden using `WEB_CONCURRENCY`.
257
+
-`MAX_WORKERS`: Maximum number of workers to use, independent of number of CPU cores.
258
+
- Default: unlimited (notset)
259
+
- Custom: `MAX_WORKERS="24"`
260
+
- [`WEB_CONCURRENCY`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): Set number of workers independently of number of CPU cores.
261
+
- Default:
262
+
- Number of CPU cores multiplied by the environment variable `WORKERS_PER_CORE`.
263
+
- In a server with2 cores and default `WORKERS_PER_CORE="1"`, default `2`.
264
+
- Custom: To have 4 workers, `WEB_CONCURRENCY="4"`
265
+
- [`TIMEOUT`](https://docs.gunicorn.org/en/stable/settings.html#timeout): Workers silent for more than this many seconds are killed and restarted.
266
+
- Default: `120`
267
+
- Custom: `TIMEOUT="20"`
268
+
- [`GRACEFUL_TIMEOUT`](https://docs.gunicorn.org/en/stable/settings.html#graceful-timeout): Number of seconds to allow workers finish serving requests before restart.
269
+
- Default:`120`
270
+
- Custom: `GRACEFUL_TIMEOUT="20"`
271
+
- [`KEEP_ALIVE`](https://docs.gunicorn.org/en/stable/settings.html#keepalive): Number of seconds to wait for requests on a Keep-Alive connection.
272
+
- Default: `2`
273
+
- Custom: `KEEP_ALIVE="20"`
274
+
-`LOG_LEVEL`: Log level for [Gunicorn](https://docs.gunicorn.org/en/latest/settings.html#logging) or [Uvicorn](https://www.uvicorn.org/settings/#logging).
275
+
- Default: `info`
276
+
- Custom (organized from greatest to least amount of logging):
277
+
-`debug`
278
+
-`info`
279
+
-`warning`
280
+
-`error`
281
+
-`critical`
282
+
-`ACCESS_LOG`: Access log file to which to write.
283
+
- Default: `"-"` (`stdout`, printin Docker logs)
284
+
- Custom:
285
+
-`ACCESS_LOG="./path/to/accesslogfile.txt"`
286
+
-`ACCESS_LOG=` (set to an empty value) to disable
287
+
-`ERROR_LOG`: Error log file to which to write.
288
+
- Default: `"-"` (`stdout`, printin Docker logs)
289
+
- Custom:
290
+
-`ERROR_LOG="./path/to/errorlogfile.txt"`
291
+
-`ERROR_LOG=` (set to an empty value) to disable
292
+
-`GUNICORN_CMD_ARGS`: Additional [command-line arguments for Gunicorn](https://docs.gunicorn.org/en/stable/settings.html). These settings will have precedence over the other environment variables andany Gunicorn config file.
293
+
- Custom: To use a custom TLS certificate, copy or mount the certificate and private key into the Docker image, and set [`--keyfile`and`--certfile`](http://docs.gunicorn.org/en/latest/settings.html#ssl) to the location of the files.
- Python code is formatted with [Black](https://black.readthedocs.io/en/stable/). Configuration for Black is stored in _[pyproject.toml](pyproject.toml)_.
305
+
- Python imports are organized automatically with [isort](https://timothycrosley.github.io/isort/).
306
+
- The isort package organizes imports in three sections:
307
+
1. Standard library
308
+
2. Dependencies
309
+
3. Project
310
+
- Within each of those groups, `import` statements occur first, then `from` statements, in alphabetical order.
311
+
- You can run isort from the command line with`poetry run isort .`.
312
+
- Configuration for isort is stored in _[pyproject.toml](pyproject.toml)_.
313
+
- Other web code (JSON, Markdown, YAML) is formatted with [Prettier](https://prettier.io/).
0 commit comments