How to Deploy GeoServer on OpenShift

Update 2018-Nov-19: Incremented JBoss Image Stream to version 1.2. Thanks to Jonathan Velasquez for finding the error!


This week, I attended the FOSS4G North America conference, in St. Louis. It’s a gathering of geospatial geeks to learn and collaborate on what’s happening in the community. A few of my colleagues and I were there representing Red Hat, a sponsor of the event. We were showing how the OpenShift Container Platform enables continuous delivery and containerization of geospatial applications.

With that thought on the brain, I’d like to show how to build a GeoServer container and deploy it on OpenShift. You’ll need access to an OpenShift install. If you don’t have it running already, try OpenShift Online, or create a Red Hat Developer account to download it for free.

First, we need to login to the cluster:

$ oc login https://ocp.example.com:443
Authentication required for https://ocp.example.com:443 (openshift)
Username: nsabine
Password:
Login successful.

You don't have any projects. You can try to create a new project, by running
    oc new-project 

And create a project:

$ oc new-project geoserver
Now using project "geoserver" on server "https://ocp.example.com:443".

Now that we have a sandbox to play in, let’s tell OpenShift how we want our container image to be built:

$ oc new-build \--binary=true \ 
 \--image-stream=jboss-webserver31-tomcat8-openshift:1.2 \ 
 \--name=geoserver

--> Found image d061e71 (5 months old) in image stream "openshift/jboss-webserver31-tomcat8-openshift" under tag "1.2" for "jboss-webserver31-tomcat8-openshift:1.2"
 
JBoss Web Server 3.1
Platform for building and running web applications on JBoss Web Server 3.1 - Tomcat v8
 
Tags: builder, java, tomcat8

 * A source build using binary input will be created
 * The resulting image will be pushed to image stream "geoserver:latest"
 * A binary build was created, use 'start-build --from-dir' to trigger a new build

--> Creating resources with label build=geoserver ...
     imagestream "geoserver" created
     buildconfig "geoserver" created
--> Success

This command creates a new Build, and specifies the Source-to-Image (S2I) builder image that we plan to use. The builder image is a container that knows how to take a set of defined inputs, such as application source code, binary files, and configuration, and produce a runnable container image for that application. It’s basically a mini CI/CD process, self contained in a container. We chose a Java servlet container builder, and told it that we will be providing a binary WAR file as input.

Now that we’ve created our builder, we need to give it the GeoServer war file as input.

Download the GeoServer Web Archive package. You can get the latest from http://geoserver.org/release/stable/. At the time of writing, the current release is 2.13.0.

$ wget http://sourceforge.net/projects/geoserver/files/GeoServer/2.13.0/geoserver-2.13.0-war.zip
$ unzip geoserver*.zip

There are a number of files in the zip archive, but we’re really just interested in the war file. We need to rename it from geoserver.war to ROOT.war, so that the application server will deploy it as the default application, so we don’t have to type in /geoserver when we go to the web interface.

$ mv geoserver.war ROOT.war

Now let’s send it to OpenShift to start the build:

$ oc start-build geoserver \--from-file=ROOT.war \--follow

Uploading file "ROOT.war" as binary input for the build ...
..
Uploading finished
build.build.openshift.io/geoserver-1 started
Receiving source from STDIN as file ROOT.war
Pulling image "registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift@sha256:29470583ce1511dbbe8e2552b7f0278d0d74595c42a00632cd23fda1ffab361e" ...
Copying all war artifacts from /tmp/src directory into /opt/webserver/webapps for later deployment...
'/tmp/src/ROOT.war' -> '/opt/webserver/webapps/ROOT.war'
Copying all war artifacts from /tmp/src/deployments directory into /opt/webserver/webapps for later deployment...

Pushing image docker-registry.default.svc:5000/geoserver/geoserver:latest ...
Pushed 5/7 layers, 72% complete
Pushed 6/7 layers, 88% complete
Pushed 7/7 layers, 100% complete
Push successful

