Skip to content

Commit 1040748

Browse files
authored
feat: switched to alpine docker images (#146)
1 parent 15bf929 commit 1040748

File tree

5 files changed

+137
-49
lines changed

5 files changed

+137
-49
lines changed

docker/admin.Dockerfile

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,65 @@
1-
FROM python:3.11-slim
1+
############################
2+
# Builder stage
3+
############################
4+
FROM python:3.11-alpine AS builder
25

3-
ARG GIT_COMMIT
4-
ENV GIT_COMMIT=$GIT_COMMIT
6+
ENV PYTHONDONTWRITEBYTECODE=1 \
7+
PYTHONUNBUFFERED=1 \
8+
APP_HOME=/home/app
59

6-
# This prevents Python from writing out pyc files
7-
ENV PYTHONDONTWRITEBYTECODE=1
8-
# This keeps Python from buffering stdin/stdout
9-
ENV PYTHONUNBUFFERED=1
10-
ENV PIP_ARGS="--progress-bar off --no-cache-dir"
11-
12-
ENV APP_HOME=/home/app
10+
RUN pip install --progress-bar off --no-cache-dir poetry==2.2.1
1311

1412
WORKDIR $APP_HOME
1513

16-
# Install dependencies using poetry
17-
RUN pip install $PIP_ARGS "poetry==2.1.1"
18-
RUN poetry self add poetry-plugin-export
19-
COPY pyproject.toml .
20-
COPY poetry.lock .
21-
RUN poetry export -f requirements.txt --without-hashes | pip install $PIP_ARGS -r /dev/stdin
14+
# Install dependencies first to leverage Docker layer caching
15+
COPY pyproject.toml poetry.lock poetry.toml ./
16+
RUN poetry install --no-interaction --no-ansi --no-cache --no-root \
17+
--no-directory --only main
2218

23-
# Copy source code
19+
# Copy source code and install the project
2420
COPY ./alembic.ini $APP_HOME/alembic.ini
2521
COPY ./statgpt/admin $APP_HOME/statgpt/admin
2622
COPY ./statgpt/common $APP_HOME/statgpt/common
23+
RUN poetry install --no-interaction --no-ansi --no-cache --no-root --only main
24+
25+
############################
26+
# Runtime stage
27+
############################
28+
FROM python:3.11-alpine AS server
2729

28-
# create the app user and chown workdir to the app user
29-
RUN adduser -u 5678 --system --disabled-password --gecos "" app && chown -R app $APP_HOME
30-
USER app
30+
# Security patches (consolidated into single layer)
31+
# CVE-2023-52425 (libexpat), CVE-2025-6965 (sqlite-libs), libcrypto3/libssl3
32+
RUN apk update && apk upgrade --no-cache \
33+
libcrypto3 libssl3 libexpat sqlite-libs \
34+
&& apk add --no-cache ca-certificates \
35+
&& update-ca-certificates \
36+
&& rm -rf /var/cache/apk/*
37+
38+
# CVE-2026-23949 (setuptools), CVE-2026-24049 (wheel)
39+
RUN pip install --no-cache-dir setuptools==80.10.2 wheel==0.46.2
40+
41+
ENV PYTHONDONTWRITEBYTECODE=1 \
42+
PYTHONUNBUFFERED=1 \
43+
APP_HOME=/home/app \
44+
WEB_CONCURRENCY=1 \
45+
PYDANTIC_V2=True
46+
47+
WORKDIR $APP_HOME
3148

32-
ENV WEB_CONCURRENCY=1
33-
ENV PYDANTIC_V2=True
49+
# Create non-root user and copy built application
50+
RUN adduser -u 1001 --disabled-password --gecos "" appuser
51+
COPY --chown=appuser --from=builder $APP_HOME .
52+
53+
COPY --chmod=755 ./docker/scripts/admin_docker_entrypoint.sh /docker_entrypoint.sh
54+
55+
EXPOSE 8000
56+
57+
USER appuser
58+
59+
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=6 \
60+
CMD wget -q --spider -T 3 http://localhost:8000/health || exit 1
61+
62+
ARG GIT_COMMIT
63+
ENV GIT_COMMIT=$GIT_COMMIT
3464

35-
CMD ["sh", "statgpt/admin/admin.sh"]
65+
ENTRYPOINT ["/docker_entrypoint.sh"]

docker/chat.Dockerfile

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,64 @@
1-
FROM python:3.11-slim
1+
############################
2+
# Builder stage
3+
############################
4+
FROM python:3.11-alpine AS builder
25

3-
ARG GIT_COMMIT
4-
ENV GIT_COMMIT=$GIT_COMMIT
6+
ENV PYTHONDONTWRITEBYTECODE=1 \
7+
PYTHONUNBUFFERED=1 \
8+
APP_HOME=/home/app
59

6-
# This prevents Python from writing out pyc files
7-
ENV PYTHONDONTWRITEBYTECODE=1
8-
# This keeps Python from buffering stdin/stdout
9-
ENV PYTHONUNBUFFERED=1
10-
ENV PIP_ARGS="--progress-bar off --no-cache-dir"
11-
12-
ENV APP_HOME=/home/app
10+
RUN pip install --progress-bar off --no-cache-dir poetry==2.2.1
1311

1412
WORKDIR $APP_HOME
1513

16-
# Install dependencies using poetry
17-
RUN pip install $PIP_ARGS "poetry==2.1.1"
18-
RUN poetry self add poetry-plugin-export
19-
COPY pyproject.toml .
20-
COPY poetry.lock .
21-
RUN poetry export -f requirements.txt --without-hashes | pip install $PIP_ARGS -r /dev/stdin
14+
# Install dependencies first to leverage Docker layer caching
15+
COPY pyproject.toml poetry.lock poetry.toml ./
16+
RUN poetry install --no-interaction --no-ansi --no-cache --no-root \
17+
--no-directory --only main
2218

23-
# Copy source code
19+
# Copy source code and install the project
2420
COPY ./statgpt/app $APP_HOME/statgpt/app
2521
COPY ./statgpt/common $APP_HOME/statgpt/common
22+
RUN poetry install --no-interaction --no-ansi --no-cache --no-root --only main
23+
24+
############################
25+
# Runtime stage
26+
############################
27+
FROM python:3.11-alpine AS server
28+
29+
# Security patches (consolidated into single layer)
30+
# CVE-2023-52425 (libexpat), CVE-2025-6965 (sqlite-libs), libcrypto3/libssl3
31+
RUN apk update && apk upgrade --no-cache \
32+
libcrypto3 libssl3 libexpat sqlite-libs \
33+
&& apk add --no-cache ca-certificates \
34+
&& update-ca-certificates \
35+
&& rm -rf /var/cache/apk/*
36+
37+
# CVE-2026-23949 (setuptools), CVE-2026-24049 (wheel)
38+
RUN pip install --no-cache-dir setuptools==80.10.2 wheel==0.46.2
39+
40+
ENV PYTHONDONTWRITEBYTECODE=1 \
41+
PYTHONUNBUFFERED=1 \
42+
APP_HOME=/home/app \
43+
WEB_CONCURRENCY=1 \
44+
PYDANTIC_V2=True
2645

27-
# create the app user and chown workdir to the app user
28-
RUN adduser -u 5678 --system --disabled-password --gecos "" app && chown -R app $APP_HOME
29-
USER app
46+
WORKDIR $APP_HOME
47+
48+
# Create non-root user and copy built application
49+
RUN adduser -u 1001 --disabled-password --gecos "" appuser
50+
COPY --chown=appuser --from=builder $APP_HOME .
3051

31-
ENV WEB_CONCURRENCY=1
32-
ENV PYDANTIC_V2=True
52+
COPY --chmod=755 ./docker/scripts/chat_docker_entrypoint.sh /docker_entrypoint.sh
3353

3454
EXPOSE 5000
3555

36-
CMD ["uvicorn", "statgpt.app.app:app", "--host", "0.0.0.0", "--port", "5000", "--lifespan", "on"]
56+
USER appuser
57+
58+
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=6 \
59+
CMD wget -q --spider -T 3 http://localhost:5000/health || exit 1
60+
61+
ARG GIT_COMMIT
62+
ENV GIT_COMMIT=$GIT_COMMIT
63+
64+
ENTRYPOINT ["/docker_entrypoint.sh"]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
export VIRTUAL_ENV="$PWD/.venv"
5+
export PATH="$VIRTUAL_ENV/bin:$PATH"
6+
7+
# If no args passed to `docker run`, then run the admin service
8+
if [ $# -lt 1 ]; then
9+
exec sh statgpt/admin/admin.sh
10+
fi
11+
12+
# Otherwise, run the user's command, for example a `sh` shell to explore the container
13+
exec "$@"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
export VIRTUAL_ENV="$PWD/.venv"
5+
export PATH="$VIRTUAL_ENV/bin:$PATH"
6+
7+
# If no args passed to `docker run`, then we assume the user is calling the service
8+
if [ $# -lt 1 ]; then
9+
exec uvicorn statgpt.app.app:app \
10+
--host 0.0.0.0 \
11+
--port 5000 \
12+
--lifespan on \
13+
--timeout-keep-alive "${TIMEOUT_KEEP_ALIVE:-5}"
14+
fi
15+
16+
# Otherwise, we assume the user wants to run his own process, for example a `sh` shell to explore the container
17+
exec "$@"

statgpt/admin/admin.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#!/bin/bash
1+
#!/bin/sh
22

33
# IMPORTANT: Do not delete this line!
44
# These settings make the script fail fast and loud on common errors:
5-
# undefined variables, command failures, and pipeline failures
6-
set -Eeuo pipefail
5+
# undefined variables and command failures
6+
set -eu
77

88
echo "ADMIN_MODE = '${ADMIN_MODE:-}'"
99

0 commit comments

Comments
 (0)