Files
xian_algorithm_new/QGIS_DOCKER_README.md
T
2026-06-21 22:30:04 +08:00

8.3 KiB
Raw Blame History

QGIS Docker 部署指南

QGIS 专题图产出通过 Docker 容器执行,主进程(Python 3.10)通过 docker exec 调用容器内的 QGIS 环境。

1. 环境要求

  • DockerWindows: Docker DesktopLinux: docker-ce
  • 项目代码已部署到服务器
  • 输出文件目录已创建

2. 拉取 QGIS 镜像

# 官方 QGIS 镜像(含 QGIS 3.x + Python + Qt5
docker pull qgis/qgis:3.44.11

# 验证
docker run --rm qgis/qgis:3.44.11 qgis --version

无外网环境(离线导入)

# 在有网的机器上导出
docker save qgis/qgis:3.44.11 -o qgis-34411.tar

# 传输到目标服务器后导入
docker load -i qgis-34411.tar

3. 启动 QGIS 容器

# ---- Linux / macOS ----

PROJECT_ROOT=/home/xian/xian_algorithm_new
FILE_STORE=/data

docker run -d \
  --name qgis-server \
  --restart unless-stopped \
  -v "${PROJECT_ROOT}:/app:ro" \
  -v "${FILE_STORE}:${FILE_STORE}" \
  qgis/qgis:3.44.11 \
  sleep infinity

# ---- Windows (cmd.exe) ----
# 注意:Windows 路径必须用正斜杠 /,不能用反斜杠 \
# 挂载目标必须是 Linux 路径(容器是 Linux

docker run -d ^
  --name qgis-server ^
  --restart unless-stopped ^
  -v "F:/project/xian/xian_algorithm_new:/app:ro" ^
  -v "G:/files:/files" ^
  qgis/qgis:3.44.11 ^
  sleep infinity

参数说明

参数 说明
--name qgis-server 容器名称,需与 settings.tomlQGIS_DOCKER_CONTAINER 一致
-v 项目代码:/app:ro 项目代码只读挂载,容器内路径由 QGIS_DOCKER_PROJECT_DIR 配置
-v 输出目录:输出目录 文件输出目录读写挂载,保持主机与容器路径一致
sleep infinity 保持容器运行,等待 docker exec 调用

4. 安装中文字体(手动)

QGIS 模板使用了 SimHei(黑体)、SimSun(宋体)、Microsoft YaHei(微软雅黑)等 Windows 中文字体, Docker 镜像默认不包含这些字体,会导致中文全部乱码。字体需手动安装,代码不会自动安装。

步骤

# 1. 创建字体目录
docker exec qgis-server mkdir -p /usr/share/fonts/truetype/winfonts

# 2. 从主机复制 Windows 中文字体
#    Windows 路径(根据实际字体文件位置调整):
docker cp "C:\Windows\Fonts\simhei.ttf"  qgis-server:/usr/share/fonts/truetype/winfonts/
docker cp "C:\Windows\Fonts\simsun.ttc"  qgis-server:/usr/share/fonts/truetype/winfonts/
docker cp "C:\Windows\Fonts\msyh.ttc"    qgis-server:/usr/share/fonts/truetype/winfonts/
docker cp "C:\Windows\Fonts\msyhbd.ttc"  qgis-server:/usr/share/fonts/truetype/winfonts/

#    Linux 字体路径示例:
#    docker cp /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf qgis-server:/usr/share/fonts/truetype/winfonts/

# 3. 刷新字体缓存
docker exec qgis-server fc-cache -fv

# 4. 验证字体已识别
docker exec qgis-server python3 -c "
from PyQt5.QtGui import QFontDatabase
db = QFontDatabase()
zh = [f for f in db.families() if 'SimHei' in f or 'YaHei' in f or 'SimSun' in f]
print('中文字体:', zh)
"

持久化方案

容器重建后字体丢失。可选方案:

方案 A:挂载单个字体文件

docker run -d ... \
  -v "C:\Windows\Fonts\simhei.ttf:/usr/share/fonts/truetype/winfonts/simhei.ttf" \
  -v "C:\Windows\Fonts\simsun.ttc:/usr/share/fonts/truetype/winfonts/simsun.ttc" \
  ...

方案 B:挂载整个字体目录(推荐)

# 先在主机创建字体目录,放入所需字体文件
mkdir -p /opt/qgis-fonts
cp /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf /opt/qgis-fonts/

docker run -d ... \
  -v "/opt/qgis-fonts:/usr/share/fonts/truetype/winfonts:ro" \
  ...

5. 验证容器

# 检查容器状态
docker ps

# 测试 QGIS 可用性
docker exec qgis-server python3 -c "from qgis.core import QgsApplication; print('OK')"

# 查看容器内项目代码
docker exec qgis-server ls /app/app/services/qgis/

6. 配置文件

所有 Docker 相关配置集中在 settings.toml[default] 段:

[default]
# ---- Docker QGIS 配置 ----
QGIS_DOCKER_CONTAINER = "qgis-server"    # 容器名称(需与 docker run --name 一致)
QGIS_DOCKER_PROJECT_DIR = "/app"          # 容器内项目代码挂载路径
QGIS_DOCKER_PYTHON = "/usr/bin/python3"   # 容器内 Python 解释器路径
QGIS_DOCKER_IMAGE = "qgis/qgis:3.44.11"  # Docker 镜像名称
QGIS_DOCKER_PREFIX_PATH = "/usr"          # QGIS prefixPath(安装根目录)
QGIS_DOCKER_PYTHONPATH = [                # QGIS Python 包路径列表
    "/usr/lib/python3/dist-packages",
    "/usr/lib/python3.10/dist-packages",
    "/usr/lib/python3.11/dist-packages",
    "/usr/lib/python3.12/dist-packages",
]
QGIS_DOCKER_QT_PLATFORM = "offscreen"     # Qt 无头模式
QGIS_DOCKER_KEEP_ALIVE = "sleep infinity" # 容器保活命令

# ---- 专题图参数 ----
QGIS_GPKG_DIR = "app/data/gpkg"           # GPKG 目录(相对于项目根)
QGIS_EXPORT_DPI = 200                     # 导出 DPI
QGIS_PARALLEL_WORKERS = 4                 # 并行 docker exec 子进程数
QGIS_MAX_CONCURRENT = 2                   # 最大并发请求数

# ---- 文件路径 ----
FILE_STORE_DIR = "G:/files"               # 主机端文件输出目录

环境变量覆盖

export QGIS_DOCKER_CONTAINER="qgis-server"
export QGIS_DOCKER_PROJECT_DIR="/app"
export QGIS_DOCKER_PYTHON="/usr/bin/python3"

跨机器适配

不同机器只需修改 settings.toml 中的路径配置:

配置项 开发机 (Windows) 生产机 (Linux)
FILE_STORE_DIR "G:/files" "/data"
DB_HOST "47.92.216.173" "10.22.245.138"
DB_PORT 7654 54321

7. 目录结构

项目根目录/
├── app/
│   ├── data/
│   │   ├── gpkg/              # 静态底图 GeoPackage 文件
│   │   └── template/          # 专题图模板 (.qgz)
│   ├── services/qgis/
│   │   ├── qgis_env.py        # Docker 环境配置(路径映射、命令构建)
│   │   ├── qgis_runner.py     # 容器内子进程入口
│   │   ├── map_service.py     # 地图生成主流程
│   │   ├── template_modifier.py  # 模板 XML 修改
│   │   └── map_exporter.py    # 图片导出
│   └── api/
│       └── qgis_map_export.py # FastAPI 专题图导出接口
├── config.py                  # Dynaconf 配置入口
├── settings.toml              # 全部配置
├── requirements.txt           # Python 依赖
├── QGIS_DOCKER_README.md      # 本文档
└── tmp/                       # 临时文件目录(容器内映射为 /app/tmp/)

8. 临时文件

  • 主机端临时 JSON(批量产图配置):写入 {项目根}/tmp/,容器内可通过 /app/tmp/ 访问
  • 容器端临时 .qgz(修改后的模板):写入容器内 /tmp/,由 runner 自动清理

9. 故障排查

# 容器未运行
docker ps -a | grep qgis-server

# 查看容器日志
docker logs qgis-server

# 手动测试 runner
docker exec qgis-server python3 /app/app/services/qgis/qgis_runner.py --help

# 检查 QGIS 依赖
docker exec qgis-server python3 -c "
from qgis.core import QgsApplication
QgsApplication.setPrefixPath('/usr', True)
app = QgsApplication([], False)
app.initQgis()
print('QGIS', QgsApplication.version())
app.exitQgis()
"

# 检查中文字体
docker exec qgis-server python3 -c "
from PyQt5.QtGui import QFontDatabase
db = QFontDatabase()
zh = [f for f in db.families() if any(k in f for k in ['SimHei','YaHei','SimSun','WenQuanYi'])]
print('中文字体:', zh if zh else '未安装!')
"

# 检查 GPKG 文件
docker exec qgis-server ls /app/app/data/gpkg/

# 检查临时文件目录
docker exec qgis-server ls /app/tmp/

10. 工作流程

用户请求 POST /qgis/export/map
  → docker inspect 检查容器是否运行
  → 查询推理结果
  → 扫描模板文件夹
  → 构建配置(路径映射到容器内路径)
  → 并行启动 docker exec 子进程
    → 容器内 qgis_runner.py
      → 加载 QGIS Python 包
      → 初始化 QgsApplication
      → 逐模板处理:
        → TemplateModifier 修改模板 XML(替换连接参数、静态层→GPKG
        → project.read() 加载模板
        → 图层过滤、缩放、文本更新
        → 导出 JPG
  → 实时写入进度表