Tech Tip #61 showed how to run Java EE 7 hands-on lab on WildFly Docker container. A couple of assumptions were made in that case:
- WildFly bundles H2 in-memory database. The Java EE 7 application uses the default database resource, which in case of WildFly, gets resolved to a JDBC connection to that in-memory database. This is a good way to start building your application but pretty soon you want to start using a real database, like MySQL.
- Typically, Application Server and Database may not be residing on the same host. This reduces the risk by avoiding a single point of failure. And so WildFly and MySQL would be on to separate host.
There is plethora of material available to show how to configure WildFly and MySQL on separate hosts. What are the design patterns, and anti-patterns, if you were to do that using Docker?
Lets take a look!
In simplified steps:
- Run the MySQL container as:
123docker run --name mysqldb -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql -e MYSQL_DATABASE=sample -e MYSQL_ROOT_PASSWORD=supersecret -d mysql - Run the WildFly container, with MySQL JDBC resource pre-configured, as:
123docker run --name mywildfly --link mysqldb:db -p 8080:8080 -d arungupta/wildfly-mysql-javaee7 - Find the IP address of the WildFly container:
123sudo docker inspect -f '{{ .NetworkSettings.IPAddress }}' mywildflyboot2docker ip
to find the IP address. - Access the application as:
123curl http://<IP_ADDRESS>:8080/employees/index.html
12345678910111213141516<!DOCTYPE html><html><head><title>Employees</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body><ul><li>GET <a href="resources/employees">all</a> employees.</li><li>GET <a href="resources/employees/1">1</a> employee.</li></ul></body></html>
123curl http://localhost:8080/employees/resources/employees/
123<?xml version="1.0" encoding="UTF-8" standalone="yes"?><collection><employee><id>1</id><name>Penny</name></employee><employee><id>2</id><name>Sheldon</name></employee><employee><id>3</id><name>Amy</name></employee><employee><id>4</id><name>Leonard</name></employee><employee><id>5</id><name>Bernadette</name></employee><employee><id>6</id><name>Raj</name></employee><employee><id>7</id><name>Howard</name></employee><employee><id>8</id><name>Priya</name></employee></collection>
If you are interested in nitty gritty, read further details.
Linking Containers
The first concept we need to understand is how Docker allows linking containers. Creating a link between two containers creates a conduit between a source container and a target container and securely transfer information about source container to target container. In our case, target container (WildFly) can see information about source container (MySQL). The important part to understand here is that none of this information needs to be publicly exposed by the source container, and is only made available to the target container.
The magic switch to enable link is, intuitively, --link
. So for example, if MySQL and WildFly containers are run as shown above, then --link mysqldb:db
links the MySQL container named mysqldb
with an alias db
to the WildFly target container. This defines some environment variables, following the defined protocol, in the target container which can then be used to access information about the source container. For example, IP address, exposed ports, username, passwords, etc. The complete list of environment variables can be seen as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[arun@localhost wildfly-mysql-javaee7]$ sudo docker run --name mywildfly --link mysqldb:db -p 8080:8080 -it arungupta/wildfly-mysql-javaee7 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=4e0458981a82
TERM=xterm
DB_PORT=tcp://172.17.0.24:3306
DB_PORT_3306_TCP=tcp://172.17.0.24:3306
DB_PORT_3306_TCP_ADDR=172.17.0.24
DB_PORT_3306_TCP_PORT=3306
DB_PORT_3306_TCP_PROTO=tcp
DB_NAME=/mywildfly3/db
DB_ENV_MYSQL_USER=mysql
DB_ENV_MYSQL_PASSWORD=mysql
DB_ENV_MYSQL_DATABASE=sample
DB_ENV_MYSQL_ROOT_PASSWORD=supersecret
DB_ENV_MYSQL_MAJOR=5.6
DB_ENV_MYSQL_VERSION=5.6.22
JAVA_HOME=/usr/lib/jvm/java
WILDFLY_VERSION=8.2.0.Final
JBOSS_HOME=/opt/jboss/wildfly
HOME=/opt/jboss
|
So you can see there are DB_*
environment variables providing plenty of information about source container.
Linking only works if all the containers are running on the same host. A better solution will be shown in the subsequent blog, stay tuned.
Override default Docker command
Dockerfile for this image inherits from jboss/wildfly:latest and starts the WildFly container. Docker containers can only run one command but we need to install JDBC driver, create JDBC resource using the correct IP address and port, and deploy the WAR file. So we will override the command by inheriting from jboss/wildfly:latest and use a custom command. This command will do everything that we want to do, and then start WildFly as well.
The custom command does the following:
- Add MySQL module
- Add MySQL JDBC driver
- Add the JDBC data source using IP address and port of the linked MySQL container
- Deploy the WAR file
- Start WildFly container
Note, WildFly is starting with -b 0.0.0.0
that allows it to be bound to any IP address. Also, the command needs to run in foreground so that the container stays active.
Customizing security
Ideally, you’ll poke holes in the firewall to enable connection to specific host/ports. But these instructions were tried on Fedora 20 running in Virtual Box. So for convenience, the complete firewall was disabled as:
1
2
3
4
|
sudo systemctl stop firewall
sudo systemctl disable firewall
|
In addition, a Host-only adapter was added using Virtual Box settings and looks like:
That’s it, that should get you going to to use WildFly and MySQL on two separate containers.
Also verified the steps on boot2docker, and it worked seamlessly there too:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
docker-images> docker run --name mysqldb -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql -e MYSQL_DATABASE=sample -e MYSQL_ROOT_PASSWORD=supersecret -d mysql
docker-images>
docker-images> docker run --name mywildfly --link mysqldb:db -p 8080:8080 -d arungupta/wildfly-mysql-javaee7
Unable to find image 'arungupta/wildfly-mysql-javaee7' locally
Pulling repository arungupta/wildfly-mysql-javaee7
791773b0e1de: Download complete
511136ea3c5a: Download complete
782cf93a8f16: Download complete
7d3f07f8de5f: Download complete
1ef0a50fe8b1: Download complete
20a1abe1d9bf: Download complete
cd5bb934bb67: Download complete
379edb00ab07: Download complete
4d37cbbfc67d: Download complete
2ea8562cac7c: Download complete
7759146eab1a: Download complete
b17a20d6f5f8: Download complete
e02bdb6c4ed5: Download complete
72d585299bb5: Download complete
90832e1f0bb9: Download complete
2c3484b42034: Download complete
38fad13dea25: Download complete
656878d9a6c6: Download complete
6510de96c354: Download complete
0cc86be8ac93: Download complete
cc4e21e8b0e7: Download complete
Status: Downloaded newer image for arungupta/wildfly-mysql-javaee7:latest
8522df362e57f5b7a5324dba692559b971c7cfda4a687212c44b1118008a4c63
docker-images> curl http://192.168.59.103:8080/employees/index.html
<!DOCTYPE html>
<html>
<head>
<title>Employees</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<ul>
<li>GET <a href="resources/employees">all</a> employees.</li>
<li>GET <a href="resources/employees/1">1</a> employee.</li>
</ul>
</body>
</html>
docker-images> curl http://192.168.59.103:8080/employees/resources/employees/
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><collection><employee><id>1</id><name>Penny</name></employee><employee><id>2</id><name>Sheldon</name></employee><employee><id>3</id><name>Amy</name></employee><employee><id>4</id><name>Leonard</name></employee><employee><id>5</id><name>Bernadette</name></employee><employee><id>6</id><name>Raj</name></employee><employee><id>7</id><name>Howard</name></employee><employee><id>8</id><name>Priya</name></employee></collection>
|
Source code for the image is at github.com/arun-gupta/docker-images/tree/master/wildfly-mysql-javaee7.
Enjoy!
Hello Arun,
I notice that if I restart the container, it will try to create the datasource again in Wildfly.
Is there a way to avoid this after the first start?
Thanks
Guilherme,
It will give an error but the data source will not be created again though, right?
Anyway following up and will get back to you.
Yes, it won’t be created again.
But I customized to use in other application, that uses JMS, Java mail and Modeshap, it is a lot of commands that will polute the log, so I would like to avoid this.
I’m thinking about to use a environment variable to flag if it was run or not, but I don’t know if it is a good idea.
Thanks
How is this different from deploying an application to a non-Docker environment?
The Wildfly in a non-Docker won’t execute the commands.
I’m new to Docker, so sorry if it’s not a good question. I would like to just execute the wildfly commands once and when it restarts, it can start faster without trying to create everything again.
By the way I like your container.
Thanks
You can consider creating a custom image with your DataSources etc baked in. This will involve writing a Dockerfile and invoking jboss-cli scripts that will make those checks, and create if necessary.
Hello,
I am starting mysql first and seems its OK, but when starting wildfly there is exception.
WFLYCTL0186: Services which failed to start: service jboss.persistenceunit.”employees.war#MyPU”
Error i am getting :
ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool — 58) MSC000001: Failed to start service jboss.persistenceunit.”employees.war#MyPU”: org.jboss.msc.service.StartException in service jboss.persistenceunit.”employees.war#MyPU”: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when ‘hibernate.dialect’ not set
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:172)