Docker部署GIS服务总失败?新手入门环境配置与避坑指南(含:实战脚本)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

《Docker部署GIS服务总失败?新手入门环境配置与避坑指南(含:实战脚本)》这篇文章,专门解决 GIS 初学者在用 Docker 部署 GeoServer、PostGIS、Nginx、WebGIS 前端服务时反复失败的问题。很多报错看起来像 Docker 问题,实际上常见原因是端口冲突、数据卷权限、容器网络、坐标数据路径、服务启动顺序没有处理好。

Docker部署GIS服务失败与GeoServer PostGIS环境配置流程图
一个典型的 Docker GIS 服务环境:WebGIS 前端、GeoServer、PostGIS 与数据卷之间的连接关系。

引言:Docker部署GIS服务为什么新手最容易卡住

Docker 的优势是“环境可复制”,但 GIS 服务并不只是启动一个容器那么简单。一个完整的 GIS 开发环境通常包含空间数据库、地图服务、切片或静态资源服务、前端 WebGIS 应用,有时还会挂载 Shapefile、GeoTIFF、MBTiles、样式文件和插件目录。

新手常见的失败现象包括:

  • PostGIS 容器启动了,但 GeoServer 连不上数据库。
  • GeoServer 页面能打开,但发布图层时报错。
  • WebGIS 前端访问 WMS、WMTS 或矢量瓦片时跨域失败。
  • 容器重启后数据丢失。
  • Windows 或 Linux 上挂载目录后,GeoServer 没有权限读取数据。
  • 端口已经被占用,导致服务看似启动,实际无法访问。

本文按一个最小可用的 GIS 服务环境来讲:PostGIS + GeoServer + Nginx。你可以在此基础上继续接入 Leaflet、OpenLayers、Cesium 或自己的 WebGIS 项目。

背景:一个 Docker GIS 入门环境应该包含什么

如果只是学习 Docker 命令,运行一个单独容器就够了。但如果目标是 Docker部署GIS服务,建议一开始就用多容器编排方式,也就是使用 docker compose。这样可以把数据库、地图服务、反向代理、数据目录、环境变量都写在一个配置文件中,后续迁移和排错会简单很多。

一个入门级 GIS 服务环境通常包括:

  • PostGIS:存储空间表、行政区边界、道路、POI、分析结果等矢量数据。
  • GeoServer:发布 WMS、WFS、WMTS 等 OGC 标准地图服务。
  • Nginx:提供反向代理、静态页面访问、统一入口和跨域配置。
  • 数据卷:保存数据库数据、GeoServer 工作空间、样式、上传的数据文件。
  • 自定义网络:让容器之间使用服务名互相访问,而不是写死 IP。

很多新手直接运行多个 docker run 命令,短期能启动,长期很难维护。更推荐用一个 docker-compose.yml 文件来管理整个 GIS 环境。

原理:Docker部署GIS服务失败通常不是“镜像坏了”

排查 Docker部署GIS服务失败时,不要第一反应就换镜像。大多数问题来自以下四类原理。

1. 容器内外端口不是一回事

例如 GeoServer 容器内部可能监听 8080,你映射为宿主机 8081:8080。这时浏览器应该访问宿主机的 8081,而容器之间访问 GeoServer 时仍应使用容器内部端口 8080

宿主机访问: http://localhost:8081/geoserver
容器间访问: http://geoserver:8080/geoserver

2. 容器之间不要用 localhost 互相访问

在 GeoServer 里配置 PostGIS 数据源时,如果数据库和 GeoServer 是两个容器,数据库主机不要填 localhost。因为对 GeoServer 容器来说,localhost 指的是 GeoServer 容器自己,不是 PostGIS 容器。

正确做法是使用 docker compose 里的服务名,例如:

PostGIS 主机:postgis
端口:5432
数据库:gis
用户名:gis
密码:gis_password

3. 数据卷决定数据是否会丢失

如果没有挂载数据卷,PostGIS 数据库文件和 GeoServer 配置很可能随着容器删除而丢失。GIS 服务的数据通常很重,数据库、样式、工作空间、栅格文件都必须明确挂载。

4. GIS 文件路径和权限经常导致发布失败

