Skip to content

Commit e1f52cf

Browse files
committed
fix bug
1 parent b762c14 commit e1f52cf

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed

docker/Dockerfile.base

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# ============================================================
2+
# AIX-DB 基础镜像 (Base Image)
3+
# 包含:Python 依赖 + 前端构建产物 + 运行时服务
4+
#
5+
# 构建时机:当 Python 或前端依赖变更时手动构建
6+
# 构建命令:make build-base
7+
# ============================================================
8+
9+
# ============ 阶段1: 构建前端 ============
10+
FROM node:18-alpine AS frontend-builder
11+
12+
WORKDIR /app/web
13+
14+
# 安装 pnpm
15+
RUN npm install -g pnpm@latest --no-fund --no-audit && \
16+
npm cache clean --force && \
17+
rm -rf /root/.npm /tmp/*
18+
19+
# 复制前端依赖文件
20+
COPY web/package.json web/pnpm-lock.yaml ./
21+
22+
# 安装前端依赖并清理
23+
RUN pnpm install --no-frozen-lockfile --prod=false --shamefully-hoist && \
24+
pnpm store prune && \
25+
rm -rf /root/.pnpm-store /tmp/* /root/.cache
26+
27+
# 复制前端源代码
28+
COPY web/ ./
29+
30+
# 构建前端并彻底清理
31+
RUN pnpm run build && \
32+
rm -rf node_modules .pnpm-store /root/.pnpm-store /tmp/* ~/.cache ~/.npm && \
33+
find . -name "*.map" -delete 2>/dev/null || true && \
34+
find . -name "*.md" -not -path "./dist/*" -delete 2>/dev/null || true
35+
36+
# ============ 阶段2: 构建 Python 依赖 ============
37+
FROM python:3.11-slim-bookworm AS backend-builder
38+
39+
WORKDIR /aix-db
40+
41+
# 设置镜像源和 PATH
42+
ENV PIP_INDEX_URL="https://mirrors.aliyun.com/pypi/simple" \
43+
PIP_EXTRA_INDEX_URL="https://pypi.doubanio.com/simple" \
44+
UV_INDEX_URL="https://mirrors.aliyun.com/pypi/simple" \
45+
PATH="/root/.local/bin:${PATH}" \
46+
PYTHONDONTWRITEBYTECODE=1 \
47+
PIP_NO_CACHE_DIR=1 \
48+
UV_NO_CACHE=1
49+
50+
# 只复制依赖文件
51+
COPY pyproject.toml uv.lock* requirements.txt* ./
52+
53+
# 安装系统依赖和 Python 依赖
54+
RUN set -eux; \
55+
apt-get update && \
56+
apt-get install -y --no-install-recommends \
57+
gcc \
58+
g++ \
59+
python3-dev \
60+
libpq-dev \
61+
libffi-dev \
62+
&& \
63+
pip install --no-cache-dir --no-compile pipx && \
64+
pipx install "uv==0.8.0" && \
65+
uv venv --clear && \
66+
. .venv/bin/activate && \
67+
uv sync --no-cache --index-url https://mirrors.aliyun.com/pypi/simple --extra-index-url "" && \
68+
# 只进行安全的清理(保留所有元数据和 entry points)
69+
find .venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true && \
70+
find .venv -type f -name "*.pyc" -delete && \
71+
find .venv -type f -name "*.pyo" -delete && \
72+
# 清理构建工具
73+
apt-get purge -y gcc g++ python3-dev libpq-dev libffi-dev && \
74+
apt-get autoremove -y --purge && \
75+
apt-get clean && \
76+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /root/.cache /root/.local/bin/uv
77+
78+
# ============ 阶段3: 基础运行镜像 ============
79+
FROM python:3.11-slim-bookworm
80+
81+
# 使用构建参数支持多架构
82+
ARG TARGETARCH
83+
ARG TARGETPLATFORM
84+
85+
WORKDIR /app
86+
87+
# 安装运行时依赖:nginx, supervisor, MinIO, PostgreSQL
88+
RUN set -eux; \
89+
apt-get update && \
90+
apt-get install -y --no-install-recommends \
91+
nginx \
92+
supervisor \
93+
libpq5 \
94+
wget \
95+
ca-certificates \
96+
curl \
97+
gnupg \
98+
lsb-release \
99+
locales \
100+
gosu \
101+
&& \
102+
# 设置 locale
103+
sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
104+
locale-gen && \
105+
# 添加 PostgreSQL 官方仓库
106+
echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
107+
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
108+
apt-get update && \
109+
# 安装 PostgreSQL 17 和 pgvector 扩展
110+
apt-get install -y --no-install-recommends \
111+
postgresql-17 \
112+
postgresql-17-pgvector \
113+
&& \
114+
# 根据架构下载并安装 MinIO deb 包
115+
if [ "$TARGETARCH" = "amd64" ]; then \
116+
MINIO_DEB_URL="https://dl.min.io/server/minio/release/linux-amd64/archive/minio_20250422221226.0.0_amd64.deb"; \
117+
elif [ "$TARGETARCH" = "arm64" ]; then \
118+
MINIO_DEB_URL="https://dl.min.io/server/minio/release/linux-arm64/archive/minio_20250422221226.0.0_arm64.deb"; \
119+
else \
120+
echo "Unsupported architecture: $TARGETARCH"; \
121+
exit 1; \
122+
fi && \
123+
wget -O /tmp/minio.deb "${MINIO_DEB_URL}" && \
124+
dpkg -i /tmp/minio.deb && \
125+
rm -f /tmp/minio.deb && \
126+
# 验证 MinIO 安装
127+
minio --version && \
128+
# 清理
129+
apt-get clean && \
130+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
131+
rm -rf /etc/nginx/sites-enabled/default /etc/nginx/sites-available/* && \
132+
rm -rf /usr/share/doc /usr/share/man /usr/share/locale && \
133+
rm -rf /usr/share/nginx/html/* && \
134+
# 创建必要的目录
135+
mkdir -p /var/log/aix-db /var/log/nginx /var/log/supervisor /var/log/minio /var/log/postgresql && \
136+
mkdir -p /var/run/postgresql && \
137+
chown -R postgres:postgres /var/run/postgresql && \
138+
# 精简 nginx 配置
139+
sed -i '/^#/d' /etc/nginx/nginx.conf 2>/dev/null || true
140+
141+
# 设置 locale 环境变量
142+
ENV LANG=en_US.UTF-8 \
143+
LANGUAGE=en_US:en \
144+
LC_ALL=en_US.UTF-8
145+
146+
# 从构建阶段复制虚拟环境
147+
COPY --from=backend-builder /aix-db/.venv /aix-db/.venv
148+
149+
# 复制前端构建结果
150+
COPY --from=frontend-builder /app/web/dist /usr/share/nginx/html
151+
152+
# 复制 nginx 配置
153+
COPY docker/nginx.conf.template /etc/nginx/conf.d/default.conf
154+
155+
# 复制 PostgreSQL 初始化脚本
156+
COPY docker/init_sql.sql /docker-entrypoint-initdb.d/init.sql
157+
158+
# 创建 PostgreSQL 数据目录并设置权限
159+
RUN mkdir -p /var/lib/postgresql/data && \
160+
chown -R postgres:postgres /var/lib/postgresql && \
161+
chmod 700 /var/lib/postgresql/data
162+
163+
# 创建日志目录
164+
RUN mkdir -p /var/log/supervisor /var/log/nginx /var/log/aix-db /var/log/minio /var/log/postgresql /var/run /data
165+
166+
# 复制 supervisor 配置文件
167+
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
168+
169+
# 修复 opentelemetry entry points 问题
170+
RUN echo 'from __future__ import annotations' > /tmp/otel_fix.py && \
171+
echo 'import logging' >> /tmp/otel_fix.py && \
172+
echo 'import typing' >> /tmp/otel_fix.py && \
173+
echo 'from contextvars import Token' >> /tmp/otel_fix.py && \
174+
echo 'from os import environ' >> /tmp/otel_fix.py && \
175+
echo 'from uuid import uuid4' >> /tmp/otel_fix.py && \
176+
echo 'from opentelemetry.context.context import Context, _RuntimeContext' >> /tmp/otel_fix.py && \
177+
echo 'from opentelemetry.context.contextvars_context import ContextVarsRuntimeContext' >> /tmp/otel_fix.py && \
178+
echo 'logger = logging.getLogger(__name__)' >> /tmp/otel_fix.py && \
179+
echo 'def _load_runtime_context() -> _RuntimeContext:' >> /tmp/otel_fix.py && \
180+
echo ' return ContextVarsRuntimeContext()' >> /tmp/otel_fix.py && \
181+
echo '_RUNTIME_CONTEXT = _load_runtime_context()' >> /tmp/otel_fix.py && \
182+
echo 'def create_key(keyname: str) -> str:' >> /tmp/otel_fix.py && \
183+
echo ' return str(uuid4())' >> /tmp/otel_fix.py && \
184+
echo 'def get_value(key: str, context: typing.Optional[Context] = None) -> typing.Optional[object]:' >> /tmp/otel_fix.py && \
185+
echo ' ctx = context if context is not None else get_current()' >> /tmp/otel_fix.py && \
186+
echo ' if ctx is not None:' >> /tmp/otel_fix.py && \
187+
echo ' return ctx.get(key)' >> /tmp/otel_fix.py && \
188+
echo ' return None' >> /tmp/otel_fix.py && \
189+
echo 'def set_value(key: str, value: object, context: typing.Optional[Context] = None) -> Context:' >> /tmp/otel_fix.py && \
190+
echo ' ctx = context if context is not None else get_current()' >> /tmp/otel_fix.py && \
191+
echo ' new_values = {key: value}' >> /tmp/otel_fix.py && \
192+
echo ' if ctx is not None:' >> /tmp/otel_fix.py && \
193+
echo ' for k, v in ctx.items():' >> /tmp/otel_fix.py && \
194+
echo ' if k not in new_values:' >> /tmp/otel_fix.py && \
195+
echo ' new_values[k] = v' >> /tmp/otel_fix.py && \
196+
echo ' return Context(new_values)' >> /tmp/otel_fix.py && \
197+
echo 'def get_current() -> typing.Optional[Context]:' >> /tmp/otel_fix.py && \
198+
echo ' ctx = _RUNTIME_CONTEXT.get_current()' >> /tmp/otel_fix.py && \
199+
echo ' if ctx is None:' >> /tmp/otel_fix.py && \
200+
echo ' ctx = Context()' >> /tmp/otel_fix.py && \
201+
echo ' return ctx' >> /tmp/otel_fix.py && \
202+
echo 'def attach(context: Context) -> Token[typing.Optional[Context]]:' >> /tmp/otel_fix.py && \
203+
echo ' return _RUNTIME_CONTEXT.attach(context)' >> /tmp/otel_fix.py && \
204+
echo 'def detach(token: Token[typing.Optional[Context]]) -> None:' >> /tmp/otel_fix.py && \
205+
echo ' return _RUNTIME_CONTEXT.detach(token)' >> /tmp/otel_fix.py && \
206+
echo '_SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation")' >> /tmp/otel_fix.py && \
207+
echo '_SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key("suppress_http_instrumentation")' >> /tmp/otel_fix.py && \
208+
cp /tmp/otel_fix.py /aix-db/.venv/lib/python3.11/site-packages/opentelemetry/context/__init__.py && \
209+
rm /tmp/otel_fix.py && \
210+
find /aix-db/.venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true && \
211+
find /aix-db/.venv -name "*.pyc" -delete && \
212+
find /aix-db/.venv -name "*.pyo" -delete
213+
214+
# 复制启动脚本
215+
COPY docker/entrypoint.sh /entrypoint.sh
216+
RUN chmod +x /entrypoint.sh
217+
218+
# 设置环境变量
219+
ENV PATH="/aix-db/.venv/bin:${PATH}" \
220+
SERVER_HOST="127.0.0.1" \
221+
SERVER_PORT="8088" \
222+
PYTHONUNBUFFERED=1 \
223+
PYTHONDONTWRITEBYTECODE=1 \
224+
PIP_NO_CACHE_DIR=1 \
225+
DOCKER_ENV=true \
226+
OTEL_PYTHON_CONTEXT=contextvars_context \
227+
# PostgreSQL 配置
228+
POSTGRES_USER=aix_db \
229+
POSTGRES_DB=aix_db \
230+
SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://aix_db:1@127.0.0.1:5432/aix_db \
231+
# MinIO 配置
232+
MINIO_ENDPOINT=127.0.0.1:9000 \
233+
MINIO_ACCESS_KEY=admin \
234+
MINIO_SECRET_KEY=admin123 \
235+
MINIO_DEFAULT_BUCKET=filedata
236+
237+
# 暴露端口
238+
EXPOSE 80 8088 5432 9000 9001
239+
240+
# 数据卷
241+
VOLUME ["/var/lib/postgresql/data", "/data"]
242+
243+
# 基础镜像不需要 CMD,由应用镜像指定

0 commit comments

Comments
 (0)