Skip to content

Commit 34728a8

Browse files
committed
first commit
0 parents  commit 34728a8

File tree

10 files changed

+276
-0
lines changed

10 files changed

+276
-0
lines changed

.dockerignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore everything
2+
**
3+
4+
# Allow files and directories
5+
!/assets/*

.github/workflows/image-build.yml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
name: Uvicorn Image Builder
3+
4+
# Publish on new pushed, and build on Monday Morning (UTC) regardless.
5+
on:
6+
push:
7+
branches:
8+
- 'main'
9+
paths-ignore:
10+
- '**/README.md'
11+
- 'templates/**'
12+
schedule:
13+
- cron: '4 0 * * WED'
14+
15+
16+
jobs:
17+
Uvicorn-Builder:
18+
runs-on: ubuntu-latest
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
python_versions: ["3.6", "3.7", "3.8", "3.9", "3.10"]
23+
package_versions: ["0.12.1", "0.12.2", "0.12.3", "0.13.0", "0.13.1", "0.13.2", "0.13.3", "0.13.4", "0.14.0", "0.15.0"]
24+
target_base: ["full", "slim", "alpine"]
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v2
28+
29+
- name: "Create and Push Image"
30+
uses: multi-py/action-python-image-builder@v1
31+
with:
32+
package: "uvicorn[standard]"
33+
package_latest_version: "0.15.0"
34+
maintainer: "Robert Hafner <[email protected]>"
35+
python_version: ${{ matrix.python_versions }}
36+
package_version: ${{ matrix.package_versions }}
37+
target_base: ${{ matrix.target_base }}
38+
registry_password: ${{ secrets.GITHUB_TOKEN }}
39+
dockerfile: "${{ github.workspace }}/dockerfile"
40+
docker_build_path: "${{ github.workspace }}/"

.github/workflows/versionator.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
name: Uvicorn Version Updater
3+
4+
# Every 30 minutes check for a new version of the package.
5+
on:
6+
push:
7+
branches:
8+
- 'main'
9+
schedule:
10+
- cron: '10,50 * * * *'
11+
12+
jobs:
13+
Version-Updater:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v2
18+
with:
19+
token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}
20+
21+
- name: "Update Image Build Workflow"
22+
uses: multi-py/action-python-versionator@v1
23+
with:
24+
package: "uvicorn"
25+
git_name: "Robert Hafner"
26+
git_email: "[email protected]"
27+
action_path: ${{ github.workspace }}/.github/workflows/image-build.yml

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Robert Hafner
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

assets/main.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
#
3+
# This is an example "pure python" uvicorn app.
4+
#
5+
6+
async def app(scope, receive, send):
7+
assert scope['type'] == 'http'
8+
9+
await send({
10+
'type': 'http.response.start',
11+
'status': 200,
12+
'headers': [
13+
[b'content-type', b'text/plain'],
14+
],
15+
})
16+
await send({
17+
'type': 'http.response.body',
18+
'body': b'Hello, world!',
19+
})

assets/start.sh

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#! /usr/bin/env bash
2+
set -e
3+
4+
#
5+
# The follow block comes from tiangolo/uvicorn-gunicorn-docker
6+
# MIT License: https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/LICENSE
7+
#
8+
9+
if [ -f /app/app/main.py ]; then
10+
DEFAULT_MODULE_NAME=app.main
11+
elif [ -f /app/main.py ]; then
12+
DEFAULT_MODULE_NAME=main
13+
fi
14+
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
15+
VARIABLE_NAME=${VARIABLE_NAME:-app}
16+
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}
17+
18+
19+
# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
20+
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
21+
echo "Checking for script in $PRE_START_PATH"
22+
if [ -f $PRE_START_PATH ] ; then
23+
echo "Running script $PRE_START_PATH"
24+
. "$PRE_START_PATH"
25+
else
26+
echo "There is no script $PRE_START_PATH"
27+
fi
28+
29+
#
30+
# End of tiangolo/uvicorn-gunicorn-docker block
31+
#
32+
33+
34+
if [[ $RELOAD == "true" ]]; then
35+
exec python -m gunicorn "$APP_MODULE" -k uvicorn.workers.UvicornWorker -k gevent -w ${WORKERS:-4} -b ${HOST:-0.0.0.0}:${PORT:-80} --log-level "${LOG_LEVEL:-info}" --reload
36+
else
37+
exec python -m gunicorn "$APP_MODULE" -k uvicorn.workers.UvicornWorker -k gevent -w ${WORKERS:-4} -b ${HOST:-0.0.0.0}:${PORT:-80} --log-level "${LOG_LEVEL:-info}"
38+
fi

