|
1 | | -# syntax=docker/dockerfile:1.7.0 |
| 1 | +# syntax=docker/dockerfile:1.7.1 |
2 | 2 |
|
3 | | -# full semver just for python base image |
4 | | -ARG PYTHON_VERSION=3.11.11 |
| 3 | +ARG PYTHON_VERSION=3.12.10 |
5 | 4 |
|
6 | | -FROM python:${PYTHON_VERSION}-slim-bullseye AS builder |
| 5 | +FROM python:${PYTHON_VERSION}-slim-bookworm as builder |
7 | 6 |
|
8 | | -# avoid stuck build due to user prompt |
9 | 7 | ARG DEBIAN_FRONTEND=noninteractive |
10 | 8 |
|
11 | | -# update apt-get repos and install dependencies |
12 | | -RUN apt-get -qq update \ |
| 9 | +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ |
| 10 | + --mount=type=cache,target=/var/lib/apt,sharing=locked \ |
| 11 | + apt-get -qq update \ |
13 | 12 | && apt-get -qq install --no-install-recommends -y \ |
| 13 | + build-essential \ |
| 14 | + ca-certificates \ |
14 | 15 | curl \ |
15 | 16 | gcc \ |
16 | | - libpq-dev \ |
17 | 17 | python3-dev \ |
18 | 18 | && rm -rf /var/lib/apt/lists/* |
19 | 19 |
|
20 | | -# pip env vars |
21 | | -ENV PIP_NO_CACHE_DIR=off |
22 | | -ENV PIP_DISABLE_PIP_VERSION_CHECK=on |
23 | | -ENV PIP_DEFAULT_TIMEOUT=100 |
| 20 | +# venv |
| 21 | +ARG UV_PROJECT_ENVIRONMENT="/opt/venv" |
| 22 | +ENV VENV="${UV_PROJECT_ENVIRONMENT}" |
| 23 | +ENV PATH="$VENV/bin:$PATH" |
24 | 24 |
|
25 | | -# poetry env vars |
26 | | -ENV POETRY_HOME="/opt/poetry" |
27 | | -ENV POETRY_VERSION=1.8.5 |
28 | | -ENV POETRY_VIRTUALENVS_IN_PROJECT=true |
29 | | -ENV POETRY_NO_INTERACTION=1 |
| 25 | +# uv |
| 26 | +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ |
30 | 27 |
|
31 | | -# path |
32 | | -ENV VENV="/opt/venv" |
33 | | -ENV PATH="$POETRY_HOME/bin:$VENV/bin:$PATH" |
| 28 | +ARG WORKDIR="/app" |
| 29 | +WORKDIR $WORKDIR |
34 | 30 |
|
35 | | -# create app directory and set as working directory |
36 | | -WORKDIR /app |
| 31 | +COPY pyproject.toml . |
37 | 32 |
|
38 | | -# copy dependencies |
39 | | -COPY requirements.txt requirements.txt |
| 33 | +# optimize startup time, don't use hardlinks, set cache for buildkit mount, |
| 34 | +# set uv timeout |
| 35 | +ENV UV_COMPILE_BYTECODE=1 |
| 36 | +ENV UV_LINK_MODE=copy |
| 37 | +ENV UV_CACHE_DIR=/opt/uv-cache/ |
| 38 | +ENV UV_HTTP_TIMEOUT=90 |
40 | 39 |
|
41 | | -# install poetry and dependencies |
42 | | -RUN python -m venv $VENV \ |
43 | | - && . "${VENV}/bin/activate" \ |
44 | | - && python -m pip install "poetry==${POETRY_VERSION}" \ |
45 | | - && python -m pip install -r requirements.txt |
| 40 | +RUN --mount=type=cache,target=/opt/uv-cache,sharing=locked \ |
| 41 | + uv venv $UV_PROJECT_ENVIRONMENT \ |
| 42 | + && uv pip install -r pyproject.toml |
46 | 43 |
|
47 | | -FROM python:${PYTHON_VERSION}-slim-bullseye AS dev |
| 44 | +FROM python:${PYTHON_VERSION}-slim-bookworm as deps |
48 | 45 |
|
49 | | -# setup path |
50 | | -ENV VENV="/opt/venv" |
51 | | -ENV PATH="${VENV}/bin:${VENV}/lib/python${PYTHON_VERSION}/site-packages:/usr/local/bin:${HOME}/.local/bin:/bin:/usr/bin:/usr/share/doc:$PATH" |
52 | | - |
53 | | -# standardise on locale, don't generate .pyc, enable tracebacks on seg faults |
54 | | -ENV LANG C.UTF-8 |
55 | | -ENV LC_ALL C.UTF-8 |
56 | | -ENV PYTHONDONTWRITEBYTECODE 1 |
57 | | -ENV PYTHONFAULTHANDLER 1 |
58 | | - |
59 | | -# workers per core |
60 | | -# https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/README.md#web_concurrency |
61 | | -ENV WEB_CONCURRENCY=2 |
62 | | - |
63 | | -# avoid stuck build due to user prompt |
64 | 46 | ARG DEBIAN_FRONTEND=noninteractive |
65 | 47 |
|
66 | | -# install dependencies |
67 | | -RUN apt-get -qq update \ |
| 48 | +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ |
| 49 | + --mount=type=cache,target=/var/lib/apt,sharing=locked \ |
| 50 | + apt-get -qq update \ |
68 | 51 | && apt-get -qq install --no-install-recommends -y \ |
69 | | - bat \ |
70 | | - curl \ |
71 | | - dpkg \ |
72 | | - git \ |
73 | | - iputils-ping \ |
74 | | - lsof \ |
75 | | - make \ |
76 | | - p7zip \ |
77 | | - perl \ |
78 | | - shellcheck \ |
79 | | - sudo \ |
80 | | - tldr \ |
81 | | - tree \ |
| 52 | + libgomp1 \ |
82 | 53 | && rm -rf /var/lib/apt/lists/* |
83 | 54 |
|
84 | | -# setup standard non-root user for use downstream |
| 55 | +FROM deps as runner |
| 56 | + |
| 57 | +ARG WORKDIR="/app" |
| 58 | +WORKDIR $WORKDIR |
| 59 | + |
85 | 60 | ARG USER_NAME=appuser |
86 | 61 | ARG USER_UID=1000 |
87 | 62 | ARG USER_GID=$USER_UID |
88 | 63 |
|
89 | 64 | RUN groupadd --gid $USER_GID $USER_NAME \ |
90 | | - && useradd --uid $USER_UID --gid $USER_GID -m $USER_NAME |
91 | | - |
92 | | -RUN echo "$USER_NAME ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER_NAME \ |
93 | | - && chmod 0440 /etc/sudoers.d/$USER_NAME |
| 65 | + && useradd --uid $USER_UID --gid $USER_GID -m $USER_NAME \ |
| 66 | + && chown -R $USER_NAME:$USER_NAME /app |
94 | 67 |
|
95 | | -# copy virtual environment from builder stage |
96 | | -COPY --from=builder --chown=${USER_NAME}:${USER_GROUP} $VENV $VENV |
97 | | - |
98 | | -# qol: tooling |
99 | | -RUN <<EOF |
100 | | -#!/usr/bin/env bash |
101 | | -# fzf |
102 | | -git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf |
103 | | -yes | ~/.fzf/install |
104 | | -EOF |
105 | | - |
106 | | -# switch to non-root user |
107 | | -USER $USER_NAME |
| 68 | +ARG VENV="/opt/venv" |
| 69 | +ENV PATH=$VENV/bin:$HOME/.local/bin:$PATH |
108 | 70 |
|
109 | | -# qol: .bashrc |
110 | | -RUN tee -a "$HOME/.bashrc" <<"EOF" |
| 71 | +COPY --from=builder \ |
| 72 | + --chown=$USER_NAME:$USER_NAME "$VENV" "$VENV" |
111 | 73 |
|
112 | | -# shared history |
113 | | -HISTFILE=/var/tmp/.bash_history |
114 | | -HISTFILESIZE=100 |
115 | | -HISTSIZE=100 |
| 74 | +COPY --chown=$USER_NAME:$USER_NAME ./app/ ${WORKDIR}/ |
116 | 75 |
|
117 | | -stty -ixon |
118 | | - |
119 | | -# fzf |
120 | | -[ -f ~/.fzf.bash ] && . ~/.fzf.bash |
121 | | - |
122 | | -# asdf |
123 | | -# https://asdf-vm.com/guide/getting-started.html |
124 | | -export ASDF_DIR="$HOME/.asdf" |
125 | | -[[ -f "${ASDF_DIR}/asdf.sh" ]] && . "${ASDF_DIR}/asdf.sh" |
126 | | - |
127 | | -# homebrew |
128 | | -export BREW_PREFIX="/home/linuxbrew/.linuxbrew/bin" |
129 | | -[[ -f "${BREW_PREFIX}/brew" ]] && eval "$(${BREW_PREFIX}/brew shellenv)" |
130 | | - |
131 | | -# aliases |
132 | | -alias ..='cd ../' |
133 | | -alias ...='cd ../../' |
134 | | -alias ll='ls -la --color=auto' |
135 | | - |
136 | | -EOF |
137 | | - |
138 | | -FROM dev AS runner |
139 | | - |
140 | | -# change working directory |
141 | | -WORKDIR /app |
| 76 | +# standardise on locale, don't generate .pyc, enable tracebacks on seg faults |
| 77 | +ENV LANG C.UTF-8 |
| 78 | +ENV LC_ALL C.UTF-8 |
| 79 | +ENV PYTHONDONTWRITEBYTECODE 1 |
| 80 | +ENV PYTHONFAULTHANDLER 1 |
142 | 81 |
|
143 | | -# $PATH |
144 | | -ENV PATH=$VENV_PATH/bin:$HOME/.local/bin:$PATH |
| 82 | +USER $USER_NAME |
145 | 83 |
|
146 | | -# port needed by app |
147 | 84 | EXPOSE 8000 |
148 | 85 |
|
149 | | -# run container indefinitely |
150 | 86 | CMD ["sleep", "infinity"] |
151 | | - |
152 | | -# metadata |
153 | | -LABEL org.opencontainers.image.title="python-class" |
| 87 | +# CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] |
| 88 | +# CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] |
0 commit comments