This uploads the war file and starts the build process. You’ll see it pause for a few moments while the file is uploaded. This may be pretty quick if your OpenShift is local, but could be a few minutes, depending on your available bandwidth. On my hotel WiFi, the build completed in two minutes.

Now that we have built a new container image, let’s run it:

$ oc new-app geoserver

--> Found image 5b17fc9 (2 minutes old) in image stream "geoserver/geoserver" under tag "latest" for "geoserver"

  docker.io/geoserver/geoserver-1:2784125a
  Platform for building and running web applications on JBoss Web Server 3.1 - Tomcat v8
  Tags: builder, java, tomcat8
  * This image will be deployed in deployment config "geoserver"
  * Ports 8080/tcp, 8443/tcp, 8778/tcp will be load balanced by service "geoserver"
  * Other containers can access this service through the hostname "geoserver"

--> Creating resources ...
    deploymentconfig "geoserver" created
    service "geoserver" created
--> Success
    Run 'oc status' to view your app.

You’re now starting up GeoServer! If you log in to the web interface, it should look something like this:

    <img src="/static/img/geoserver/geoserver-deployed.png" alt="GeoServer Deployment in OpenShift Console" />


<figcaption>
    <p>
    GeoServer Deployment in OpenShift Console
    
        
    
    </p> 
</figcaption>

It will take a minute for the application server to start up and deploy the app, so you can monitor progress by viewing the logs. First, find the name of the Kubernetes pod running the application, then tail the logs of that pod:

$ oc get pods
NAME                READY     STATUS      RESTARTS   AGE
geoserver-1-build   0/1       Completed   0          11m
geoserver-1-sk6fd   1/1       Running     0          7m

$ oc logs -f pods/geoserver-1-sk6fd

... SNIP ...

2018-05-16 02:32:53,475 [main] INFO  org.apache.catalina.startup.Catalina- Server startup in 54708 ms

Ctrl-C to exit the logs once you see this last line, indicating the server has started.

Now that the server is running, we just need to create a route so we can access it. To do this, we expose a service, then query for the generated URL:

$ oc expose svc/geoserver
route "geoserver" exposed

$ oc get routes
NAME        HOST/PORT                           PATH      SERVICES    PORT       TERMINATION   WILDCARD
geoserver   geoserver-geoserver.apps.example.com             geoserver   8080-tcp                 None

Open your favorite web browser to that URL, and you’ve got GeoServer!

    <img src="/static/img/geoserver/geoserver.png" alt="Containerized GeoServer, deployed on OpenShift!" />


<figcaption>
    <p>
    Containerized GeoServer, deployed on OpenShift!
    
        
    
    </p> 
</figcaption>

One more thing to do… containers are immutable, so any configuration or data stored in GeoServer will be erased when the container is restarted. The pod needs persistent storage. Challenge accepted.

Create a Persistent Volume Claim. Note that this assumes your OpenShift has Persistent Volumes precreated or a Dynamic Volume Provisioner.

$ oc set volume dc/geoserver --add --name=geoserver-data -t pvc \
  --claim-name=geoserver-claim \
  --claim-size=50G \
  --mount-path=/geoserver-data

deploymentconfig "geoserver" updated

Once the the container redeploys, it will have an empty persistent volume attached. We need to populate it with the initial data load before GeoServer will recognize it. To do this, we need to run a command in the container. First, get the pods, noting the name of the pod with status Running, then run the following exec command to copy the data:

$ oc get pods
NAME                READY     STATUS      RESTARTS   AGE
geoserver-1-build   0/1       Completed   0          1h
geoserver-5-qg7hd   1/1       Running     0          15m

$ oc exec geoserver-5-qg7hd -- bash -c 'cp -rp /deployments/ROOT/data/* /geoserver-data/'

Now that the persistent volume is populated, let’s tell GeoServer to use it:

$ oc set env dc/geoserver GEOSERVER_DATA_DIR=/geoserver-data
deploymentconfig "geoserver" updated

After the application finishes redeploying, GeoServer will preserve configuration and data across restarts. Map away!


comments powered by Disqus