So you want customized WebSphere container images? Here’s how!

John Alcorn
AI+ Enterprise Engineering
7 min readMar 11, 2020

Customers often want to create customized, curated container images of both traditional WebSphere Application Server (tWAS) and WebSphere/Open Liberty, and have their developers start from these when writing cloud-native apps. Let’s look at how to go about that.

Developing microservices that run in a containerized WebSphere is easy. Usually you would just pull either traditional WebSphere (docker pull ibmcom/websphere-traditional:latest-ubi) or Liberty (docker pull openliberty/open-liberty:kernel-java8-openj9-ubi) from DockerHub, add your war/ear file and any configuration (like a Liberty server.xml, a tWAS Jython script, a trust store with an SSL cert to trust, etc), and call configure.sh to prepare the image.

Let’s take a look at my notification-twitter microservice, from my IBM Stock Trader sample. As we saw in an earlier article, I have both tWAS and Open Liberty versions of it. Here’s the Dockerfile for the tWAS version:

FROM ibmcom/websphere-traditional:latest-ubiCOPY --chown=was:root jvm.options /config/jvm.optionsCOPY --chown=was:root target/notification-twitter-1.0-SNAPSHOT.war /work/app/NotificationTwitter.warCOPY --chown=was:root installApp.py /work/config/installApp.pyCOPY --chown=was:root registerTwitterSSLCertificate.py /work/config/registerTwitterSSLCertificate.pyENV ENABLE_BASIC_LOGGING=trueRUN /work/configure.sh

That’s pretty straightforward. Just start from the image in DockerHub, copy in the war file and a few configuration files, set up basic logging (so oc logs shows human-readable logs, rather than JSON), and kick off the configuration script.

The Liberty version (whether using commercial Liberty or Open Liberty) of the Dockerfile is quite similar:

# FROM ibmcom/websphere-liberty:kernel-java8-openj9-ubiFROM openliberty/open-liberty:kernel-java8-openj9-ubiCOPY --chown=1001:0 server.xml /config/server.xmlCOPY --chown=1001:0 jvm.options /config/jvm.optionsCOPY --chown=1001:0 target/notification-twitter-1.0-SNAPSHOT.war /config/apps/NotificationTwitter.warCOPY --chown=1001:0 key.jks /config/resources/security/key.jksRUN configure.sh

Again, we just started from what IBM published to DockerHub, copied in our app’s unique contents, and kicked off the configuration.

For quick samples and proof-of-concept situations, the above is sufficient. But in the real world, most customers want to customize the image somewhat, and then put that customized version in their curated container image registry (as most customers have security policies prohibiting pulling stuff from DockerHub during a production build).

For example, the images in DockerHub contain a generated self-signed SSL certificate, and most customers lock down their browsers to not allow you to connect to such an https URL with an untrusted certificate. Most of the enterprise customers we deal with either host their own private Certificate Authority (CA), or they have purchased legit certificates from some public CA like Digicert or Let’s Encrypt. And thus they want their customized images to use and trust certificates issued by that CA.

In Liberty, for example, that would mean copying a .jks or .p12 file (usually created/maintained via the keytool utility) into the container’s /config/resources/security directory, and having the /config/server.xml point to that file and provide its password, such as this snippet from my notification-twitter microservice’s server.xml:

<keyStore id="defaultKeyStore" password="passw0rd" type="jks" location="${server.config.dir}/resources/security/key.jks" /><sslDefault sslRef="RpSSLConfig" /><ssl id="RpSSLConfig" keyStoreRef="defaultKeyStore" />

One trick you can do in Liberty, in the customized image’s server.xml, is to make use of an include stanza. So you’d get the default server configuration the way you want it for all of your company’s apps (including, perhaps, having a naming convention that the war file always be called application.war or whatever, so that the default server.xml in the customized image can contain the webApplication stanza), then have it do an include of an additional XML file, such as <include location="/config/includes/server-addons.xml" />, so that applications can extend the server configuration with any additional features or other stanzas they require — but starting from a known good, corporate approved default server configuration.

You can even get fancy and use an environment variable there, so that you include a different XML based on a field in a config map that gets passed to the container as an env var such as AUTH_TYPE, like <include location="/config/includes/${env.AUTH_TYPE}.xml, so that you don’t have to rebuild the container to switch authentication types.

Other customizations we often encounter include pre-configuring corporate-approved shared libraries, and setting JVM properties like heap size, etc. Most customers also generally want to subject it to some good vulnerability scans and intrusion tests, so you only publish customized images that meet your security requirements.

Of course, normal licensing considerations apply, whether working directly with the IBM published container images, or with customized versions you create. Likewise, the normal support process would be followed, regardless of any customizations you might have made.

