はじめに
RAG を業務で利用する場合、回答生成モデルだけでなく、PDF や画像から検索可能なテキストを作る前処理が重要になります。
社内ドライブには、次のような形式の異なる資料が混在しています。
- テキスト主体の PDF
- 表を含む会議資料
- スキャンされた帳票
- 複数カラムのマニュアル
- 図やチャートを含む提案書
- PowerPoint から出力した PDF
- Office 文書
通常の PDF テキスト抽出では、見出し、読み順、表の行列関係が崩れることがあります。
そこで今回は、文書解析ツールの MinerU 3.4 を OCI の NVIDIA A10 GPU インスタンスへデプロイし、PDF / 画像 / Office ファイルを直接解析できる mineru-api として利用します。
使用する Terraform スタックは次のリポジトリです。
https://github.com/engchina/no.1-oci-a10-mineru-deployer
この記事では、2026-08-09 に公開した v0.2.12 を前提に説明します。
https://github.com/engchina/no.1-oci-a10-mineru-deployer/releases/tag/v0.2.12
まず結論
現在のスタックは、古い vLLM OpenAI-compatible API だけの構成ではありません。
OCI Resource Manager から実行すると、次の環境を自動構築します。
OCI Resource Manager
|
v
OCI Compute
VM.GPU.A10.1 / VM.GPU.A10.2
Ubuntu 24.04
|
+-- NVIDIA Driver
+-- Docker
+-- NVIDIA Container Toolkit
|
v
Docker image build
vllm/vllm-openai:a230095847e93bd4df9888b33dab956fa9504537b828a23657d2b26fed57b5c9
+ mineru[core]==3.4.4
+ onnxruntime-gpu==1.28.0
|
v
mineru-api
MinerU 3.4 hybrid-engine
デプロイ後は、次の API で PDF を直接アップロードできます。
POST http://<PUBLIC_IP>:80/file_parse
POST http://<PUBLIC_IP>:80/tasks
GET http://<PUBLIC_IP>:80/tasks/<task_id>
GET http://<PUBLIC_IP>:80/tasks/<task_id>/result
GET http://<PUBLIC_IP>:80/health
重要なのは、この記事の構成では /v1/chat/completions へ画像を投げる vLLM 単体 API ではなく、MinerU 公式の mineru-api を起動する点です。
そのため、PDF をページ画像へ分解して OpenAI 互換 API に投げる必要はありません。
旧構成との違い
以前の構成は、opendatalab/MinerU2.5-Pro-2605-1.2B を vLLM OpenAI-compatible API として公開するものでした。
その場合の構成は、概念的には次のようになります。
PDF
|
v
クライアント側でページ画像へ変換
|
v
/v1/chat/completions
|
v
MinerU VLM
この方式でも GPU は使えますが、完全な MinerU の PDF / Office 解析パイプラインではありません。
現在の構成では、mineru-api が次の処理をサーバー側で担当します。
PDF / 画像 / Office
|
v
mineru-api
|
+-- レイアウト解析
+-- VLM 解析
+-- 表解析
+-- Markdown 生成
+-- 中間 JSON 生成
+-- 画像抽出
API の呼び出し方も変わります。
旧: /v1/models
旧: /v1/chat/completions
新: /file_parse
新: /tasks
新: /health
MinerU とは
MinerU は、PDF や文書画像から、本文、見出し、表、画像などの文書構造を抽出するためのドキュメント解析ツールです。
単に画像内の文字を左上から順番に読む OCR ではなく、ページ内のレイアウトを解析して、後段で利用しやすい構造へ変換することを得意とします。
代表的な出力は次のとおりです。
Markdown
HTML Table
Content List
Middle JSON
Model Output
Extracted Images
このため、次のような処理に向いています。
PDF の Markdown 化
RAG 用テキストの生成
見出し単位のチャンク分割
表構造の保持
文書内画像の分離
RAG 前処理で MinerU が効く理由
RAG では、PDF から文字が取れればよいわけではありません。
次の情報をできるだけ残す必要があります。
- 見出しと本文の関係
- 表の行列関係
- ページ番号
- 画像の位置
- 文書内の章構造
- 元ファイルとの対応
MinerU の Markdown と HTML Table を使うと、これらを後段の処理へ渡しやすくなります。
MinerU
|
+-- Markdown
| -> 見出し単位で分割
|
+-- HTML Table
| -> 表単位で分割
|
+-- Images
| -> 必要な画像だけ別 OCR
|
+-- Metadata
-> 元 PDF・ページ番号と関連付け
実運用では、まず MinerU で文書全体を処理し、MinerU でテキスト化できなかった図やチャートだけを別の視覚 OCR へ渡す構成が扱いやすいです。
PDF
|
v
MinerU で一次解析
|
+-- 本文・見出し
| -> Markdown を利用
|
+-- 表
| -> HTML Table を利用
|
+-- 図・チャート
-> 必要なものだけ別 OCR
対応構成
v0.2.12 の主な仕様は次のとおりです。
| 項目 | 内容 |
|---|---|
| リージョン | 東京 ap-tokyo-1 / 大阪 ap-osaka-1
|
| OS | Canonical Ubuntu 24.04 |
| GPU |
VM.GPU.A10.1 / VM.GPU.A10.2
|
| API |
mineru-api FastAPI |
| 公開ポート | TCP 80 |
| MinerU | mineru[core]==3.4.4 |
| 実測バージョン | MinerU 3.4.4 |
| backend | hybrid-engine |
| effort |
high を推奨 |
| ベースイメージ | vllm/vllm-openai:a230095847e93bd4df9888b33dab956fa9504537b828a23657d2b26fed57b5c9 |
| Python | MinerU 公式対応範囲の >=3.10,<3.14 を Docker build 時に検証 |
| ONNX Runtime | CPU 版を削除し、onnxruntime-gpu==1.28.0 を導入 |
| GPU メモリー使用率 | A10.1 / A10.2 とも 0.5
|
| VLM プリロード | 有効 |
| A10.1 runtime 既定値 | 同時解析数 2、処理ウィンドウ 64
|
| A10.2 runtime 既定値 | 同時解析数 4、処理ウィンドウ 64
|
| ネットワークルール | スタックでは作成しない |
A10.1 と A10.2 の違い
| シェイプ | GPU 構成 | Tensor Parallel | 既定同時解析数 | 既定処理ウィンドウ |
|---|---|---|---|---|
VM.GPU.A10.1 |
A10 24GB x 1 | 1 | 2 | 64 |
VM.GPU.A10.2 |
A10 24GB x 2 | 2 | 4 | 64 |
A10.2 は、48GB の単一 GPU として動作するわけではありません。
24GB の A10 を 2 枚使い、--tensor-parallel-size 2 として vLLM 側のモデル処理を分散します。
GPU 0 --+
+-- MinerU VLM
GPU 1 --+
検証や少人数の利用では、まず A10.1 で始めるのが扱いやすいです。
同時処理数を増やしたい場合や、実 PDF の長時間負荷テストで A10.1 の余裕が足りない場合は、A10.2 を検討します。
デプロイ前の準備
デプロイ前に、次の OCI リソースを用意します。
- コンパートメント
- A10 GPU のサービス制限
- A10 GPU の空き容量がある可用性ドメイン
- VCN
- パブリック・サブネット
- インターネット・ゲートウェイ
- 外部 package / model download 用の egress
- SSH 公開鍵
- API 接続元を制限する Security List または NSG
v0.2.12 のスタックは、NSG、Security List、ホスト firewall を作成しません。
これは意図した設計です。ネットワーク、セキュリティ、リソース配置は、運用側で手動管理する前提にしています。
本番用途では、少なくとも次を手動で制御してください。
TCP 22
-> 管理端末の CIDR のみ
TCP 80
-> 利用元サーバー、API Gateway、Load Balancer など必要な CIDR のみ
Egress
-> package download / model download に必要な宛先
OCI Resource Manager からデプロイする
1. Deploy to Oracle Cloud を開く
GitHub リポジトリの README にある Deploy to Oracle Cloud ボタンを押します。
v0.2.12 の Resource Manager 用 ZIP は次の URL です。
https://github.com/engchina/no.1-oci-a10-mineru-deployer/releases/download/v0.2.12/v0.2.12.zip
2. OCI 環境を選択する
Resource Manager の入力画面で、次の項目を指定します。
コンパートメント
可用性ドメイン
VCN
パブリック・サブネット
SSH 公開鍵
東京リージョンは次のとおりです。
ap-tokyo-1
大阪リージョンは次のとおりです。
ap-osaka-1
現在のスタックでは、OCI provider の region は選択した subnet OCID から推定します。
そのため、Resource Manager のフォーム上で以前のような region 入力が見えなくても問題ありません。
ただし、次の 3 つは同じリージョンにそろえる必要があります。
availability_domain
vcn_id
subnet_id
3. Ubuntu image の扱い
instance_image_ocid を空欄にすると、選択した subnet のリージョンとシェイプで利用可能な最新の Canonical Ubuntu 24.04 platform image を Terraform plan 時に選択します。
東京と大阪では image OCID が異なります。
このため、Osaka を選択した場合は Osaka 側の image が lookup されます。
将来の Ubuntu image 更新による差し替わりを避けたい場合は、Resource Manager の output selected_ubuntu_image_ocid を控えて、次回以降 instance_image_ocid に指定します。
4. GPU シェイプを選択する
検証環境では、まず次の構成から始めます。
Shape:
VM.GPU.A10.1
Boot Volume:
200 GB
API Port:
80
同時処理数を増やす場合は、VM.GPU.A10.2 を選択します。
Resource Manager のフォームでは、選択した shape に応じて A10.1 用または A10.2 用の推奨値だけを表示します。
5. MinerU と model source を設定する
既定の MinerU package は次のとおりです。
mineru[core]==3.4.4
完全な再現性を優先する場合は、次のように固定できます。
mineru[core]==3.4.4
モデル取得元は次から選択できます。
huggingface
modelscope
Hugging Face からの取得が遅い環境では、mineru_model_download_source を modelscope に変更できます。
Hugging Face token は任意です。
hf_xxxxxxxxxxxxxxxxx
API 認証について
現在の mineru-api 自体には、sk- で始まる API key 認証はありません。
旧 vLLM OpenAI-compatible API の場合は、次のような Authorization header を使いました。
Authorization: Bearer sk-example-mineru-api-key
しかし、この記事の mineru-api 構成では、この方式は使いません。
本番公開する場合は、ネットワーク側と前段コンポーネントで保護します。
推奨例は次のとおりです。
Client
|
v
OCI API Gateway / Load Balancer / Reverse Proxy
|
+-- TLS
+-- 認証
+-- CIDR 制限
|
v
mineru-api
少なくとも、Security List または NSG で API 接続元 CIDR を絞ってください。
初期化の流れ
Apply 後、インスタンス上では mineru-bootstrap.service が初期化を実行します。
処理の流れは次のとおりです。
A10 インスタンス作成
|
v
Ubuntu 24.04 起動
|
v
NVIDIA Driver 確認
|
+-- 未導入ならインストール
| |
| v
| 自動再起動
|
v
Docker 導入
|
v
NVIDIA Container Toolkit 導入
|
v
MinerU GPU image build
|
v
MinerU model download
|
v
mineru-api 起動
|
v
GPU / CUDA / ONNX Runtime / hybrid-engine 検証
NVIDIA driver が導入されていない場合、初期化中にインスタンスが 1 回再起動します。
再起動後は、systemd service が初期化処理を継続します。
初期化ログを確認する
Apply 完了後も、Docker image build や model download が続いている場合があります。
SSH 接続後、次のコマンドで初期化 service を確認します。
sudo systemctl status mineru-bootstrap.service --no-pager
ログをリアルタイムで確認します。
sudo journalctl -u mineru-bootstrap.service -f
同じ内容は次のファイルにも出力されます。
/var/log/mineru-bootstrap.log
コンテナログは次で確認します。
sudo docker logs -f mineru-api
MinerU の永続化先は次のとおりです。
/opt/mineru/home
/opt/mineru/output
/opt/mineru/logs
bootstrap の状態ファイルは次の場所にあります。
/var/lib/mineru-deployer
初期化完了マーカーは次です。
/var/lib/mineru-deployer/bootstrap-complete
GPU を確認する
ホスト上で GPU を確認します。
nvidia-smi
コンテナ内からも確認します。
sudo docker exec -it mineru-api nvidia-smi
PyTorch CUDA と ONNX Runtime CUDA provider を確認します。
sudo docker exec -it mineru-api bash -lc '
python3 - <<PY
import torch
import onnxruntime as ort
print("torch.cuda.is_available =", torch.cuda.is_available())
print("torch.version.cuda =", torch.version.cuda)
print("onnxruntime providers =", ort.get_available_providers())
PY
'
少なくとも次の状態を期待します。
torch.cuda.is_available = True
onnxruntime providers = [..., "CUDAExecutionProvider", ...]
スタックは起動後にこれらを自動検証します。
CPU fallback を GPU 配備成功として扱わないようにしています。
API の動作を確認する
Resource Manager の output には、次が含まれます。
mineru_api_url
mineru_docs_url
mineru_health_url
ヘルスチェックを実行します。
curl "http://<PUBLIC_IP>:80/health"
正常であれば、次のような JSON が返ります。
{
"status": "healthy",
"version": "3.4.4",
"protocol_version": 2,
"queued_tasks": 0,
"processing_tasks": 0,
"completed_tasks": 0,
"failed_tasks": 0,
"max_concurrent_requests": 2,
"processing_window_size": 64
}
PDF を同期解析する
高精度の同期解析例です。
curl -X POST "http://<PUBLIC_IP>:80/file_parse" \
-F "files=@sample.pdf" \
-F "backend=hybrid-engine" \
-F "effort=high" \
-F "image_analysis=true" \
-F "formula_enable=true" \
-F "table_enable=true" \
-F "return_md=true" \
-F "return_middle_json=true" \
-F "return_content_list=true" \
-F "return_images=true" \
-F "response_format_zip=true" \
-o mineru-result.zip
backend=hybrid-engine は明示することを推奨します。
MinerU 3.4 の CLI / API では既定 backend が hybrid-engine ですが、クライアントや将来バージョンの既定値変更による誤解を避けるためです。
速度を優先する場合は、effort=high を effort=medium に変更できます。
PDF を非同期解析する
長時間処理や大きな文書では、非同期 API を使うとクライアント側の HTTP timeout を避けやすくなります。
TASK_ID=$(
curl -sS -X POST "http://<PUBLIC_IP>:80/tasks" \
-F "files=@sample.pdf" \
-F "backend=hybrid-engine" \
-F "effort=high" \
-F "image_analysis=true" \
-F "formula_enable=true" \
-F "table_enable=true" \
-F "return_md=true" \
-F "return_middle_json=true" \
-F "return_content_list=true" \
-F "return_images=true" \
-F "response_format_zip=true" |
python3 -c 'import json,sys; print(json.load(sys.stdin)["task_id"])'
)
curl "http://<PUBLIC_IP>:80/tasks/${TASK_ID}"
curl -L "http://<PUBLIC_IP>:80/tasks/${TASK_ID}/result" -o mineru-result.zip
GET /tasks/<task_id> は状態確認です。
この polling 自体は GPU inference を発生させません。
ローカルホストで smoke test する
API インスタンス上で簡単な PDF を作ってテストする場合は、次のようにできます。
python3 - <<'PY'
from pathlib import Path
path = Path("/tmp/mineru-smoke-test.pdf")
lines = [
"MinerU API smoke test PDF",
"Generated on Linux for local API testing.",
"This file has simple searchable text.",
]
ops = ["BT", "/F1 18 Tf", "72 720 Td"]
for index, line in enumerate(lines):
if index == 1:
ops.append("/F1 12 Tf")
if index > 0:
ops.append("0 -28 Td")
escaped = line.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)")
ops.append(f"({escaped}) Tj")
ops.append("ET")
stream = ("\n".join(ops) + "\n").encode("latin-1")
objects = [
b"1 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n",
b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 1 0 R >> >> /Contents 4 0 R >>\nendobj\n",
f"4 0 obj\n<< /Length {len(stream)} >>\nstream\n".encode("latin-1") + stream + b"endstream\nendobj\n",
b"5 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n",
]
pdf = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n")
offsets = [0]
for obj in objects:
offsets.append(len(pdf))
pdf.extend(obj)
xref_offset = len(pdf)
pdf.extend(f"xref\n0 {len(objects) + 1}\n".encode("latin-1"))
pdf.extend(b"0000000000 65535 f \n")
for offset in offsets[1:]:
pdf.extend(f"{offset:010d} 00000 n \n".encode("latin-1"))
pdf.extend(
(
f"trailer\n<< /Size {len(objects) + 1} /Root 5 0 R >>\n"
f"startxref\n{xref_offset}\n%%EOF\n"
).encode("latin-1")
)
path.write_bytes(pdf)
print(path)
PY
ローカルから同期 API を呼びます。
curl -sS -X POST "http://127.0.0.1:80/file_parse" \
-H "accept: application/json" \
-F "files=@/tmp/mineru-smoke-test.pdf;type=application/pdf" \
-F "backend=hybrid-engine" \
-F "effort=high" \
-F "parse_method=auto" \
-F "image_analysis=true" \
-F "formula_enable=true" \
-F "table_enable=true" \
-F "return_md=true" \
-F "return_middle_json=false" \
-F "return_model_output=false" \
-F "return_content_list=false" \
-F "return_images=false" \
-F "response_format_zip=false" \
-F "return_original_file=false"
成功すると、次のように status=completed と Markdown が返ります。
{
"status": "completed",
"backend": "hybrid-engine",
"version": "3.4.4",
"results": {
"mineru-smoke-test": {
"md_content": "## MinerU API smoke test PDF\n\nGenerated on Linux for local API testing.\n\nThis file has simple searchable text."
}
}
}
ランタイム値を負荷テストする
v0.2.12 では、MinerU API インスタンス上で runtime 値を matrix test するスクリプトを追加しています。
scripts/mineru_runtime_stress_test.py
確認対象は主に次の 2 つです。
MINERU_API_MAX_CONCURRENT_REQUESTS
MINERU_PROCESSING_WINDOW_SIZE
実行例です。
sudo python3 scripts/mineru_runtime_stress_test.py \
--api-url "http://127.0.0.1:80" \
--concurrency-values 1,2,3 \
--window-values 32,48,64 \
--case-duration-seconds 60
実運用に近い判断をする場合は、生成 PDF ではなく実際の文書を入れたディレクトリを指定します。
sudo python3 scripts/mineru_runtime_stress_test.py \
--api-url "http://127.0.0.1:80" \
--pdf-dir /path/to/test-pdfs \
--concurrency-values 1,2,3 \
--window-values 32,48,64 \
--case-duration-seconds 180
スクリプトは各組み合わせごとに次を行います。
/etc/mineru-deployer/mineru.env を更新
mineru-api container を再起動
/health を確認
/tasks へ非同期リクエストを投入
GPU memory / GPU util / latency / failure を記録
summary.tsv を出力
最後に元の env へ戻す
既定では、テスト後に元の /etc/mineru-deployer/mineru.env へ戻します。
推奨値をそのまま残す場合だけ、--keep-best を付けます。
sudo python3 scripts/mineru_runtime_stress_test.py \
--api-url "http://127.0.0.1:80" \
--pdf-dir /path/to/test-pdfs \
--concurrency-values 3 \
--window-values 64 \
--case-duration-seconds 600 \
--keep-best
A10.1 の負荷テスト結果
生成 PDF を使った 60 秒 matrix test では、次の結果でした。
1/32: throughput 34.14/min, p95 2.140s, failures 0/35
1/48: throughput 34.29/min, p95 2.190s, failures 0/35
1/64: throughput 34.21/min, p95 2.181s, failures 0/35
2/32: timeout, failures 2/2
2/48: throughput 47.30/min, p95 5.087s, failures 0/48
2/64: throughput 48.23/min, p95 5.180s, failures 0/49
3/32: throughput 61.69/min, p95 3.505s, failures 0/63
3/48: throughput 62.78/min, p95 3.319s, failures 0/64
3/64: throughput 63.31/min, p95 3.217s, failures 0/65
生成 PDF では 3/64 が最速でした。
ただし、生成 PDF は実運用のスキャン PDF、複雑な表、数式、長文ドキュメントより軽いです。
そのため、Terraform の A10.1 既定値は、性能と安定性のバランスを見て次にしています。
MINERU_API_MAX_CONCURRENT_REQUESTS=2
MINERU_PROCESSING_WINDOW_SIZE=64
A10.2 では、2 GPU の余裕を使う性能寄りの既定値として次にしています。
MINERU_API_MAX_CONCURRENT_REQUESTS=4
MINERU_PROCESSING_WINDOW_SIZE=64
本番採用前には、実 PDF で 10 分以上のテストを推奨します。
sudo python3 scripts/mineru_runtime_stress_test.py \
--api-url "http://127.0.0.1:80" \
--pdf-dir /path/to/test-pdfs \
--concurrency-values 2,3 \
--window-values 64 \
--case-duration-seconds 600
MINERU_PROCESSING_WINDOW_SIZE が影響するもの
MINERU_API_MAX_CONCURRENT_REQUESTS は、API が同時に処理する解析 task 数です。
MINERU_API_MAX_CONCURRENT_REQUESTS=2
-> 同時に 2 task まで処理
MINERU_PROCESSING_WINDOW_SIZE は、MinerU が 1 task 内で扱う処理ウィンドウの大きさです。
MINERU_PROCESSING_WINDOW_SIZE=64
-> 1 task 内の処理単位を 64 にする
大きい window は大文書で throughput が出やすい一方、GPU memory や内部 queue への負荷が増える可能性があります。
小さい window は常に安定するとは限りません。
実測では、2/32 は 2 task とも processing のまま timeout しました。
一方、2/64 と 3/64 は成功しました。
つまり、concurrency と window は単独で判断せず、組み合わせで測る必要があります。
GPU 使用率が 0% に見える場合
nvidia-smi の GPU-Util が 0% のままでも、必ずしも GPU 未使用という意味ではありません。
例えば、次のような状態があり得ます。
GPU memory:
python3 1390MiB
VLLM::EngineCore 10834MiB
GPU-Util:
0%
この場合、モデルは GPU memory 上にロードされています。
ただし、GET /tasks/<task_id> は状態確認の polling です。
この HTTP access 自体は GPU inference を発生させません。
また、短い PDF や検索可能 PDF では GPU kernel 実行が短く、1 秒間隔の watch nvidia-smi では utilization の山を見逃すことがあります。
GPU 負荷を確認したい場合は、次を使います。
nvidia-smi dmon -s pucm -d 1
sudo docker logs -f mineru-api
実 PDF の処理中も GPU-Util と stress test の max_gpu_util_percent が常に 0 の場合は、次を確認します。
curl -s http://127.0.0.1:80/health | python3 -m json.tool
sudo docker logs --tail=200 mineru-api
sudo docker exec -it mineru-api nvidia-smi
今回遭遇した問題と修正
ここからは、実際の検証中に遭遇した問題と修正内容を整理します。
1. 旧構成が完全な MinerU API ではなかった
最初の構成は、vLLM OpenAI-compatible API として MinerU VLM を公開するだけでした。
この場合、PDF を直接アップロードできず、完全な MinerU pipeline でもありません。
修正後は、mineru-api を起動する構成に変更しました。
旧:
/v1/chat/completions
MinerU2.5-Pro VLM
新:
/file_parse
/tasks
MinerU 3.4 hybrid-engine
2. MinerU の最新系に合わせる必要があった
検証時点では MinerU 3.4.4 を使用しました。
Docker build は、MinerU 公式 Dockerfile の install 経路に合わせています。
python3 -m pip install -U "mineru[core]==3.4.4"
python3 -m pip cache purge
Python は MinerU 公式対応範囲の >=3.10,<3.14 を Docker build 時に検証します。
3. pip cache purge が失敗した
一時期、pip cache を無効化した状態で次を実行していました。
python3 -m pip cache purge
その結果、次のエラーになりました。
ERROR: pip cache commands can not function since cache is disabled.
修正として、Docker build から cache 無効化を外し、公式 Dockerfile と同じように pip install -U 後に pip cache purge を実行する形に戻しました。
4. pip resolver conflict が表示された
vLLM base image には lmcache などが入っており、MinerU 3.4 系の transformers<5 要件と metadata 上の conflict が表示されることがあります。
例です。
lmcache requires huggingface_hub>=1.5.0
lmcache requires transformers>=5.4
mineru installs transformers 4.x
これは公式 Dockerfile と同じ install 経路でも表示され得る metadata conflict です。
このため、pip check で build を落とすのではなく、起動後に実際の GPU / CUDA / ONNX Runtime / hybrid-engine を検証する方針にしました。
5. PaddlePaddle GPU を追加導入しない
検証中、paddlepaddle-gpu を追加導入するかを確認しました。
しかし、MinerU 公式 Dockerfile は追加で paddlepaddle-gpu を入れていません。
追加導入すると dependency resolver conflict や CUDA package index の問題を増やす可能性があります。
そのため、既定では次のように空欄にしています。
paddlepaddle_gpu_package = ""
必要がある場合だけ明示的に指定します。
6. ONNX Runtime GPU を明示導入する
CPU 版 onnxruntime が入っていると、GPU provider が使われない可能性があります。
そのため、Docker build では CPU 版を削除してから GPU 版を入れます。
python3 -m pip uninstall -y onnxruntime || true
python3 -m pip install "onnxruntime-gpu==1.28.0"
起動後には、CUDAExecutionProvider が見えることを検証します。
7. debconf: delaying package configuration は問題ではない
Ubuntu package install 中に次の warning が表示されることがあります。
debconf: delaying package configuration, since apt-utils is not installed
これは minimal image でよく出る warning であり、package install が失敗していなければ修正不要です。
8. Docker build が止まって見えた
Docker build では、base image pull、MinerU install、model download に時間がかかります。
例えば次のような箇所で止まって見えることがあります。
Step 3/17 : ARG MINERU_PACKAGE_SPEC
この場合は、別 terminal から次を確認します。
sudo journalctl -u mineru-bootstrap.service -f
sudo docker ps -a
sudo docker system df
ログが進んでいれば待ちます。
9. CUDA-capable device(s) is/are busy or unavailable
最初の API smoke test で、次の CUDA error が発生しました。
CUDA error: CUDA-capable device(s) is/are busy or unavailable
原因は、vLLM と pipeline 側が同じ GPU を使う hybrid workload で、GPU compute mode が適切でない状態でした。
修正として、起動時に NVIDIA GPU compute mode を Default にそろえる処理を追加しました。
nvidia-smi -c 0
現在の bootstrap は、compute mode を確認し、必要なら Default に変更します。
10. public API から server_url を渡すと SSRF 保護に引っかかった
API 呼び出し時に、次のエラーが出ました。
{
"detail": "Publicly exposed API disables *-http-client backends and server_url by default. Rebind to 127.0.0.1 or start with --allow-public-http-client if you understand the SSRF risk."
}
原因は、public に bind された API へ server_url=string を渡したことです。
MinerU は SSRF リスクを避けるため、public exposed API では server_url や http-client backend を既定で無効化します。
通常の /file_parse や /tasks 呼び出しでは、server_url を送る必要はありません。
修正後の curl では、server_url を削除します。
curl -sS -X POST "http://127.0.0.1:80/file_parse" \
-F "files=@/tmp/mineru-smoke-test.pdf;type=application/pdf" \
-F "backend=hybrid-engine" \
-F "effort=high" \
-F "image_analysis=true" \
-F "return_md=true"
--allow-public-http-client は SSRF リスクを理解している場合だけ使います。
通常は使わない方が安全です。
11. NSG / Security List は作成しない方針にした
途中の設計では、Terraform 側で network security group を作る案もありました。
しかし、実運用ではネットワーク、セキュリティ、配置ポリシーを手動管理したいケースが多いため、スタックからは NSG / Security List / host firewall 作成を削除しました。
現在の方針は次です。
Terraform stack:
-> Compute と MinerU API を作る
Network security:
-> 既存 VCN / subnet / Security List / NSG を運用側で管理
12. Region 選択と image OCID の扱い
Resource Manager 画面で region 入力が見えないため、最初は不安になりました。
現在の stack では、選択した subnet OCID から region を推定します。
subnet_id
|
v
selected_region
|
v
OCI provider region
|
v
Ubuntu 24.04 image lookup
東京と大阪では image OCID が異なります。
そのため、subnet が Osaka の場合は Osaka 側の image が lookup されます。
13. /tasks polling が続いていても GPU が動いていないように見える
ログに次のような行が大量に出ることがあります。
GET /tasks/<task_id> HTTP/1.1 200 OK
これは task 状態確認の polling です。
この access 自体は GPU を使いません。
GPU が使われているかは、処理中の nvidia-smi dmon、コンテナ内の nvidia-smi、PyTorch CUDA、ONNX Runtime provider で確認します。
14. stress test 中に進行状況が見えにくかった
最初の stress test script は、各 case が終わるまで CASE_RESULT を出しませんでした。
そのため、重い task が走ると止まって見えました。
現在は 15 秒ごとに PROGRESS を出します。
PROGRESS case=concurrency-3_window-64 elapsed_seconds=45 remaining_seconds=15 completed=48 successes=48 failures=0 current_gpu_util_percent=100 peak_gpu_util_percent=100
completed が増えていれば処理は進んでいます。
remaining_seconds が 0 になったあとも戻らない場合は、最後に投入された task の完了または timeout を待っています。
15. processing のまま終わらない task を timeout として扱う
2/32 のテストでは、2 task が processing のまま完了しませんでした。
旧 script では、最後に取得した task status が processing のため、失敗理由が分かりにくい表示でした。
現在は timeout 時に次を残すようにしています。
status=timeout
last_task_status=processing
これにより、API submit は成功したが task が完了しなかったことを区別できます。
セキュリティ上の注意
API をそのまま広く公開しない
mineru-api 自体には認証がありません。
本番で API を広く公開する場合は、次を検討してください。
- Security List / NSG による接続元制限
- OCI API Gateway
- OCI Load Balancer
- Reverse Proxy
- TLS
- アプリケーション側の認証
- Private subnet
- VPN / FastConnect
FastAPI Docs を無効化する
既定では FastAPI docs が有効です。
本番で CIDR を広くする場合は、次を検討してください。
enable_fastapi_docs = false
Terraform state と Compute metadata に注意する
hf_token を指定した場合、Terraform state と Compute cloud-init user data に含まれます。
state と Compute metadata の閲覧権限を最小化してください。
RAG 向けの処理フロー
実運用では、MinerU を一次解析器として利用します。
入力ファイル
|
v
MinerU
|
+-- Markdown
| |
| v
| 見出し単位でチャンク分割
|
+-- HTML Table
| |
| v
| 表単位でインデックス化
|
+-- 抽出画像
|
v
必要なものだけ視覚 OCR で補完
MinerU の出力に次のような画像参照が含まれる場合、その画像だけを別 OCR へ送ります。