GeoServer 发布 Shapefile 或 GeoTIFF 时,如果文件挂载到容器内的路径不对,或者容器用户没有读取权限,就会出现“文件不存在”“无法读取数据源”“图层预览为空”等问题。

步骤:使用 docker compose 部署 PostGIS + GeoServer + Nginx

下面给出一个适合学习和本地开发的实战脚本。生产环境需要进一步处理 HTTPS、备份、安全密码、资源限制和访问控制。

步骤 1:准备项目目录

在你的电脑上创建一个目录,例如:

gis-docker-stack/
├── docker-compose.yml
├── nginx/
│   └── default.conf
├── geoserver_data/
├── postgis_data/
└── web/
    └── index.html

Linux 或 macOS 可以执行:

mkdir -p gis-docker-stack/{nginx,geoserver_data,postgis_data,web}
cd gis-docker-stack

Windows 用户也可以手动创建同样的目录。建议路径不要包含中文、空格和特殊符号,避免挂载路径被 Docker Desktop 解析出错。

步骤 2:编写 docker-compose.yml

services:
  postgis:
    image: postgis/postgis:16-3.4
    container_name: gisyxs_postgis
    environment:
      POSTGRES_DB: gis
      POSTGRES_USER: gis
      POSTGRES_PASSWORD: gis_password
    ports:
      - "5432:5432"
    volumes:
      - ./postgis_data:/var/lib/postgresql/data
    networks:
      - gisnet
    restart: unless-stopped

  geoserver:
    image: docker.osgeo.org/geoserver:2.25.2
    container_name: gisyxs_geoserver
    environment:
      GEOSERVER_ADMIN_USER: admin
      GEOSERVER_ADMIN_PASSWORD: geoserver_password
    ports:
      - "8081:8080"
    volumes:
      - ./geoserver_data:/opt/geoserver_data
    depends_on:
      - postgis
    networks:
      - gisnet
    restart: unless-stopped

  nginx:
    image: nginx:1.27-alpine
    container_name: gisyxs_nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./web:/usr/share/nginx/html:ro
    depends_on:
      - geoserver
    networks:
      - gisnet
    restart: unless-stopped

networks:
  gisnet:
    driver: bridge

这个配置里有几个关键点:

  • postgisgeoservernginx 是服务名,容器之间可以直接使用这些名字访问。
  • 宿主机访问 GeoServer 使用 http://localhost:8081/geoserver
  • 宿主机访问 Nginx 使用 http://localhost:8080
  • PostGIS 数据挂载到 ./postgis_data,GeoServer 配置挂载到 ./geoserver_data

步骤 3:编写 Nginx 反向代理配置

创建 nginx/default.conf

server {
    listen 80;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }

    location /geoserver/ {
        proxy_pass http://geoserver:8080/geoserver/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
        add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization";

        if ($request_method = OPTIONS) {
            return 204;
        }
    }
}

这样前端可以通过 Nginx 访问 GeoServer:

http://localhost:8080/geoserver/

对 WebGIS 开发者来说,这种方式比在前端直接写 http://localhost:8081/geoserver 更接近真实部署结构,也更容易统一处理跨域。

步骤 4:准备一个简单前端页面

创建 web/index.html。如果严格只测试 Nginx 是否工作,可以写一个最简单的 HTML 文件:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>GIS Docker Stack</title>
</head>
<body>
  <h2>GIS Docker Stack is running</h2>
  <p>GeoServer proxy: /geoserver/</p>
</body>
</html>

启动后访问 http://localhost:8080,如果能看到页面,说明 Nginx 静态服务正常。

步骤 5:启动服务

docker compose up -d

查看容器状态:

docker compose ps

查看日志:

docker compose logs -f postgis
docker compose logs -f geoserver
docker compose logs -f nginx

如果容器反复重启,先看日志,不要盲目删除目录。PostGIS 首次初始化、GeoServer 首次写入数据目录都可能需要一点时间。

步骤 6:验证 PostGIS 是否可用

进入 PostGIS 容器:

docker exec -it gisyxs_postgis psql -U gis -d gis

检查 PostGIS 扩展:

SELECT PostGIS_Version();

如果能返回版本信息,说明 PostGIS 已经可用。也可以创建一个测试表:

