Advanced Java Application Deployment Using Docker

AdvancedJava

 

Following Deploying a Java Application with Docker and Tutum this article will demonstrate how to implement and deploy a more enterprisefocused application using Tutum. In a nutshell, we will start from the previously developed hello world application and add some important features in a real life application:

  • multiple instances of the hello world service
  • a backend storage shared by all those instance
  • a load balancer

Of course, we will take advantage of all the nifty features in Tutum and Docker to simplify the deployment.

Prerequisites

In order to perform this tutorial you will need to have:

  • a Java development environment installed including:
  • Docker which is required to build the images even if we don’t need to run them in the development environment.
  • an account with Tutum.

You should also be familiar with the basic concepts of Tutum (Node Clusters and Services), read the article on Load balancing a Web Service, and be familiar with container linking. Reading these articles will provide you with a good background.

Deployment Strategy

The wonderful graphic below illustrates what we are going to deploy:
Deployment Scheme

Each box or box group will be a service which will run on a specific node cluster. The blue box services will be deployed from Tutum jumpstart images while the green box will use our custom image.

Container Links

The connections between services are unidirectional. The load balancer connects to the Java application instances, which in turn will connect to the single Redis server. This is the perfect situation to use Docker’s container linking. In a nutshell, Docker enables a container to be linked to another container, and both are managed by the same Docker daemon. This is done using the --link option when running the container. The target container will then be populated with environment variables describing the ports EXPOSEd by the source container image. In addition to the environment variables, an entry will be added to the target container /etc/hosts enabling connection to the source container to be performed using a host name chosen when starting the target container. For instance running a container with --link redis-server:redis will create environment variables prefixed by DB_ as well as adding a db entry in /etc/hosts. For a refresher, head over and check out the Docker documentation.

Oh Wait …

Container links are great but they are only working for containers managed by the same Docker daemon. This is obviously not working in our deployment strategy where services won’t be managed by the same Docker daemon and actually be running on different physical servers. Luckily for us, Tutum provides a very nifty feature to help us: service links. This feature extends the Docker container links to containers managed by different Docker daemons using the ambassador pattern. In addition, service links propagate the environment variables from the server to the client.

Redis Server

Although Redis is a cache rather than an actual storage, we will use it for this purpose in this article since it is very simple to use and every mechanisms used here could be implemented with a database (SQL or NoSQL), a REST storage service, etc.

We will use Tutum’s jumpstart redis server to implement the Redis server. The deployment steps will be described later but at this point we will only focus on setting a password to the Redis server. Tutum’s image will generate a random password on startup or use a predefined one if the REDIS_PASS environment variable is defined in the container.

Java Application

The application start point will be the original Spring flavored Hello World application deployed as a Spring Boot application rather than a war. The source code is available on bitbucket.

Controller

In the original application, the controller was storing the hit count in an AtomicLong. Thanks to Redisson, the new version is very similar:

@RestController
public class HelloWorldController {
    @Autowired
    private Redisson redisson;

    private String hostName;

    @PostConstruct
    public void init() throws UnknownHostException {
        hostName = InetAddress.getLocalHost().getHostName();
    }

    @RequestMapping("/")
    public HelloWorld sayHello(HttpServletRequest request) throws UnknownHostException {
        return new HelloWorld("Hello World",
                new Date(),
                request.getRemoteAddr(),
                hostName,
                redisson.getAtomicLong("HitCount").incrementAndGet());
    }
}

We will use Redisson to access the Redis service. With it the only difference is the injection of a Redisson instance and the retrieval of the AtomicLong from Redis using getAtomicLong("HitCount").

Redis Configuration

We will create a Spring configuration class to create the Redisson bean that will be injected into the controller. This class will only require to have a single method returning a correctly configured Redisson instance. Since we are using a single Redis server, the only parameters required to establish the connection would be: the Redis server connection information (address or hostname and port) and the password.

In a classical application, we would need to implement a configuration system using configuration files, environment variables, etc. to get the three required parameters. Since we will connect the Java application to the Redis service using service links, we could hard code the server name and the server port. The only variable part will be the password which will be transmitted to the Java application in the REDIS_ENV_REDIS_PASS environment variable (assuming that the Redis service will be aliased as redis in the Java application container).

With these assumptions, the RedisConfiguration will be like this:

@Configuration
public class RedissonConfig {
    @Value("${REDIS_ENV_REDIS_PASS:#{null}}")
    private String password;

    @Bean
    public Redisson redisson() {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis:6379");
        if (password != null) {
            singleServerConfig.setPassword(password);
        }
        return Redisson.create(config);
    }
}

And voilà: the Java application will now work using a Redis server to store the hit counter.

Deployment

Docker Image

This part is very similar to the one in the original tutorial and the Dockerfile for our application will look like this:

