Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 5

Intro

I left you in the previous post with the promise of add Akka services to our architecture; and that’s what will do in this and the next post. As always you may download the full code from the repository:

 git clone https://github.com/gitgabrio/container-microservices-tutorial-5.git

actorserver-service

Let’s begin adding the module declaration to the parent pom:

...
   </parent>
      <modules>
        <module>servicecommon</module>
        <module>registrationservice</module>
        <module>persistenceservice</module>
        <module>timeconsumingservice</module>
        <module>configurationservice</module>
        <module>actorserverservice</module>
        <module>docker</module>
    </modules>
    <properties>
...

This is the actor-service pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>net.microservices.tutorial</groupId>
        <artifactId>container-microservice</artifactId>
        <version>0.1</version>
        <relativePath>../</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>actorserverservice</artifactId>
    <properties>
        <akka.version>2.4.2</akka.version>
        <!-- CONFIGURATIONS -->
        <start-class>net.microservices.tutorial.actorserverservice.ActorServerServer</start-class>
        <finalName>actorserverservice-${project.version}</finalName>
        <service.port>5555</service.port>
        <rs.port>1111</rs.port>
        <rs.ip>localhost</rs.ip>
        <akka.port>12552</akka.port>
        <container.ip>localhost</container.ip>
    </properties>
    <build>
        <finalName>${finalName}</finalName>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>servicecommon</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_2.11</artifactId>
            <version>${akka.version}</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_2.11</artifactId>
            <version>${akka.version}</version>
        </dependency>
    </dependencies>

</project>

Here you may notice the akka dependencies and a couple of akkas-specific properties:

  1. akka.port: this is the port where the Akka actor is listening
  2. container.ip: this is the public ip server, used when not injected by the docker plugin

code

In this module we will inject some properties in the eureka metadata map, to make them available for eventual “clients”, namely, the port:

net/microservices/tutorial/actorserverservice/resources/application.yml:

...
eureka:
  client:
    ...
  instance:
    ...
    metadata-map:
      port: ${AKKA_PORT:@akka.port@}
...

We will use this map in the next part of the tutorial; anyway, just note that the value could be populated by the module pom or by the docker plugin.

net/microservices/tutorial/actorserverservice/resources/application.conf:

akka {
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
    serializers {
        proto = "akka.remote.serialization.ProtobufSerializer"
    }
  }
  remote {
    netty.tcp {
      hostname = localhost
      port = 4324
    }
 }
 log-sent-messages = on
 log-received-messages = on
 }

This is a bare-bone akka configuration. Unfortunately Akka does not read YAML files; it could be possible (see here for an example) with some extra code, but I do not think it is important, here, so I provided one as base configuration. Please note that the netty parameters (hostname and port) will be overridden at startup.
The important details are in the configuration class:

net/microservices/tutorial/actorserverservice/configurations/ActorServerConfiguration.kt:

...
     @Value("\${eureka.instance.metadata-map.port}")
    var akkaPort: Int = 0

    @Value("\${akka.remote.netty.tcp.hostname}")
    var akkaHostName: String = ""



    @Bean
    open fun actorSystem(): ActorSystem {
        val defaultApplication: Config = ConfigFactory.defaultApplication()
                .withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(akkaHostName))
                .withValue("akka.remote.netty.tcp.port", ConfigValueFactory.fromAnyRef(akkaPort))
        val system = ActorSystem.create("RemoteWorkerSystem", defaultApplication)
        system.actorOf(Props.create(ServerActor::class.java), "serverActor")
        return system
    }
...

First, there are two properties injected by Spring, namely

  1. eureka.instance.metadata-map.port -> akkaPort
  2. akka.remote.netty.tcp.hostname -> akkaHostName

Then, in the ActorSystem instantiation, we define its configuration

defaultApplication: Config = ConfigFactory.defaultApplication()

we are loading the default configuration, that will read application.conf;

.withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(akkaHostName))

we are overriding the akka.remote.netty.tcp.hostname value;

.withValue("akka.remote.netty.tcp.port", ConfigValueFactory.fromAnyRef(akkaPort))

we are overriding the akka.remote.netty.tcp.port value;

val system = ActorSystem.create("RemoteWorkerSystem", defaultApplication)

we instantiate the ActorSystem whose “akka-address” will be RemoteWorkerSystem

system.actorOf(Props.create(ServerActor::class.java), "serverActor")

we instantiate the ServerActor that will listen and respond to incoming messages.
And that’s is the bare-minimum akka implementation, for now.

image creation

Let’s update the docker configuration.
docker/pom.xml:

...
 <properties>
      ...
        <!-- services properties -->
        <registration.service.port>1111</registration.service.port>
        <persistence.service.port>2222</persistence.service.port>
        <timeconsuming.service.port>3333</timeconsuming.service.port>
        <configuration.service.port>4444</configuration.service.port>
        <actorserver.service.port>5555</actorserver.service.port>
    </properties>
</properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>registrationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>persistenceservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>timeconsumingservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>configurationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>actorserverservice</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>
...

but this time we will introduce some modifications. As you may have experienced, each docker container take a lot of resources, even if the actual code inside it is pretty small, as in this tutorial. There is a way to build and start only a subset of the images actually configured, with the docker plugin configuration tag filter; here you may define the image or the comma-separated list of images to manage.

...
<configuration>
   <filter>(IMAGES-TO-INCLUDE)</filter>
....

.
Now, to have a flexible build configuration, I have defined a global images property:

...
   <actorserver.service.port>5555</actorserver.service.port>
        <!-- Images to build and run - default to all-->
        <images>registrationservice, persistenceservice, timeconsumingservice, configurationservice,
            actorserverservice
        </images>
    </properties>