CREATE TABLE test_points (
  id serial PRIMARY KEY,
  name text,
  geom geometry(Point, 4326)
);

INSERT INTO test_points (name, geom)
VALUES ('gisyxs_test', ST_SetSRID(ST_MakePoint(116.391, 39.907), 4326));

SELECT id, name, ST_AsText(geom) FROM test_points;

步骤 7:在 GeoServer 中连接 PostGIS

浏览器打开:

http://localhost:8081/geoserver

使用配置中的管理员账号登录:

用户名:admin
密码:geoserver_password

创建 PostGIS 数据源时,重点填写:

  • hostpostgis
  • port5432
  • databasegis
  • usergis
  • passwdgis_password
  • schema:通常填写 public

注意:这里的 host 不是 localhost,也不是宿主机 IP,而是 docker compose 服务名 postgis

步骤 8:发布测试图层并验证 WMS

在 GeoServer 中发布 test_points 表后,可以通过图层预览检查 WMS 是否正常。如果通过 Nginx 访问,URL 结构类似:

http://localhost:8080/geoserver/你的工作区/wms

如果地图不显示,先检查以下三点:

  • 图层坐标系是否正确设置为 EPSG:4326。
  • 图层边界是否计算成功。
  • 前端请求的图层名是否包含工作区前缀,例如 workspace:test_points

常见坑:Docker GIS 环境配置最容易忽略的错误

坑 1:端口冲突导致服务访问失败

如果你的电脑已经安装了 PostgreSQL,本机 5432 可能被占用。此时 Docker 会提示端口绑定失败。可以把宿主机端口改成 15432

ports:
  - "15432:5432"

注意,容器之间访问 PostGIS 仍然使用 postgis:5432,不要改成 15432

坑 2:GeoServer 连接数据库时写 localhost

这是 Docker部署GIS服务中最典型的新手错误。容器内部的 localhost 只代表当前容器自己。GeoServer 连接 PostGIS 时必须写服务名 postgis

坑 3:删除容器后发现数据没了

如果没有配置 volumes,删除容器可能会让数据随容器消失。PostGIS 和 GeoServer 都应该挂载数据目录。开发环境可以使用相对路径,生产环境建议使用明确的数据盘路径。

坑 4:Windows 路径和权限问题

Docker Desktop 在 Windows 上挂载目录时,路径权限、换行符、盘符共享都可能造成问题。建议:

  • 项目目录放在英文路径下。
  • 避免路径中出现空格和中文。
  • 使用 WSL2 后端时,尽量把项目放在 Linux 文件系统内。
  • 如果 GeoServer 读取挂载文件失败,先用 docker exec 进入容器检查文件是否真的存在。

坑 5:depends_on 不等于服务完全可用

depends_on 只能保证容器启动顺序,不保证 PostGIS 已经完成初始化。GeoServer 启动后第一次连接数据库失败,不一定是配置错,也可能是数据库还没准备好。

可以等待 PostGIS 日志出现类似“database system is ready to accept connections”的信息后,再去 GeoServer 中配置数据源。

坑 6:WMS 能访问,前端却加载失败

这通常与跨域、代理路径、图层名、坐标系或请求参数有关。先用浏览器直接打开 GetCapabilities:

http://localhost:8080/geoserver/ows?service=WMS&version=1.3.0&request=GetCapabilities

如果能力文档能返回,说明服务本身大概率正常。再检查前端 OpenLayers 或 Leaflet 的图层 URL、参数和控制台报错。

方法比较:docker run、docker compose 与手动安装怎么选

方式 适合场景 优点 缺点
docker run 临时测试单个服务 命令直接,启动快 多容器管理困难,参数容易丢
docker compose 本地 GIS 开发环境、教学环境、小型项目演示 服务关系清晰,便于复现和迁移 需要理解网络、端口、数据卷
手动安装 需要深度定制的服务器环境 可控性强,便于与系统服务集成 环境差异大,迁移和恢复成本高
Kubernetes 多节点、高可用、生产集群 扩展能力强,适合平台化部署 学习成本高,不适合新手入门起步

对于 GIS 初学者和入门级 GIS 工程师,建议先用 docker compose。它能覆盖绝大多数 Docker GIS 入门环境配置问题,也能帮助你理解真实项目中的服务拆分方式。

