Real life story – the journey from dev to prod with containers

This story on Techcrunch, ‘I want to run stateful containers too‘ rang home with me as it reads very similar to my own learning experience playing with containers so far, although at this point I haven’t had the opportunity to take a container based system into production, but he covers a lot of the same questions as I have so far. I imagine this journey and shift in thinking from monolithic systems to container based services is a common experience for everyone starting down this path.

Wildfly Swarm JAX-RS in a Docker Container deployed to IBM Bluemix

More playing with Docker. Now, where can I deploy a container ‘for real’ to the cloud? A few options:

  • AWS has EC2 Container Service (ECS). If you haven’t used AWS before you can get a year of AWS for free. If you’ve already used up your free year, then it looks like you’ll have to pay as you go for ECS
  • Google Container Engine have a $300 free offer for 60 days
  • Digital Ocean – $5/month on lowest spec plan
  • Heroku has Docker support using a Docker image of their own runtime platform, Heroku Cedar. This is an interesting approach, but sounds more attractive if you’re already planning on deploying to Heroku as your target platform
  • OpenShift Origin v3 is Docker based, but only if you’re running OpenShift Origin yourself. OpenShift Online is still their own proprietary gears and cartridges (although still an awesome PaaS that I use for a lot of my side-projects – this blog is hosted on OpenShift).

So where does IBM’s Bluemix fit in to this picture? I have to admit, until seeing IBM’s presentations at JavaOne this year about how awesome their cloud service is, I hadn’t even taken a look at what they have to offer (I think years of devolping and deploying to Websphere v5 had left me emotionally scarred). Bluemix is Cloud Foundry based, with tons of other stuff thrown in. Including support for hosting Docker containers. And, free developer accounts (when CPU and stoarage kept below a certain level) – check out the pricing calculator. Ok, so I’m in, let’s get set up.

Deployment is via the CloudFoundry CLI with the IBM Containers plugin, instructions are here. Here’s a summary:

  • Installer Docker if not already installed
  • Install CloudFoundry CLI
  • Install IBM Containers cf plugin
  • Login to Bluemix: cf login -a api.ng.bluemix.net
  • Login in IBM Containers: cf ic login (login with Bluemix credentials when prompted)

There’s a couple of options to interact with your containers and walking through the docs the first time it’s a little confusing as they list all three alternatives for each step – either with docker cli directly, with cf cli or with the ice cli.

From the Bluemix containers dashboard, when you click add custom container, the steps it gives you are using cf cli to login, and then use the Docker cli, so this is the approach I used (the ice cli seems to give me 503 Bd Gateway errors that I couldn’t get beyond, so I followed the cf and Docker steps).

There’s an interactive tutorial for using the ice utilities from here. If you’ve worked through some other online interactive tutorials then this will look familiar 🙂 (e.g. Git tutorial)

I already have my image running a JAX-RS endpoint using WildFly Swarm – let’s get it deployed to Bluemix.

I already have my image, built with:

docker build -t wildflyswarm-jaxrs

Next, we need to tag it:

docker tag wildflyswarm-jaxrs registry.ng.bluemix.net/kevinhooke/wildflyswarm-jaxrs
  • the first param is the tag name from when we built the image
  • second param is the Bluemix image repository uri, followed by your account namespace (set when you register with Bluemix), and then the tagname for your image
To push the tagged image to the Bluemix repository:
docker push registry.ng.bluemix.net/kevinhooke/wildflyswarm-jaxrs

At this point if you head over to your Bluemix Containers dahboard, you’ll see your image listed (along with some provided images from IBM):

 Using the cf cli with the IBM Containers plugin, ic), you can issue docker comands direct to Bluemix, so to list containers remotely:

cf ic images

Let’s start a container from the image!

cf ic run --name jaxrs-test registry.ng.bluemix.net/kevinhooke/wildflyswarm-jaxrs

This should return you a container id.

So is it running? Let’s pass a Docker ps using cf ic:

cf ic ps

We’re up and running! (or at least building, it hasn’t started yet)

Now how do we access the service in the container? It seems like we need to go to the online console and request a public IP for the container. The free account gives you 2 free public IPs:

From the cli, the approach is to request a public ip:

cf ic ip request

and note the returned ip.

Now start the container and pass the ip from the previous step – I’m also binding 8080 from my container to 8080 on the host to make it accessible:

cf ic run -p 134.168.10.169:8080:8080 --name jaxrs-test registry.ng.bluemix.net/kevinhooke/wildflyswarm-jaxrs

The docs say the public ip may take a few minutes before it’s available.

