Cómo crear un microservicio Spring Boot en Docker

Vamos a aprender cómo ejecutar un microservicio de Spring Boot dentro de un contenedor Docker.

En un post previo aprendimos como instalar Docker y como ejecutar un Java Hello Word dentro del contenedor Docker. Ahora aprenderemos como ejecutar una aplicación de microservicio Spring Boot dentro de Docker.

Spring Boot Docker

Dependencias mínimas de un microservicio Spring Boot

Lo primero que vamos a hacer es definir las dependencias que necesitamos.
Spring Boot requiere al menos estas dependencias:

org.springframework.boot:spring-boot-starter-web
org.springframework.boot:spring-boot-starter-test

La configuración para Gradle de estas dependencias es:

plugins {
    id 'org.springframework.boot' version '2.1.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'dev.experto'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

La configuración para Maven de estas dependencias para Spring Boot:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>dev.experto</groupId>
    <artifactId>springbootdocker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootdocker</name>
    <description>Demo project for Spring Boot with Docker</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Crear el microservicio Spring Boot

Debes crear la clase main que iniciará Spring.
Anotamos la clase con @SpringBootApplication
La anotación @SpringBootApplication es equivalente a usar @Configuration + @EnableAutoConfiguration + @ComponentScan con sus atributos por default .

@SpringBootApplication
public class SpringbootdockerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootdockerApplication.class, args);
    }

}

Luego creas un controlador rest que anotas con @RestController
El metodo home mapea la base de la url de tu servicios.
Creas una clase Home y anota el método con @RequestMapping

@RestController
public class Home {

    @RequestMapping("/")
    public String home() {
        return "Hello Spring Boot with Docker";
    }

}

.
Puedes ejecutar la aplicacion desde la consola.
Si usas gradle gradle bootRun
Si usas maven mvn spring-boot:run
Si usas el wrapper de gradle ./gradlew bootRun

Abres la URL localhost:8080.

Finalizar la ejecución de SpringBoot con Gradle pulsando Ctrl+C :
En ocasiones, aunque en realidad (a mi me pasa todas las veces :/) gradlew no termina cuando pulsas CTRL+C en la consola.
Si no puedes finalizar gradlew cuando pulsas ctrl + c , tendrás que abrir otra consola
y ejecutar ./gradlew -stop

Build de tu aplicación Spring Boot

Antes de seguir con los otros pasos asegurate de hacer un build del proyecto para dejar disponible el archivo jar que necesitaremos más adelante.

con gradle
Gradle deja el jar en la carpeta \build\libs de tu proyecto con el nombre que tienes en el archivo settings.gradle
gradlew build

Gradle crear archivo jar con el nombre que tienes especificado en el archivo settings.gradle.

con maven
Maven deja el jar en la carpeta \target de tu proyecto con el nombre que tengas definido en el tag artifactId de tu archivo pom.xml
mvnw package

Crear un Docker file para ejecutar SpringBoot

Bien, ya tienes tu aplicación de servicio Rest funcionando. Empezarás ahora a configurar Docker para que tu servicio corra allí.
Para crear la configuración de Docker para SpringBoot creas un archivo de texto Dockerfile con estas instrucciones:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8080
ARG JAR_FILE=build/libs/springbootdocker-0.0.1-SNAPSHOT.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

FROM:
Observa que en la primer línea usas una imagen de docker que ya está armada con linux y java 8. Es decir, nuestro servicio correrá sobre un sistema linux y Java 8.

ADD:
La instrucción ADD copia nuestra aplicación dentro de Docker.
Nuestro config de gradle o maven deja el file jar con el nombre de “springbootdocker-0.0.1-SNAPSHOT.jar”.
Este archivo springbootdocker-0.0.1-SNAPSHOT.jar se agrega al contenedor con la instruccion ADD con el nombre app.jar
Si la ruta de tu aplicación jar está en otro lugar, deberás modificarla con la ruta correcta. Para el caso de que hayas creado tu app con maven deberás usar la ruta del target de maven.

ENTRYPOINT:
Luego ejecutamos el archivo al iniciar docker con la instrucción ENTRYPOINT

Una vez definido el archivo Dockerfile ejecutas docker build . El parámetro -t define el nombre que le daremos al contenedor y que usaremos luego para ejecutarlo.

 docker build -t springbootdockerexample .