...

and a couple of profiles to eventually override it at build-time:

...
<profiles>
        <profile>
            <id>configuration</id>
            <properties>
                <images>registrationservice, persistenceservice, timeconsumingservice, configurationservice
                </images>
            </properties>
        </profile>
        <profile>
            <id>akka</id>
            <properties>
                <images>registrationservice, actorserverservice
                </images>
            </properties>
        </profile>
    </profiles>
...

Last, I use the property in the plugin configuration:

...
<build>
        <plugins>
            <!-- DOCKERIZE WITH FABRIC8 -->
            <plugin>
                <!-- The Docker Maven plugin is used to create docker image with the fat jar -->
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.fabric8.version}</version>
                <configuration>
                    <logDate>default</logDate>
                    <autoPull>true</autoPull>
                    <filter>${images}</filter>
                    <images>
...

Now, depending on the profile (or lack of) used for the docker goal, we could decide which image(s) build and run.

After installing the actorserverservice artifact and starting docker with the akka profile, we should have the actorserver service registered:

and the image running here
.
In the logs, you will see the akka instance listening:

Conclusion

Well, you will find the image definition (and all the other missing stuff) in the project:

git clone https://github.com/gitgabrio/container-microservices-tutorial-5.git

In the next part we will add the Akka Client service so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!

Advertisements

Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 4

Intro

In the previous post we have add a microservice that returns data slowly,the timeconsuming one. Now we will add the service that query with other twos to show data. We can pretend this is the “frontend” layer, maybe (in real application) full of javascript magics! Only the crucial aspects of the implementation will be covered here, so you may download the full code from the repository:

 git clone https://github.com/gitgabrio/container-microservices-tutorial-4.git

configuration-service

Let’s begin adding the module declaration to the parent pom:

...
   </parent>
      <modules>
        <module>servicecommon</module>
        <module>registrationservice</module>
        <module>persistenceservice</module>
        <module>timeconsumingservice</module>
         <module>configurationservice</module>
        <module>docker</module>
    </modules>
    <properties>
...

This is the configuration-service pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>container-microservice</artifactId>
        <groupId>net.microservices.tutorial</groupId>
        <version>0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>configurationservice</artifactId>

    <properties>
        <start-class>net.microservices.tutorial.configurationservice.ConfigurationServer</start-class>
        <finalName>configurationservice-${version}</finalName>
        <service.port>4444</service.port>
        <container.ip>localhost</container.ip>
        <rs.port>1111</rs.port>
        <rs.ip>localhost</rs.ip>
        <ps.port>2222</ps.port>
        <ps.ip>localhost</ps.ip>
        <ts.port>3333</ts.port>
        <ts.ip>localhost</ts.ip>
    </properties>
    <build>
        <finalName>${finalName}</finalName>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>servicecommon</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>
</project>

code

In this module we will actually use the Eureka architecture to

  1. ask the registrar for the address of the first available instance of the target service
  2. send request to the found instance of the target service

net/microservices/tutorial/configurationservice/resources/application.yml:

...
   persistenceservice:
  url: http://PERSISTENCE-SERVICE
timeconsumingservice:
  url: http://TIMECONSUMING-SERVICE
...

This is the first piece of the implementation: we are defining two properties to map the name of the services we want to talk with. As you may recall, this is the same name (upper case) we have defined for the services with the directive spring.application.name in the bootstrap.yml files.This properties are injected in the configuration file (forgive the naming! ):
net/microservices/tutorial/configurationservice/configurations/ConfigurationConfiguration.kt:

...
     @Value("\${persistenceservice.url}")
    var persistenceServiceUrl :String = ""

    @Value("\${timeconsumingservice.url}")
    var timeConsumingserviceUrl :String = ""

    /**
     * A customized RestTemplate that has the ribbon load balancer build in.
     * @return
     */
    @LoadBalanced
    @Bean
    open fun restTemplate(): RestTemplate {
        return RestTemplate()
    }

    /**
     * A customized RestTemplate that has the ribbon load balancer build in.
     * @return
     */
    @LoadBalanced
    @Bean
    open fun asyncRestTemplate(): AsyncRestTemplate {
        return AsyncRestTemplate()
    }
...

Please note also the AsyncRestTemplate, more about it later.

The service class forward the requests coming from the controller to the target -remote – destinations:

net/microservices/tutorial/configurationservice/services/UsersService.kt:

...
    @Service
class UsersService(persistenceServiceUrl: String, timeConsumingserviceUrl: String) {

    @Autowired
    @LoadBalanced
    private var restTemplate: RestTemplate? = null

    @Autowired
    @LoadBalanced
    private var asyncRestTemplate: AsyncRestTemplate? = null

    private var persistenceServiceUrl: String

    private var timeConsumingserviceUrl: String

    private var logger = Logger.getLogger(UsersService::class.java.name)

    init {
        this.persistenceServiceUrl = if (persistenceServiceUrl.startsWith("http"))
            persistenceServiceUrl
        else
            "http://" + persistenceServiceUrl
        this.timeConsumingserviceUrl = if (timeConsumingserviceUrl.startsWith("http"))
            timeConsumingserviceUrl
        else
            "http://" + timeConsumingserviceUrl
    }

    /**
     * The RestTemplate works because it uses a custom request-factory that uses
     * Ribbon to look-up the service to use. This method simply exists to show
     * this.
     */
    @PostConstruct
    fun demoOnly() {
        // Can't do this in the constructor because the RestTemplate injection
        // happens afterwards.
        logger.warning("The RestTemplate request factory is " + restTemplate!!.requestFactory.javaClass)
    }

    @Throws(Exception::class)
    fun findAll(): List<UserDTO>? {
        logger.info("findAll() invoked")
        var users: Array<UserDTO>? = null
        try {
            users = restTemplate!!.getForObject(persistenceServiceUrl + "/persons/", Array<UserDTO>::class.java)
        } catch (e: HttpClientErrorException) { // 404
            // Nothing found
            return null
        }
        if (users == null || users.size == 0)
            return null
        else
            return Arrays.asList(*users)
    }
...

    @Throws(Exception::class)
    fun findAsyncByNumber(id: Int): UserDTO? {
        logger.info("findAsyncByNumber() invoked")
        var user: UserDTO? = null
        try {
            val method = HttpMethod.GET
            val responseType = genericClass<UserDTO>()
            //create request entity using HttpHeaders
            val headers = HttpHeaders()
            headers.contentType = MediaType.TEXT_PLAIN
            val requestEntity = HttpEntity<String>("params", headers)
            val future = asyncRestTemplate?.exchange(timeConsumingserviceUrl + "/deferredpersons/{id}", method, requestEntity, responseType, id)
            //waits for the result
            val entity = future?.get()
            //prints body source code for the given URL
            user = entity?.body
        } catch (e: Exception) {
            logger.log(Level.SEVERE, e.message, e)
        }
        return user
    }
    
    private inline fun <reified T : Any> genericClass(): Class<T> = T::class.java
...

Well, there is something going on here.
First, we inject the services “url” in the contructor and use this to set the two instance variables persistenceServiceUrl and timeConsumingserviceUrl.
Next, we define the two variables restTemplate and asyncRestTemplate as Autowired and LoadBalanced. This last annotation tells to the RestTemplate to use a LoadBalancerClient. With this in place, each service’ request is forwarded to the first available instance of the target type of destination.
The AsyncRestTemplate is used to talk to the timeconsuming service because it return a ListenableFuture, that is what we need to make this type of requests.

The controller is like the others, so I will skip the details

image creation

Let’s update the docker configuration.
docker/pom.xml:

...
 <properties>
      ...
        <!-- services properties -->
        <registration.service.port>1111</registration.service.port>
        <persistence.service.port>2222</persistence.service.port>
        <timeconsuming.service.port>3333</timeconsuming.service.port>
        <configuration.service.port>4444</configuration.service.port>
    </properties>
</properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>registrationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>persistenceservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>timeconsumingservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>configurationservice</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>
...

This time, in the image definition we will add three links:

...
registrationservice:rs, persistenceservice:ps, timeconsumingservice:ts
...

With this definition, the fabric8 plugin will inject a bunch of RS_XXX, PS_XXX and TS_XXX variables. Actually, for our project only the RS_XXX ones are needed, because the configuration service never call directly the others, but instead “ask” the address to the registration service.

After installing the artifacts and starting docker, we should have the configuration service registered:

and the image running here

with the persistence service requests

and the timeconsuming ones (of course, actual data will vary)

Conclusion

Well, you will find the image definition (and all the other missing stuff) in the project:

git clone https://github.com/gitgabrio/container-microservices-tutorial-4.git

In the next part we will begin to add Akka services so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!

Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 3

Intro

In the previous post we have built our first microservice, the persistence one. Now we will add a new one to simulate a long lasting requesting, i.e. a request that may take long time to be fulfilled.  Only the crucial aspects of the implementation will be covered here, so you may download the full code from the repository:

 git clone https://github.com/gitgabrio/container-microservices-tutorial-3.git

timeconsuming-service

For this tutorial we will reuse the User entity defined in service-common.Let’s begin adding the module declaration to the parent pom:

...
   </parent>
      <modules>
        <module>servicecommon</module>
        <module>registrationservice</module>
        <module>persistenceservice</module>
        <module>timeconsumingservice</module>
        <module>docker</module>
    </modules>
    <properties>
...

This is the timeconsuming-service pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>container-microservice</artifactId>
        <groupId>net.microservices.tutorial</groupId>
        <version>0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>timeconsumingservice</artifactId>

