Skip to content

Commit 1b1c1e9

Browse files
authored
Overhaul the Docker setup (#117)
This patch rewrites the Docker setup used to create a deployable container running a production LNT server. - Split the Docker image into two, a basic image with LNT installed and an image with the actual production server as an entry point. - Relocate all of the Docker-related files under docker/. - Document the various Docker-related files. - Improve input validation in the docker entrypoint. - Using proper Docker secrets to transmit sensitive information to the LNT webserver entry point and the Postgres database. - Update to Postgres 18. We might as well use the latest version available since we're standing this up from scratch. With this setup, I am able to spin up a local LNT server instance with: docker compose --file docker/compose.yaml --env-file <secrets> up An example of a secrets file would be LNT_DB_PASSWORD=foo LNT_AUTH_TOKEN=bar
1 parent 3af5368 commit 1b1c1e9

File tree

8 files changed

+136
-72
lines changed

8 files changed

+136
-72
lines changed

.github/workflows/build-docker.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ jobs:
3232
with:
3333
push: ${{ github.ref == 'refs/heads/main' }} # only push to ghcr.io on the main branch
3434
tags: ghcr.io/${{github.repository}}:latest
35+
file: docker/lnt.dockerfile
3536
context: . # use the current directory as context, as checked out by actions/checkout -- needed for setuptools_scm

Dockerfile

Lines changed: 0 additions & 23 deletions
This file was deleted.

docker-compose.yaml

Lines changed: 0 additions & 35 deletions
This file was deleted.

docker/compose.yaml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# This file composes a full service running LNT. A LNT service is comprised
2+
# of a container running a Postgres database and a container running a
3+
# production LNT webserver.
4+
#
5+
# In order to build the full service, some secrets are required. They are taken
6+
# as environment variables, which means they can be stored in a file and included
7+
# via `--env-file <secrets-file>`. These environment variables are:
8+
#
9+
# LNT_DB_PASSWORD
10+
# The password to use for logging into the database.
11+
#
12+
# LNT_AUTH_TOKEN
13+
# The authentication token used to require authentication to
14+
# perform destructive actions.
15+
16+
name: llvm-lnt
17+
18+
services:
19+
webserver:
20+
container_name: webserver
21+
build:
22+
context: ../
23+
dockerfile: docker/lnt.dockerfile
24+
environment:
25+
- DB_USER=lntuser
26+
- DB_HOST=dbserver
27+
- DB_NAME=lnt.db
28+
- DB_PASSWORD_FILE=/run/secrets/lnt-db-password
29+
- AUTH_TOKEN_FILE=/run/secrets/lnt-auth-token
30+
secrets:
31+
- lnt-db-password
32+
- lnt-auth-token
33+
depends_on:
34+
- db
35+
deploy:
36+
restart_policy:
37+
condition: on-failure
38+
ports:
39+
- "8000:8000"
40+
volumes:
41+
- instance:/var/lib/lnt
42+
- logs:/var/log/lnt
43+
44+
db:
45+
container_name: dbserver
46+
image: docker.io/postgres:13-alpine
47+
environment:
48+
- POSTGRES_PASSWORD_FILE=/run/secrets/lnt-db-password
49+
- POSTGRES_USER=lntuser
50+
- POSTGRES_DB=lnt.db
51+
secrets:
52+
- lnt-db-password
53+
volumes:
54+
- database:/var/lib/postgresql
55+
56+
volumes:
57+
instance:
58+
logs:
59+
database:
60+
61+
secrets:
62+
lnt-db-password:
63+
environment: "LNT_DB_PASSWORD"
64+
lnt-auth-token:
65+
environment: "LNT_AUTH_TOKEN"

docker/docker-entrypoint.sh

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
#!/bin/sh
22

3-
DB_PATH=${DB_ENGINE:-postgresql}://${DB_USER:-lntuser}:${DB_PWD:?}@${DB_HOST:?}
4-
DB_BASE=${DB_BASE:-lnt}
3+
set -u
54

6-
if [ ! -r /etc/lnt/lnt.cfg ]; then
7-
DB_BASE_PATH="${DB_PATH}/${DB_BASE}" wait_db
5+
password="$(cat ${DB_PASSWORD_FILE})"
6+
token="$(cat ${AUTH_TOKEN_FILE})"
7+
DB_PATH="postgres://${DB_USER}:${password}@${DB_HOST}"
8+
9+
# Set up the instance the first time this gets run.
10+
if [ ! -e /var/lib/lnt/instance/lnt.cfg ]; then
11+
lnt-wait-db "${DB_PATH}/${DB_NAME}"
812
lnt create /var/lib/lnt/instance \
9-
--config /etc/lnt/lnt.cfg \
1013
--wsgi lnt_wsgi.py \
1114
--tmp-dir /tmp/lnt \
1215
--db-dir "${DB_PATH}" \
13-
--default-db "${DB_BASE}"
14-
if [ -n "${LNT_AUTH_TOKEN:-}" ]; then
15-
sed -i "s/# \(api_auth_token =\).*/\1 '${LNT_AUTH_TOKEN}'/" /etc/lnt/lnt.cfg
16-
fi
16+
--default-db "${DB_NAME}"
17+
sed -i "s/# \(api_auth_token =\).*/\1 '${token}'/" /var/lib/lnt/instance/lnt.cfg
1718
fi
1819

20+
# Run the server under gunicorn.
1921
cd /var/lib/lnt/instance
2022
exec gunicorn lnt_wsgi:application \
2123
--bind 0.0.0.0:8000 \
@@ -24,4 +26,4 @@ exec gunicorn lnt_wsgi:application \
2426
--name lnt_server \
2527
--log-file /var/log/lnt/lnt.log \
2628
--access-logfile /var/log/lnt/gunicorn_access.log \
27-
--max-requests 250000 "$@"
29+
--max-requests 250000

docker/wait_db renamed to docker/lnt-wait-db

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/usr/bin/env python
22

3-
import os
4-
from sqlalchemy import create_engine
3+
import sys
4+
import sqlalchemy
55

6-
db_base_path = os.environ['DB_BASE_PATH']
6+
if len(sys.argv) != 2:
7+
raise "Missing db path for lnt-wait-db"
78

8-
engine = create_engine(db_base_path)
9+
db = sys.argv[1]
10+
engine = sqlalchemy.create_engine(db)
911
started = False
1012

1113
while not started:

docker/lnt.dockerfile

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This Dockerfile defines an image that contains a production LNT server.
2+
# This image is intended to be built from a Docker Compose file, as it
3+
# requires additional information passed as environment variables:
4+
#
5+
# DB_USER
6+
# The username to use for logging into the database.
7+
#
8+
# DB_HOST
9+
# The hostname to use to access the database.
10+
#
11+
# DB_NAME
12+
# The name of the database on the server.
13+
#
14+
# DB_PASSWORD_FILE
15+
# File containing the password to use for logging into the database.
16+
#
17+
# AUTH_TOKEN_FILE
18+
# File containing the authentication token used to require authentication
19+
# to perform destructive actions.
20+
21+
FROM python:3.10-alpine
22+
23+
# Install dependencies
24+
RUN apk update \
25+
&& apk add --no-cache --virtual .build-deps git g++ postgresql-dev yaml-dev \
26+
&& apk add --no-cache libpq
27+
28+
# Install LNT itself, without leaving behind any sources inside the image.
29+
RUN --mount=type=bind,source=.,target=./lnt-source \
30+
cp -R lnt-source /tmp/lnt-src && \
31+
cd /tmp/lnt-src && \
32+
pip3 install -r requirements.server.txt && apk --purge del .build-deps && \
33+
rm -rf /tmp/lnt-src
34+
35+
# Prepare volumes that will be used by the server
36+
VOLUME /var/lib/lnt /var/log/lnt
37+
38+
# Set up the actual entrypoint that gets run when the container starts.
39+
COPY docker/docker-entrypoint.sh docker/lnt-wait-db /usr/local/bin/
40+
ENTRYPOINT ["docker-entrypoint.sh"]
41+
EXPOSE 8000

docs/intro.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,14 @@ To install the extra packages for the server config::
110110
gunicorn app_wrapper:app --bind 0.0.0.0:8000 --workers 8 --timeout 300 --name lnt_server --log-file /var/log/lnt/lnt.log --access-logfile /var/log/lnt/gunicorn_access.log --max-requests 250000
111111

112112

113+
Running a LNT Server via Docker
114+
-------------------------------
115+
116+
We provide a Docker Compose setup with Docker containers that can be used to
117+
easily bring up a fully working production server within minutes. The container
118+
can be built and run with::
119+
120+
docker compose --file docker/compose.yaml --env-file <secrets> up
121+
122+
``<secrets>`` should be the path to a file containing environment variables
123+
required by the containers. Please refer to the Docker Compose file for details.

0 commit comments

Comments
 (0)