File size: 7,381 Bytes
0b4ff34
 
5583f46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b4ff34
 
 
 
 
 
 
 
 
5583f46
 
0b4ff34
 
5583f46
 
 
0b4ff34
 
5583f46
 
 
 
 
 
0b4ff34
 
 
 
 
5583f46
 
 
0b4ff34
 
 
 
 
 
 
5583f46
0b4ff34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5583f46
 
 
 
 
 
 
 
 
 
 
 
0b4ff34
2f1e7ff
0b4ff34
5583f46
 
 
 
 
0b4ff34
 
 
 
 
 
 
 
2f1e7ff
0b4ff34
 
 
 
 
 
 
5583f46
 
 
2f1e7ff
5583f46
a29f872
2f1e7ff
5583f46
 
 
 
 
 
 
 
 
 
 
 
 
 
0b4ff34
 
 
5583f46
 
0b4ff34
 
 
 
 
 
 
 
 
 
 
 
 
 
2f1e7ff
0b4ff34
 
 
 
 
 
 
 
 
5583f46
 
0b4ff34
5583f46
2f1e7ff
 
 
 
 
 
 
 
 
0b4ff34
 
 
 
 
 
 
 
 
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# syntax=docker/dockerfile:1.4

# Pin official release checksums and extract only the CPU runtime. Importing the
# GPU image duplicates gigabytes under VFS before the runtime COPY can filter it.
FROM debian:bookworm-slim AS ollama-source
ARG TARGETARCH
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl zstd \
    && rm -rf /var/lib/apt/lists/*
RUN --mount=type=secret,id=proxy_ca \
    set -eu; \
    if [ -f /run/secrets/proxy_ca ]; then export CURL_CA_BUNDLE=/run/secrets/proxy_ca; fi; \
    case "$TARGETARCH" in \
        amd64) ollama_sha=c164ab8b1b28e2a247c29719b8e404c423c5b83ae64c3ab4e59d50c7ff7fc5e0 ;; \
        arm64) ollama_sha=0a279d9428ad17b06fdb7dddf3b059b2c148433585fa3b30676a102fc459bb9f ;; \
        *) echo "Unsupported Ollama CPU architecture: $TARGETARCH" >&2; exit 1 ;; \
    esac; \
    curl --fail --location --retry 3 \
        "https://github.com/ollama/ollama/releases/download/v0.40.6/ollama-linux-${TARGETARCH}.tar.zst" \
        --output /tmp/ollama.tar.zst; \
    echo "$ollama_sha  /tmp/ollama.tar.zst" | sha256sum --check --strict; \
    tar --zstd --extract --file /tmp/ollama.tar.zst --directory /usr \
        --wildcards --no-wildcards-match-slash --no-recursion 'bin/ollama' 'lib/ollama/*'; \
    rm /tmp/ollama.tar.zst

FROM debian:bookworm-slim AS ollama-models
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates curl libstdc++6 libgomp1 \
    && rm -rf /var/lib/apt/lists/*
COPY --from=ollama-source /bin/ollama /bin/ollama
COPY --from=ollama-source /usr/lib/ollama/*.so* /usr/lib/ollama/
COPY --from=ollama-source /usr/lib/ollama/llama-server /usr/lib/ollama/llama-server
COPY --from=ollama-source /usr/lib/ollama/*LICENSE* /usr/lib/ollama/
ARG OLLAMA_EMBEDDING_MODEL=embeddinggemma-2:270m
ARG OLLAMA_CHAT_MODEL=
ENV OLLAMA_MODELS=/opt/ollama-models OLLAMA_NUM_PARALLEL=1 OLLAMA_MAX_LOADED_MODELS=1 OLLAMA_NO_CLOUD=true
COPY scripts/preload_ollama_models.sh /tmp/preload_ollama_models.sh
RUN --mount=type=secret,id=proxy_ca \
    if [ -f /run/secrets/proxy_ca ]; then export SSL_CERT_FILE=/run/secrets/proxy_ca; fi; \
    OLLAMA_EMBEDDING_MODEL="$OLLAMA_EMBEDDING_MODEL" OLLAMA_CHAT_MODEL="$OLLAMA_CHAT_MODEL" \
    bash /tmp/preload_ollama_models.sh

# Assemble CPU/model files once, avoiding repeated large runtime snapshots on VFS.
FROM scratch AS ollama-runtime
COPY --from=ollama-models /bin/ollama /usr/bin/ollama
COPY --from=ollama-models /usr/lib/ollama /usr/lib/ollama
COPY --from=ollama-models /opt/ollama-models /opt/ollama-models

# ── Stage 1: Console builder ────────────────────────────────
FROM node:22.22.0-bookworm-slim AS console-builder

WORKDIR /console
COPY console/package.json console/package-lock.json ./
RUN --mount=type=secret,id=proxy_ca \
    if [ -f /run/secrets/proxy_ca ]; then export NODE_EXTRA_CA_CERTS=/run/secrets/proxy_ca; fi; \
    npm ci
COPY console/ ./
RUN chmod +x node_modules/.bin/* 2>/dev/null || true
RUN npm run build

# ── Stage 2: Python builder ─────────────────────────────────
FROM python:3.11.14-slim-bookworm AS builder

WORKDIR /app

ARG INSTALL_LOCAL_ML=false

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_RETRIES=8 \
    PIP_TIMEOUT=900 \
    PIP_DEFAULT_TIMEOUT=900 \
    POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_IN_PROJECT=true \
    HF_HOME=/app/.cache \
    SENTENCE_TRANSFORMERS_HOME=/app/.cache \
    TIKTOKEN_CACHE_DIR=/app/.cache/tiktoken

RUN --mount=type=secret,id=proxy_ca \
    set -eu; \
    if [ -f /run/secrets/proxy_ca ]; then \
        export PIP_CERT=/run/secrets/proxy_ca GIT_SSL_CAINFO=/run/secrets/proxy_ca; \
    fi; \
    apt-get update && apt-get install -y --no-install-recommends \
        build-essential git postgresql-15 postgresql-server-dev-15 \
    && rm -rf /var/lib/apt/lists/* \
    && pip install --no-cache-dir poetry==2.1.3 \
    && git clone --branch v0.5.1 https://github.com/pgvector/pgvector.git /tmp/pgvector \
    && cd /tmp/pgvector && make && make install \
    && rm -rf /tmp/pgvector /root/.cache

COPY --chmod=644 pyproject.toml poetry.lock ./

RUN --mount=type=secret,id=proxy_ca \
    if [ -f /run/secrets/proxy_ca ]; then \
        export SSL_CERT_FILE=/run/secrets/proxy_ca PIP_CERT=/run/secrets/proxy_ca REQUESTS_CA_BUNDLE=/run/secrets/proxy_ca; \
    fi; \
    if [ "$INSTALL_LOCAL_ML" = "true" ]; then \
        poetry sync --no-root --only main --with local-ml; \
    else \
        poetry sync --no-root --only main; \
    fi \
    && mkdir -p "$TIKTOKEN_CACHE_DIR" \
    && /app/.venv/bin/python -c "import tiktoken; tiktoken.get_encoding('cl100k_base')" \
    && rm -rf /root/.cache /tmp/*

COPY --chmod=755 src/ ./src/
COPY scripts/preload_docker_models.py ./scripts/

RUN if [ "$INSTALL_LOCAL_ML" = "true" ]; then \
        /app/.venv/bin/python scripts/preload_docker_models.py; \
    fi \
    && rm -rf /root/.cache /tmp/*

# Validate small runtime inputs before combining them with large dependencies.
FROM debian:bookworm-slim AS runtime-config
WORKDIR /runtime/app
COPY --chmod=755 deploy/ ./deploy/
COPY entrypoint.sh ./
COPY --chmod=644 CONNECTION.md ./
COPY --chmod=755 scripts/ ./scripts/
RUN test -f scripts/start_with_ollama.py \
    && test -f scripts/preload_ollama_models.sh \
    && chmod +x entrypoint.sh

# Assemble files on an empty root, avoiding a full PostgreSQL/Python root copy
# for every small COPY when the cloud Docker daemon uses VFS snapshots.
FROM scratch AS runtime-files
COPY --from=builder /usr/lib/postgresql/15/lib/vector.so /usr/lib/postgresql/15/lib/
COPY --from=builder /usr/share/postgresql/15/extension/vector* /usr/share/postgresql/15/extension/
COPY --from=runtime-config /runtime/ /
COPY --from=console-builder /console/dist /app/console/dist
COPY --from=builder /app /app
COPY --from=ollama-runtime / /

# ── Stage 3: runtime ────────────────────────────────────────
FROM python:3.11.14-slim-bookworm

WORKDIR /app

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PATH="/app/.venv/bin:$PATH" \
    HF_HOME=/app/.cache \
    SENTENCE_TRANSFORMERS_HOME=/app/.cache \
    TIKTOKEN_CACHE_DIR=/app/.cache/tiktoken \
    PIP_NO_CACHE_DIR=1 \
    PIP_RETRIES=8 \
    PIP_TIMEOUT=900 \
    PIP_DEFAULT_TIMEOUT=900

RUN apt-get update && apt-get upgrade -y \
    && apt-get install -y --no-install-recommends \
    curl \
    git \
    util-linux \
    postgresql-15 \
    postgresql-contrib-15 \
    redis-server \
    nginx \
    supervisor \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/* \
    && python -m pip uninstall -y setuptools wheel \
    && groupadd -r appuser && useradd -r -g appuser -m appuser \
    && mkdir -p /app/plugins && chown appuser:appuser /app/plugins

COPY --from=runtime-files / /

# Revision labels must not invalidate the dependency-installation layer.
ARG BUILD_DATE
ARG BUILD_SHA
ENV BUILD_SHA=${BUILD_SHA}

LABEL build_date="${BUILD_DATE}" \
      build_sha="${BUILD_SHA}"

VOLUME ["/app/plugins"]

EXPOSE 7860

HEALTHCHECK --interval=30s --timeout=30s --start-period=120s --retries=5 \
    CMD curl -f http://127.0.0.1:7860/health || exit 1

USER root
ENTRYPOINT ["/app/entrypoint.sh"]