Docker para desarrollar con tomcat

Las ventajas de utilizar docker para desarrollar y desplegar la aplicaciones nunca deja de sorprenderme. Conseguimos con una simple definición en texto crear máquinas con configuraciones exactas a ejecutar en todas partes. Una de las cosas en donde todavía no había aplicado docker es en la aplicaciones web que desarrollo en Java. Habitualmente se requiere de un servidor tomcat (con cierta versión de máquina virtual de java) y una base de datos (yo he trabajado con mysql mayoritariamente) con otra versión particular.

Dado que las versiones de base de datos y de tomcat y de java han sido muy variables con el tiempo y que los proyectos no solo hay que desarrollarlos y entregarlos sino que hay que mantenerlos, nos encontramos con una situación imposible en que cualquier combinación de estos elementos es posible y necesaria para probar cualquier mínimo cambio que queramos realizar (si hay cosas muy compatibles que se pueden hacer con versiones nuevas y muchas otras que no)… El caso es que esta vez voy a deciros cómo podemos olvidarnos de una vez por todas de estos problemas utilizando docker y docker-compose.

Si eres el desgraciado usuario de una versión de windows las cosas pueden ser un poco más complicadas, pero si usas un sistema operativo de verdad la instalación y puesta en marcha de docker es trivial e inmediata (ya lo he tratado antes en el blog). Voy a dar por hecho que ya tienes un proyecto Java a ejecutar con tomcat (si es con maven o gradle que te generen el .war automáticamente, mejor) y que ya tienes docker instalado en tu equipo (y docker-compose). Lo que sigue es lo que tienes que hacer para probar tu war en un tomcat con una versión determinada que use una base de datos de una versión determinada:

Crea un directorio docker al mismo nivel que tienes el pom.xml si usas maven y dentro de ese directorio crea el archivo docker-compose.yml

version: "3"
services:

  tomcat:
    container_name: miproyecto-tomcat
    image: tomcat:8-jdk8
    ports:
      - 8080:8080
    volumes:
      - ../target/miproyecto.war:/usr/local/tomcat/webapps/miproyecto.war
      - ../target/miproyecto:/usr/local/tomcat/webapps/miproyecto
    links:
      - "mysql:db"

  mysql:
    container_name: miproyecto-mysql
    image: mysql:8.0
    environment:
       MYSQL_ROOT_PASSWORD: "mipassword"
    volumes:
      - ../mysql:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: unless-stopped

En el directorio del pom.xml ejecuta lo siguiente:

mvn package

Esto te genera el war dentro de target y ya estarás en disposición de entrar dentro del directorio docker y ejecutar:

docker-compose up -d

Quiá la primera vez podrías querer ejecutarlo sin la opción -d para ver todo el log que sale (también es cierto que la primera vez se descarga todos los contenedores y tardará un poco). A partir de que haya arrancado podemos acceder a la aplicación en:

http://localhost:8080/miproyecto

También se puede acceder a la base de datos directamente:

mysql -h 127.0.0.1 -u root -p

Y yasta… En próximas entradas veremos como depurar con docker también.

Subir a maven central una librería propia

Ahora que ya acabas de construir una librería interesante en Java, la has hecho pública (en github, por ejemplo) y quieres que todo el mundo la use… Queda una tarea pendiente, subirla a un repositorio maven para ponerla a disposición de los que utilicen este sistema (o gradle, que hoy en día ya son casi todos).

Vamos a verlo con un ejemplo que he subido esta mañana… Hay cosas que todavía no entiendo del todo, pero el resultado ha sido bueno, por lo que, al menos, podremos usar esta receta como guía para próximas veces.

El código que intento subir es una librería simple que tengo alojada en github con su pom.xml básico y que si te descargas el proyecto podrías compilar e instalar en tu maven con mvn install. La dirección es esta:

https://github.com/yoprogramo/nomorepass-java/

Ahora, para que todo el mundo pueda descargárselo como dependencia y no tenga que hacer el mvn install del proyecto, tenemos que subirlo a un repositorio público, podemos ver una guía en esta página: Guide to Public Maven Repositories. Tal como explican en la página, lo más sencillo para publicar en Maven Central es usar el repositorio Sonatype. Dicho y hecho… Lo intentamos por aquí.