I don’t know how long the containers in Bluemix normally take before they transition to running, for me it seemed to take a while in the ‘networking’ status.

After waiting a few minutes and checking the console, SUCCESS, we’ve transitioned to ‘running’:

And pointing a browser at the public ip and port, the app is up and running! JAX-RS using WildFly Swarm, in a Docker container, deployed to IBM Bluemix!

 

WildFly Swarm microservice development: JAX-RS app deployed to a Docker container

WildFly Swarm is an interesting approach to the ‘small is good’ current trend that is ignoring the traditional, overly large Java EE App Servers, and instead deploying self-contained, executable Jars without needing a whole App Server to execute.

Rather than requiring the whole Java EE stack, WildFly Swarm lets you chose only the implementations of the parts that your service needs to execute. Need JAX-RS? Then pull it in via a Maven dependency. Need JPA? Pull that in too. Any other required transitive dependencies are automatically pulled in via Maven. Pulling in JAX-RS also requires a Servlet container and APIs, but this is pulled in for you via Maven transitive dependencies. The end result is a self-contained, packaged Jar that contains everything it needs to run with a ‘java -jar’ command.

I’ve been spending some time recently looking at getting Weblogic Portal 10.3.6 running in a Docker container (check my work in progress changes on GitHub here). As EE containers go, it’s big. It’s heavy. If you want to describe anything as a monolith, then this is your perfect example. So switching gears I wanted to go to the other extreme and look at how you would build a lightweight Java based service, and WildfFly Swarm looked pretty interesting.

I attended one of the sessions at JavaOne this year giving an intro to Swarm, so it’s been on my todo list to take a look.

Getting started with Swarm is actually pretty easy, as it’s all driven by Maven dependencies and plugins. There’s an easy to follow tutorial here. The example apps showing different WildFly components packaged using Swarm are also worth a look here.

I worked through the examples putting together a JAX-RS helloworld app, and also a Dockerfile to package and deploy it to a Docker container. It was actually pretty easy, and my app ended up looking much like the provided examples.

My example JAX-RS resource is pretty simple, nothing complicated here:

If you’re looking for the different WildFly services that you can package with Swarm, browsing the mvnrepository is a good place to start to quickly grab the mvn deps, or browse the examples or source.

For the Maven war and wildfly-storm plugins:

[code]<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins>/groupId>
<artifactId>maven-war-plugin>/artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>WEB-INF/lib/wildfly-swarm-*.jar>/packagingExcludes>
</configuration>
</plugin>
<plugin>
<groupId>org.wildfly.swarm</groupId>
<artifactId>wildfly-swarm-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>[/code]

Next, adding a dependency for the WildFly Swarm JAX-RS:

[code]
<dependency>
<groupId>org.wildfly.swarm</groupId>
<artifactId>wildfly-swarm-jaxrs</artifactId>
<version>1.0.0.Alpha5</version>
</dependency>[/code]

To build the self-contained executable Jar, build with ‘mvn package’ – this will build the app as normal to target, but also include a *-swarm.jar – this the self-contained Jar containing all the WildFly dependencies, and can be run standalone with ‘java -jar helloworld-swarm-0.0.1-SNAPSHOT-swarm.jar’

Building a Docker container

Given that we’ve now got a simple, single, self-contained Jar, deploying this into a Docker container is also pretty easy as we have no other dependencies to worry about (we just need a JVM).

An example Dockerfile would look like (scroll to the right):

[code]
FROM java:openjdk-8-jdk
ADD target/helloworld-swarm-0.0.1-SNAPSHOT-swarm.jar /opt/helloworld-swarm.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/opt/helloworld-swarm.jar"]
[/code]

All we’re doing here is:
1. Creating an image based on the official openjdk image from DockerHub

2. Adding our built WildFly Swarm jar containing our app into /opt in the image

3. Exposing port 8080

4. Defining the Entrypoint that executes when the container starts

To create an image based on this Dockerfile:

docker build -t helloworld .

To start a container based on this image:

docker run -d -p 8080:8080 imageid

… this creates a new container based on the new container, runs it as a daemon, with port 8080 in the container exposed to 8080 on the host. Done!

Hit the URL of our endpoint using the IP of the running Docker-Machine:

To watch the logs of the running container, do:

docker logs -f containerid

In this case you see the output as the WildFly Undertow Servlet container starts up, and initializes our JAX-RS based app:

Pretty simple! We’ve got a minimal WildFly server starting up inside a Docker container in about 10s, and our app deploying in 2s. That’s pretty good if you ask me 🙂