检查清单:Docker部署GIS服务失败时按这个顺序排查

  • 第一步:看容器是否启动,执行 docker compose ps,确认状态不是 Exited 或反复重启。
  • 第二步:看日志,执行 docker compose logs -f 服务名,优先找端口、权限、数据库初始化、配置文件语法错误。
  • 第三步:查端口映射,确认浏览器访问的是宿主机端口,例如 8081:8080 应访问 8081
  • 第四步:查容器网络,容器之间使用服务名访问,例如 GeoServer 连接 PostGIS 写 postgis
  • 第五步:查数据卷,确认数据库数据、GeoServer 数据目录是否正确挂载。
  • 第六步:查文件权限,进入容器查看挂载文件是否存在、是否可读。
  • 第七步:查坐标系,确认 PostGIS 表的 SRID、GeoServer 图层坐标系、前端地图坐标系是否匹配。
  • 第八步:查服务接口,先测试 GetCapabilities,再测试前端代码。

如果你不确定问题在哪里,最稳妥的方式是从 PostGIS、GeoServer、Nginx 依次验证,而不是一上来就看 WebGIS 前端页面。

FAQ:Docker GIS 服务部署常见问题

Q1:Docker部署GIS服务需要先学 Linux 吗?

不需要先成为 Linux 高手,但至少要会看目录、端口、日志和权限。GIS 服务部署经常涉及数据目录挂载、配置文件修改和服务日志排查,这些基础操作比背 Docker 命令更重要。

Q2:GeoServer 容器能打开页面,但连不上 PostGIS,怎么办?

优先检查数据库主机名是否写成了 localhost。在 docker compose 网络中,GeoServer 连接 PostGIS 应填写服务名 postgis。然后检查用户名、密码、数据库名和 schema 是否一致。

Q3:为什么容器重启后 GeoServer 配置丢失?

通常是 GeoServer 数据目录没有正确挂载。需要把容器内的 GeoServer 数据目录挂载到宿主机目录,例如本文中的 ./geoserver_data:/opt/geoserver_data。不同镜像的数据目录位置可能不同,使用前要查看镜像说明。

Q4:Docker 部署 PostGIS 后,QGIS 怎么连接?

如果端口映射是 5432:5432,QGIS 中主机填写 localhost,端口填写 5432,数据库、用户名、密码与 compose 文件保持一致。如果宿主机端口改成 15432,QGIS 端口就填 15432

Q5:WebGIS 前端访问 GeoServer 出现跨域错误怎么办?

入门阶段推荐通过 Nginx 统一代理 GeoServer,并在 Nginx 中添加跨域响应头。前端请求同一个入口,例如 http://localhost:8080/geoserver/,比直接请求 GeoServer 宿主机端口更容易管理。

Q6:Docker GIS 环境适合生产部署吗?

可以,但本文脚本主要面向学习和本地开发。生产环境还需要处理强密码、HTTPS、备份恢复、资源限制、日志轮转、访问控制、数据库性能优化、镜像版本固定和安全更新。

Q7:PostGIS 表在 GeoServer 中看不到,是什么原因?

常见原因包括表没有 geometry 字段、geometry 字段 SRID 不明确、数据库连接 schema 填错、表权限不足、GeoServer 没有刷新数据源。可以先在 PostGIS 中执行 SELECT Find_SRID('public','表名','geom'); 检查 SRID。

结论:先把网络、端口、数据卷和日志跑通

Docker部署GIS服务并不难,难点在于 GIS 服务链条比普通 Web 服务更长:PostGIS 管数据,GeoServer 发服务,Nginx 做入口,前端负责渲染。任何一个环节的端口、路径、权限或坐标系出错,都会表现为“部署失败”。

新手最推荐的学习路线是:先用 docker compose 跑通 PostGIS、GeoServer 和 Nginx;再发布一个最简单的点图层;最后接入 OpenLayers、Leaflet 或 Cesium。只要你按日志、端口、网络、数据卷、坐标系的顺序排查,大多数 Docker GIS 入门环境配置问题都能定位到具体原因。

记住一个原则:容器启动成功不等于 GIS 服务可用。真正可用的标准是数据库能连接、图层能发布、OGC 服务能访问、前端地图能正确显示。