Delivering crafts in containers – HelloBeer merges with TerraX Micro-Brewery Inc

Alright, the owners of HelloBeerTM have rested on their laurels for quite some time now. All their profits have been spent on the finer things in life. Time to get back in business again. And time to get more commercial while doing so.

For this HelloBeer recently joined Terra 10 – a startup specialized in containers and cloud beers. Their respective breweries have been merged to form Terrax Micro-Brewery Inc, a collab that promises to take the beer brewing business by storm.

This blog will be the start of a series of blogs where we will examine Docker, Containerization, Kubernetes and finally see our brewery startup making its move to the Amazon Cloud.

In this first blog of the series we’ll start at the basics. We’ll just run a simple Spring Boot Rest service in a Docker container and connect to a MySQL database running on the same host as where Docker is running.

First things first, let’s setup all the dependencies before we dive into the code. Oh and the final code, as always, can be found on GitHub here.

Docker installation

There are lots of good guides out there. I used the official guy found here.

MySQL installation

For the MySQL installation (on Ubuntu, what else?) I recommend to follow this guide here.

After installation we can setup a schema that will hold the data for our Spring Boot Service. The initial setup we’re using is explained in detail here.

First we create the database and user that we’ll use to store our beer data.

create database db_terrax; -- Create the new database
create user 'tb_admin'@'%' identified by 'tb_admin'; -- Creates the user
grant all on db_terrax.* to 'tb_admin'@'%'; -- Gives all the privileges to the new user on the newly created database

To make our lives easier for we just use the hibernate database initialization option to initially create our database tables.

Spring Boot application

This goal of this blog to demonstrate the Docker features. So I’ll not explain the application in detail here. The application consists of a couple of REST services. Both expose simple crud operations for the entities Beer, resp. Brewery:

For the code, like I said, check GitHub. For inspiration on how to create such an application, I’ve put quite a few blogs online. You can create it from scratch using the Spring Initializr, like I did in this blog here (and that’s actually the way I created the services for this blog). Or to get even more of a headstart you could use JHipster, like I did in this blog here.

First run (without Docker)

In this first run we’re gonna run the application locally against the mysql database. This will create the tables and gives us a chance to put some data in place.

For this we’ll make use of Spring profiles. The production profile has the necessary properties needed to connect with the mysql database. These properties are set in the application-prod.yml file:

server:
  port: 8090
spring:
  jpa:
    properties:
      javax:
        persistence:
          schema-generation:
            create-source: metadata
            database:
              action: update
              scripts:
                action: drop-and-create
                create-target: ./create.sql
                drop-target: ./drop.sql
  datasource:
    url: jdbc:mysql://localhost:3306/db_terrax
    username: tb_admin
    password: tb_admin

Optionally adjust the settings to make them reflect your environment. The database.action=update property ensures that the database tables are created in the mysql database. I’ve also put in place some properties to generate ddl scripts.

I’ll add these scripts to the GitHub source code so we could basically skip this whole section and just run the create.sql script against our database schema. That would be enough to lay the groundwork to directly work with the Docker images we’ll build next (see the next paragraph).

To set the profile to production, set the following environment variable (as an alternative you could also set this variable in the application.yml file) (note that if you don’t set an active profile, the default will be the dev profile and your application will startup running against an in-memory H2 database)

export SPRING_PROFILES_ACTIVE=prod

Now run the application:

mvn spring-boot:run

If all goes well the application will startup successfully (adding the necessary database objects in the mysql database while doing so) and we can test the application. The easiest way to do so is via the Swagger UI that should be available at http://localhost:8090/swagger-ui.html.

Just POST a few breweries and eventually issue a GET to see what’s been created:

Enter Docker

Alright. Now that we’ve put some data in our mysql database. Let’s containerize our Spring Boot services and see if we can connect them with our database running on the docker host.

For the containerized application, we will use a different Spring profile. The properties will be set in the application-docker.yml file

server:
  port: ${SERVER_PORT}
spring:
  jpa:
    properties:
      javax:
        persistence:
          schema-generation:
            database:
          	  action: none
  datasource:
    url: jdbc:${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

The biggest difference with the production properties is that we’ve externalized the server port and database properties (these will be passed when we run a container based on the Docker image we’ll be creating soon). And we also set the schema-generation.database.action to none here (we don’t want to introduce database changes when we spin up a new container instance).

Dockerfile

The Dockerfile serves as a basis for building a Docker image. For the contents of the file in case of Dockerizing a Spring Boot application, I’ll refer to the information provided here.

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
ENV SPRING_PROFILES_ACTIVE=docker
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","nl.terrax.tbspringbootdocker.TbSpringbootDockerApplication"]

Notice that I set the active Spring profile to docker in the Dockerfile.

Deploy the image to DockerHub

Alright we’re almost there. Let’s build the application and the docker image and subsequently push the image to DockerHub:

To build the image:

mvn clean install dockerfile:build

Now when you enter docker image ls (or docker images) at the command line, you’ll see that the image is available locally (together with the openjdk image it depends upon)(notice that rphgoossens is my Docker Hub repository):

Now let’s push the image to Docker Hub, so it can be shared:

mvn dockerfile:push

The image is now available on Docker Hub here. So no need to build the image from scratch anymore. When you want to run a container based on this image, Docker will download for you from Docker Hub.

Running the application

Network-wise there are two ways of running the application as a Docker container, i.e. bridged (the default) and hosted. The hosted variant will expose your host environment in its entirety to the container and is therefore not really recommended. It is the easiest to setup though, so I’ll mention it here briefly

host networking

The main advantage here is that the mysql database is directly available on localhost. The image we pushed to Docker Hub can be run directly in a container via docker like this:

docker run --network host -t -e "SERVER_PORT=8090" -e "DB_USERNAME=tb_admin" -e "DB_PASSWORD=tb_admin" -e "DB_URL=mysql://localhost:3306/db_terrax" rphgoossens/tb-springboot-docker

The -e option is used to set the environment variables, while the -t (as opposed to the -d) setting runs the container in the terminal window and shows you all the logging upon startup.

After the container has been started up, you can verify that the Swagger page is available on http://localhost:8090/swagger-ui.html.

You can spin up another container like this on a different port.

When you hit docker container ls after that you’ll see two containers running:

Alright this is not the prefered way of running the application in a container since it’s way less secure that using bridge networking. So let’s stop the containers (docker stop ) and remove them (docker container rm ) before heading to bridge networking.

bridge networking

The is the default and way more secure way of connecting your container to the mysql database. In this setting the database is NOT available on localhost (since localhost will refer to the container itself when using bridge networking).

So we will need to bind the mysql database to the IP address of the host (the container wil thenl be able to connect via that IP address).

Checkout the IP address you host is running on (sudo ifconfig, check for the docker0 interface) and add the following line to your mysql configuration (available at /etc/mysql/mysql.conf.d/mysqld.cnf)

bind-address = <ip-address-host>

That’s it. After a restart of mysql (sudo service mysql restart), the database will be bound to the host’s IP address. You can check it by running sudo netstat -tln (you should see a <ip-address-host>:3306 line there)

Now let’s spin up a container in bridge mode:

docker run -p 8091:8090 -t -e "SERVER_PORT=8090" -e "DB_USERNAME=tb_admin" -e "DB_PASSWORD=tb_admin" -e "DB_URL=mysql://<<i>ip-address-host</i>>:3306/db_terrax" rphgoossens/tb-springboot-docker

The biggest difference with the host networking variant is that you have to bind the port that the application is running on in the container (8090 in the example) to a port available on your host (8091 in the example), you do this with the -p (or –publish) option.

After startup the Swagger page of the application will be available on