    <properties>
        <start-class>net.microservices.tutorial.timeconsumingservice.TimeConsumingServer</start-class>
        <finalName>timeconsumingservice-${project.version}</finalName>
        <service.port>3333</service.port>
        <container.ip>localhost</container.ip>
        <rs.port>1111</rs.port>
        <rs.ip>localhost</rs.ip>
    </properties>
    <build>
        <finalName>${finalName}</finalName>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>servicecommon</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>

</project>

code

Most of the code and configuration is really similar to the persistence-service one, so I won’t repeat the details. But there are some differences in the Controller and Service related to the asynchronous call:
net/microservices/tutorial/timeconsumingservice/services/TimeConsumingService.kt:

...
    @Throws(Exception::class)
    fun findAll(): List<UserDTO>? {
        logger.info("findAll() invoked")
        val toReturn: ArrayList<UserDTO> = ArrayList()
        repeat(6) { i ->
            val toAdd: UserDTO = createUserEntity(i)
            toReturn.add(toAdd)
            try {
                Thread.sleep(1000)
            } catch (e : InterruptedException) {
                logger.info("I've been interrupted")
            }
        }
        return toReturn
    }
...

This service returns five users just created on demand, but with some delay, to simulate a time consuming elaboration (it should take almost one minute to complete).

net/microservices/tutorial/timeconsumingservice/controllers/TimeConsumingController.kt:

...
    /*
     Use for long-polling
     */
    @RequestMapping("/deferredpersons")
    @Async // This means it will run on a different thread
    fun allDeferredUsers(): DeferredResult<List<UserDTO>?> {
        logger.info("web-service allDeferredUsers() invoked")
        val toReturn: DeferredResult<List<UserDTO>?> = DeferredResult()
        Thread({
            val users = timeConsumingService.findAll()
            logger.info("web-service allDeferredUsers() found: " + users!!)
            toReturn.setResult(users)
        }, "MyThread-$counter").start()
        counter += 1
        return toReturn
    }
...

This controller is actually implementing a long polling, returning a Spring Web DeferredResult.Basically, the actual data is populated inside the thread, and the client calling this method should do it in asynchronous manner, as we will see in the next part.

image creation

The docker image management is identical as what done before for the other services, beside the different image name and listening port.
docker/pom.xml:

...
 <properties>
      ...
        <!-- services properties -->
        <registration.service.port>1111</registration.service.port>
        <persistence.service.port>2222</persistence.service.port>
        <timeconsuming.service.port>3333</timeconsuming.service.port>
    </properties>
</properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>registrationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>persistenceservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>timeconsumingservice</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>
...

After installing the artifacts and starting docker, we should have the timeconsuming service registered:

and the image running here;

with the time consuming service; here’s the result (of course, actual data will vary):

Conclusion

Well, you will find the &ltg;image&gt, definition (and all the other missing stuff) in the project:

git clone https://github.com/gitgabrio/container-microservices-tutorial-3.git

In the next part we will add another Eureka client container that will talk with persistence and the timeconsuming services so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!

Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 2

Intro

In the previous post we have built the scaffold of our microservice project, implementing the Eureka registration service. Now we will begin ti add some real functionality, beginning (surprisingly) with the data access. Only the crucial aspects of the implementation will be covered here, so you may download the full code from the repository:

git clone https://github.com/gitgabrio/container-microservices-tutorial-2.git

persistence-service

For this tutorial we will create a simple entity, User, with the basic CRUD operations. We will use HsqlDb as in-memory database and JPA for ORM-mapping, with the utilities offered by Spring-Data. Let’s begin adding the module declaration to the parent pom:

...
   </parent>
      <modules>
        <module>servicecommon</module>
        <module>registrationservice</module>
        <module>persistenceservice</module>
        <module>docker</module>
    </modules>
    <properties>
...

This is the persistence-service pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>container-microservice</artifactId>
        <groupId>net.microservices.tutorial</groupId>
        <version>0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>persistenceservice</artifactId>

    <properties>
        <start-class>net.microservices.tutorial.persistenceservice.PersistenceServer</start-class>
        <finalName>registrationservice-${project.version}</finalName>
        <service.port>2222</service.port>
        <container.ip>localhost</container.ip>
        <rs.port>1111</rs.port>
        <rs.ip>localhost</rs.ip>
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>servicecommon</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${finalName}</finalName>
    </build>

</project>

Inside the resources directory we will put all the html templates used by Thymeleaf and the Spring configurations file: bootstrap.yml and application.yml:

persistenceservice/resources/bootstrap.yml:

# Spring properties
# Service registers under this name
spring:
  application:
      name: persistence-service

persistenceservice/resources/application.yml:

# Spring properties
spring:
  # Ignore Eureka dashboard FreeMarker templates
  freemarker:
      enabled: false
  # Allow Thymeleaf templates to be reloaded at runtime
  thymeleaf:
       cache: false
  # Trailing / mandatory
  # Template location for this application only
       prefix: classpath:/templates/

  # Database configuration
  # Spring Boot automatically creates a JPA EntityManagerFactory using Hibernate
  # but we need to override some defaults:
  #
  #   1. Stop Hibernate automatically creating a schema, we are doing it in
  #      schema.sql. Instead check the tables match their JPA mapped classes
  jpa:
    hibernate:
      ddl-auto: none
      naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
    database: HSQL
    show-sql: true

# Map the error path to error template (for Thymeleaf)
error:
  path: /error

# HTTP Server
# HTTP (Tomcat) port
server:
  port:  ${SERVICE_PORT:@service.port@}
eureka:
  client:
    serviceUrl:
      defaultZone: http://${RS_PORT_1111_TCP_ADDR:@rs.ip@}:${RS_PORT_1111_TCP_PORT:@rs.port@}/eureka/
  instance:
    preferIpAddress: true
    # DO NOT DO THIS IN PRODUCTION
    leaseRenewalIntervalInSeconds: 5

#Test db
testDatasource:
  url: jdbc:hsqldb:file:testdb
  username: SA
  password:
  driverClassName: org.hsqldb.jdbcDriver

# Disabling security for Actuator' REST services
security:
  basic:
    enabled: false
management:
  security:
    enabled: false

code

Since this tutorial is focused on the microservices architecture, I will not explain all the details of this configuration: fell free to ask for further explanation, if needed.
Please note:

  • port: ${SERVICE_PORT:@service.port@}: this is the listening port of the application; as for the registration-server, will be injected at compile time;
  • defaultZone:http://${RS_PORT_1111_TCP_ADDR:@rs.ip@}:${RS_PORT_1111_TCP_PORT:@rs.port@}/eureka/: this is the address of the registration service; RS_PORT_1111_TCP_ADDR and RS_PORT_1111_TCP_PORT will be dynamically injected by the fabric8 plugin during image building (more about them in the image configuration), otherwise rs.ip and rs.port (defined in the persistenceservice pom.xml)will be used
  • preferIpAddress: true: this is needed mostly for applications running inside docker containers; without this, the service will register itself with the container id, so it could not be attainable by the other services

persistenceservice/src/main/kotlin/net/microservices/tutorial/persistenceservice/PersistenceServer.kt

@file:JvmName("PersistenceServer")
package net.microservices.tutorial.persistenceservice


import net.microservices.tutorial.persistenceservice.configurations.PersistenceConfiguration
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.context.annotation.Import

import java.util.logging.Logger

/**
 * Run as a micro-service, registering with the Discovery Server (Eureka).
 *
 *
 * Note that the configuration for this application is imported from
 * [PersistenceConfiguration]. This is a deliberate separation of concerns.
 */
@EnableAutoConfiguration
@EnableDiscoveryClient
@Import(PersistenceConfiguration::class)
open class PersistenceServer {

