🧑💻 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