Skip to content

Commit

Permalink
Merge pull request #1 from PieterjanMontens/master
Browse files Browse the repository at this point in the history
Adding full docker development (& prod) files, including docker-compose, and updating to 12-factor
  • Loading branch information
avdempsey authored Apr 27, 2022
2 parents d4a460b + 3e2d843 commit 351389e
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 28 deletions.
77 changes: 77 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Multi-stage unique docker build script, provides both dev & prod environments

# ----------------------------------------------------
# Base-image
# ----------------------------------------------------
FROM python:3.9-slim-buster as common-base
# Django directions: https://blog.ploetzli.ch/2020/efficient-multi-stage-build-django-docker/
# Pip on docker : https://pythonspeed.com/articles/multi-stage-docker-python/
# https://blog.mikesir87.io/2018/07/leveraging-multi-stage-builds-single-dockerfile-dev-prod/
# https://pythonspeed.com/articles/base-image-python-docker-images/

# Default environment: Dev
ARG ENV=dev

ENV PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100

ENV HOST=0.0.0.0 \
PORT=8000

WORKDIR /app

COPY ./docker/install-packages.sh .
RUN ./install-packages.sh

# ----------------------------------------------------
# Install dependencies
# ----------------------------------------------------
FROM common-base AS dependencies
ENV PATH="/opt/venv/bin:$PATH"

# apt-get install build-essential -y
COPY requirements.txt /app/

RUN pip install --target /opt/packages -r requirements.txt

# ----------------------------------------------------
# Copy project
# ----------------------------------------------------
FROM common-base AS app-run
COPY --from=dependencies /opt/packages /opt/packages
ENV PYTHONPATH "${PYTHONPATH}:/opt/packages"
# ENV PYTHONPATH="$PYTHONPATH:/app/lemarche:/app/config"
COPY ./ark ./ark
COPY ./ark_import ./ark_import
COPY ./arklet ./arklet
COPY ./manage.py ./manage.py
COPY ./docker/entrypoint.sh ./entrypoint.sh

# ----------------------------------------------------
# Run Dev
# ----------------------------------------------------
FROM app-run AS dev
ENV ENV="dev" \
ARKLET_DEBUG="True"

CMD ["bash"]

# ----------------------------------------------------
# Run Prod
# ----------------------------------------------------
FROM app-run AS prod
ENV ENV="prod" \
ARKLET_DEBUG="False"

CMD ["./entrypoint.sh"]

# # For some _real_ performance, at cost of ease of use:
# FROM python:3.9-alpine as prod
# ENV PATH="/opt/venv/bin:$PATH"
# COPY . .
# RUN apk add python3-dev build-base linux-headers pcre-dev
# RUN pip install uwsgi
79 changes: 74 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ See https://arks.org/
## What is Arklet?
Arklet is a Python Django application for minting, binding, and resolving ARKs. It is intended to follow best practices set out by https://arks.org/.

## Running Locally with Postgres
## Running

Create the default config YAML file: **/etc/arklet.yml**
### Locally with Postgres

- Set a value for **ARKLET_DJANGO_SECRET_KEY**.
- For local development, set **ARKLET_DEBUG** to True.
Create the default `.env` file in the project's root directory

```
# /!\ Set your own secret key /!\
ARKLET_DJANGO_SECRET_KEY=[YOUR_SECRET]
# For local development, set to True
ARKLET_DEBUG=True
```

Run Postgres, install into virtual environment, and start the app:
```
Expand All @@ -29,11 +36,73 @@ python manage.py createsuperuser
python manage.py runserver
```

### Separate dockers
Using docker, we can use a [this provided](./docker/env.docker.local) config file.

See above for running PostgreSQL, and run the **Arklet** docker as follows (in *bash*):
```
docker build \
--target dev \
-t "arklet" -f ./Dockerfile . \
--build-arg ENV=DEV \
&& docker run --rm -it \
-p 8000:8000 \
--env-file=./docker/env.docker.local \
-e ARKLETDEBUG="true" \
--name arklet \
-v `pwd`/ark:/app/ark \
-v `pwd`/ark_import:/app/ark_import \
-v `pwd`/arklet:/app/arklet \
arklet
```

### With docker-compose
Using the provided `docker-compose.yml` with default settings in the [docker
configuration directory](./docker) :

```
docker-compose up
```

By default, the folders `ark`, `ark_import` and `arklet` are mounted in the
container. Should you wish to attach a console to the `arklet` container (needed
to create the django superuser) :
```
# In another shell
docker exec -it arklet_django /bin/bash
# You're now in the docker container
./manage.py createsuperuser
```

### First steps
Create your first NAAN, Key, and Shoulder in the admin:
127.0.0.1:8000/admin