    protected var logger = Logger.getLogger(PersistenceServer::class.java.name)

    companion object {

        /**
         * Run the application using Spring Boot and an embedded servlet engine.

         * @param args
         * *            Program arguments - ignored.
         */
        @JvmStatic fun main(args: Array<String>) {
            SpringApplication.run(PersistenceServer::class.java, *args)
        }
    }
}

This is the application entry point. As for the registration service, there is a single annotation needed to transform the module in an Eureka client:

@EnableDiscoveryClient

With this in place, we are telling Spring to instantiate the class as a Eureka client, taking the properties from the *.yml files.

image creation

What is left now is to configure the image to be built inside the docker pom docker/pom.xml: first, as the dependency on the persistence service:

<dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>persistenceservice</artifactId>
            <version>0.1</version>
        </dependency>

then add this just below the registration service’ one:
docker/pom.xml:

...
                        </image>
            <!-- Persistence service -->
                        <image>
                            <!-- Alias name which can used for linking containers during runtime -->
                            <alias>persistenceservice</alias>
                            <name>${docker.repo}/persistence-service:${project.version}</name>
                            <!-- ....................................... -->
                            <!-- Build configuration for creating images -->
                            <!-- ....................................... -->
                            <build>
                                <from>java:8u40</from>
                                <!-- Assembly descriptor holds the reference to the created artifact-->
                                <assembly>
                                    <descriptor>${basedir}/../persistenceservice/src/main/fabric8/assembly.xml</descriptor>
                                </assembly>
                                <!-- Expose ports -->
                                <ports>
                                    <port>${persistence.service.port}</port>
                                </ports>
                                <!-- Default command for the build image -->
                                <cmd>java -Djava.security.egd=file:/dev/./urandom -jar /maven/persistenceservice.jar
                                </cmd>
                            </build>
                            <!-- ............................................................... -->
                            <!-- Runtime configuration for starting/stopping/linking containers -->
                            <!-- ............................................................... -->
                            <run>
                                <!-- Assign dynamically mapped ports to maven variables (which can be reused in integration tests) -->
                                <ports>
                                    <port>${persistence.service.port}:${persistence.service.port}</port>
                                </ports>
                                <env>
                                    <SERVICE_PORT>${persistence.service.port}</SERVICE_PORT>
                                </env>
                                <wait>
                                    <!-- Check for this URL to return a 200 return code .... -->
                                    <url>http://${docker.host.address}:${persistence.service.port}/</url>
                                     <!-- ... but at max 30 seconds -->
                                    <time>30000</time>
                                </wait>
                                <dependsOn>
                                    <container>
                                        registrationservice
                                    </container>
                                </dependsOn>
                                <links>
                                    <!-- Links can be referenced via alias (registrationservice) or name (${docker.repo}/registration-service:${project.version}) -->
                                    <!-- THIS SHOULD CREATE SOME RS_XXX variables -->
                                    <link>registrationservice:rs</link>
                                </links>
                                <log>
                                    <prefix>TC</prefix>
                                    <color>cyan</color>
                                </log>
                            </run>
                        </image>
                    </images>
...

This is almost identical to the registration service, but there are a couple of new tags:

  • dependsOn: this is to indicate the container dependency, i.e. we are telling to the plugin that the container for this image requires the registrationservice container
  • links: this is needed to inject in the persistence service container some properties of the registration service one

In the previous post I have explained how to inject properties inside container as environment variables, and that some of them are automatically generated and injected by the fabric8 plugin. In the documentation you will find more explanation; simply put, with  &lt;link&gt;registrationservice:rs&lt;/link&gt; we are telling to the plugin to use “rs” as prefix for some automatically generated properties, with the following convention (everything upper-case):

(prefix)_PORT=(protocol)://(container_ip):(exposed_port_number)
(prefix)_PORT_(port_number)_TCP=tcp://(container_ip):(port_number)
(prefix)_PORT_(port_number)_TCP_PROTO=tcp
(prefix)_PORT_(port_number)_TCP_PORT=(port_number)
(prefix)_PORT_(port_number)_TCP_ADDR=(container_ip)

For the current project, the port number is 1111 (registration.service.port), the prefix is rs, and let’s say that the registration container ip is 172.17.0.2. Following the above rules, we will have:


RS_PORT=tcp://172.17.0.2:1111
RS_PORT_1111_TCP=tcp://172.17.0.2:1111
RS_PORT_1111_TCP_PROTO=tcp
RS_PORT_1111_TCP_PORT=1111
RS_PORT_1111_TCP_ADDR=172.17.0.2

This properties will be injected as environment variables in the persistence service container. Now, if you look back at application.yml, you will find exactly these:

...
defaultZone: http://${RS_PORT_1111_TCP_ADDR}:${RS_PORT_1111_TCP_PORT}/eureka/
...

Now, calling from the root directory

 
mvn install 

We should be able to build all the modules and the two images.
Last, let’s start them all. Go to the docker directory and type

mvn docker:start

We should have:

the two  containers up and running


# docker ps
CONTAINER ID IMAGE                        COMMAND                CREATED            STATUS            PORTS                  NAMES
36909d6973b0 ***/persistence-service:0.1 "/bin/sh -c 'java -Dj"  About a minute ago Up About a minute 0.0.0.0:2222->2222/tcp distracted_varahamihira
3f8bebf677ca ***/registration-service:0.1 "/bin/sh -c 'java -Dj" 2 minutes ago      Up About a minute 0.0.0.0:1111->1111/tcp jovial_galileo


the registration service  here

the persistence service here

the persistence service registered inside the registration service

some REST for CRUD operation

Conclusion

Well, feel free to look in the code to see what REST services are implemented
To clone the project:

git clone https://github.com/gitgabrio/container-microservices-tutorial-2.git

In the next part we will add another Eureka client container that will implement a long time request service so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!

Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 1

Intro

Hi all! Recently I have begun my journey toward the “microservices” world, and I have found out that there are actually a lot of different ways to implement that simple concept, i.e. split a complex project in small, well focused, components. So, I decided to create a “microservices” architecture stub that could be used as a model for *real* implementations. In that stub I will put different technologies, not necessarily to be always used, but to work as template. This tutorial will follow step-by-step the project development: I will start from a multi-module SpringBoot project and “Dockerize” it… have fun!!!!

Tutorial parts