FROM ggtools/java8:openjdk
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/urandom", "-jar", "/spring-redis-0.1-SNAPSHOT.jar"]
CMD []
ADD spring-redis-0.1-SNAPSHOT.jar /spring-redis-0.1-SNAPSHOT.jar
EXPOSE 8080

The only changes are the use of a lighter base image and the addition of the -Djava.security.egd=file:/dev/urandom argument which prevents the application from waiting for entropy on startup (check this nice article if you need to be convinced that using /dev/urandom is good enough to seed Tomcat PRNG and this one to see the impacts of not using it.)

If you are using the pom.xml from bitbucket, the Docker image creation will be performed automagically with: mvn package. After that, you’ll need to push the image on your Tutum private registry:

docker push tutum.co/yourtutumid/hello-world-redis

Before Deploying

In order to deploy this application you need to first create a bunch of node clusters and tag them so you can deploy the right service on the right cluster. During the writing of this article I used:

  • a Redis cluster tagged with Redis and with only one node
  • a Hello World cluster to run the Java application instances tagged with Hello-World and with four nodes
  • finally, a load balancer cluster to run the load balancing proxy tagger with Load-Balancer and with only a single node.

Redis Service

As stated before we will use Tutum’s Redis image to deploy this service. It will be selected from the jumpstarts images under the Cache servers category:

Redis image selection

Next step will be naming the service redis-service and add the Redis deploy tag:

Naming the servicePlease note that we didn’t need to publish any port as the Redis service will only be accessed from our private infrastructure. However, you can publish the ports if you want to access the Redis server using the Redis client.

At this point, we are not ready yet as we need to assign a preset password to the server. This is done by setting the REDIS_PASS environment variable:

Assigning a pre-set passwordAlthough I used MyPassword for illustrative purposes, I actually used a password randomly generated by 1Password at this point. Since the password will be transmitted to the Java application through the service links mechanism, saving the generated password is not even required.

After setting the password, everything is ready and you can create and deploy the Redis service

Java Application

The Java application will be created from the hello-world-redis image in the private repository:

Selecting the Java application image

The next step will be naming the service. I chose hello-world-service and assigned a Hello-World Deploy tag. In addition to these basic tasks, I also set the number of containers to 4 and published the 8080 port. There is no need to change the default dynamic configuration as the service instances won’t be accessed directly except for test purposes. Actually forcing a fixed port number might lead to issues if two or more service instances need to be deployed on the same node.

Setting instances, name, etc.The next step will be on the Environment variables page; link the hello-world service to the redis service. You can do this by selecting redis-service from the drop down menu of Link services and then click on the + Add button. You will see the Link section populated with redis-service.

Link to redis serviceAfter adding the service link, you have to click on the alias and change it to redis:

Screen Shot 2014-12-24 at 10.36.36

Testing the Hello World Service

At this point, we can test the newly created service. From the service page, you can see the list of containers running the service:

Container listYou can view the container detail by clicking on the container name and visit the web service by clicking on the boxed arrow icon to the right of the port number:

Container detailAnd if everything is configured correctly, you should see this:

Webservice working

You can try for every container and you’ll see that the hitCount field is indeed shared between containers.

Adding the Load Balancer

For this part, I will be referring to Tutum’s documentation on how to load balance a web-service.

We start from Tutum’s ha-proxy image:

HA-Proxy imageOn the Service configuration screen, we give our service a name and publish port 80, thereby fixing the outside port to 80:

Port and Name

On the Environment variables screen we need to add the full access API Role, link the load balancer to the hello-world-service and lastly change the PORT environment variable from 80 to 8080:

HA Env

Test Everything

With everything in place, we can now test the full application. From the load balancer service, we can get the endpoint:

HA Endpoint

Clicking on the boxed arrow icon on the right will take you to the load balanced service. Refreshing several times will show you that the hit counter is incremented and that the server hostname is changing every time you reload:

Full service

The clientIP field might seem strange since it’s the address from the load balancer ambassador. In order to get the actual client IP address we should change the Java application to display the X-Forwarded-For header contents if it exists.

Tagged with: , , ,
Posted in Tutorial
5 comments on “Advanced Java Application Deployment Using Docker
  1. […] Advanced Java Application Deployment Using Docker […]

  2. 全身脱毛 says:

    世間で|世の中で|行う悩んでいる女性|女子|女の人が多い|いっぱい|たくさんですね。脱毛の悩み|苦悩|恥ずかしいことを少しでも|ちょっとでも|わずかでも減らせればいいなと思って|考えて|感じてサイト|WEBサイト|情報を立ち上げ|作り|まとめました。悩み|苦悩|恥ずかしいことは誰にも|他の人|他人相談できないので、自分も|私も|自分自身も悩んでました|困ってました|つらかったです。包茎|下半身|下のことの悩み|苦悩|恥ずかしいことは自分一人で解決する|なんとかすることも難しい|大変|困難ですよね。

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories
%d bloggers like this: