Building a multi-container Spring Boot and MongoDB webapp with Docker 1.12.x

I’ve spent a bunch of time playing with apps in single containers up until now and reached the point where I started to think about questions like:

“ok, so how is this done if you have an application with multiple services, where you may need to scale individual services but not others?”

and

“how does an app in one container talk to another container?”

Composing applications from multiple containers and handling the scaling I think this is the sweetspot for Kubernetes, but I don’t feel ready to ramp up quite that much just yet. Besides, with Docker 1.12 adding ‘swarm mode’ and with docker-compose, it’s looking like Docker has most of the tools you need to build and scale a multi-container app out of the box without adding additional tools to the stack.

So here’s where I started:

  • Container 1: Spring Boot app with JAX-RS RESTful endpoints
  • Container 2: MongoDB database
  • Container 3: Data volume container for MongoDB

As it turns out, this is doesn’t involve too much additional work than just building Dockerfiles for the individual containers and then wiring them together with a docker-compose.yml file.

Starting with each container, here’s the pretty simple Dockerfiles for each:

Spring Boot container:

FROM java:openjdk-8-alpine
ADD SpringBootAddressBook-0.0.1-SNAPSHOT.jar /opt/SpringBootAddressBook-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENV MONGODB_DB_NAME addressbook
ENV MONGODB_DB_HOST mongo
ENV MONGODB_DB_PORT 27017
ENTRYPOINT ["java", "-jar", "/opt/SpringBootAddressBook-0.0.1-SNAPSHOT.jar"]

 

MongoDB container:
MongoDB can be run straight from the official dockerfiles on Docker Hub, using one container for the server and one for a data container – see the complete docker-compose file below.

Bringing it all together, here’s the Docker Compose file to orchestrate the containers together:

version: '2'
services:
    mongodata:
        image: mongo:3.2
        volumes:
        - /data/db
        entrypoint: /bin/bash
    mongo:
        image: mongo:3.2
        depends_on: 
            - mongodata
        volumes_from:
            - mongodata
        ports:
        #kh: only specify internal port, not external, so we can scale with docker-compose scale
            - "27017"
    addressbook:
        image: addressbook
        depends_on: 
            - mongo
        environment:
            - MONGODB_DB_NAME=addressbook
        ports:
            - 8080:8080
        links:
            - mongo

At this point, the group of containers can be brought up as a whole with:

docker-compose up

… and brought down with:

docker-compose down

You can individually scale any container with:

docker-compose scale containername=count

…. where count is the number of container instances to spin up.

 

So what if you want to add in a web frontend as a container too? Easy enough. Here’s an AngularJS frontend, served by nginx:

    web:
        image: docker-web-angularjs
        ports:
            - "80"

Now, if we spin up multiple containers for the REST backend and the nginx frontend as well, we need a load balancer as well, right? Also easy, just add in haproxy:

    lb:
        image: dockercloud/haproxy
        depends_on: 
            - addressbook
        environment:
            - STATS_PORT=1936
            - STATS_AUTH="admin:password"
        links:
            - addressbook
            - web
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
        ports:
            - 80:80
            - 1936:1936

I had noticed that the startup order of the containers was not always predictable, and sometimes the Spring Boot container would start before the MongoDB container was up. This can be fixed by adding the depends_on element. I’m not sure if I really needed to add all the dependencies that I did in order to force a very specific startup order, but this seems to work for me. The order I have in the complete docker-compose.yml is (from first to last):

  • mongodata (data container)
  • mongo
  • addressbook (REST backend)
  • web (AngularJS frontend)
  • haproxy

The complete source for the AddressBook backend is available in this project on github. The deploy-* folders contain the individual dockerfiles:

https://github.com/kevinhooke/SpringBootRESTAddressBook

 

Docker Swarm node set up with Docker 1.12 RC on Raspberry Pi

I’ve been following through the steps in this article to set up a Docker swarm cluster on a pair of Raspberry Pis. Most of the steps work as-is from Mac OS, but for a few steps there’s a couple of variations.

This is most likely going to be part 1 of a few posts as I work through and get this working.

For example, to copy your ssh key to the Pis, instead of:

ssh-copy-id pirate@pi3.local

… you can do (from tip here):

cat ~/.ssh/id_rsa.pub | ssh user@machine "mkdir ~/.ssh; cat >> ~/.ssh/authorized_keys"

To switch between remote hosts:

eval $(docker-machine env pi1)

where pi1 is the name of the remote host.

To switch back to the localhost (not entirely obvious but found the answer here):

eval "$(docker-machine env -u)"

After ‘swarm init’ and adding the other nodes to the cluster, ‘docker swarm ls’ lists the nodes in the cluster:

More to come in part 2:-)

Docker 1.12 RC on the Raspberry Pi

If you’re excited about playing with the swarm mode in the new 1.12 version of Docker, you can download the release candidates for Linux, Windows, Mac, but it’s not obvious where you can get an ARM compatible version to run on the Raspberry Pi.

The awesome guys over at Hypriot talk about running the 1.12 RC on the Pi, but their prebuilt OS images for the Pi don’t yet include the 1.12 RC version. I found this blog post however, which mentions a download location where you can pick up a .deb file and install it on the Pi with dpkg.

Downloaded, installed on my “to be” Pi cluster of 2 Pis, and up and running! Now to deploy something with swarm 🙂

Mac Docker 1.12.0-rc3-beta18 native client and insecure registry

A while back I set up a local Docker Registry to share images between different machines, and configured it as an ‘insecure registry’ since it’s just for testing. With the latest native Docker engine for Mac OS, I was having difficulty pushing to the Registry, it was just fail with a cryptic message:

$ docker push 192.168.1.66:5000/rpi-mongodb

The push refers to a repository [192.168.1.66:5000/rpi-mongodb]

Get https://192.168.1.66:5000/v1/_ping: http: server gave HTTP response to HTTPS client

I noticed in the Docker menu from the menu bar that there’s a Insecure Registries section under Preferences, Advanced Options. Adding the IP and port of my Registry there fixed my problem, now I can push:

$ docker push 192.168.1.66:5000/rpi-mongodb:latest

The push refers to a repository [192.168.1.66:5000/rpi-mongodb]

3d4f4a09f67b: Pushed 

fc91d495516f: Pushed 

5f70bf18a086: Pushed 

532820a7256b: Pushed 

ed62ae893def: Pushed 

994d5442545b: Pushed 

f097d343f850: Pushed 

latest: digest: sha256:c71c6743924243d6117050a1b5b95adf4effee7c9059315c0bfe500f67e0d16b size: 260