  1. Project setup and registration service
  2. Accessing db with persistence service
  3. Long task to be consumed asynchronously with timeconsuming service
  4. Asking data to other services with configuration service
  5. Adding an Akka server with actorserver service
  6. Create the Akka client service to talk with the actoreserver

Frameworks and architecture

The project will be written in Kotlin (there will be very few lines of codes, so it should not be an issues), and will use Spring Cloud as framework and Docker as container manager. The microservices orchestration will be entrusted to the Eureka service. I would like to manage everything, even the Docker images, with maven goals, so I have looked around for available plugins. At the end I have choosen the RedHat Fabric8 plugin, of which you can read here.

 System requirements

All the development will be done on an OpenSuSE Linux, where I have installed Docker (1.12.6) and Oracle JDK 1.8.0_65. Last, I use IntelliJ IDEA as IDE, but the project will be 100% Maven-based, there should be no issues with other IDEs. Check that your Docker environment is working before proceeding.

Ready? Go on….

Setup project

You may download the full code from the repository:

git clone https://github.com/gitgabrio/container-microservices-tutorial-1.git

service-parent (parent pom/multimodule)

The project will be a Maven multimodule, so let’s begin creating the base pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.microservices.tutorial</groupId>
    <artifactId>container-microservice</artifactId>
    <version>0.1</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-parent</artifactId>
        <version>Dalston.RELEASE</version>
        <relativePath></relativePath>
    </parent>
    <properties>
        <!-- TEST -->
        <junit.version>4.12</junit.version>
        <!-- LOG -->
        <slf4j.version>1.7.12</slf4j.version>
        <!-- PLUGINS -->
        <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
        <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
        <maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
        <!-- CONFIGURATIONS -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.source>1.8</java.source>
        <java.target>1.8</java.target>
        <kotlin.version>1.1.0</kotlin.version>
    </properties>
    <dependencies>
        <dependency>
            <!-- Setup Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <!-- Setup Spring MVC & REST, use Embedded Tomcat -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <!-- Setup Spring Data common components -->
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>
        <dependency>
            <!-- Testing starter -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <!-- Setup Spring Data JPA Repository support -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <!-- In-memory database for testing/demos -->
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
        </dependency>
        <dependency>
            <!-- Spring Cloud starter -->
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <dependency>
            <!-- Eureka service registration -->
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
        <dependency>
            <!-- Kotlin -->
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src/main/kotlin</sourceDirectory>
        <testSourceDirectory>src/test/kotlin</testSourceDirectory>
        <resources>
            <resource>
                <filtering>true</filtering>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.yml</include>
                    <include>**/*.sql</include>
                    <include>**/*.html</include>
                    <include>**/*.conf</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.source}</source>
                    <target>${java.target}</target>
                    <maxmem>256M</maxmem>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                                <source>src/main/kotlin</source>
                            </sourceDirs>
                        </configuration>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>process-test-sources</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>${maven-gpg-plugin.version}</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

That’s it. I have declared spring-cloud-starter-parent as parent to have available all the SpringBoot + Eureka stack out-of-the-box.
For the moment being, it is just an empty, useless box, but we will begin to fill it immediately.

service-common

First of all, let’s create a “common” module: it will be used to contain classes and utilities used by real “microservices”. I have read somewhere that there should be absolutely no code-linking between the microservices, i.e. they should not have any code in common: clearly this “service-common” breaks the rule, and I understand the sense in keeping code-bases completely isolated between them, in real situations, with really complex projects. But on the other side I think that for this tutorial it could be accepted, just to avoid repeated code.
Let’s add it as “module” to the “parent” pom, just below the close of the “parent” tag, in the parent pom:

... 
</parent>
<modules>
    <module>servicecommon</module>
</modules>
<properties>
...

Here’s the “servicecommon” pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>servicecommon</artifactId>
    <packaging>jar</packaging>
    <parent>
        <groupId>net.microservices.tutorial</groupId>
        <artifactId>container-microservice</artifactId>
        <version>0.1</version>
        <relativePath>../</relativePath>
    </parent>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.5.2.RELEASE</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Please note the spring-boot-maven-plugin configuration, to avoid “repackage” goal to be executed in this module.

registration-service

The registration service will be the first “microservice” to be implemented. Add it as a module, just below the “servicecommon”, in the parent pom:

... 
</parent>
<modules>
  <module>servicecommon</module>
  <module>registrationservice</module>
</modules>
<properties>
...

Now, let’s create the “registrationservice” pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>registrationservice</artifactId>
    <packaging>jar</packaging>
    <parent>
        <groupId>net.microservices.tutorial</groupId>
        <artifactId>container-microservice</artifactId>
        <version>0.1</version>
        <relativePath>../</relativePath>
    </parent>
    <properties>
        <start-class>net.microservices.tutorial.registrationservice.RegistrationServer</start-class>
        <finalName>registrationservice-${project.version}</finalName>
        <service.port>1111</service.port>
        <container.ip>localhost</container.ip>
    </properties>
    <build>
        <finalName>${finalName}</finalName>
    </build>
</project>

Please take note of the service.port and container.ip properties: more about them later.

code

We will use the Eureka protocol for microservices orchestration. Simply put, there is (at least) one service that act as “registrar” to keep note of all the other running instances (microservice type and ip). All other services are “client”s, and register themselves with the registration service. Whenever one service needs to talk with another one, it asks the registration service for the address of the first available instance (there is a load-balancing in there) of that type of service, and then use the received address for the communication.
Using SpringBoot dark magic, setting up a registrar is a matter of few lines of yml and annotations:
registrationservice/src/main/resources/application.yml

# Configure this Discovery Server
eureka:
  instance:
    hostname: ${CONTAINER_IP:@container.ip@}
    #enableSelfPreservation: false
    preferIpAddress: true
    # DO NOT DO THIS IN PRODUCTION
    leaseRenewalIntervalInSeconds: 5
  client:
    registerWithEureka: false
    fetchRegistry: false

server:
  port: ${SERVICE_PORT:@service.port@}
# Discovery Server Dashboard uses FreeMarker.  Don't want Thymeleaf templates
spring:
  thymeleaf:
    enabled: false
security:
  basic:
    enabled: false
management:
  security:
    enabled: false

Please note the two “MAVEN-style” properties ${CONTAINER_IP:@container.ip@} and ${SERVICE_PORT:@service.port@}: here we are telling the compiler to use the first variable (CONTAINER_IP, SERVICE_PORT) if available, otherwise the second one (@container.ip@, @service.port@) if not.
CONTAINER_IP and SERVICE_PORT will be populated later during docker image creation, inside the fabric8 plugin configuration, while @container.ip@ and @service.port@ are injected from the pom.xml. With this in place we can run the service stand-alone, starting it with

mvn spring-boot:start

from the registrationservice directory, or inside the docker container, as we will see later.
registrationservice/src/main/kotlin/net/microservices/tutorial/registrationservice/RegistrationServer.kt

@file:JvmName("RegistrationServer")
package net.microservices.tutorial.registrationservice

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration

@Configuration
@EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class, DataSourceTransactionManagerAutoConfiguration::class, HibernateJpaAutoConfiguration::class))
@ComponentScan
@EnableEurekaServer
open class RegistrationServer {

    companion object {

            /**
             * Run the application using Spring Boot and an embedded servlet engine.

             * @param args
             * *            Program arguments - ignored.
             */
            @JvmStatic fun main(args: Array<String>) {
                SpringApplication.run(RegistrationServer::class.java, *args)
            }
        }
}

