Mapserver Docker image using lighttpd build with GDAL/OGR, PostGIS to serve out WMS, WFS and WCS services.
docker build -t pdok/mapserver .
docker run -e MS_MAPFILE=/srv/data/example.map -e SERVICE_TYPE=WMS --rm -d \
-p 80:80 --name mapserver-example -v `pwd`/example:/srv/data pdok/mapserver
docker run -e MS_MAPFILE=/srv/data/example.map -e SERVICE_TYPE=WFS --rm -d \
-p 80:80 --name mapserver-example -v `pwd`/example:/srv/data pdok/mapserver
docker stop mapserver-example
This project aims to fulfill two needs:
- create OGC services that are deployable on a scalable infrastructure.
- create a useable Docker base image.
Fulfilling the first need the main purpose is to create an Docker base image that can be run on a platform like Kubernetes.
Regarding the second need, finding a usable Mapserver Docker image is a challenge. Most images expose the &map=... QUERY_STRING in the GetCapabilities, don't run in FastCGI and are based on Apache.
It will create an Mapserver application that runs through lighttpd. With lua scripting the map=... QUERY_STRING is filter from incoming request. In other words the used Mapfile can only be set with an ENV.
The included EPSG file containing the projection parameters only contains a small set of available EPSG code, namely the once used by our organization. If one wants to use additional EPSG projections one can overwrite this file.
The Docker image contains 2 stages:
- builder
- service
The builder stage compiles Mapserver. The Dockerfile contains all the available Mapserver build option explicitly, so it is clear which options are enabled and disabled.
The service stage copies the Mapserver application, build in the first stage to the service stage, and configures lighttpd
docker build -t pdok/mapserver .
For a specific Dutch version which includes a specific (and smaller) epsg file and necessary grid corrections files.
docker build -t pdok/mapserver:nl -f Dockerfile.NL .
This image can be run straight from the command-line. A volume needs to be
mounted on the container directory /srv/data
. The mounted volume needs to
contain a mapserver *.map
file that matches the MS_MAPFILE
env var.
docker run \
--rm -d \
-e MS_MAPFILE=/srv/data/example.map \
-p 80:80 \
--name mapserver-example \
-v `pwd`/example:/srv/data \
pdok/mapserver
Running the example above will create a service on the url http://localhost/mapserver?REQUEST=GetCapabilities&SERVICE=WMS that will accept something like a (GetMap request).
The environment variables that can be set are the following:
DEBUG
MIN_PROCS
MAX_PROCS
MAX_LOAD_PER_PROC
IDLE_TIMEOUT
MS_MAPFILE
PROJ_LIB
The environment variables, with the exception of MS_MAPFILE
have a default
value set in the Dockerfile
.
The GDAL PROJ_LIB
env var is default set with the value
/usr/share/proj
. For performance reasons one would like to set a custom
PROJ_LIB
containing a minimum of available EPSG codes. This can be done with
the mentioned PROJ_LIB
env var.
docker run \
--rm -d \
-p 80:80 \
--name mapserver-run-example \
-v `pwd`/example:/srv/data \
-e DEBUG=0 \
-e MIN_PROCS=1 \
-e MAX_PROCS=3 \
-e MAX_LOAD_PER_PROC=4 \
-e IDLE_TIMEOUT=20 \
-e MS_MAPFILE=/srv/data/example.map \
pdok/mapserver
Altering the proj file is done for different reasons, adding custom projections or removing 'unused' ones for better performance. This can be done in a couple of ways through this setup.
The best example for this is the Dockerfile.NL in this repository. This Dockerfile uses the main Dockerfile as a base image copies specific geodetic grid files and overwrites the default espg with a tuned one for the Netherlands.
A good resource for these geodetic files is the PROJ.org Datumgrid CDN.
Another option is to create a proj file (like in the nl
folder) and
mount this to the container and set the PROJ_LIB
env var to that location by
adding the following parameters to the docker command:
-e PROJ_LIB=/my-custom-proj-dir \
-v `pwd`/path/to/proj/dir:/my-custom-proj-dir \
When starting the container it will create a WMS & WFS service on the
accespoint: http://localhost/mapserver
.
Example requests when the container is run as WMS service
http://localhost/mapserver?SERVICE=WMS&REQUEST=GetCapabilities
http://localhost/mapserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX=50,2,54,9&CRS=EPSG:4326&WIDTH=905&HEIGHT=517&LAYERS=example&STYLES=&FORMAT=image/png&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE
http://localhost/mapserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo&BBOX=48.9306039592783506,0.48758765231731171,55.46504193821721884,12.33319204541738756&CRS=EPSG:4326&WIDTH=1530&HEIGHT=844&LAYERS=example&STYLES=&FORMAT=image/png&QUERY_LAYERS=example&INFO_FORMAT=text/html&I=389&J=537&FEATURE_COUNT=10
Example requests when the container is run as WFS service
http://localhost/mapserver?SERVICE=WFS&REQUEST=GetCapabilities
http://localhost/mapserver?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=example:example&COUNT=1
In our previous configurations we would run NGINX, while this is a good web service and has a lot of configuration options, it runs with multiple processes. There for we needed supervisord for managing this, whereas lighttpd runs as a single process. Also all the routing configuration options aren't needed, because that is handled by the infrastructure/platform, like Kubernetes. If one would like to configure some simple routing is still can be done in the lighttpd.conf.