dockerfile

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
ARG python_version=3.9
2+
ARG publish_target=$python_version
3+
ARG package_version
4+
FROM python:ghcr.io/multi-py/python-gunicorn:py$publish_target-LATEST
5+
6+
# Add args to container scope.
7+
ARG publish_target
8+
ARG python_version
9+
ARG package
10+
ARG package_version
11+
ARG maintainer=""
12+
ARG TARGETPLATFORM=""
13+
LABEL python=$python_version
14+
LABEL package=$package
15+
LABEL maintainer=$maintainer
16+
LABEL org.opencontainers.image.description="python:$publish_target $package:$package_version $TARGETPLATFORM"
17+
18+
19+
COPY --from=ghcr.io/multi-py/python-uvicorn:py${publish_target}-${$package_version} /usr/local/lib/python${$python_version}/site-packages/* /usr/local/lib/python${$python_version}/site-packages/
20+
21+
# Startup Script
22+
COPY ./assets/start.sh /start.sh
23+
RUN chmod +x /start.sh
24+
25+
# Example application so container "works" when run directly.
26+
COPY ./assets/main.py /app/main.py
27+
WORKDIR /app/
28+
29+
ENV PYTHONPATH=/app
30+
31+
CMD ["/start.sh"]

templates/description.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
A multiarchitecture container image for running Python with Gunicorn and Uvicorn.

templates/documentation.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
## Environmental Variables
2+
3+
### `PORT`
4+
5+
The port that the application inside of the container will listen on. This is different from the host port that gets mapped to the container.
6+
7+
8+
### `WORKERS`
9+
10+
The number of workers to launch. A good starting point is 4 processes per core (defaults to 4).
11+
12+
13+
### `LOG_LEVEL`
14+
15+
The uvicorn log level. Must be one of the following:
16+
17+
* `critical`
18+
* `error`
19+
* `warning`
20+
* `info`
21+
* `debug`
22+
* `trace`
23+
24+
25+
### `MODULE_NAME`
26+
27+
The python module that uvicorn will import. This value is used to generate the APP_MODULE value.
28+
29+
30+
### `VARIABLE_NAME`
31+
32+
The python variable containing the ASGI application inside of the module that uvicorn imports. This value is used to generate the APP_MODULE value.
33+
34+
35+
### `APP_MODULE`
36+
37+
The python module and variable that is passed to uvicorn. When used the `VARIABLE_NAME` and `MODULE_NAME` environmental variables are ignored.
38+
39+
40+
### `PRE_START_PATH`
41+
42+
Where to find the prestart script.
43+
44+
45+
### `RELOAD`
46+
47+
When this is set to the string `true` uvicorn is launched in reload mode. If any files change uvicorn will reload the modules again, allowing for quick debugging. This comes at a performance cost, however, and should not be enabled on production machines.

templates/quick_start.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
## How To
2+
{% set short_repository = repository.split("/")[1] -%}
3+
4+
### Add Your App
5+
6+
By default the startup script checks for the following packages and uses the first one it can find-
7+
8+
* `/app/app/main.py`
9+
* `/app/main.py`
10+
11+
If you are using pip to install dependencies your dockerfile could look like this-
12+
13+
```dockerfile
14+
FROM ghcr.io/{{ organization }}/{{ short_repository }}:py{{ python_versions|last }}-{{ package_versions|last }}
15+
16+
COPY requirements /requirements
17+
RUN pip install --no-cache-dir -r /requirements
18+
COPY ./app app
19+
```
20+
21+
22+
### Multistage Example
23+
24+
In this example we use a multistage build to compile our libraries in one container and then move them into the container we plan on using. This creates small containers while avoiding the frustration of installing build tools in a piecemeal way.
25+
26+
```dockerfile
27+
FROM ghcr.io/{{ organization }}/{{ short_repository }}:py{{ python_versions|last }}-{{ package_versions|last }}
28+
29+
# Build any packages in the bigger container with all the build tools
30+
COPY requirements /requirements
31+
RUN pip install --no-cache-dir -r /requirements
32+
33+
34+
FROM ghcr.io/{{ organization }}/{{ short_repository }}:py{{ python_versions|last }}-slim-{{ package_versions|last }}
35+
36+
# Copy the compiled python libraries from the first stage
37+
COPY --from=0 /usr/local/lib/python3.9 /usr/local/lib/python3.9
38+
39+
COPY ./app app
40+
```
41+
42+
43+
### PreStart Script
44+
45+
When the container is launched it will run the script at `/app/prestart.sh` before starting the uvicorn service. This is an ideal place to put things like database migrations.
46+

0 commit comments

Comments
 (0)