Please note the open class definition: this is needed, as in all the Kotlin classes managed by Spring, because at runtime Spring creates proxies of these classes, and by default Kotlin classes are final (Kotlin, I love you!).

Actually, all the magic is here:

@EnableEurekaServer

With this annotation we are telling Spring to setup a Eureka Registrar Server, using as properties the ones defined (by default) in the application.yml file.

docker

Last step will be the creation of the docker module.
To cut a long story short, fabric8 plugin uses the maven-assembly-plugin to build the images. Unfortunately, making this plugin work for a multimodule project is a little bit tricky (you may find detailed explanation here), so one solution is to create an “empty” module used just to create the images.
Let’s go back to the parent pom, and add the module declaration:

... 
</parent>
<modules>
  <module>servicecommon</module>
  <module>registrationservice</module>
  <module>docker</module>
</modules>
<properties>
...

Now, the pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>container-microservice</artifactId>
        <groupId>net.microservices.tutorial</groupId>
        <version>0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>docker</artifactId>
    <packaging>pom</packaging>
    <properties>
        <!-- PLUGINS -->
        <docker.maven.plugin.fabric8.version>0.21.0</docker.maven.plugin.fabric8.version>
        <!-- CONFIGURATIONS -->
        <docker.repo>(YOUR-DOCKER-REPO-NAME)</docker.repo>
        <!-- services properties -->
        <registration.service.port>1111</registration.service.port>
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>registrationservice</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- DOCKERIZE WITH FABRIC8 -->
            <plugin>
                <!-- The Docker Maven plugin is used to create docker image with the fat jar -->
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.fabric8.version}</version>
                <configuration>
                    <logDate>default</logDate>
                    <autoPull>true</autoPull>
                    <images>
                        <!-- Registration service -->
                        <image>
                            <!-- Alias name which can used for linking containers during runtime -->
                            <alias>registrationservice</alias>
                            <name>${docker.repo}/registration-service:${project.version}</name>
                            <!-- ....................................... -->
                            <!-- Build configuration for creating images -->
                            <!-- ....................................... -->
                            <build>
                                <from>java:8u40</from>
                                <!-- Assembly descriptor holds the reference to the created artifact-->
                                <assembly>
                                    <descriptor>${basedir}/../registrationservice/src/main/fabric8/assembly.xml
                                    </descriptor>
                                </assembly>
                                <!-- Expose ports -->
                                <ports>
                                    <port>${registration.service.port}</port>
                                </ports>
                                <!-- Default command for the build image -->
                                <cmd>java -Djava.security.egd=file:/dev/./urandom -jar /maven/registrationservice.jar
                                </cmd>
                            </build>
                            <!-- ............................................................... -->
                            <!-- Runtime configuration for starting/stopping/linking containers -->
                            <!-- ............................................................... -->
                            <run>
                                <!-- Assign dynamically mapped ports to maven variables (which can be reused in integration tests) -->
                                <ports>
                                    <port>${registration.service.port}:${registration.service.port}</port>
                                </ports>
                                <env>
                                    <SERVICE_PORT>${registration.service.port}</SERVICE_PORT>
                                    <CONTAINER_IP>${docker.container.registrationservice.ip}</CONTAINER_IP>
                                </env>
                                <wait>
                                    <!-- Check for this URL to return a 200 return code .... -->
                                    <url>http://${docker.host.address}:${registration.service.port}/</url>
                                    <!-- ... but at max 20 seconds -->
                                    <time>20000</time>
                                </wait>
                                <log>
                                    <prefix>TC</prefix>
                                    <color>cyan</color>
                                </log>
                            </run>
                        </image>
                    </images>
                </configuration>
                <!-- Hooking into the lifecycle -->
                <executions>
                    <execution>
                        <id>docker-build</id>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>start</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>build</goal>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.5.2.RELEASE</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Well, there is some hot stuff going on here.