Lo primero es crear una cuenta en el Jira de Sonatype aquí. Lo siguiente, y esto es un poco «tricky» es crear un ticket solicitando un nuevo id de grupo en esta dirección. No se puede pedir cualquier id de grupo (en mi caso quería pedir com.nomorepass) y generalmente se pedirá alguna prueba de que el dominio es tuyo. En mi caso este es el ticket que creé: https://issues.sonatype.org/browse/OSSRH-49426, para demostrar que el dominio era mío cambié el DNS e incluí una entrada TXT con el identificador del ticket:

Una vez autorizado (tarda un poco, es un proceso manual) hay que modificar nuestro código y prepararlo para la subida, pero, antes de eso, tenemos que generar nuestras claves gpg para poder firmar el código. eso se hace con este comando:

gpg --gen-key

Una vez generada podremos acceder a la lista de claves con el comando:

gpg --list-keys

Toma nota del id de la clave y recuerda la contraseña que usaste para generarla, porque tendrás que recordarla. Además, tendrás que publicarla en algún servidor de claves públicas para que pueda ser comprobada.

gpg --keyserver hkp://keys.gnupg.net --send-keys <el-id-de-la-clave>

Ahora empezamos a modificar el pom.xml para que cumpla con los requisitos para el repositorio Maven Central. En nuestro caso pusimos esto:

<groupId>com.nomorepass</groupId>
  <artifactId>nomorepass</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>Nomorepass java library</name>
  <description>NoMorePass protocol 2 implemented on Java.</description>
  <url>https://nomorepass.com</url>

  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>

  <developers>
    <developer>
      <name>Jose Antonio Espinosa</name>
      <email>[email protected]</email>
      <organization>Nomorepass</organization>
      <organizationUrl>https://nomorepass.com</organizationUrl>
    </developer>
  </developers>

  <scm>
    <connection>scm:git:git://github.com/yoprogramo/nomorepass-java.git</connection>
    <developerConnection>scm:git:ssh://github.com:yoprogramo/nomorepass-java.git</developerConnection>
    <url>https://github.com/yoprogramo/nomorepass-java/tree/master</url>
</scm>

Y, una vez informado de todo esto, hay que incluir los plugins que nos permitirán hacer el despliegue directamente. Yo añadí esto:

<distributionManagement>
    <snapshotRepository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
</distributionManagement>

Y puse en mi directorio de maven settings.xml los datos de mi usuario

<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>xxxxxxxxxx</username>
      <password>xxxxxxxxxx</password>
    </server>
  </servers>
</settings>

Por último, toda la sección de build (que no tenía) la sustituí por esto:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-gpg-plugin</artifactId>
        <executions>
          <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
              <goal>sign</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.sonatype.plugins</groupId>
        <artifactId>nexus-staging-maven-plugin</artifactId>
        <version>1.6.7</version>
        <extensions>true</extensions>
        <configuration>
          <serverId>ossrh</serverId>
          <nexusUrl>https://oss.sonatype.org/</nexusUrl>
          <autoReleaseAfterClose>true</autoReleaseAfterClose>
        </configuration>
      </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>2.2.1</version>
          <executions>
            <execution>
              <id>attach-sources</id>
              <goals>
                <goal>jar-no-fork</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-javadoc-plugin</artifactId>
          <version>2.9.1</version>
          <executions>
            <execution>
              <id>attach-javadocs</id>
              <goals>
                <goal>jar</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
    </plugins>
</build>

Y ya, finalmente, pude ejecutar el mágico:

mvn clean deploy

Si todo ha ido bien, el artefacto estará subido a un repositorio que tendremos que promocionar a «Release» para que se sincronice con el repositorio central… Pero al final ya lo tendremos disponible para todo el mundo…

Aquí podéis encontrar lo que acabo de subir: https://search.maven.org/artifact/com.nomorepass/nomorepass/1.0/jar