Once you’ve written the Dockerfile for your customized image, to start from the IBM provided image (for either tWAS or Liberty) and then add whatever customizations you want, you then would just build that image like usual and tag and push it to your container image registry:

docker build -t customized-liberty -f Dockerfile.customized .
docker tag customized-liberty:latest corporate-registry/customized-liberty:latest
docker push corporate-registry/customized-liberty:latest

Then your microservice would just change its FROM line to reference that (and copy the add-on server.xml configuration file), as in this for our notification-twitter example (Liberty flavor):

FROM corporate-registry/customized-liberty:latestCOPY --chown=1001:0 server.xml /config/includes/server-addons.xmlCOPY --chown=1001:0 jvm.options /config/jvm.optionsCOPY --chown=1001:0 target/notification-twitter-1.0-SNAPSHOT.war /config/apps/NotificationTwitter.warCOPY --chown=1001:0 key.jks /config/resources/security/key.jksRUN configure.sh

Note that our IBM Cloud Transformation Advisor tool will assess your existing tWAS ND cells and will provide feedback on the effort to modernize those apps to Liberty in OpenShift; this includes generating a Dockerfile for you. You would make the same update as I did above to what it generates, to get it to use your customized image.

So now we’ve seen the typical story for how you can create and use a customized image of WebSphere. This is the generally recommended path: just start from the image in DockerHub, add files to it as desired, then build and push that to your private image registry and have your developers start from that.

That being said, there is one last thing we’ll cover here. In certain special situations, you may not be able to start from the image IBM published to DockerHub. For example, if your company only allows images based on SuSE Linux, rather than the typical images IBM provides based on either Ubuntu or RHEL (the ones ending in -ubi — for Universal Base Image — are RHEL-based images, and are the typically recommended images these days), or if you require a non-standard JVM (such as GraalVM), then you wouldn’t be able to start from the IBM image.

In such an unusual case, where the layers underneath the layer for WebSphere itself in the Docker image are unacceptable, the recommendation is to clone the open sourced Git repo that IBM actually uses to build the image in DockerHub, and edit the Dockerfile you see there, like to start FROM a different Linux distribution.

For example, if you really need to build a Liberty image completely from scratch, you’d git clone https://github.com/WASdev/ci.docker and follow the instructions in its README.md file. You’d likely want to edit the Dockerfile at https://github.com/WASdev/ci.docker/blob/master/ga/latest/kernel/Dockerfile.ubi.ibmjava8 before kicking off the docker build. BTW, while looking around there, I see they copied in a couple of my Stock Trader microservices as test cases, under https://github.com/WASdev/ci.docker/tree/master/test, like the test-stock-trader (my Trader UI) and the test-stock-quote (that gets the stock quotes from the internet) subdirectories — very cool!

The story is very similar for tWAS, if you git clone https://github.com/WASdev/ci.docker.websphere-traditional and follow the instructions in its README.md, likely focusing on tweaking what you see at https://github.com/WASdev/ci.docker.websphere-traditional/blob/master/docker-build/9.0.5.x/Dockerfile.ubi8.

In summary, it’s very easy to build microservices that run in containerized WebSphere middleware. It’s also a fairly straightforward process for how customers can create their own customized images, that they can publish to their curated corporate image registry. And generally their developers will only need to edit the first line in their Dockerfile to start from such a customized image. A customer might even make multiple versions of such images, such as one for use by one department, and another for use by a different group, simply using different tags, and telling their developers which image and tag to specify in their Dockerfile when building their microservices.

Oh, and one final thought — the WebSphere team has done a great job creating and open-sourcing an OpenShift operator for installing a microservice based on Open Liberty. Take a look at https://github.com/OpenLiberty/open-liberty-operator/blob/master/doc/user-guide.md — it’s pretty cool; it even has some day 2 operations you can invoke, like to get at traces and dumps.

Open Liberty operator

You just specify the name of the image and tag you used when you built and pushed your microservice image to your corporate image registry. For example, if you built a hello-world microservice based on your customized Liberty container, you’d specify corporate-registry/hello-world:latest in the Operator form UI.

Operator form UI

I personally learned a lot from this open-sourced operator when writing the umbrella operator for installing my Stock Trader sample, especially the CustomResourceDefinition (CRD) and ClusterServiceVersion (CSV) files. The operator for Open Liberty is a first-class citizen of Operator Hub, and is now a full member of the Red Hat OpenShift Application Runtimes (RHOAR) — good stuff!

The Open Liberty operator in Operator Hub

--

--

John Alcorn
AI+ Enterprise Engineering

Member of the Cloud Journey Optimization Team at Kyndryl. Usually busy writing/testing code, or teaching others what I’ve learned.