Skip to content

Commit bf32c5c

Browse files
committed
Overhaul the Docker setup
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 fed3b58 commit bf32c5c

File tree

8 files changed

+170
-72
lines changed

8 files changed

+170
-72
lines changed

.github/workflows/build-docker.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ jobs:
3232
with:
3333
push: ${{ startsWith(github.ref, 'refs/tags/') }} # only push to ghcr.io on tags
3434
tags: ghcr.io/${{github.repository}}:latest
35+
file: docker/lnt.dockerfile
36+
target: llvm-lnt
3537
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: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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-prod
17+
18+
services:
19+
webserver:
20+
container_name: webserver
21+
build:
22+
context: ../
23+
dockerfile: docker/lnt.dockerfile
24+
target: llvm-lnt-prod
25+
args:
26+
DB_USER: lntuser
27+
DB_HOST: dbserver
28+
DB_NAME: lnt.db
29+
secrets:
30+
- lnt-db-password
31+
- lnt-auth-token
32+
depends_on:
33+
- db
34+
deploy:
35+
restart_policy:
36+
condition: on-failure
37+
ports:
38+
- "8000:8000"
39+
volumes:
40+
- instance:/var/lib/lnt
41+
- logs:/var/log/lnt
42+
43+
db:
44+
container_name: dbserver
45+
image: docker.io/postgres:18-alpine
46+
environment:
47+
- POSTGRES_PASSWORD_FILE=/run/secrets/lnt-db-password
48+
- POSTGRES_USER=lntuser
49+
- POSTGRES_DB=lnt.db
50+
secrets:
51+
- lnt-db-password
52+
volumes:
53+
- database:/var/lib/postgresql
54+
55+
volumes:
56+
instance:
57+
logs:
58+
database:
59+
60+
secrets:
61+
lnt-db-password:
62+
environment: "LNT_DB_PASSWORD"
63+
lnt-auth-token:
64+
environment: "LNT_AUTH_TOKEN"

docker/docker-entrypoint.sh

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

3-
DB_PATH=${DB_ENGINE:-postgresql}://${DB_USER:-lntuser}:${DB_PWD:?}@${DB_HOST:?}
4-
DB_BASE=${DB_BASE:-lnt}
3+
if [ -z ${DB_USER+x} ]; then
4+
echo "Missing DB_USER environment variable"
5+
exit 1
6+
fi
7+
8+
if [ -z ${DB_HOST+x} ]; then
9+
echo "Missing DB_HOST environment variable"
10+
exit 1
11+
fi
12+
13+
if [ -z ${DB_NAME+x} ]; then
14+
echo "Missing DB_NAME environment variable"
15+
exit 1
16+
fi
17+
18+
if [ ! -f /run/secrets/lnt-db-password ]; then
19+
echo "Missing secret lnt-db-password"
20+
exit 1
21+
fi
22+
DB_PASSWORD="$(cat /run/secrets/lnt-db-password)"
23+
24+
if [ ! -f /run/secrets/lnt-auth-token ]; then
25+
echo "Missing secret lnt-auth-token"
26+
exit 1
27+
fi
28+
AUTH_TOKEN="$(cat /run/secrets/lnt-auth-token)"
29+
30+
DB_PATH="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}"
531

6-
if [ ! -r /etc/lnt/lnt.cfg ]; then
7-
DB_BASE_PATH="${DB_PATH}/${DB_BASE}" wait_db
32+
# Set up the instance the first time this gets run.
33+
if [ ! -e /var/lib/lnt/instance/lnt.cfg ]; then
34+
lnt-wait-db "${DB_PATH}/${DB_NAME}"
835
lnt create /var/lib/lnt/instance \
9-
--config /etc/lnt/lnt.cfg \
1036
--wsgi lnt_wsgi.py \
1137
--tmp-dir /tmp/lnt \
1238
--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
39+
--default-db "${DB_NAME}"
40+
sed -i "s/# \(api_auth_token =\).*/\1 '${AUTH_TOKEN}'/" /var/lib/lnt/instance/lnt.cfg
1741
fi
1842

43+
# Run the server under gunicorn.
1944
cd /var/lib/lnt/instance
2045
exec gunicorn lnt_wsgi:application \
2146
--bind 0.0.0.0:8000 \
@@ -24,4 +49,4 @@ exec gunicorn lnt_wsgi:application \
2449
--name lnt_server \
2550
--log-file /var/log/lnt/lnt.log \
2651
--access-logfile /var/log/lnt/gunicorn_access.log \
27-
--max-requests 250000 "$@"
52+
--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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# This Dockerfile defines a basic 'llvm-lnt' image that contains an installed
2+
# copy of LNT. That image can be built and run with:
3+
#
4+
# $ docker build --file docker/lnt.dockerfile --target llvm-lnt .
5+
# $ docker run -it <sha> /bin/sh
6+
#
7+
# It also defines a 'llvm-lnt-prod' image which is set up to run a production
8+
# LNT server. This image is intended to be built from a Docker Compose file,
9+
# as it requires additional information like secrets and build arguments:
10+
#
11+
# ARG DB_USER
12+
# The username to use for logging into the database.
13+
#
14+
# ARG DB_HOST
15+
# The hostname to use to access the database.
16+
#
17+
# ARG DB_NAME
18+
# The name of the database on the server.
19+
#
20+
# secret: lnt-db-password
21+
# The password to use for logging into the database.
22+
#
23+
# secret: lnt-auth-token
24+
# The authentication token used to require authentication to
25+
# perform destructive actions.
26+
27+
FROM python:3.10-alpine AS llvm-lnt
28+
29+
# Install dependencies
30+
RUN apk update \
31+
&& apk add --no-cache --virtual .build-deps git g++ postgresql-dev yaml-dev \
32+
&& apk add --no-cache libpq
33+
34+
# Install LNT itself
35+
COPY . /var/tmp/lnt
36+
WORKDIR /var/tmp/lnt
37+
RUN pip3 install -r requirements.server.txt && apk --purge del .build-deps
38+
39+
40+
FROM llvm-lnt AS llvm-lnt-prod
41+
42+
# Prepare volumes that will be used by the server
43+
VOLUME /var/lib/lnt /var/log/lnt
44+
45+
# Set up the actual entrypoint that gets run when the container starts.
46+
COPY docker/docker-entrypoint.sh docker/lnt-wait-db /usr/local/bin/
47+
ARG DB_USER DB_HOST DB_NAME
48+
ENV DB_USER=${DB_USER}
49+
ENV DB_HOST=${DB_HOST}
50+
ENV DB_NAME=${DB_NAME}
51+
ENTRYPOINT ["docker-entrypoint.sh"]
52+
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)