Creando imagen oci para wildfly con datasource de oracle

2021/03/11

Update 1: se agrega script generate-wildfly-patch.sh para generar los parches necesarios a partir de un tarball de wildfly.

Update 2: se agrega script patch-add-ojdbc-to-wildfly.sh para modificar wildfly agregando ojdbc.

Se busca crear una imagen oci utilizando podman a partir de la imagen oficial de wildfly en dockerhub y que esté configurada para utilizar un datasource que se conecte a una base de datos oracle.

Verificar imagen

Suele ocurrir que durante la creación de las imagenes los scripts descargan aplicaciones y modifican parte de los archivos descargados, por lo que se desea conocer si la imagen que se utilizará es un caso de estos.

Inspeccionar la creación de la imagen

Para ello se inspeccionan los Dockerfile de las imagenes. En este caso, inspeccionando el Dockerfile de jboss/wildfly:22.0.1.Final se comprueba que no fué modificado ningún archivo.

Inspeccionar los archivos en la imagen

Hay veces que puede ser engorroso inspeccionar los distintos Dockerfiles que forman una imagen y resulta más sencillo extraer directamente el o los archivos deseados que pueden ser relevantes si cambió una imagen.

Para esos casos alcanza con ejecutar un shell en el contenedor y confirmar la existencia y contenido de los archivos.

Si no se sabe exactamente que buscar puede utilizarse un shell para inspeccionar la imagen:

$ podman pull docker.io/jboss/wildfly:22.0.1.Final
$ podman run -it --name wildfly docker.io/jboss/wildfly:22.0.1.Final /bin/bash

Una alternativa a lo anterior es copiar directamente los archivos de la imagen. Utilizando como ejemplo la imagen que estamos interesados:

$ podman create --name wildfly docker.io/jboss/wildfly:22.0.1.Final
$ podman cp wildfly:/opt/jboss/wildfly/standalone/configuration/standalone.xml /tmp/standalone.xml

Una vez extraido el archivo, se puede eliminar el contenedor utilizando:

$ podman rm wildfly

Configuración de datasource oracle

Si bien en la documentación de wildfly 22 no se encuentra información relacionada a como configurar un datasource para oracle, esta se puede encontrar en Example Oracle Datasource (JBoss EAP 7.3). Conviene recordar -se nos recuerda en la página de descargas de wildfly- que:

The technology behind WildFly is also available in JBoss Enterprise Application Platform 7. JBoss EAP is a hardened enterprise subscription with Red Hat’s world-class support, long multi-year maintenance cycles, and exclusive content.

Este ejemplo es replicado en varios sitios de internet, donde se indica la creación del archivo module.xml de forma manual o utilizando jboss-cli.sh, pero suele perderse la indicación:

Important

Using the module management CLI command to add and remove modules is provided as Technology Preview only. This command is not appropriate for use in a managed domain or when connecting to the management CLI remotely. Modules should be added and removed manually in a production environment.

Tomando en cuenta la advertencia anterior, se generará el archivo modules.xml de forma local utilizando jboss-cli.sh y se verán las diferencias con un servidor wildfly sin modificar, extrayendo los archivos para generar la imagen docker.

Descarga de wildfly

Se comienza descargando el archivo wildfly-22.0.1.Final.tar.gz y realizando un respaldo para luego poder ver los cambios ocurridos:

$ DIR="$HOME/ws-customize-wildfly-oracle"; mkdir "$DIR"; cd "$DIR"
$ wget -P . 'https://download.jboss.org/wildfly/22.0.1.Final/wildfly-22.0.1.Final.tar.gz'
$ tar xf wildfly-22.0.1.Final.tar.gz
$ cp -a wildfly-22.0.1.Final wildfly-22.0.1.Final.orig
$ cd wildfly-22.0.1.Final

Ejecución de wildfly

Para que funcione correctamente la configuración del datasource hay truco. Se busca utilizar para la configuración variables de entorno, similar a como se utiliza en la definición de los layers extra de galleon para oracle. Copiando un extracto:

<param name="data-source" value="OracleDS"/>
<param name="jndi-name" value="java:jboss/datasources/${env.ORACLE_DATASOURCE,env.OPENSHIFT_ORACLE_DATASOURCE:OracleDS}"/>
<param name="connection-url" value="${env.ORACLE_URL, env.OPENSHIFT_ORACLE_URL}"/>

El problema que podemos tener es que si no están definidas las variables de entorno para la instancia en ejecución de wildfly a la que se conectará jboss-cli.sh nos aparece un mensaje similar al siguiente al querer utilizar variables de entorno en la definición del datasource:

$ ./bin/jboss-cli.sh -c
[standalone@localhost:9990 /] data-source add --name=OracleDS \
    --driver-name=oracle \
    --jndi-name=java:jboss/datasources/${env.ORACLE_DATASOURCE:OracleDS} \
    --connection-url=${env.ORACLE_URL}
{"WFLYCTL0062: Composite operation failed and was rolled back. Steps that failed:" => {"Operation step-1" => "W
FLYCTL0211: Cannot resolve expression '${env.ORACLE_URL}'"}}

Para evitar esto alcanza con definir previamente las variables de entorno que se utilizarán.

Tomando como ejemplo la definición de los layers extra de galleon para oracle y obviando las variables de entorno relacionadas a openshift, se definirán las siguientes variables:

quedando la invocación a wildfly de la siguiente forma (los valores de las variables de entorno no necesitan ser válidos):

$ ORACLE_URL=localhost
$ ORACLE_USER=user
$ ORACLE_PASSWORD=pass
$ ./bin/standalone.sh

Descarga del driver jdbc de oracle

Por otro lado en la página de descargas de oracle jdbc se indica que los drivers pueden descargarse del repositorio de maven central, por lo cual el driver se descarga usando maven-dependency-plugin:

$ mvn dependency:copy -Dartifact=com.oracle.database.jdbc:ojdbc11:21.1.0.0 -DoutputDirectory="$DIR"

Eliminación de datasource de ejemplo ExampleDS

El archivo standalone.xml tiene como ejemplo el datasource ExampleDS, el cual utiliza como base de datos h2. Como no se piensa utilizar a futuro dicho datasource se eliminará, teniendo cuidado de modificar el default-binding para el datasource, ya que según la seccion Default EE Bindings:

The Jakarta EE Specification mandates the existence of a default instance for each of the following resources:

El valor que se utilizará corresponderá al nombre del datasource de oracle que se agregará más adelante.

Para realizar todo lo anterior se utilizará jboss-cli.sh:

$ cat <<'END' | ./bin/jboss-cli.sh -c
/subsystem=ee/service=default-bindings:write-attribute(name="datasource", \
    value="java:jboss/datasources/${env.ORACLE_DATASOURCE:OracleDS}")

data-source remove --name=ExampleDS
reload

/subsystem=datasources/jdbc-driver=h2:remove()
END

Creación del módulo oracle y el datasource

La creación del módulo y el datasource se agrega utilizando jboss-cli.sh, siguiendo las indicaciones de Example Oracle Datasource (JBoss EAP 7.3) y las variables de entorno del layers extra de galleon para oracle.

$ cat <<'END' | ./bin/jboss-cli.sh -c
module add \
    --name=com.oracle \
    --resources=../ojdbc11-21.1.0.0.jar \
    --dependencies=javax.api,javax.transaction.api

/subsystem=datasources/jdbc-driver=oracle:add( \
    driver-name=oracle, \
    driver-module-name=com.oracle, \
    driver-xa-datasource-class-name=oracle.jdbc.xa.client.OracleXADataSource)

data-source add \
    --name=OracleDS \
    --driver-name=oracle \
    --jndi-name=java:jboss/datasources/${env.ORACLE_DATASOURCE:OracleDS} \
    --connection-url=${env.ORACLE_URL} \
    --user-name=${env.ORACLE_USER} \
    --password=${env.ORACLE_PASSWORD} \
    --enabled=true \
    --jta=true \
    --use-ccm=false \
    --min-pool-size=2 \
    --max-pool-size=5 \
    --pool-prefill=true \
    --share-prepared-statements=false
END

Nota: en el agregado del datasource y el driver de oracle es donde pueden cambiarse las opciones según las necesidades de la aplicación que utilizará la imagen.

Extracción de archivos

Una vez realizados los cambios con jboss-cli.sh, se termina la ejecución de wildfly y se comparan los archivos para determinar los cambios:

$ cd "$DIR"
$ diff -qr wildfly
$ diff -qr wildfly-22.0.1.Final{.orig,}
Only in wildfly-22.0.1.Final/modules: com
Files wildfly-22.0.1.Final.orig/standalone/configuration/logging.properties and wildfly-22.0.1.Final/standalone/configuration/logging.properties differ
Files wildfly-22.0.1.Final.orig/standalone/configuration/standalone.xml and wildfly-22.0.1.Final/standalone/configuration/standalone.xml differ
Only in wildfly-22.0.1.Final/standalone/configuration: standalone_xml_history
Only in wildfly-22.0.1.Final/standalone: data
Only in wildfly-22.0.1.Final/standalone: log
Only in wildfly-22.0.1.Final/standalone/tmp: vfs

Aquí se utilizó la opción -q ya que como se ve existen diferencias debido a la ejecución de wildfly (data, log, tmp) y los cambios en el archivo standalone.xml (standalone_xml_history, logging.properties).

Descartando los directorios y archivos que no nos interesan, los cambios realizados se pueden inspeccionar ejecutando:

$ diff -urN -x standalone_xml_history -x data -x log -x tmp \
    -x logging.properties wildfly-22.0.1.Final{.orig,}

Para listar solamente los archivos en los cuales estamos interesados se utiliza:

$ diff -qrN -x standalone_xml_history -x data -x log -x tmp \
    -x logging.properties wildfly-22.0.1.Final{.orig,}
Files wildfly-22.0.1.Final.orig/modules/com/oracle/main/module.xml and wildfly-22.0.1.Final/modules/com/oracle/main/module.xml differ
Files wildfly-22.0.1.Final.orig/modules/com/oracle/main/ojdbc11-21.1.0.0.jar and wildfly-22.0.1.Final/modules/com/oracle/main/ojdbc11-21.1.0.0.jar differ
Files wildfly-22.0.1.Final.orig/standalone/configuration/standalone.xml and wildfly-22.0.1.Final/standalone/configuration/standalone.xml differ

De lo anterior, se extraen los archivos que serán copiados a la nueva imagen, utilizando la ruta correspondiente en la imagen oficial de wildfly en dockerhub:

$ mkdir -p ./opt/jboss/wildfly/standalone/configuration \
    ./opt/jboss/wildfly/modules
$ cp wildfly-22.0.1.Final/standalone/configuration/standalone.xml \
    ./opt/jboss/wildfly/standalone/configuration
$ cp -a wildfly-22.0.1.Final/modules/com/ ./opt/jboss/wildfly/modules

Creación de imagen oci

Para crear la imagen del nuevo container se crea el archivo Containerfile indicando que la nueva imagen deriva de la imagen oficial de wildfly en dockerhub, agregandole los archivos extraidos previamente y ajustando permisos:

$ cat > Containerfile <<'END'
FROM docker.io/jboss/wildfly:22.0.1.Final
ADD opt /opt
USER root
RUN chown -R jboss:0 ${JBOSS_HOME} && chmod -R g+rw ${JBOSS_HOME}
USER jboss

Nota: los archivos deben copiarse con usuario jboss, en caso contrario luego al intentar ejecutar el servidor no se pueden crear archivos en los directorios ya que los archivos por defecto se copian como root.

Luego se crea la imagen utilizando podman:

$ podman image build --tag=jumapico/wildfly-oracle-datastore:22.0.1.Final-11 .

Prueba de la imagen

Para ejecutar la imagen creada hay que recordar el definir las variables de entorno prefijadas por ORACLE_:

$ podman run --detach --name wildfly \
    -e ORACLE_DATASOURCE=oracleds \
    -e ORACLE_URL=localhost \
    -e ORACLE_USER=user \
    -e ORACLE_PASSWORD=pass \
    jumapico/wildfly-oracle-datastore:22.0.1.Final-11
$ podman logs -f wildfly

Conclusiones