And by the way, you now host a working ARK resolver! You can already
try the following ones :
- [http://127.0.0.1:8000/ark:/13960/t5n960f7n](http://127.0.0.1:8000/ark:/13960/t5n960f7n)
- [http://127.0.0.1:8000/ark:/67375/C0X-SPWFRSGR-N](http://127.0.0.1:8000/ark:/67375/C0X-SPWFRSGR-N)
- [http://127.0.0.1:8000/ark:/12148/bpt6k65358454](http://127.0.0.1:8000/ark:/12148/bpt6k65358454)
- ...

Happy minting, binding, and resolving!

## Configuration Options

See arklet/settings.py for the full list of options to put in your config file.
See arklet/settings.py for the full list of options to put in your config file.

## Deploying
### With docker
Using the provided Dockerfile (is you wish to set a build target, use `prod`,
but being the default target you can skip this), provide the following values
in your environment:

- ARKLET_DJANGO_SECRET_KEY=[YOUR_SECRET]
- ARKLET_DEBUG=False
- ARKLET_HOST=0.0.0.0
- ARKLET_PORT=[Port of choice]
- ARKLET_POSTGRES_NAME=[DB NAME]
- ARKLET_POSTGRES_USER=[DB USER]
- ARKLET_POSTGRES_PASSWORD=[DB PASS]
- ARKLET_POSTGRES_HOST=[DB HOST]
- ARKLET_POSTGRES_PORT=[DB PORT]
11 changes: 5 additions & 6 deletions ark/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,9 @@ def resolve_ark(request, ark: str):
return HttpResponseRedirect(ark.url)
except Ark.DoesNotExist:
try:
naan = Naan.objects.get(naan=naan)
resolver = naan.url or "https://n2t.net"
# TODO: more robust resolver URL creation
return HttpResponseRedirect(f"{resolver}/ark:/{naan.naan}/{assigned_name}")
naan_obj = Naan.objects.get(naan=naan)
return HttpResponseRedirect(f"{naan_obj.url}/ark:/{naan_obj.naan}/{assigned_name}")
except Naan.DoesNotExist:
# TODO: make nice page saying we don't know about the ARK or NAAN
raise Http404
resolver = "https://n2t.net"
# TODO: more robust resolver URL creation
return HttpResponseRedirect(f"{resolver}/ark:/{naan}/{assigned_name}")
45 changes: 28 additions & 17 deletions arklet/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,22 @@
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
import yaml


with open(os.environ.get("ARKLET_CONF", "/etc/arklet.yml")) as f:
conf = yaml.safe_load(f)

import environ

env = environ.Env(
# set default values
ARKLET_HOST=(str, "127.0.0.1"),
ARKLET_DEBUG=(bool, False),
ARKLET_POSTGRES_NAME=(str, "arklet"),
ARKLET_POSTGRES_HOST=(str, "127.0.0.1"),
ARKLET_POSTGRES_PORT=(str, "5432"),
ARKLET_POSTGRES_USER=(str, "arklet"),
ARKLET_POSTGRES_PASSWORD=(str, "arklet"),
ARKLET_SENTRY_DSN=(str, ""),
ARKLET_SENTRY_TRANSACTIONS_PER_TRACE=(int, 1),
ARKLET_STATIC_ROOT=(str, "static"),
ARKLET_MEDIA_ROOT=(str, "media"),
)
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

Expand All @@ -29,13 +40,13 @@
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = conf["ARKLET_DJANGO_SECRET_KEY"] # Intentionally no default value
SECRET_KEY = env.str("ARKLET_DJANGO_SECRET_KEY") # Intentionally no default value

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = conf.get("ARKLET_DEBUG", False)
DEBUG = env("ARKLET_DEBUG")

ALLOWED_HOSTS = [
conf.get("ARKLET_HOST", "127.0.0.1"),
env("ARKLET_HOST"),
"wbgrp-svc302.us.archive.org",
"qa-ark.archive.org",
"ark.archive.org",
Expand Down Expand Up @@ -107,11 +118,11 @@
# "ENGINE": "django.db.backends.sqlite3",
# "NAME": BASE_DIR / "db.sqlite3",
"ENGINE": "django.db.backends.postgresql",
"NAME": conf.get("ARKLET_POSTGRES_NAME", "arklet"),
"HOST": conf.get("ARKLET_POSTGRES_HOST", "127.0.0.1"),
"PORT": conf.get("ARKLET_POSTGRES_PORT", "5432"),
"USER": conf.get("ARKLET_POSTGRES_USER", "arklet"),
"PASSWORD": conf.get("ARKLET_POSTGRES_PASSWORD", "arklet"),
"NAME": env("ARKLET_POSTGRES_NAME"),
"HOST": env("ARKLET_POSTGRES_HOST"),
"PORT": env("ARKLET_POSTGRES_PORT"),
"USER": env("ARKLET_POSTGRES_USER"),
"PASSWORD": env("ARKLET_POSTGRES_PASSWORD"),
"DISABLE_SERVER_SIDE_CURSORS": True,
}
}
Expand Down Expand Up @@ -158,17 +169,17 @@

STATIC_URL = "/static/"

STATIC_ROOT = conf.get("ARKLET_STATIC_ROOT")
STATIC_ROOT = env.str("ARKLET_STATIC_ROOT")

MEDIA_ROOT = conf.get("ARKLET_MEDIA_ROOT")
MEDIA_ROOT = env.str("ARKLET_MEDIA_ROOT")

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

SENTRY_DSN = conf.get("ARKLET_SENTRY_DSN", "")
SENTRY_SAMPLE_RATE = 1 / int(conf.get("ARKLET_SENTRY_TRANSACTIONS_PER_TRACE", 1))
SENTRY_DSN = env("ARKLET_SENTRY_DSN")
SENTRY_SAMPLE_RATE = 1 / int(env("ARKLET_SENTRY_TRANSACTIONS_PER_TRACE"))
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[DjangoIntegration()],
Expand Down
35 changes: 35 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: "3.7"

services:
postgres:
container_name: arkled_db
image: postgres:14-alpine
env_file:
- ./docker/env.docker.local
volumes:
- postgres:/var/lib/postgresql/data
restart: always
ports:
- "5432:5432"

arklet:
container_name: arklet_django
restart: always
build:
context: .
target: dev
dockerfile: ./Dockerfile
command: /app/entrypoint.sh
volumes:
- ./ark:/app/ark
- ./ark_import:/app/ark_import
- ./arklet:/app/arklet
env_file:
- ./docker/env.docker.local
ports:
- "8000:8000"
depends_on:
- postgres

volumes:
postgres:
Loading

0 comments on commit 351389e

Please sign in to comment.