Mira en la consola con lo que pasa con tu config de Docker que creaste.
En cada paso ves que se crea la imagen linux con java 8 (FROM), luego que se expone el puerto 8080 desde el contenedor hacia el host del contenedor (EXPOSE), se copia el el jar (ADD) y se ejecuta (ENTRYPOINT).

Terminado el build del docker puedes ejecutarlo con el nombre que le diste.

Ejecutando Docker con Spring Boot

Una vez ya tienes la imagen de docker, puedes ejecutarla con docker run
El parámetro -p le dice a docker que exponga el puerto 8080 de nuestra aplicación que se está ejecutando dentro del contendor, en el puerto 5000 del sistema operativo.

docker run  -p 5000:8080 springbootdockerexample

Ahora abres la url apuntando al puerto que le dijiste a docker. En este caso 5000
http://localhost:5000/

Sonríe tu aplicación está funcionando en un contenedor Docker!

Crear la imagen de Docker con Spring Boot usando Gradle

Si usas Gradle puedes agregar un plugin para construir la imagen y ejecutarla directamente desde los comandos de gradle.
Para esto modificas el build.gradle agregando este plugin https://github.com/palantir/gradle-docker

Lo nuevo que agregas es lo que ves en buildscript, luego el apply del plugin y por último una nueva task docker.

// docker: new plugin 
buildscript {
    dependencies {
        classpath "gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.22.1"
    }
}

plugins {
    id 'org.springframework.boot' version '2.1.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'dev.experto'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

// docker: plugin apply  
apply plugin: 'com.palantir.docker'

// docker: new task docker 
docker {
    dependsOn build
    name "${project.name}"
    files bootJar.archivePath
    buildArgs(['JAR_FILE': "${bootJar.archiveName}"])
}

Analicemos la nueva tarea docker del archivo build de gradlew que hemos modificado:

  • dependsOn build le decimos que esta tarea depende del build de gradle
  • name "${project.name}" será el nombre del contenedor, le daremos el nombre del proyecto que tenemos en settings.gradle
  • files bootJar.archivePath pondra a disposición el archivo jar generado por el build
  • buildArgs(['JAR_FILE': "${bootJar.archiveName}"]) envia como argumento el nombre del archivo jar.

Ejecutando la imagen de Docker con Spring Boot usando Gradle

Si ya modificaste tu archivo build de gradle agregando el plugin com.palantir.docker, puedes ejecutar directamente el build y crear tu imagen desde gradlew.
Desde tu consola usarás gradlew build docker para construir a imagen

gradlew build docker
docker run -p 5000:8080 springbootdocker

Crear la imagen de Docker con Spring Boot usando Maven

En el caso de usar Maven debes agregar este plugin si deseas crear la imagen directamente desde mvn
https://github.com/spotify/dockerfile-maven

            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.4.9</version>
                <configuration>
                    <repository>${project.artifactId}</repository>
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
                <executions>
                    <execution>
                        <id>default</id>
                        <phase>install</phase>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Tu pom.xml completo queda de esta manera:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>dev.experto</groupId>
    <artifactId>springbootdocker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootdocker</name>
    <description>Demo project for Spring Boot with Docker</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.4.13</version>
                <configuration>
                    <repository>${project.artifactId}</repository>
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
                <executions>
                    <execution>
                        <id>default</id>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Ejecutando la imagen de Docker con Spring Boot usando Maven

Una vez que hayas agregado el plugin https://github.com/spotify/dockerfile-maven ejecutas:

mvnw dockerfile:build
docker run -p 5000:8080 springbootdocker

Al ejecutar docker run tu app quedará en la url http://localhost:5000/

Un resumen de los comandos de docker que fueron útiles:

  • docker build para crear la imagen de docker
  • docker run para ejecutar la imagen de docker
  • docker images ver las imágenes de docker que tienes disponibles.

Como siempre este código lo puedes encontrar el github y gitlab

https://github.com/gustavopeiretti/springboot-docker-example
https://gitlab.com/gustavopeiretti/springboot-docker-example

Conclusión

Aprendiste cómo crear una aplicación de Spring Boot Rest, configurar un dockerfile para tu aplicación, construir tu contenedor Docker y ejecutar tu microservicio desde el contenedor. También aprendiste algunos comandos básicos de docker.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.