Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
139 lines (100 loc) · 4.42 KB
/
Copy pathDockerfile
File metadata and controls
139 lines (100 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# syntax=docker/dockerfile:1
# Keep this syntax directive! It's used to enable Docker BuildKit
# Based on: https://gist.github.com/usr-ein/c42d98abca3cb4632ab0c2c6aff8c88a
##################################################
# Stage 1 - Build Frontend
# Pull base image
FROM node:26-alpine AS frontend-build
# pnpm store location (shared with the BuildKit cache mount below)
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# Set work directory
WORKDIR /app/frontend
# Copy manifest, pnpm lockfile and workspace config to Docker environment
COPY ./frontend/package.json ./frontend/pnpm-lock.yaml ./frontend/pnpm-workspace.yaml /app/frontend/
# Install the pnpm version from packageManager and all required node packages
RUN npm install -g "$(node -p "require('./package.json').packageManager")"
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Copy everything over to Docker environment
COPY ./frontend /app/frontend
# Build the frontend; the Sentry plugin injects the release and, given the
# token, uploads the source maps, which the image then leaves out
ARG VERSION
RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN,env=SENTRY_AUTH_TOKEN \
pnpm run build && find build -name '*.map' -delete
##################################################
# Stage 2 - Backend base
# Pull base image
FROM python:3.15-alpine AS backend-base
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
\
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=0 \
\
PYSETUP_PATH="/opt/pysetup" \
VIRTUAL_ENV="/opt/pysetup/.venv"
# Add Venv to Path
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
##################################################
# Stage 3 - Backend builder
# Use backend-base image as base
FROM backend-base AS backend-builder
# Install build dependencies
RUN apk update && apk add postgresql-dev gcc musl-dev python3-dev
# Install uv
COPY --from=ghcr.io/astral-sh/uv:0.13.7 /uv /bin/uv
# Set work directory
WORKDIR $PYSETUP_PATH
# Copy project requirement files here to ensure they will be cached
COPY backend/uv.lock $PYSETUP_PATH
COPY backend/pyproject.toml $PYSETUP_PATH
# Install runtime dependencies
# The --mount will mount the buildx cache directory to where
# uv stores its cache so that it can re-use it
RUN --mount=type=cache,target=/root/.cache \
uv sync --locked --no-default-groups --group prod
##################################################
# Stage 4 - The Production Environment
# Use backend-base image as base
FROM backend-base AS request-manager-production
# Fixed non-root UID/GID; k8s runAsNonRoot cannot verify a named user.
# High UID so it maps to no host account if the container escapes.
RUN addgroup -S -g 65532 appgroup && adduser -S -u 65532 -G appgroup appuser
# Copy built runtime dependencies from builder container
COPY --from=backend-builder $PYSETUP_PATH $PYSETUP_PATH
# Install runtime dependency for psycopg[c]
RUN apk update && apk add --no-cache libpq
# Copy everything over to Docker environment
COPY ./backend /app/backend
# Copy built frontend assets
RUN mkdir -p /app/frontend/build
COPY --from=frontend-build /app/frontend/build /app/frontend/build-temp
# Have to move all static files other than index.html to root/ for whitenoise middleware
WORKDIR /app/frontend
RUN mkdir build/root && mv build-temp/index.html build/index.html && mv build-temp/static build/static && mv build-temp/* build/root && rm -r build-temp
# Change the owner of all files to the app user
RUN chown -R appuser:appgroup /app
# Change to the app user
LABEL image.uid="65532" image.gid="65532"
USER 65532
# Collect static files
WORKDIR /app/backend
RUN python manage.py collectstatic --no-input --clear --settings=core.settings.base
# Open port
EXPOSE 8000
# Copy and run entrypoint.sh (make sure line endings are UNIX style)
COPY --chown=appuser:appgroup ./docker-entrypoint.sh /app/entrypoint.sh
RUN dos2unix /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
# Set health check
HEALTHCHECK --start-period=20s --interval=30s --retries=5 --timeout=30s \
CMD python manage.py health_check readyz --no-http
# Release reported to Sentry, picked up by sentry_sdk from the environment.
# Set late: it changes every build and invalidates the cache of later layers.
ARG VERSION
ENV SENTRY_RELEASE=${VERSION:+request-manager@$VERSION}
# Start the server
CMD ["gunicorn", "--bind=0.0.0.0:8000", "--workers=5", "--threads=2", "--timeout=60", "--graceful-timeout=30", "core.wsgi"]