We'll build a simple Spring Boot app with a REST endpoint that takes an incoming message and adds it to an ActiveMQ message queue. A second endpoint will use a receiver component to consume the next message from the queue and return it to the client. The application itself is simple but it'll provide a pretty realistic use case for how you might use multiple Docker containers during development.
|Fig 1.0 - Boot & ActiveMQ - Multi Container App|
Application ComponentsI'm not going to cover the application code in a huge amount of detail but I'll describe the fundamentals components and how they fit together. The MessageController below exposes 2 endpoints, one for posting a message and one for retrieving a message. I've injected a MessageSender and MessageReceiver which are used to publish messages to and retrieve messages from an ActiveMQ message broker.
MessageSender uses a jmsTemplate to send a message to the queue. This method is called from the REST controller to publish the message received via the HTTP endpoint.
MesssageReceiver uses a JmsTemplate to retrieve a message from the queue. The retrieveMessage method is invoked by the REST controller which will return the message to the client.
SimpleMessageConverter is used by the MessageSender and MessageReceiver components to do some simple transformation. MessageSender uses this class to convert a simple message POJO to a String before publishing to the queue. MessageReceiver uses this class to extract the message String from the Message returned from the queue.
The application.properties below contains a single property that defines the message broker URL. We'll see later that the host name value (activemq) is significant when we come to linking Docker containers.
Running ActiveMQNow that we've defined the app lets look at setting up ActiveMQ. We need an ActiveMQ instance with a single queue definition called TestQueue. Remember that we reference TestQueue as the destination in MessageSender and MessageReceiver. To save you time I've already created such a Docker image and pushed it to DockerHub. Running the command below will pull the image from DockerHub and start a container.
The --name flag allows us to provide a name for the container we're starting. We'll use this name later when we are linking the app container to this ActiveMQ container. The -p 8161:8161 tells Docker to expose port 8161 to the host machine on port 8161. This is required so that we can access the message broker admin console when it starts and check that the message queue is configured and ready to use. When the container starts up open a browser and go to http://localhost:8161/admin/queues.jsp. You should see a single queue defined as shown below.
|Fig 1.1 - ActiveMQ Queue Definition|
Building the Application ImageWe'll start by defining a simple Dockerfile that takes a pre built JAR from the target directory and adds it to the image. The ENTRYPOINT tells Docker to run the executable JAR on container startup.
To create an image form the Dockerfile simply run docker build -t "docker-activemq" . as shown below.
Linking ContainersIf you've followed the steps so far, you should have an ActiveMQ container up and running and an application image built and ready to run. We're now going to start a second container to run our application and link it to the ActiveMQ container we started earlier.
- -p 8080:8080 exposes port 8080 on the container to port 8080 on the host machine. This is required as the Boot app will start on container port 8080 as soon as the image is started.
- --link activemq:activemq defines the container we want to link to and an alias that we can use to reference it. In this case we are linking to the activemq container we started earlier.
- docker-activemq is the name of the application Docker image we built earlier.,
So how does the app container know how to resolve the above URL to a service exposed in another container? To see how, we're going to open a shell in the app container and have a look. After the app container has started run the following from another terminal window. Note that the value inside quotes is the ID of the application container. To get the container ID run docker ps.
You should now have shell access to the app image and your current working directory should be /app. Remember /app is the working directory we created earlier to add the JAR to. Now lets take a look at the hosts file. Simply run vi /etc/hosts as shown below.
On container startup when we specified the --link command we supplied the container we wanted to link to and an alias that we'd use to reference it. Docker used that alias to create a hosts entry (line 7) to map the container alias activemq to the IP of the activemq container.
When the application in one container attempts to connect to ActivceMQ in the other container, the broker URL tcp://activemq:61616 will resolve to tcp://172.17.0.2:61616. To check that this IP address is indeed the IP address of the ActiveMQ container run the following command.
Testing the ApplicationNow that we've started and established a link between the application and ActiveMQ containers, its time to test it. Post a message to the MessageController endpoint using CURL as follows.
In the app logs you should see a message saying that the message was added to the queue.
You can confirm that the message is indeed on the queue by opening the ActiveMQ admin console.
To retrieve the message from the queue, run curl -i localhost:8080/message/.