さらに、MinerU の結果が短すぎるページを検出して、fallback する方法もあります。
if len(extracted_text.strip()) < minimum_length:
run_visual_ocr(page_image)
実際には、文字数だけでなく、次の条件を組み合わせます。
本文が空
本文が極端に短い
画像参照だけが含まれる
表が期待されるのに表がない
ページの大部分が画像
これにより、全ページを複数モデルへ送るよりも、GPU コストを抑えられます。
まとめ
no.1-oci-a10-mineru-deployer の v0.2.12 を使うと、MinerU 3.4 の完全な mineru-api を OCI A10 GPU 上に構築できます。
OCI Resource Manager
|
v
Ubuntu 24.04 + NVIDIA A10
|
v
Docker + NVIDIA Container Toolkit
|
v
mineru-api
|
v
MinerU 3.4 hybrid-engine
重要なポイントは次です。
PDF を直接 /file_parse または /tasks へアップロードできる
backend=hybrid-engine を明示する
effort=high で高精度解析を使う
GPU / CUDA / ONNX Runtime / hybrid-engine を起動後に検証する
NSG / Security List / firewall は手動管理する
public API に server_url を渡さない
A10.1 既定値は 2/64
A10.2 既定値は 4/64
実 PDF では stress test で最終値を決める
MinerU は、テキスト PDF、会議録、仕様書、マニュアル、表を含む業務文書の一次解析に向いています。
一方で、図やチャート内部の数値、SmartArt 内の文字などは、抽出画像を別 OCR で補完する構成が扱いやすいです。
まず MinerU で文書全体を高速に構造化し、画像として残った部分だけを補完する。
この 2 段構成が、RAG 前処理において文書構造、処理速度、GPU コストのバランスを取りやすい構成です。