Skip to content

Commit

Permalink
Add a (mostly) working docker environment
Browse files Browse the repository at this point in the history
  • Loading branch information
haydngreatnews committed May 6, 2024
1 parent f1c5ecc commit 558e83f
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 1 deletion.
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/.git*
.git/
.github/
.circleci/

docs/
infra/
node_modules/
var/

dev.env*
docker/database
docker/httpd
**/Dockerfile
**/.dockerignore
docker-bake.yml
docker-compose.yml
**/README.md
**/*.sql*
.justfile

appspec.yml
compose.yml

.files/*
64 changes: 64 additions & 0 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// this is cache definition used for caching purposes only
variable "CACHE" { default = "" }
// this is remote registry to push to
variable "REGISTRY" { default = "" }
variable "ENVIRONMENT" { default = "preview" }
variable "PROJECT" { default = "cdhweb" }
variable "VERSION" { default = "latest" }

// targets in groups are built in parallel
group "default" {
targets = ["tasks", "app"]
}

group "testing" {
targets = ["app-test"]
}

target "app" {
dockerfile = "docker/application/Dockerfile"
target = "app"
cache-from = notequal("", CACHE) ? ["${CACHE},name=app"] : []
cache-to = notequal("", CACHE) ? ["${CACHE},mode=max,name=app"] : []

args = {
VERSION : VERSION,
}

tags = notequal("", REGISTRY) ? [
"${REGISTRY}/${PROJECT}-app:${ENVIRONMENT}-latest",
"${REGISTRY}/${PROJECT}-app:${ENVIRONMENT}-${VERSION}",
"${REGISTRY}/${PROJECT}-app:common-${VERSION}",
] : []
}

target "app-test" {
dockerfile = "docker/application/Dockerfile"
target = "app-test"
cache-from = notequal("", CACHE) ? ["${CACHE},name=app-test", "${CACHE},name=base"] : []
cache-to = notequal("", CACHE) ? ["${CACHE},mode=max,name=app-test"] : []

args = {
VERSION : VERSION,
}

// this tag is different as we're going to load it
tags = ["${PROJECT}/app-test:${VERSION}"]
}

target "tasks" {
dockerfile = "docker/application/Dockerfile"
target = "tasks"
cache-from = notequal("", CACHE) ? ["${CACHE},name=tasks"] : []
cache-to = notequal("", CACHE) ? ["${CACHE},mode=max,name=tasks"] : []

args = {
VERSION : VERSION,
}

tags = notequal("", REGISTRY) ? [
"${REGISTRY}/${PROJECT}-tasks:${ENVIRONMENT}-latest",
"${REGISTRY}/${PROJECT}-tasks:${ENVIRONMENT}-${VERSION}",
"${REGISTRY}/${PROJECT}-tasks:common-${VERSION}",
] : []
}
62 changes: 62 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
version: "3.7"

services:
application:
restart: on-failure
build:
context: .
dockerfile: docker/application/Dockerfile
target: app-dev
volumes:
- ".:/app"
ports:
- "${PORT_PREFIX:-561}80:8000"
# env_file:
# - dev.env
environment:
VIRTUAL_HOST: "cdh.dev.springload.nz"
VIRTUAL_PORT: 8000
VIRTUAL_MEDIA_DOMAIN: # Change me once set up
entrypoint: ["wait-for", "database:5432", "--"]
command:
[
"/usr/local/bin/gunicorn",
"--config",
"/app/docker/gunicorn.py",
"--reload",
"cdhweb.wsgi",
]
depends_on:
- database
- cache
networks:
default:
aliases:
- "cdh.dev.springload.nz"
nginx-proxy:

database:
restart: on-failure
image: postgres:16-alpine
platform: linux/x86_64
ports:
- "${PORT_PREFIX:-561}32:5432" # default 56132
volumes:
- "./docker/database:/docker-entrypoint-initdb.d/:ro"
environment:
WAGTAIL_SITE_HOSTNAME: "${SITE_HOSTNAME}"
WAGTAIL_SITE_PORT: "443"
POSTGRES_DB: "cdhweb"
POSTGRES_USER: "cdhweb"
POSTGRES_PASSWORD: "cdhweb"

cache:
restart: on-failure
image: redis:5-alpine
platform: linux/x86_64
ports:
- "${PORT_PREFIX:-561}79:6379"

networks:
nginx-proxy:
external: true
10 changes: 10 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# application

The container builds backend image from Python3.7.

It uses Docker staging and contains two stages:

1. Dev
2. Production

Dev stage installs additional Python dependencies and utilises daemon auto-reload on code change.
107 changes: 107 additions & 0 deletions docker/application/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# syntax=docker/dockerfile:1.4

## Frontend TODO once Springload approach added
# # frontend assets
# FROM node:20-alpine as frontend

# RUN apk add --update git

# WORKDIR /app

# # Install packages
# COPY package.json package-lock.json /app/
# RUN --mount=type=cache,target=/root/.npm NPM_CACHE_FOLDER=/root/.npm \
# npm ci

# # Copy config/build files
# COPY webpack.config.js webpack.fix-django-paths.js tsconfig.json /app/
# COPY core/static_src /app/core/static_src
# COPY core/templates_src /app/core/templates_src

# RUN npm run build

# CMD ["npm", "run build"]


## -----
# Base backend
# On Debian, as their prod is Ubuntu
FROM --platform=linux/x86_64 python:3.11-slim-bullseye as base

ENV PROJECT cdh-web
ENV ENVIRONMENT unknown
ENV APPLICATION_VERSION dev

WORKDIR /app

COPY requirements /app/requirements
COPY docker/requirements.txt docker/constraints.txt /app/docker/

# RUN --mount=type=cache,target=/var/lib/apt/lists \
# --mount=type=cache,target=/var/cache/apt \
# rm -f /etc/apt/apt.conf.d/docker-clean && \
# apt-get update -y && apt-get install -y \
# make nc

COPY requirements /app/requirements
COPY docker/requirements.txt docker/constraints.txt /app/docker/

RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -U pip setuptools wheel && \
pip3 install -r docker/requirements.txt && \
pip3 install -r requirements/prod.txt




RUN adduser --system www -u 1000 && chown -R www /app


## -----
# NO production stage, as this app is run in Princeton's env, on bare-metal


## -----
# development stage
FROM base as app-dev

ENV DJANGO_SETTINGS_MODULE cdhweb.settings

ADD --chmod=755 https://github.com/mrako/wait-for/releases/download/v1.0.0/wait-for /usr/local/bin/

RUN --mount=type=cache,target=/var/lib/apt/lists \
--mount=type=cache,target=/var/cache/apt \
apt-get update -y && apt-get install -y netcat

RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r /app/requirements/dev.txt

# No COPY here as the local directory is volume-mounted in docker-compose

CMD ["/usr/local/bin/gunicorn", "--config", "/app/docker/gunicorn.py", "--reload", "cdhweb.wsgi" ]


## -----
# test stage
FROM base as app-test

ENV DJANGO_SETTINGS_MODULE app.settings.test

RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r /app/requirements/test.txt

COPY ./ ./
# TODO: FRONTEND
# COPY --link --from=frontend /app/core/static /app/core/static
# COPY --link --from=frontend /app/core/templates /app/core/templates

CMD ["/bin/sh", "/app/test.sh"]


## -----
# Huey stage
FROM app as tasks
CMD ["python", "/app/manage.py", "run_huey", "--huey-verbose"]

FROM app-dev as tasks-dev
CMD ["python", "/app/manage.py", "run_huey", "--huey-verbose"]
1 change: 1 addition & 0 deletions docker/constraints.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gunicorn==22.0.0
1 change: 1 addition & 0 deletions docker/database/01_continue_on_error.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
psql+=( -v ON_ERROR_STOP=0 )
6 changes: 6 additions & 0 deletions docker/database/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# database

The container runs PostgreSQL server.
Any `.sql`, `.sql.gz` or `.sh` script in this folder will be executed on database container creation.

Ask DevOps team to provide `initdb.sql.gz` file and place it into `docker/database` directory
21 changes: 21 additions & 0 deletions docker/database/run-after-init-script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash

set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
DO \$\$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='wagtailcore_site') THEN
UPDATE wagtailcore_site SET
hostname = '${WAGTAIL_SITE_HOSTNAME-localhost}',
port = '${WAGTAIL_SITE_PORT-80}',
site_name = '${WAGTAIL_SITE_NAME-Dev}'
WHERE is_default_site = TRUE;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='accounts_user') THEN
UPDATE accounts_user SET
password = '${WAGTAIL_USER_PASSWORD-pbkdf2_sha256\$36000\$Q6T4uYTWfQnP\$pErPo1iWfTDAHTRZxC+aboPjo3NzIrR0Ks9x521APAg=}';
END IF;
END
\$\$;
EOSQL
17 changes: 17 additions & 0 deletions docker/gunicorn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
accesslog = "-"
errorlog = "-"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" "%({X-Forwarded-For}i)s"'
capture_output = True
forwarded_allow_ips = "*"
secure_scheme_headers = {"X-CLOUDFRONT": "yes"}
workers = 2
worker_class = "gthread"
threads = 4
bind = ":8000"
keep_alive = 75
chdir = "/app"

# Obfuscate the Server header (to the md5sum of "Springload")
import gunicorn # noqa E402

gunicorn.SERVER_SOFTWARE = "04e96149a2f64d6135c82d199ab62122"
3 changes: 3 additions & 0 deletions docker/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# WebServer
-c constraints.txt
gunicorn~=22.0.0
2 changes: 1 addition & 1 deletion requirements/prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ bleach
wagtail-autocomplete
wagtailcodeblock
wagtail_modeladmin>=1.0,<2.0
django-split-settings
django-split-settings

0 comments on commit 558e83f

Please sign in to comment.