GIS项目依赖环境复杂?用Docker Compose一键部署PostGIS+GeoServer(含:编排模板)
GIS项目依赖环境复杂?用Docker Compose一键部署PostGIS+GeoServer(含:编排模板)这类问题,在WebGIS项目交付、课程实验环境搭建、团队协作开发中非常常见。PostGIS需要数据库扩展、GeoServer需要Java运行环境和数据目录,手工安装经常出现端口冲突、版本不一致、插件缺失、数据丢失等问题。本文用一个可复制的Docker Compose模板,把PostGIS和GeoServer放到同一个GIS服务栈里,适合快速搭建开发、测试和教学环境。

引言:为什么GIS项目适合用Docker Compose部署PostGIS+GeoServer
很多GIS项目不是一个单独软件能跑起来的,而是一组依赖共同工作:空间数据库负责存储矢量数据,地图服务负责发布WMS、WFS,前端WebGIS再去请求服务。只要其中一个环节版本或配置不一致,就可能导致整个项目无法复现。
用Docker Compose部署PostGIS+GeoServer的核心价值,是把环境描述成一个文本文件。新成员拿到项目后,不需要逐个安装PostgreSQL、PostGIS、Java、GeoServer,只要执行一条命令,就能得到基本一致的GIS运行环境。
本文面向GIS学生、入门GIS工程师、WebGIS开发者和需要本地测试地图服务的空间数据分析人员。目标不是讲Docker全部原理,而是给你一套能落地的PostGIS GeoServer Docker Compose编排模板,并说明每个关键配置为什么这样写。
背景:手工安装PostGIS和GeoServer容易遇到哪些问题
在传统安装方式下,一个GIS项目常见的环境问题包括:
- PostGIS扩展未启用:PostgreSQL装好了,但数据库里没有执行
CREATE EXTENSION postgis;,导致空间字段和空间函数不可用。 - GeoServer版本不一致:本机、测试机、服务器使用了不同版本,样式、插件或REST接口行为不完全一致。
- 端口冲突:本地已有PostgreSQL占用5432端口,GeoServer或Tomcat占用8080端口。
- 数据目录混乱:GeoServer的数据目录如果没有持久化,容器重建后工作区、图层、样式可能丢失。
- 团队协作困难:每个人都说“我这里能跑”,但数据库版本、坐标系定义、服务配置并不一致。
Docker Compose适合解决这种“多组件、多端口、多数据卷”的环境问题。它不会替代你理解PostGIS和GeoServer,但可以把重复安装、重复配置的成本降下来。
原理:Docker Compose如何编排PostGIS和GeoServer
Docker Compose是Docker提供的多容器编排工具。你可以在一个docker-compose.yml文件里声明多个服务,例如一个PostGIS容器、一个GeoServer容器,以及它们共用的网络和数据卷。
在本文的GIS环境里,整体关系可以理解为:
- PostGIS容器:提供PostgreSQL数据库和PostGIS空间扩展,用来存储点、线、面、栅格索引等空间数据。
- GeoServer容器:连接PostGIS数据库,把表发布为WMS、WFS等OGC服务。
- Docker网络:让GeoServer可以通过服务名访问PostGIS,例如主机名写
postgis,而不是写本机IP。 - 数据卷:保存数据库文件和GeoServer数据目录,避免容器删除后配置和数据一起丢失。
需要注意的是,Docker Compose部署PostGIS+GeoServer并不等于生产环境已经完备。生产环境还需要备份、权限、日志、安全、反向代理、HTTPS、资源限制和监控。本文模板更适合开发、测试、教学和小型内网演示。
步骤:准备Docker Compose一键部署PostGIS+GeoServer
1. 准备项目目录
建议为GIS环境单独建一个目录,便于管理编排文件、初始化SQL和备份文件。例如:
gis-stack/
docker-compose.yml
initdb/
01-init-postgis.sql
backup/
docker-compose.yml负责声明服务,initdb目录用于首次启动数据库时自动执行初始化SQL,backup目录可以放数据库导入导出文件。
2. 编写PostGIS初始化SQL
在initdb/01-init-postgis.sql中写入以下内容,用于创建PostGIS扩展和一个示例schema:
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
CREATE SCHEMA IF NOT EXISTS gis;
CREATE TABLE IF NOT EXISTS gis.demo_points (
id SERIAL PRIMARY KEY,
name TEXT,
geom geometry(Point, 4326)
);
INSERT INTO gis.demo_points (name, geom)
VALUES
('sample_point_1', ST_SetSRID(ST_MakePoint(116.391, 39.907), 4326)),
('sample_point_2', ST_SetSRID(ST_MakePoint(121.473, 31.230), 4326))
ON CONFLICT DO NOTHING;
CREATE INDEX IF NOT EXISTS demo_points_geom_idx
ON gis.demo_points
USING GIST (geom);
这里使用EPSG:4326作为示例坐标系,也就是常见的经纬度坐标。实际项目中,如果你使用CGCS2000、高斯投影、Web Mercator或地方坐标系,要在建表时明确SRID,并确保GeoServer发布图层时识别正确。
3. 编写docker-compose.yml编排模板
下面是一份适合开发测试使用的Docker Compose部署PostGIS和GeoServer模板:
services:
postgis:
image: postgis/postgis:16-3.4
container_name: gis_postgis
restart: unless-stopped
environment:
POSTGRES_DB: gisdb
POSTGRES_USER: gisuser
POSTGRES_PASSWORD: gispassword
TZ: Asia/Shanghai
ports:
- "5432:5432"
volumes:
- postgis_data:/var/lib/postgresql/data
- ./initdb:/docker-entrypoint-initdb.d
networks:
- gis_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gisuser -d gisdb"]
interval: 10s
timeout: 5s
retries: 5
geoserver:
image: kartoza/geoserver:2.25.2
container_name: gis_geoserver
restart: unless-stopped
environment:
GEOSERVER_ADMIN_USER: admin
GEOSERVER_ADMIN_PASSWORD: geoserver
GEOSERVER_DATA_DIR: /opt/geoserver/data_dir
TZ: Asia/Shanghai
ports:
- "8080:8080"
volumes:
- geoserver_data:/opt/geoserver/data_dir
depends_on:
postgis:
condition: service_healthy
networks:
- gis_net
volumes:
postgis_data:
geoserver_data:
networks:
gis_net:
driver: bridge
如果你本机已经有PostgreSQL占用5432端口,可以把左侧端口改成其他端口,例如:
ports:
- "15432:5432"
这表示主机用15432访问容器内的5432。GeoServer连接PostGIS时,仍然应该使用Docker网络内的服务名postgis和端口5432,而不是localhost。
4. 启动GIS服务栈
在gis-stack目录下执行:
docker compose up -d
查看容器状态:
docker compose ps
查看日志:
docker compose logs -f postgis
docker compose logs -f geoserver
正常情况下,你应该能看到PostGIS数据库启动成功,GeoServer也完成初始化。第一次启动GeoServer可能需要较长时间,尤其是镜像首次拉取和数据目录首次生成时。
5. 验证PostGIS是否可用
进入PostGIS容器:
docker exec -it gis_postgis psql -U gisuser -d gisdb
执行以下SQL检查PostGIS扩展:
SELECT postgis_full_version();
SELECT id, name, ST_AsText(geom)
FROM gis.demo_points;
如果能够返回PostGIS版本信息和两个示例点,说明数据库、扩展、初始化SQL都正常。
6. 登录GeoServer并连接PostGIS
在浏览器访问:
http://localhost:8080/geoserver
使用模板中的账号登录:
- 用户名:
admin - 密码:
geoserver
然后按以下思路创建PostGIS数据存储:
- 进入GeoServer管理界面。
- 创建工作区,例如
gis。 - 新建数据存储,选择PostGIS。
- 数据库连接参数填写:主机
postgis,端口5432,数据库gisdb,用户gisuser,密码gispassword。 - 保存后,发布
gis.demo_points图层。 - 在图层预览中查看WMS或WFS输出。
这里最容易写错的是主机名。GeoServer在容器内部运行,它访问的localhost是GeoServer容器自己,不是PostGIS容器,也不是你的宿主机。因此,在Docker Compose网络内应使用服务名postgis。
7. 用QGIS验证WMS和PostGIS连接
建议用QGIS做两类验证:
- 验证PostGIS直连:在QGIS中新建PostgreSQL连接,主机填
localhost,端口填宿主机映射端口,例如5432或15432。 - 验证GeoServer服务:在QGIS中新建WMS/WMTS连接,URL填写
http://localhost:8080/geoserver/wms。
如果QGIS能直接加载PostGIS表,也能通过GeoServer加载WMS图层,说明数据库、服务发布和客户端访问链路都已经打通。
常见坑:Docker Compose部署PostGIS GeoServer失败怎么排查
1. GeoServer连接PostGIS时不要写localhost
这是最常见的问题。GeoServer容器里的localhost指向GeoServer自身,不会指向PostGIS。正确写法是使用Compose服务名:
host: postgis
port: 5432
只有从宿主机上的QGIS、DBeaver、pgAdmin访问PostGIS时,才使用localhost加映射端口。
2. 初始化SQL只在数据库数据卷首次创建时执行
/docker-entrypoint-initdb.d中的SQL脚本只会在PostgreSQL数据目录第一次初始化时执行。如果你已经启动过容器,后来修改了01-init-postgis.sql,它不会自动再次执行。
开发环境可以删除数据卷后重新初始化:
docker compose down -v
docker compose up -d
但这会删除数据库数据。真实项目中请先备份,不要直接在生产环境执行。
3. GeoServer数据目录必须持久化
如果没有给GeoServer配置数据卷,容器删除后,工作区、图层、样式、数据存储等配置可能丢失。模板中使用:
volumes:
- geoserver_data:/opt/geoserver/data_dir
这能让GeoServer配置随数据卷保存,而不是只保存在容器临时文件系统中。
4. 端口占用导致服务启动失败
如果本机已有数据库或其他服务占用5432、8080,Docker会提示端口绑定失败。解决方法是修改宿主机映射端口,例如:
ports:
- "18080:8080"
修改后GeoServer访问地址变为:
http://localhost:18080/geoserver
5. 坐标系和SRID不一致会导致图层位置错误
PostGIS表的几何字段有SRID,GeoServer发布图层时也会读取坐标参考系统。如果表中SRID为空、错误或混用,WebGIS前端可能出现图层偏移、范围异常、无法缩放到图层等问题。
排查SQL可以使用:
SELECT ST_SRID(geom), COUNT(*)
FROM gis.demo_points
GROUP BY ST_SRID(geom);
如果结果中出现多个SRID,说明数据需要统一坐标系后再发布。
方法比较:手工安装、Docker单容器和Docker Compose怎么选
| 方式 | 适用场景 | 优点 | 限制 |
|---|---|---|---|
| 手工安装PostGIS和GeoServer | 单机长期使用、需要深度定制环境 | 可控性强,便于学习底层配置 | 迁移困难,团队环境容易不一致 |
| Docker单容器启动 | 只测试一个组件,例如单独测试PostGIS | 命令简单,启动快 | 多服务关系不清晰,网络和数据卷容易散乱 |
| Docker Compose编排 | PostGIS+GeoServer+WebGIS联调、课程实验、团队开发 | 配置集中,可复制,可版本管理 | 仍需理解端口、网络、数据卷和安全配置 |
| Kubernetes部署 | 生产集群、多服务伸缩、平台化运维 | 适合大规模和高可用场景 | 学习成本高,小型项目可能过度复杂 |
对大多数GIS开发和教学项目来说,Docker Compose是比较合适的中间方案:比手工安装更可复现,比Kubernetes更轻量。等项目进入生产阶段,再考虑反向代理、权限加固、备份策略和容器编排平台。
检查清单:部署前后建议逐项确认
- 版本是否固定:不要长期使用不明确的latest标签,建议固定PostGIS和GeoServer镜像版本。
- 密码是否修改:模板中的
gispassword和geoserver只适合本地测试,正式环境必须修改。 - 端口是否冲突:确认5432和8080是否已被本机服务占用。
- 数据卷是否配置:PostGIS数据目录和GeoServer数据目录都应持久化。
- 初始化SQL是否执行:用
SELECT postgis_full_version();确认PostGIS扩展已启用。 - GeoServer连接主机是否正确:容器内访问PostGIS应填写
postgis,不是localhost。 - SRID是否一致:发布图层前检查空间表SRID,避免地图偏移。
- 是否能用QGIS验证:分别测试PostGIS直连和GeoServer WMS服务。
- 是否有备份策略:不要把Docker数据卷当成唯一备份。
FAQ:Docker Compose部署PostGIS+GeoServer常见问题
Q1:Docker Compose部署PostGIS+GeoServer适合生产环境吗?
可以作为生产环境的基础,但不能直接把本文开发模板原样用于生产。生产环境至少要补充强密码、网络隔离、定期备份、反向代理、HTTPS、资源限制、日志管理和访问权限控制。本文模板更适合开发测试和教学演示。
Q2:GeoServer连接PostGIS时主机名为什么填postgis?
因为postgis是docker-compose.yml里的服务名。Docker Compose会为同一网络下的服务提供名称解析。GeoServer容器可以通过postgis:5432访问PostGIS容器。
Q3:为什么修改初始化SQL后没有生效?
PostgreSQL官方镜像的初始化脚本只在数据目录为空时执行。如果数据卷已经存在,脚本不会再次运行。你可以手动进入数据库执行SQL,或者在确认不需要保留数据后使用docker compose down -v删除数据卷重新初始化。
Q4:PostGIS端口改成15432后,GeoServer也要改吗?
通常不需要。15432:5432表示宿主机通过15432访问容器内5432。GeoServer和PostGIS在同一个Docker网络中通信,仍然访问postgis:5432。只有QGIS、DBeaver等宿主机软件访问数据库时,才使用localhost:15432。
Q5:GeoServer数据目录应该放在命名卷还是本地目录?
开发测试可以使用命名卷,简单稳定。如果你需要把GeoServer配置纳入备份、迁移或版本化管理,可以改成本地目录挂载。但要注意文件权限,尤其是在Linux服务器上运行容器时。
Q6:如何备份Docker里的PostGIS数据库?
可以使用pg_dump从容器中导出:
docker exec -t gis_postgis pg_dump -U gisuser -d gisdb -Fc > backup/gisdb.dump
恢复时可以使用pg_restore。正式项目建议制定定期备份策略,并把备份文件存放到容器和宿主机之外的可靠位置。
结论:用编排模板降低GIS环境复现成本
PostGIS和GeoServer是很多WebGIS项目的基础组合,但它们的安装、版本、端口、数据目录和连接配置容易让初学者反复踩坑。用Docker Compose部署PostGIS+GeoServer,可以把这些依赖集中到一个可复制的编排文件中,让开发、测试和教学环境更容易复现。
实际使用时,建议先用本文模板跑通PostGIS扩展、GeoServer连接和QGIS验证,再逐步加入自己的空间数据、样式、工作区和WebGIS前端。记住三个关键点:GeoServer连PostGIS用服务名postgis,数据库和GeoServer都要配置数据卷,正式环境必须单独处理安全和备份。