# QGIS Docker 部署指南 QGIS 专题图产出通过 Docker 容器执行,主进程(Python 3.10)通过 `docker exec` 调用容器内的 QGIS 环境。 ## 1. 环境要求 - Docker(Windows: Docker Desktop;Linux: docker-ce) - 项目代码已部署到服务器 - 输出文件目录已创建 ## 2. 拉取 QGIS 镜像 ```bash # 官方 QGIS 镜像(含 QGIS 3.x + Python + Qt5) docker pull qgis/qgis:3.44.11 # 验证 docker run --rm qgis/qgis:3.44.11 qgis --version ``` ### 无外网环境(离线导入) ```bash # 在有网的机器上导出 docker save qgis/qgis:3.44.11 -o qgis-34411.tar # 传输到目标服务器后导入 docker load -i qgis-34411.tar ``` ## 3. 启动 QGIS 容器 ```bash # ---- Linux / macOS ---- docker run -d \ --name qgis-server \ --restart unless-stopped \ -v "/www/wwwroot/xian_algorithm_new:/app:ro" \ -v "/www/wwwroot/xian_algorithm_new/files:/files" \ 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.toml` 中 `QGIS_DOCKER_CONTAINER` 一致 | | `-v 项目代码:/app:ro` | 项目代码只读挂载,容器内路径由 `QGIS_DOCKER_PROJECT_DIR` 配置 | | `-v 输出目录:输出目录` | 文件输出目录读写挂载,保持主机与容器路径一致 | | `sleep infinity` | 保持容器运行,等待 `docker exec` 调用 | ## 4. 预拷贝静态数据到容器本地 FS(必须,性能关键) WSL2 9P 文件系统随机读取极慢(GPKG 62MB 耗时 6-10s/模板,模板 ZIP 也慢),拷贝到容器内 `/data/` 后读取仅需 ~0.5s。 ### 方式一:Python 脚本(推荐) ```bash python app/script/copy_data_to_container.py ``` 输出示例: ``` === 预拷贝静态数据到容器 qgis-server === [GPKG] 主机目录: F:\project\xian\xian_algorithm_new\app\data\gpkg 文件数: 23, 总大小: 62.3 MB 容器目标: qgis-server:/data/gpkg 拷贝完成: 3.2s, 容器内 23 个文件 [模板] 主机目录: F:\project\xian\xian_algorithm_new\app\data\template 文件数: 34, 总大小: 45.1 MB 容器目标: qgis-server:/data/template 拷贝完成: 2.1s, 容器内 34 个文件 === 总耗时: 5.4s === ``` 其他用法: ```bash python app/script/copy_data_to_container.py --dry-run # 仅查看信息 python app/script/copy_data_to_container.py --only gpkg # 只拷贝 GPKG python app/script/copy_data_to_container.py --only template # 只拷贝模板 ``` ### 方式二:手动 docker cp ```bash # 清理旧文件 docker exec qgis-server rm -rf /data/gpkg /data/template # 拷贝 GPKG docker cp F:\project\xian\xian_algorithm_new\app\data\gpkg qgis-server:/data/gpkg # 拷贝模板 docker cp F:\project\xian\xian_algorithm_new\app\data\template qgis-server:/data/template # 验证 docker exec qgis-server ls -la /data/gpkg docker exec qgis-server ls -la /data/template/rainfall docker exec qgis-server ls -la /data/template/earthquake ``` ### 何时需要重新执行 - GPKG 或模板文件有更新时 - 容器重建后(`/data` 目录丢失) - 首次部署时 ## 5. 安装中文字体(手动,必须执行) QGIS 模板使用了 SimHei(黑体)、SimSun(宋体)、Microsoft YaHei(微软雅黑)等 Windows 中文字体, Docker 镜像默认不包含这些字体,会导致中文全部乱码。**字体需手动安装,代码不会自动安装。** ### 5.1 准备字体文件 字体文件存放在项目根目录的 `fonts/` 目录下,已预置 4 个常用中文字体: ``` fonts/ ├── simhei.ttf — 黑体(模板默认字体) ├── simsun.ttc — 宋体 ├── msyh.ttc — 微软雅黑 └── msyhbd.ttc — 微软雅黑粗体 ``` 如果字体缺失,从 Windows 主机复制(`C:\Windows\Fonts\`): ```bash # Linux 服务器用 SCP 从 Windows 传 scp "C:\Windows\Fonts\simhei.ttf" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ scp "C:\Windows\Fonts\simsun.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ scp "C:\Windows\Fonts\msyh.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ scp "C:\Windows\Fonts\msyhbd.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ ``` ### 5.2 一键安装到容器 ```bash python app/script/install_fonts_to_container.py ``` 输出示例: ``` === 安装中文字体到 Docker 容器 qgis-server === 字体目录: /www/wwwroot/xian_algorithm_new/fonts 字体文件: 4 个 - msyh.ttc (4165 KB) - msyhbd.ttc (4167 KB) - simhei.ttf (2637 KB) - simsun.ttc (10126 KB) 容器目标: qgis-server:/usr/share/fonts/truetype/winfonts [1/3] 复制字体文件... OK msyh.ttc OK msyhbd.ttc OK simhei.ttf OK simsun.ttc [2/3] 刷新字体缓存... OK [3/3] 验证字体... 中文字体: ['SimHei', 'SimSun', 'Microsoft YaHei', 'Microsoft YaHei UI'] === 完成,耗时 3.2s === ``` 其他用法: ```bash python app/script/install_fonts_to_container.py --dry-run # 仅查看信息 python app/script/install_fonts_to_container.py --container my # 指定容器名 ``` ### 5.3 持久化(推荐) 容器重建后字体丢失,**强烈建议挂载 `fonts/` 目录**。 ```bash # 停掉旧容器 docker stop qgis-server && docker rm qgis-server # 重建容器,加上字体挂载 docker run -d \ --name qgis-server \ --restart unless-stopped \ -v "/www/wwwroot/xian_algorithm_new:/app:ro" \ -v "/www/wwwroot/xian_algorithm_new/files:/files" \ -v "/www/wwwroot/xian_algorithm_new/fonts:/usr/share/fonts/truetype/winfonts:ro" \ qgis/qgis:3.44.11 \ sleep infinity # 挂载后只需刷新一次缓存 docker exec qgis-server fc-cache -fv ``` ### 5.4 无法获取 Windows 字体时的替代方案 ```bash # Linux 服务器安装开源中文字体 yum install wqy-microhei-fonts # CentOS / RHEL apt install fonts-wqy-microhei # Debian / Ubuntu # 将系统字体复制到项目 fonts/ 目录 cp /usr/share/fonts/wqy-microhei/wqy-microhei.ttc /www/wwwroot/xian_algorithm_new/fonts/ ``` ## 6. 验证容器 ```bash # 检查容器状态 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/ ``` ## 7. 配置文件 所有 Docker 相关配置集中在 `settings.toml` 的 `[default]` 段: ```toml [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_DOCKER_GPKG_DIR = "/data/gpkg" # 容器内 GPKG 本地路径(预拷贝后读取) QGIS_DOCKER_TEMPLATE_DIR = "/data/template" # 容器内模板本地路径(预拷贝后读取) QGIS_EXPORT_DPI = 200 # 导出 DPI QGIS_PARALLEL_WORKERS = 4 # 并行 docker exec 子进程数 QGIS_MAX_CONCURRENT = 2 # 最大并发请求数 # ---- 文件路径 ---- FILE_STORE_DIR = "G:/files" # 主机端文件输出目录 ``` ### 环境变量覆盖 ```bash 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` | ## 8. 目录结构 ``` 项目根目录/ ├── 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 专题图导出接口 ├── script/ │ └── copy_data_to_container.py # GPKG + 模板预拷贝脚本 ├── config.py # Dynaconf 配置入口 ├── settings.toml # 全部配置 ├── requirements.txt # Python 依赖 ├── QGIS_DOCKER_README.md # 本文档 └── tmp/ # 临时文件目录(容器内映射为 /app/tmp/) ``` ## 9. 临时文件 - 主机端临时 JSON(批量产图配置):写入 `{项目根}/tmp/`,容器内可通过 `/app/tmp/` 访问 - 容器端临时 .qgz(修改后的模板):写入容器内 `/tmp/`,由 runner 自动清理 ## 10. 故障排查 ```bash # 容器未运行 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 /data/gpkg/ # 本地路径(快,需预拷贝) # 检查模板文件(容器本地路径) docker exec qgis-server ls /data/template/rainfall/ # 本地路径(快) docker exec qgis-server ls /data/template/earthquake/ # 检查临时文件目录 docker exec qgis-server ls /app/tmp/ ``` ### 静态数据相关问题 | 问题 | 原因 | 解决 | |------|------|------| | 产图慢(>60s) | GPKG/模板从挂载目录读取(9P 慢) | 执行 `python app/script/copy_data_to_container.py` | | 日志显示 `gpkg_dir=/app/...` | 未预拷贝或 `QGIS_DOCKER_GPKG_DIR` 为空 | 检查 settings.toml 配置 | | 容器重建后变慢 | `/data` 目录丢失 | 重新执行拷贝脚本 | | `docker cp` 权限错误 | 容器未运行 | `docker start qgis-server` | ## 11. 工作流程 ``` 用户请求 POST /qgis/export/map → docker inspect 检查容器是否运行 → 查询推理结果 → 扫描模板文件夹 → 构建配置(路径映射到容器内路径) → 并行启动 docker exec 子进程 → 容器内 qgis_runner.py → 加载 QGIS Python 包 → 初始化 QgsApplication → 逐模板处理: → TemplateModifier 修改模板 XML(替换连接参数、静态层→GPKG) → project.read() 加载模板 → 图层过滤、缩放、文本更新 → 导出 JPG → 实时写入进度表 ```