|
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