docs: add Dockerfile refactoring documentation v0.1.17
This commit is contained in:
parent
8867eef33e
commit
e862cb6667
2 changed files with 485 additions and 302 deletions
604
Dockerfile
604
Dockerfile
|
|
@ -54,36 +54,36 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|||
|
||||
# 配置 APT 重試機制
|
||||
RUN set -ex && \
|
||||
echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::http::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::https::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::ftp::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'DPkg::Lock::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries
|
||||
echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::http::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::https::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::ftp::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'DPkg::Lock::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries
|
||||
|
||||
# 安裝基礎工具
|
||||
RUN set -ex && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
unzip \
|
||||
ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
unzip \
|
||||
ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安裝 Bun(根據架構選擇版本)
|
||||
ARG BUN_VERSION=1.3.6
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
BUN_ASSET="bun-linux-aarch64.zip"; \
|
||||
else \
|
||||
BUN_ASSET="bun-linux-x64-baseline.zip"; \
|
||||
fi && \
|
||||
curl -fsSL --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o /tmp/bun.zip \
|
||||
"https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${BUN_ASSET}" && \
|
||||
unzip -j /tmp/bun.zip -d /usr/local/bin && \
|
||||
rm /tmp/bun.zip && \
|
||||
chmod +x /usr/local/bin/bun
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
BUN_ASSET="bun-linux-aarch64.zip"; \
|
||||
else \
|
||||
BUN_ASSET="bun-linux-x64-baseline.zip"; \
|
||||
fi && \
|
||||
curl -fsSL --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o /tmp/bun.zip \
|
||||
"https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${BUN_ASSET}" && \
|
||||
unzip -j /tmp/bun.zip -d /usr/local/bin && \
|
||||
rm /tmp/bun.zip && \
|
||||
chmod +x /usr/local/bin/bun
|
||||
|
||||
# ==============================================================================
|
||||
# Stage 2: Install - Node Dependencies
|
||||
|
|
@ -116,121 +116,121 @@ FROM base AS system-tools
|
|||
|
||||
# 4.1 配置 APT
|
||||
RUN set -ex && \
|
||||
echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::http::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::https::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'APT::Get::Assume-Yes "true";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'DPkg::Lock::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries
|
||||
echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::http::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::https::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'APT::Get::Assume-Yes "true";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'DPkg::Lock::Timeout "120";' >> /etc/apt/apt.conf.d/80-retries
|
||||
|
||||
# 4.2 基礎系統工具
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
locales ca-certificates curl wget unzip openssl git xz-utils && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
locales ca-certificates curl wget unzip openssl git xz-utils && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.3 核心轉換工具
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
assimp-utils dcraw dvisvgm ghostscript graphicsmagick \
|
||||
mupdf-tools poppler-utils potrace qpdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
assimp-utils dcraw dvisvgm ghostscript graphicsmagick \
|
||||
mupdf-tools poppler-utils potrace qpdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.4 dasel(JSON/YAML/TOML 轉換)
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then DASEL_ARCH="linux_arm64"; \
|
||||
else DASEL_ARCH="linux_amd64"; fi && \
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/TomWright/dasel/releases/download/v2.8.1/dasel_${DASEL_ARCH}" \
|
||||
-o /usr/local/bin/dasel && \
|
||||
chmod +x /usr/local/bin/dasel
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then DASEL_ARCH="linux_arm64"; \
|
||||
else DASEL_ARCH="linux_amd64"; fi && \
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/TomWright/dasel/releases/download/v2.8.1/dasel_${DASEL_ARCH}" \
|
||||
-o /usr/local/bin/dasel && \
|
||||
chmod +x /usr/local/bin/dasel
|
||||
|
||||
# 4.5 resvg(僅 AMD64)
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ resvg 無 ARM64 版本,跳過"; \
|
||||
else \
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/linebender/resvg/releases/download/v0.44.0/resvg-linux-x86_64.tar.gz" \
|
||||
-o /tmp/resvg.tar.gz && \
|
||||
tar -xzf /tmp/resvg.tar.gz -C /tmp/ && \
|
||||
mv /tmp/resvg /usr/local/bin/resvg && \
|
||||
chmod +x /usr/local/bin/resvg && \
|
||||
rm -rf /tmp/resvg.tar.gz; \
|
||||
fi
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ resvg 無 ARM64 版本,跳過"; \
|
||||
else \
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/linebender/resvg/releases/download/v0.44.0/resvg-linux-x86_64.tar.gz" \
|
||||
-o /tmp/resvg.tar.gz && \
|
||||
tar -xzf /tmp/resvg.tar.gz -C /tmp/ && \
|
||||
mv /tmp/resvg /usr/local/bin/resvg && \
|
||||
chmod +x /usr/local/bin/resvg && \
|
||||
rm -rf /tmp/resvg.tar.gz; \
|
||||
fi
|
||||
|
||||
# 4.6 deark(編譯安裝)
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends build-essential && \
|
||||
cd /tmp && git clone --depth 1 https://github.com/jsummers/deark.git && \
|
||||
cd deark && make -j$(nproc) && \
|
||||
cp deark /usr/local/bin/deark && chmod +x /usr/local/bin/deark && \
|
||||
cd / && rm -rf /tmp/deark && \
|
||||
apt-get remove -y build-essential && apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends build-essential && \
|
||||
cd /tmp && git clone --depth 1 https://github.com/jsummers/deark.git && \
|
||||
cd deark && make -j$(nproc) && \
|
||||
cp deark /usr/local/bin/deark && chmod +x /usr/local/bin/deark && \
|
||||
cd / && rm -rf /tmp/deark && \
|
||||
apt-get remove -y build-essential && apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.7 vtracer
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
||||
else \
|
||||
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
||||
fi && \
|
||||
curl -L --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o /tmp/vtracer.tar.gz \
|
||||
"https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
||||
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
||||
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
||||
chmod +x /usr/local/bin/vtracer && \
|
||||
rm -rf /tmp/vtracer.tar.gz
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
||||
else \
|
||||
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
||||
fi && \
|
||||
curl -L --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o /tmp/vtracer.tar.gz \
|
||||
"https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
||||
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
||||
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
||||
chmod +x /usr/local/bin/vtracer && \
|
||||
rm -rf /tmp/vtracer.tar.gz
|
||||
|
||||
# 4.8 FFmpeg
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ffmpeg libavcodec-extra libva2 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
ffmpeg libavcodec-extra libva2 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.9 圖像處理工具(ImageMagick, Inkscape, vips 等)
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
imagemagick inkscape libheif-examples libjxl-tools \
|
||||
libvips-tools xauth xvfb && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
imagemagick inkscape libheif-examples libjxl-tools \
|
||||
libvips-tools xauth xvfb && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.10 文件處理工具(Calibre, Pandoc)
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
calibre libemail-outlook-message-perl pandoc && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
calibre libemail-outlook-message-perl pandoc && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.11 LibreOffice
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends libreoffice && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends libreoffice && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.12 TexLive 基礎
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-base texlive-latex-base texlive-latex-recommended \
|
||||
texlive-fonts-recommended texlive-xetex latexmk lmodern && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-base texlive-latex-base texlive-latex-recommended \
|
||||
texlive-fonts-recommended texlive-xetex latexmk lmodern && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.13 TexLive 語言包
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-lang-cjk texlive-lang-german texlive-lang-french \
|
||||
texlive-lang-arabic texlive-lang-other && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-lang-cjk texlive-lang-german texlive-lang-french \
|
||||
texlive-lang-arabic texlive-lang-other && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4.14 Tesseract OCR
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-tra \
|
||||
tesseract-ocr-chi-sim tesseract-ocr-jpn tesseract-ocr-kor \
|
||||
tesseract-ocr-deu tesseract-ocr-fra ocrmypdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-tra \
|
||||
tesseract-ocr-chi-sim tesseract-ocr-jpn tesseract-ocr-kor \
|
||||
tesseract-ocr-deu tesseract-ocr-fra ocrmypdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ==============================================================================
|
||||
# Stage 5: Fonts(拆分安裝)
|
||||
|
|
@ -239,11 +239,11 @@ FROM system-tools AS fonts
|
|||
|
||||
# 5.1 系統字型
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
fonts-noto-cjk fonts-noto-cjk-extra fonts-noto-core \
|
||||
fonts-noto-color-emoji fonts-liberation fonts-dejavu-core \
|
||||
fonts-dejavu-extra fonts-freefont-ttf fonts-droid-fallback && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
fonts-noto-cjk fonts-noto-cjk-extra fonts-noto-core \
|
||||
fonts-noto-color-emoji fonts-liberation fonts-dejavu-core \
|
||||
fonts-dejavu-extra fonts-freefont-ttf fonts-droid-fallback && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 5.2 複製自訂字型
|
||||
RUN mkdir -p /usr/share/fonts/truetype/custom
|
||||
|
|
@ -251,12 +251,12 @@ COPY fonts/ /usr/share/fonts/truetype/custom/
|
|||
|
||||
# 5.3 設定 BabelDOC 字型目錄
|
||||
RUN mkdir -p /root/.cache/babeldoc/fonts && \
|
||||
for font in GoNotoKurrent-Regular.ttf SourceHanSerifCN-Regular.ttf \
|
||||
SourceHanSerifTW-Regular.ttf SourceHanSerifJP-Regular.ttf \
|
||||
SourceHanSerifKR-Regular.ttf BiauKai.ttf; do \
|
||||
[ -f "/usr/share/fonts/truetype/custom/${font}" ] && \
|
||||
cp "/usr/share/fonts/truetype/custom/${font}" /root/.cache/babeldoc/fonts/ || true; \
|
||||
done
|
||||
for font in GoNotoKurrent-Regular.ttf SourceHanSerifCN-Regular.ttf \
|
||||
SourceHanSerifTW-Regular.ttf SourceHanSerifJP-Regular.ttf \
|
||||
SourceHanSerifKR-Regular.ttf BiauKai.ttf; do \
|
||||
[ -f "/usr/share/fonts/truetype/custom/${font}" ] && \
|
||||
cp "/usr/share/fonts/truetype/custom/${font}" /root/.cache/babeldoc/fonts/ || true; \
|
||||
done
|
||||
|
||||
# 5.4 更新字型快取
|
||||
RUN fc-cache -fv
|
||||
|
|
@ -268,10 +268,10 @@ FROM fonts AS python-tools
|
|||
|
||||
# 6.1 Python 基礎環境
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 python3-pip python3-venv python3-numpy \
|
||||
python3-tinycss2 python3-opencv python3-img2pdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 python3-pip python3-venv python3-numpy \
|
||||
python3-tinycss2 python3-opencv python3-img2pdf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 6.2 uv 套件管理器
|
||||
RUN pip3 install --no-cache-dir --break-system-packages uv
|
||||
|
|
@ -281,12 +281,12 @@ RUN uv pip install --system --break-system-packages --no-cache huggingface_hub
|
|||
|
||||
# 6.4 endesive(PDF 簽章)
|
||||
RUN apt-get update --fix-missing && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential swig libpcsclite-dev python3-dev && \
|
||||
uv pip install --system --break-system-packages --no-cache endesive && \
|
||||
apt-get remove -y build-essential swig python3-dev && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential swig libpcsclite-dev python3-dev && \
|
||||
uv pip install --system --break-system-packages --no-cache endesive && \
|
||||
apt-get remove -y build-essential swig python3-dev && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 6.5 markitdown
|
||||
RUN uv pip install --system --break-system-packages --no-cache "markitdown[all]"
|
||||
|
|
@ -296,16 +296,16 @@ RUN uv pip install --system --break-system-packages --no-cache pdf2zh
|
|||
|
||||
# 6.7 babeldoc
|
||||
RUN uv pip install --system --break-system-packages --no-cache babeldoc || \
|
||||
echo "⚠️ babeldoc 安裝可能有警告"
|
||||
echo "⚠️ babeldoc 安裝可能有警告"
|
||||
|
||||
# 6.8 MinerU(僅 AMD64)
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ ARM64:MinerU 不支援,跳過安裝"; \
|
||||
else \
|
||||
uv pip install --system --break-system-packages --no-cache -U "mineru[all]"; \
|
||||
fi
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ ARM64:MinerU 不支援,跳過安裝"; \
|
||||
else \
|
||||
uv pip install --system --break-system-packages --no-cache -U "mineru[all]"; \
|
||||
fi
|
||||
|
||||
# 6.9 tiktoken
|
||||
RUN uv pip install --system --break-system-packages --no-cache tiktoken
|
||||
|
|
@ -324,95 +324,95 @@ ENV BABELDOC_CACHE_DIR="/root/.cache/babeldoc"
|
|||
|
||||
# 7.1 創建目錄結構
|
||||
RUN mkdir -p /opt/convertx/models/mineru && \
|
||||
mkdir -p /root/.cache/babeldoc/models && \
|
||||
mkdir -p /root/.cache/babeldoc/fonts && \
|
||||
mkdir -p /root/.cache/babeldoc/cmap && \
|
||||
mkdir -p /root/.cache/babeldoc/tiktoken
|
||||
mkdir -p /root/.cache/babeldoc/models && \
|
||||
mkdir -p /root/.cache/babeldoc/fonts && \
|
||||
mkdir -p /root/.cache/babeldoc/cmap && \
|
||||
mkdir -p /root/.cache/babeldoc/tiktoken
|
||||
|
||||
# 7.2 複製預下載的 ONNX 模型
|
||||
COPY models/ /root/.cache/babeldoc/models/
|
||||
|
||||
# 7.3 下載 MinerU Pipeline 模型(僅 AMD64)
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ ARM64:跳過 MinerU 模型下載"; \
|
||||
else \
|
||||
python3 -c " \
|
||||
from huggingface_hub import snapshot_download; \
|
||||
import os; \
|
||||
models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru'); \
|
||||
print(f'下載 PDF-Extract-Kit-1.0 到 {models_dir}...'); \
|
||||
snapshot_download( \
|
||||
repo_id='opendatalab/PDF-Extract-Kit-1.0', \
|
||||
local_dir=f'{models_dir}/PDF-Extract-Kit-1.0', \
|
||||
local_dir_use_symlinks=False, \
|
||||
resume_download=True \
|
||||
); \
|
||||
print('✅ PDF-Extract-Kit-1.0 下載完成'); \
|
||||
"; \
|
||||
fi
|
||||
ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo "⚠️ ARM64:跳過 MinerU 模型下載"; \
|
||||
else \
|
||||
python3 -c " \
|
||||
from huggingface_hub import snapshot_download; \
|
||||
import os; \
|
||||
models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru'); \
|
||||
print(f'下載 PDF-Extract-Kit-1.0 到 {models_dir}...'); \
|
||||
snapshot_download( \
|
||||
repo_id='opendatalab/PDF-Extract-Kit-1.0', \
|
||||
local_dir=f'{models_dir}/PDF-Extract-Kit-1.0', \
|
||||
local_dir_use_symlinks=False, \
|
||||
resume_download=True \
|
||||
); \
|
||||
print('✅ PDF-Extract-Kit-1.0 下載完成'); \
|
||||
"; \
|
||||
fi
|
||||
|
||||
# 7.4 產生 MinerU 配置檔
|
||||
RUN set -ex && \
|
||||
ARCH=$(uname -m) && \
|
||||
mkdir -p /opt/convertx && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo '{"models-dir":{"pipeline":"","vlm":""},"model-source":"local","note":"ARM64 - MinerU not supported"}' > /opt/convertx/mineru.json && \
|
||||
cp /opt/convertx/mineru.json /root/mineru.json && \
|
||||
echo "⚠️ ARM64:產生空的 mineru.json"; \
|
||||
else \
|
||||
python3 -c " \
|
||||
import json; \
|
||||
import os; \
|
||||
mineru_models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru'); \
|
||||
config = { \
|
||||
'models-dir': { \
|
||||
'pipeline': f'{mineru_models_dir}/PDF-Extract-Kit-1.0', \
|
||||
'vlm': '' \
|
||||
}, \
|
||||
'model-source': 'local', \
|
||||
'latex-delimiter-config': { \
|
||||
'display': {'left': '@@', 'right': '@@'}, \
|
||||
'inline': {'left': '@', 'right': '@'} \
|
||||
} \
|
||||
}; \
|
||||
os.makedirs('/opt/convertx', exist_ok=True); \
|
||||
with open('/opt/convertx/mineru.json', 'w') as f: \
|
||||
json.dump(config, f, indent=2); \
|
||||
with open('/root/mineru.json', 'w') as f: \
|
||||
json.dump(config, f, indent=2); \
|
||||
print('✅ mineru.json 已產生'); \
|
||||
"; \
|
||||
fi
|
||||
ARCH=$(uname -m) && \
|
||||
mkdir -p /opt/convertx && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
echo '{"models-dir":{"pipeline":"","vlm":""},"model-source":"local","note":"ARM64 - MinerU not supported"}' > /opt/convertx/mineru.json && \
|
||||
cp /opt/convertx/mineru.json /root/mineru.json && \
|
||||
echo "⚠️ ARM64:產生空的 mineru.json"; \
|
||||
else \
|
||||
python3 -c " \
|
||||
import json; \
|
||||
import os; \
|
||||
mineru_models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru'); \
|
||||
config = { \
|
||||
'models-dir': { \
|
||||
'pipeline': f'{mineru_models_dir}/PDF-Extract-Kit-1.0', \
|
||||
'vlm': '' \
|
||||
}, \
|
||||
'model-source': 'local', \
|
||||
'latex-delimiter-config': { \
|
||||
'display': {'left': '@@', 'right': '@@'}, \
|
||||
'inline': {'left': '@', 'right': '@'} \
|
||||
} \
|
||||
}; \
|
||||
os.makedirs('/opt/convertx', exist_ok=True); \
|
||||
with open('/opt/convertx/mineru.json', 'w') as f: \
|
||||
json.dump(config, f, indent=2); \
|
||||
with open('/root/mineru.json', 'w') as f: \
|
||||
json.dump(config, f, indent=2); \
|
||||
print('✅ mineru.json 已產生'); \
|
||||
"; \
|
||||
fi
|
||||
|
||||
# 7.5 BabelDOC warmup
|
||||
RUN set -ex && \
|
||||
export BABELDOC_CACHE_PATH="/root/.cache/babeldoc" && \
|
||||
if command -v babeldoc >/dev/null 2>&1; then \
|
||||
babeldoc --warmup 2>&1 || echo "⚠️ warmup 可能有警告"; \
|
||||
else \
|
||||
echo "⚠️ babeldoc 不可用,跳過 warmup"; \
|
||||
fi
|
||||
export BABELDOC_CACHE_PATH="/root/.cache/babeldoc" && \
|
||||
if command -v babeldoc >/dev/null 2>&1; then \
|
||||
babeldoc --warmup 2>&1 || echo "⚠️ warmup 可能有警告"; \
|
||||
else \
|
||||
echo "⚠️ babeldoc 不可用,跳過 warmup"; \
|
||||
fi
|
||||
|
||||
# 7.6 下載 tiktoken 編碼
|
||||
RUN python3 -c " \
|
||||
try: \
|
||||
import tiktoken; \
|
||||
for enc_name in ['cl100k_base', 'p50k_base', 'r50k_base']: \
|
||||
try: \
|
||||
enc = tiktoken.get_encoding(enc_name); \
|
||||
print(f'✅ tiktoken {enc_name} 已下載'); \
|
||||
except Exception as e: \
|
||||
print(f'⚠️ tiktoken {enc_name} 下載失敗: {e}'); \
|
||||
except ImportError: \
|
||||
print('⚠️ tiktoken 未安裝,跳過'); \
|
||||
"
|
||||
try: \
|
||||
import tiktoken; \
|
||||
for enc_name in ['cl100k_base', 'p50k_base', 'r50k_base']: \
|
||||
try: \
|
||||
enc = tiktoken.get_encoding(enc_name); \
|
||||
print(f'✅ tiktoken {enc_name} 已下載'); \
|
||||
except Exception as e: \
|
||||
print(f'⚠️ tiktoken {enc_name} 下載失敗: {e}'); \
|
||||
except ImportError: \
|
||||
print('⚠️ tiktoken 未安裝,跳過'); \
|
||||
"
|
||||
|
||||
# 7.7 清理下載快取
|
||||
RUN rm -rf /tmp/hf_download_cache /root/.cache/huggingface \
|
||||
/root/.cache/pip /root/.cache/uv && \
|
||||
find /usr -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
/root/.cache/pip /root/.cache/uv && \
|
||||
find /usr -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
# ==============================================================================
|
||||
# Stage 8: Final Release Image
|
||||
|
|
@ -442,39 +442,39 @@ RUN fc-cache -fv
|
|||
# PDF 簽章憑證
|
||||
# ==============================================================================
|
||||
RUN mkdir -p /app/certs && \
|
||||
openssl req -x509 -newkey rsa:2048 \
|
||||
-keyout /tmp/key.pem -out /tmp/cert.pem \
|
||||
-days 3650 -nodes \
|
||||
-subj "/CN=PDF Packager Default/O=ConvertX-CN/C=TW" && \
|
||||
openssl pkcs12 -export \
|
||||
-inkey /tmp/key.pem -in /tmp/cert.pem \
|
||||
-out /app/certs/default.p12 \
|
||||
-passout pass: && \
|
||||
rm -f /tmp/key.pem /tmp/cert.pem && \
|
||||
chmod 644 /app/certs/default.p12
|
||||
openssl req -x509 -newkey rsa:2048 \
|
||||
-keyout /tmp/key.pem -out /tmp/cert.pem \
|
||||
-days 3650 -nodes \
|
||||
-subj "/CN=PDF Packager Default/O=ConvertX-CN/C=TW" && \
|
||||
openssl pkcs12 -export \
|
||||
-inkey /tmp/key.pem -in /tmp/cert.pem \
|
||||
-out /app/certs/default.p12 \
|
||||
-passout pass: && \
|
||||
rm -f /tmp/key.pem /tmp/cert.pem && \
|
||||
chmod 644 /app/certs/default.p12
|
||||
|
||||
# ==============================================================================
|
||||
# Locale 設定
|
||||
# ==============================================================================
|
||||
RUN sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# ko_KR.UTF-8 UTF-8/ko_KR.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
locale-gen
|
||||
sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# ko_KR.UTF-8 UTF-8/ko_KR.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
locale-gen
|
||||
|
||||
# ==============================================================================
|
||||
# 最終清理
|
||||
# ==============================================================================
|
||||
RUN rm -rf /usr/share/doc/texlive* && \
|
||||
rm -rf /usr/share/texlive/texmf-dist/doc && \
|
||||
rm -rf /usr/share/doc/* && \
|
||||
rm -rf /usr/share/man/* && \
|
||||
rm -rf /usr/share/info/* && \
|
||||
rm -rf /tmp/* && \
|
||||
rm -rf /var/tmp/*
|
||||
rm -rf /usr/share/texlive/texmf-dist/doc && \
|
||||
rm -rf /usr/share/doc/* && \
|
||||
rm -rf /usr/share/man/* && \
|
||||
rm -rf /usr/share/info/* && \
|
||||
rm -rf /tmp/* && \
|
||||
rm -rf /var/tmp/*
|
||||
|
||||
# 複製驗證腳本
|
||||
COPY scripts/verify-models.sh /app/scripts/verify-models.sh
|
||||
|
|
@ -488,87 +488,87 @@ RUN mkdir -p data
|
|||
# 🔒 Runtime 離線驗證
|
||||
# ==============================================================================
|
||||
RUN echo "======================================" && \
|
||||
echo "🔒 Runtime 離線驗證" && \
|
||||
echo "======================================" && \
|
||||
ARCH=$(uname -m) && \
|
||||
VALIDATION_PASSED=true && \
|
||||
\
|
||||
# 驗證核心工具
|
||||
echo "🔍 驗證核心工具..." && \
|
||||
for cmd in ffmpeg convert gm vips inkscape pandoc soffice; do \
|
||||
if command -v ${cmd} >/dev/null 2>&1; then \
|
||||
echo " ✅ ${cmd}: $(which ${cmd})"; \
|
||||
else \
|
||||
echo " ❌ ${cmd}: 未找到" && VALIDATION_PASSED=false; \
|
||||
fi; \
|
||||
done && \
|
||||
\
|
||||
# 驗證 MinerU(僅 AMD64)
|
||||
echo "🔍 驗證 MinerU..." && \
|
||||
if [ "$ARCH" != "aarch64" ]; then \
|
||||
if command -v mineru >/dev/null 2>&1; then \
|
||||
echo " ✅ mineru: $(which mineru)"; \
|
||||
else \
|
||||
echo " ❌ mineru 不可執行" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
if [ -d "/opt/convertx/models/mineru/PDF-Extract-Kit-1.0" ]; then \
|
||||
echo " ✅ MinerU 模型目錄存在"; \
|
||||
else \
|
||||
echo " ❌ MinerU 模型目錄不存在" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
if [ -f "/root/mineru.json" ]; then \
|
||||
echo " ✅ mineru.json 存在"; \
|
||||
else \
|
||||
echo " ❌ mineru.json 不存在" && VALIDATION_PASSED=false; \
|
||||
fi; \
|
||||
else \
|
||||
echo " ⚠️ ARM64:跳過 MinerU 驗證"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 BabelDOC
|
||||
echo "🔍 驗證 BabelDOC..." && \
|
||||
if command -v babeldoc >/dev/null 2>&1; then \
|
||||
echo " ✅ babeldoc: $(which babeldoc)"; \
|
||||
else \
|
||||
echo " ⚠️ babeldoc 不可用"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 pdf2zh
|
||||
echo "🔍 驗證 pdf2zh..." && \
|
||||
if command -v pdf2zh >/dev/null 2>&1; then \
|
||||
echo " ✅ pdf2zh: $(which pdf2zh)"; \
|
||||
else \
|
||||
echo " ⚠️ pdf2zh 不可用"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 ImageMagick
|
||||
echo "🔍 驗證 ImageMagick..." && \
|
||||
if command -v convert >/dev/null 2>&1; then \
|
||||
echo " ✅ ImageMagick: $(convert --version | head -1)"; \
|
||||
else \
|
||||
echo " ❌ ImageMagick 未安裝" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 ONNX 模型
|
||||
echo "🔍 驗證 ONNX 模型..." && \
|
||||
if [ -f "/root/.cache/babeldoc/models/doclayout_yolo_docstructbench_imgsz1024.onnx" ]; then \
|
||||
echo " ✅ DocLayout-YOLO ONNX 存在"; \
|
||||
else \
|
||||
echo " ⚠️ DocLayout-YOLO ONNX 不存在"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證字型
|
||||
echo "🔍 驗證字型..." && \
|
||||
FONTS_COUNT=$(ls /usr/share/fonts/truetype/custom/*.ttf 2>/dev/null | wc -l || echo "0") && \
|
||||
echo " ✅ 自訂字型數量: ${FONTS_COUNT}" && \
|
||||
\
|
||||
echo "======================================" && \
|
||||
if [ "$VALIDATION_PASSED" = "true" ]; then \
|
||||
echo "✅ 離線驗證通過!"; \
|
||||
else \
|
||||
echo "❌ 離線驗證失敗!" && exit 1; \
|
||||
fi && \
|
||||
echo "======================================"
|
||||
echo "🔒 Runtime 離線驗證" && \
|
||||
echo "======================================" && \
|
||||
ARCH=$(uname -m) && \
|
||||
VALIDATION_PASSED=true && \
|
||||
\
|
||||
# 驗證核心工具
|
||||
echo "🔍 驗證核心工具..." && \
|
||||
for cmd in ffmpeg convert gm vips inkscape pandoc soffice; do \
|
||||
if command -v ${cmd} >/dev/null 2>&1; then \
|
||||
echo " ✅ ${cmd}: $(which ${cmd})"; \
|
||||
else \
|
||||
echo " ❌ ${cmd}: 未找到" && VALIDATION_PASSED=false; \
|
||||
fi; \
|
||||
done && \
|
||||
\
|
||||
# 驗證 MinerU(僅 AMD64)
|
||||
echo "🔍 驗證 MinerU..." && \
|
||||
if [ "$ARCH" != "aarch64" ]; then \
|
||||
if command -v mineru >/dev/null 2>&1; then \
|
||||
echo " ✅ mineru: $(which mineru)"; \
|
||||
else \
|
||||
echo " ❌ mineru 不可執行" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
if [ -d "/opt/convertx/models/mineru/PDF-Extract-Kit-1.0" ]; then \
|
||||
echo " ✅ MinerU 模型目錄存在"; \
|
||||
else \
|
||||
echo " ❌ MinerU 模型目錄不存在" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
if [ -f "/root/mineru.json" ]; then \
|
||||
echo " ✅ mineru.json 存在"; \
|
||||
else \
|
||||
echo " ❌ mineru.json 不存在" && VALIDATION_PASSED=false; \
|
||||
fi; \
|
||||
else \
|
||||
echo " ⚠️ ARM64:跳過 MinerU 驗證"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 BabelDOC
|
||||
echo "🔍 驗證 BabelDOC..." && \
|
||||
if command -v babeldoc >/dev/null 2>&1; then \
|
||||
echo " ✅ babeldoc: $(which babeldoc)"; \
|
||||
else \
|
||||
echo " ⚠️ babeldoc 不可用"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 pdf2zh
|
||||
echo "🔍 驗證 pdf2zh..." && \
|
||||
if command -v pdf2zh >/dev/null 2>&1; then \
|
||||
echo " ✅ pdf2zh: $(which pdf2zh)"; \
|
||||
else \
|
||||
echo " ⚠️ pdf2zh 不可用"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 ImageMagick
|
||||
echo "🔍 驗證 ImageMagick..." && \
|
||||
if command -v convert >/dev/null 2>&1; then \
|
||||
echo " ✅ ImageMagick: $(convert --version | head -1)"; \
|
||||
else \
|
||||
echo " ❌ ImageMagick 未安裝" && VALIDATION_PASSED=false; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證 ONNX 模型
|
||||
echo "🔍 驗證 ONNX 模型..." && \
|
||||
if [ -f "/root/.cache/babeldoc/models/doclayout_yolo_docstructbench_imgsz1024.onnx" ]; then \
|
||||
echo " ✅ DocLayout-YOLO ONNX 存在"; \
|
||||
else \
|
||||
echo " ⚠️ DocLayout-YOLO ONNX 不存在"; \
|
||||
fi && \
|
||||
\
|
||||
# 驗證字型
|
||||
echo "🔍 驗證字型..." && \
|
||||
FONTS_COUNT=$(ls /usr/share/fonts/truetype/custom/*.ttf 2>/dev/null | wc -l || echo "0") && \
|
||||
echo " ✅ 自訂字型數量: ${FONTS_COUNT}" && \
|
||||
\
|
||||
echo "======================================" && \
|
||||
if [ "$VALIDATION_PASSED" = "true" ]; then \
|
||||
echo "✅ 離線驗證通過!"; \
|
||||
else \
|
||||
echo "❌ 離線驗證失敗!" && exit 1; \
|
||||
fi && \
|
||||
echo "======================================"
|
||||
|
||||
# ==============================================================================
|
||||
# 🔐 Runtime 環境變數(強制離線模式)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue