🧑💻 Added Docker
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
:8080 {
|
||||||
|
reverse_proxy * http://frontend:3000
|
||||||
|
reverse_proxy /api* http://api:80
|
||||||
|
reverse_proxy /openapi.json http://api:80 # Only use if you need to serve the OpenAPI spec
|
||||||
|
reverse_proxy /docs http://api:80 # Only use if you need to serve the Swagger UI
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3.10-slim
|
||||||
|
COPY classquiz/ /app/classquiz/
|
||||||
|
COPY init_db.py /app/
|
||||||
|
COPY *start.sh /app/
|
||||||
|
COPY gunicorn_conf.py /app/
|
||||||
|
|
||||||
|
COPY Pipfile* /app/
|
||||||
|
WORKDIR /app/
|
||||||
|
RUN pip install pipenv && pipenv install --system
|
||||||
|
EXPOSE 80
|
||||||
|
ENV PYTHONPATH=/app
|
||||||
|
RUN chmod +x start.sh
|
||||||
|
ENV APP_MODULE=classquiz:app
|
||||||
|
CMD ["./start.sh"]
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
restart: always
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
redis: ""
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- api
|
||||||
|
environment:
|
||||||
|
REDIS_URL: redis://redis:6379/0?decode_responses=True
|
||||||
|
API_URL: http://api:80
|
||||||
|
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
- redis
|
||||||
|
|
||||||
|
environment:
|
||||||
|
DB_URL: "postgresql://postgres:classquiz@db:5432/classquiz"
|
||||||
|
MAIL_ADDRESS: "email@email@email.email"
|
||||||
|
MAIL_PASSWORD: "PASSWORT"
|
||||||
|
MAIL_USERNAME: "email@email@email.email"
|
||||||
|
MAIL_SERVER: "email@email@email.emai"
|
||||||
|
MAX_WORKERS: "5" # Very important
|
||||||
|
MAIL_PORT: "587"
|
||||||
|
SECRET_KEY: "mysecretkey"
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES: 30
|
||||||
|
SKIP_EMAIL_VERIFICATION: True
|
||||||
|
HCAPTCHA_KEY: "HCAPTCHA_PRIVATE_KEY"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "redis-cli","ping" ]
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:alpine
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: "classquiz"
|
||||||
|
POSTGRES_DB: "classquiz"
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/postgresql/data
|
||||||
|
proxy:
|
||||||
|
image: caddy:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile-docker:/etc/caddy/Caddyfile
|
||||||
|
ports:
|
||||||
|
- "8000:8080"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
### Build Step
|
||||||
|
# pull the Node.js Docker image
|
||||||
|
ARG redis
|
||||||
|
FROM node:16 as builder
|
||||||
|
# ENV API_URL=http://api:80
|
||||||
|
ENV API_URL=https://mawoka.eu
|
||||||
|
ENV REDIS_URL=$REDIS
|
||||||
|
# change working directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# copy the package.json files from local machine to the workdir in container
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# run npm install in our local machine
|
||||||
|
RUN apt update && apt install -y curl && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm && pnpm i
|
||||||
|
|
||||||
|
# copy the generated modules and all other files to the container
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# build the application
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
### Serve Step
|
||||||
|
# pull the Node.js Docker image
|
||||||
|
FROM node:16
|
||||||
|
|
||||||
|
# change working directory
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apt update && apt install -y curl && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm && apt purge curl -y && apt autoremove -y
|
||||||
|
# copy files from previous step
|
||||||
|
COPY --from=builder /usr/src/app/build .
|
||||||
|
COPY --from=builder /usr/src/app/package.json .
|
||||||
|
COPY --from=builder /usr/src/app/node_modules ./node_modules
|
||||||
|
|
||||||
|
# our app is running on port 3000 within the container, so need to expose it
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# the command that starts our app
|
||||||
|
CMD ["pnpm", "run", "run:prod"]
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import json
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
|
||||||
|
workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
|
||||||
|
max_workers_str = os.getenv("MAX_WORKERS")
|
||||||
|
use_max_workers = None
|
||||||
|
if max_workers_str:
|
||||||
|
use_max_workers = int(max_workers_str)
|
||||||
|
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
|
||||||
|
|
||||||
|
host = os.getenv("HOST", "0.0.0.0")
|
||||||
|
port = os.getenv("PORT", "80")
|
||||||
|
bind_env = os.getenv("BIND", None)
|
||||||
|
use_loglevel = os.getenv("LOG_LEVEL", "info")
|
||||||
|
if bind_env:
|
||||||
|
use_bind = bind_env
|
||||||
|
else:
|
||||||
|
use_bind = f"{host}:{port}"
|
||||||
|
|
||||||
|
cores = multiprocessing.cpu_count()
|
||||||
|
workers_per_core = float(workers_per_core_str)
|
||||||
|
default_web_concurrency = workers_per_core * cores
|
||||||
|
if web_concurrency_str:
|
||||||
|
web_concurrency = int(web_concurrency_str)
|
||||||
|
assert web_concurrency > 0
|
||||||
|
else:
|
||||||
|
web_concurrency = max(int(default_web_concurrency), 2)
|
||||||
|
if use_max_workers:
|
||||||
|
web_concurrency = min(web_concurrency, use_max_workers)
|
||||||
|
accesslog_var = os.getenv("ACCESS_LOG", "-")
|
||||||
|
use_accesslog = accesslog_var or None
|
||||||
|
proc_name = os.getenv("PROC_NAME", "classquiz_api")
|
||||||
|
errorlog_var = os.getenv("ERROR_LOG", "-")
|
||||||
|
use_errorlog = errorlog_var or None
|
||||||
|
graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120")
|
||||||
|
timeout_str = os.getenv("TIMEOUT", "120")
|
||||||
|
keepalive_str = os.getenv("KEEP_ALIVE", "5")
|
||||||
|
|
||||||
|
# Gunicorn config variables
|
||||||
|
loglevel = use_loglevel
|
||||||
|
workers = web_concurrency
|
||||||
|
bind = use_bind
|
||||||
|
errorlog = use_errorlog
|
||||||
|
worker_tmp_dir = "/dev/shm"
|
||||||
|
accesslog = use_accesslog
|
||||||
|
graceful_timeout = int(graceful_timeout_str)
|
||||||
|
timeout = int(timeout_str)
|
||||||
|
keepalive = int(keepalive_str)
|
||||||
|
|
||||||
|
# For debugging and testing
|
||||||
|
log_data = {
|
||||||
|
"loglevel": loglevel,
|
||||||
|
"workers": workers,
|
||||||
|
"bind": bind,
|
||||||
|
"graceful_timeout": graceful_timeout,
|
||||||
|
"timeout": timeout,
|
||||||
|
"keepalive": keepalive,
|
||||||
|
"errorlog": errorlog,
|
||||||
|
"accesslog": accesslog,
|
||||||
|
# Additional, non-gunicorn variables
|
||||||
|
"workers_per_core": workers_per_core,
|
||||||
|
"use_max_workers": use_max_workers,
|
||||||
|
"host": host,
|
||||||
|
"port": port,
|
||||||
|
}
|
||||||
|
print(json.dumps(log_data))
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
python3 init_db.py
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#! /usr/bin/env sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -f /app/app/main.py ]; then
|
||||||
|
DEFAULT_MODULE_NAME=app.main
|
||||||
|
elif [ -f /app/main.py ]; then
|
||||||
|
DEFAULT_MODULE_NAME=main
|
||||||
|
fi
|
||||||
|
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
|
||||||
|
VARIABLE_NAME=${VARIABLE_NAME:-app}
|
||||||
|
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}
|
||||||
|
|
||||||
|
if [ -f /app/gunicorn_conf.py ]; then
|
||||||
|
DEFAULT_GUNICORN_CONF=/app/gunicorn_conf.py
|
||||||
|
elif [ -f /app/app/gunicorn_conf.py ]; then
|
||||||
|
DEFAULT_GUNICORN_CONF=/app/app/gunicorn_conf.py
|
||||||
|
else
|
||||||
|
DEFAULT_GUNICORN_CONF=/gunicorn_conf.py
|
||||||
|
fi
|
||||||
|
export GUNICORN_CONF=${GUNICORN_CONF:-$DEFAULT_GUNICORN_CONF}
|
||||||
|
export WORKER_CLASS=${WORKER_CLASS:-"uvicorn.workers.UvicornWorker"}
|
||||||
|
|
||||||
|
# If there's a prestart.sh script in the /app directory or other path specified, run it before starting
|
||||||
|
PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh}
|
||||||
|
echo "Checking for script in $PRE_START_PATH"
|
||||||
|
if [ -f $PRE_START_PATH ] ; then
|
||||||
|
echo "Running script $PRE_START_PATH"
|
||||||
|
. "$PRE_START_PATH"
|
||||||
|
else
|
||||||
|
echo "There is no script $PRE_START_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start Gunicorn
|
||||||
|
exec gunicorn -k "$WORKER_CLASS" -c "$GUNICORN_CONF" "$APP_MODULE"
|
||||||
Reference in New Issue
Block a user