Note that the packaging is pom, since there is no code in that project.
Also, please note the dependency on the registrationservice artifact, needed for the assembly plugin to work.
Now, the interesting part.
The guys at Fabric8 decided that all (or almost all) the image configuration (creation and starting) should be inside the pom, so there are a lot a properties reflecting the ones of the standard Dockerfile.
The configuration is made of two parts: build and run.
Inside the build we will put information for the image creation:

  • : the base image (java:8u40 in this case)
  • : list of ports exposed by the container (please note, this is not the  external_port:internal_port mapping)
  • : the command to be executed inside the container
  • : this is the pointer to the maven-assembly plugin descriptor; basically, this descriptor tells maven what should be put inside the built image and what should be the name of the jar (the assembly.xml will be shown below).

Inside the run we will put information for the container starting:

  • : list of  external_port:internal_port mapping
  • : variables injected inside the container
  • : command to execute to check that the container has started successfully.

Beside that, there are some properties that are populated when the container starts, for example:

  • docker.container.(image-name).ip: this contain the “public” ip of the running container;
  • docker.host.address: this contain the host address of the running container (localhost);

To make them available inside the container, we can declare them in the env tag, and the property name will be the one created inside the container; e.g.:

<env> 
   <SERVICE_PORT>${registration.service.port}</SERVICE_PORT>
   <CONTAINER_IP>${docker.container.registrationservice.ip}</CONTAINER_IP>
</env>

Inside the container we will have $SERVICE_PORT (with the registration.service.port value, statically defined in the pom) and $CONTAINER_IP (with the docker.container.registrationservice.ip value, dynamically defined at start time).
There are another couple of run tags used for images linking/dependency, but we will look at them in the next part of the tutorial.

Assembly

The following snippet instruct the plugin to look for the assembly description in a specific file:

...
<assembly>
       <descriptor>${basedir}/../registrationservice/src/main/fabric8/assembly.xml
       </descriptor>
</assembly>
...

The file is inside the registrationservice module:
registrationservice/src/main/fabric8/assembly.xml

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <!-- Assembly specifying Dockerbuild for fabric8/docker-maven-plugin -->
    <dependencySets>
        <dependencySet>
            <useProjectArtifact>false</useProjectArtifact>
            <includes>
                <include>net.microservices.tutorial:registrationservice</include>
            </includes>
            <outputDirectory>.</outputDirectory>
            <outputFileNameMapping>registrationservice.jar</outputFileNameMapping>
        </dependencySet>
    </dependencySets>
</assembly>

Here we are telling maven to include only the registrationservice artifact and to put it inside registrationservice.jar
Note that it is possible to avoid that file, putting its content inside the assembly directive, in the inline tag:

...
<assembly>
    <inline>
    <dependencySets>
        <dependencySet>
            <useProjectArtifact>false</useProjectArtifact>
            <includes>
                <include>net.microservices.tutorial:registrationservice</include>
            </includes>
            <outputDirectory>.</outputDirectory>
            <outputFileNameMapping>registrationservice.jar</outputFileNameMapping>
        </dependencySet>
    </dependencySets>
    </inline>
</assembly>
...

but actually I do prefer to keep it outside – it could be easier to manage, eventually, without cluttering too much the pom itself.

docker image creation, start and stop

Now we have all the pieces in place to build and start our first image, but before proceeding any further remember to replace the (YOUR-DOCKER-REPO-NAME) property with the name of your Docker repository.
Calling

mvn install

from the console, in the project root, will:

  1.  recursively build all the modules in the project
  2. install all the modules
  3. create the reactiveservice image.

The first time that the image is built, the base image will be downloaded (if not already present in the local Docker instance). Soon after the build, the image is tested starting a container and invoking the command defined in the wait directive: if the command is successfull, the build is marked with success. Whenever a new version of the image is built, the plugin takes care of removing the old one.

Now, to start the container, let’s go in the docker directory and type:

mvn docker:start

This will create and start a new container with the image just built and the parameters defined in the run configuration. If you execute the command in debug mode (with the -X parameter) you will see the actual configuration sent (i.e. with all the placeholders replaced) and also the http requests issued to the Docker daemon (and, for free, all the image’ data transferred to it, in hexadecimal format!).
To verify that everything is working, just open the browser here and you should see the Eureka dashboard.

Finally, to stop the container execute

mvn docker:stop

This will stop the container and remove it.

Conclusion

Well, I think we have touched a lot of important topics.
To clone the project:

 git clone https://github.com/gitgabrio/container-microservices-tutorial-1.git

In the next parts we will add other containers, to see how to make Eureka clients talking between them and how to deploy them in different docker images. We will also add some microservices communicating with Akka so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!