Skip to content

Commit ed643b5

Browse files
Tom DoyleTom Doyle
authored andcommitted
Inital commit
0 parents  commit ed643b5

13 files changed

Lines changed: 340 additions & 0 deletions

.config/config.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[app]
2+
secret_key =

.config/gunicorn-cfg.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bind = '0.0.0.0:5005'
2+
workers = 1
3+
accesslog = '-'
4+
loglevel = 'debug'
5+
capture_output = True
6+
enable_stdio_inheritance = True

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
src/config/
2+
src/api.keys
3+
letsencrypt/

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM python:3.9
2+
3+
ENV FLASK_APP app.py
4+
5+
COPY requirements.txt ./
6+
RUN pip install -r requirements.txt
7+
8+
COPY . ./
9+
10+
EXPOSE 5005
11+
WORKDIR src/
12+
CMD ["gunicorn", "--config", "config/gunicorn-cfg.py", "app:app"]

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
config:
2+
./configure
3+
4+
build:
5+
./configure
6+
docker-compose up -d --build
7+
8+
traefik:
9+
./configure
10+
docker-compose --file docker-compose-traefik.yml up -d --build
11+
12+
clean:
13+
rm -rf src/config/
14+
rm -rf api.keys
15+
16+
local:
17+
./configure local
18+
docker-compose --file docker-compose-traefik-local.yml up -d --build
19+
20+
stop:
21+
docker-compose down

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## Python API Template
2+
3+
This repo is a template for a python api with Flask
4+
5+
6+
### Configure & Install
7+
8+
Configure `config.yml`
9+
10+
**Optional** To generate only the configuration files
11+
12+
```bash
13+
make config
14+
```
15+
16+
To run container on port 5005
17+
18+
```bash
19+
make build
20+
```
21+
22+
To run the container with traefik proxy and letsencrypt
23+
24+
```bash
25+
make traefik
26+
```
27+
28+
**Note:** If you are running this locally you won't be able to get a cert from letsencrypt
29+
You should then build for local
30+
31+
```bash
32+
make local
33+
```
34+
35+
If you want to stop all containers
36+
37+
```bash
38+
make stop
39+
```
40+
41+
If you want to remove all generated config files
42+
```bash
43+
make clean
44+
```
45+
46+
47+
### Issues
48+
49+
If you have any questions or issues please open an issue on this repo

config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
gunicorn:
2+
bind: '0.0.0.0:5005'
3+
workers: 1
4+
accesslog: '-'
5+
loglevel: 'debug'
6+
capture_output: True
7+
enable_stdio_inheritance: True
8+
api:
9+
example_key: 'w8iqHcy4p1a9xlQL4dQZkxA7Qo8EmcWFretixnvSPzm1iF2wUh'
10+
app:
11+
secret_key: WfQ2mha43Pfzwu1qYu3k4eBaKVDMV6dA9cD54cef
12+
traefik:
13+
letsencrypt:
14+
email: user@example.com

configure

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/bash
2+
3+
#
4+
# Configure script
5+
#
6+
7+
8+
#
9+
# Parse config.yml
10+
#
11+
12+
parse_yaml() {
13+
local prefix=$2
14+
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
15+
sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
16+
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
17+
awk -F$fs '{
18+
indent = length($1)/2;
19+
vname[indent] = $2;
20+
for (i in vname) {if (i > indent) {delete vname[i]}}
21+
if (length($3) > 0) {
22+
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
23+
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
24+
}
25+
}'
26+
}
27+
28+
if [[ ! -d "src/config" ]]; then
29+
mkdir src/config
30+
fi
31+
32+
#
33+
# Generate gunicorn.cnf
34+
#
35+
36+
eval $(parse_yaml config.yml "config_")
37+
38+
echo "bind = $config_gunicorn_bind" > src/config/gunicorn-cfg.py
39+
echo "workers = $config_gunicorn_workers" >> src/config/gunicorn-cfg.py
40+
echo "accesslog = $config_gunicorn_accesslog" >> src/config/gunicorn-cfg.py
41+
echo "loglevel = $config_gunicorn_loglevel" >> src/config/gunicorn-cfg.py
42+
echo "capture_output = $config_gunicorn_capture_output" >> src/config/gunicorn-cfg.py
43+
echo "enable_stdio_inheritance = $config_gunicorn_enable_stdio_inheritance" >> src/config/gunicorn-cfg.py
44+
45+
#
46+
# Generate api.keys
47+
#
48+
49+
printf "{\n $config_api_example_key : \"example_key\"\n}" > src/api.keys
50+
51+
#
52+
# Generate config.ini
53+
#
54+
55+
cp .config/config.ini src/config/config.ini
56+
sed -i "s/secret_key = /secret_key = ${config_app_secret_key}/" src/config/config.ini
57+
58+
59+
#
60+
# Generate traefik config
61+
#
62+
63+
sed -i -e "s/- \"--certificatesresolvers.myresolver.acme.email=.*\"/- \"--certificatesresolvers.myresolver.acme.email=${config_traefik_letsencrypt_email}\"/" docker-compose-traefik.yml

docker-compose-traefik-local.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
version: '3'
2+
services:
3+
example_app:
4+
container_name: example_app
5+
restart: always
6+
build: .
7+
networks:
8+
- web
9+
labels:
10+
- "traefik.http.routers.example.rule=Host(`app.internal`)"
11+
- "traefik.http.routers.example.entrypoints=web"
12+
- "traefik.http.routers.example.service=example"
13+
- "traefik.http.services.example.loadbalancer.server.port=5005"
14+
- "traefik.docker.network=web"
15+
- "traefik.http.routers.example.tls=false"
16+
17+
example_traefik:
18+
container_name: "example_traefik"
19+
image: "traefik:latest"
20+
restart: always
21+
command:
22+
- "--entrypoints.web.address=:80"
23+
- "--entrypoints.websecure.address=:443"
24+
- "--providers.docker=true"
25+
- "--providers.docker.exposedbydefault=true"
26+
- "--api.dashboard=true"
27+
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
28+
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
29+
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-v01.api.letsencrypt.org/directory"
30+
- "--certificatesresolvers.myresolver.acme.email=thomas.doyle9@mail.dcu.ie"
31+
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
32+
ports:
33+
- "80:80"
34+
- "443:443"
35+
networks:
36+
- web
37+
volumes:
38+
- "./letsencrypt:/letsencrypt"
39+
- "/var/run/docker.sock:/var/run/docker.sock:ro"
40+
labels:
41+
# Dashboard
42+
- "traefik.http.routers.traefik.rule=Host(`traefik.internal`)"
43+
- "traefik.http.routers.traefik.service=api@internal"
44+
- "traefik.http.routers.traefik.entrypoints=web"
45+
- "traefik.http.routers.traefik.tls=false"
46+
- "traefik.http.routers.traefik.tls.certresolver=myresolver"
47+
48+
# global redirect to https
49+
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
50+
- "traefik.http.routers.http-catchall.entrypoints=web"
51+
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
52+
53+
networks:
54+
web:
55+
external: true

docker-compose-traefik.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
version: '3'
2+
services:
3+
example_app:
4+
container_name: example_app
5+
restart: always
6+
build: .
7+
networks:
8+
- web
9+
labels:
10+
- "traefik.http.routers.example.rule=Host(`app.your.domain`)"
11+
- "traefik.http.routers.example.entrypoints=websecure"
12+
- "traefik.http.routers.example.service=example"
13+
- "traefik.http.services.example.loadbalancer.server.port=5005"
14+
- "traefik.docker.network=web"
15+
- "traefik.http.routers.example.tls=true"
16+
17+
example_traefik:
18+
container_name: "example_traefik"
19+
image: "traefik:latest"
20+
restart: always
21+
command:
22+
- "--entrypoints.web.address=:80"
23+
- "--entrypoints.websecure.address=:443"
24+
- "--providers.docker=true"
25+
- "--providers.docker.exposedbydefault=true"
26+
- "--api.dashboard=true"
27+
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
28+
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
29+
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-v01.api.letsencrypt.org/directory"
30+
- "--certificatesresolvers.myresolver.acme.email=thomas.doyle9@mail.dcu.ie"
31+
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
32+
ports:
33+
- "80:80"
34+
- "443:443"
35+
networks:
36+
- web
37+
volumes:
38+
- "./letsencrypt:/letsencrypt"
39+
- "/var/run/docker.sock:/var/run/docker.sock:ro"
40+
labels:
41+
# Dashboard
42+
- "traefik.http.routers.traefik.rule=Host(`your.domain`)"
43+
- "traefik.http.routers.traefik.service=api@internal"
44+
- "traefik.http.routers.traefik.entrypoints=websecure"
45+
- "traefik.http.routers.traefik.tls=true"
46+
- "traefik.http.routers.traefik.tls.certresolver=myresolver"
47+
48+
# global redirect to https
49+
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
50+
- "traefik.http.routers.http-catchall.entrypoints=web"
51+
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
52+
53+
networks:
54+
web:
55+
external: true

0 commit comments

Comments
 (0)