Enterprise Java

MySQL as Kubernetes Service, Access from WildFly Pod

Java EE 7 and WildFly on Kubernetes using Vagrant (Tech Tip #71) explained how to run a trivial Java EE 7 application on WildFly hosted using Kubernetes and Docker. The Java EE 7 application was the hands-on lab that have been delivered around the world. It uses an in-memory database that is bundled with WildFly and allows to understand the key building blocks of Kubernetes. This is good to get you started with initial development efforts but quickly becomes a bottleneck as the database is lost when the application server goes down. This tech tip will show how to run another trivial Java EE 7 application and use MySQL as the database server. It will use Kubernetes Services to explain how MySQL and WildFly can be easily decoupled.

Lets get started!

Make sure to have a working Kubernetes setup as explained in Kubernetes using Vagrant.

The complete source code used in this blog is available at github.com/arun-gupta/kubernetes-java-sample.

Start MySQL Kubernetes pod

First step is to start the MySQL pod. This can be started by using the MySQL Kubernetes configuration file:

kubernetes> ./cluster/kubectl.sh create -f ../kubernetes-java-sample/mysql.json
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth create -f ../kubernetes-java-sample/mysql.json
mysql

The configuration file used is at github.com/arun-gupta/kubernetes-java-sample/blob/master/mysql.json.

Check the status of MySQL pod:

kubernetes> ./cluster/kubectl.sh get pods
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth get pods
POD                 IP                  CONTAINER(S)        IMAGE(S)            HOST                    LABELS              STATUS
mysql               10.246.1.3          mysql               mysql:latest        10.245.1.3/10.245.1.3   name=mysql          Pending

Wait till the status changes to “Running”. It will look like:

KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth get pods
POD                 IP                  CONTAINER(S)        IMAGE(S)            HOST                    LABELS              STATUS
mysql               10.246.1.3          mysql               mysql:latest        10.245.1.3/10.245.1.3   name=mysql          Running

It takes a few minutes for MySQL server to be in that state, so grab a coffee or a quick fast one miler!

Start MySQL Kubernetes service

Pods, and the IP addresses assigned to them, are ephemeral. If a pod dies then Kubernetes will recreate that pod because of its self-healing features, but it might recreate it on a different host. Even if it is on the same host, a different IP address could be assigned to it. And so any application cannot rely upon the IP address of the pod.

Kubernetes services is an abstraction which defines a logical set of pods. A service is typically back-ended by one or more physical pods (associated using labels), and it has a permanent IP address that can be used by other pods/applications. For example, WildFly pod can not directly connect to a MySQL pod but can connect to MySQL service. In essence, Kubernetes service offers clients an IP and port pair which, when accessed, redirects to the appropriate backends.

techtip72-kubernetes-services

Lets start MySQL service.

kubernetes> ./cluster/kubectl.sh create -f ../kubernetes-java-sample/mysql-service.json
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth create -f ../kubernetes-java-sample/mysql-service.json
mysql

The configuration file used is at github.com/arun-gupta/kubernetes-java-sample/blob/master/mysql-service.json. In this case, only a single MySQL instance is started. But multiple MySQL instances can be easily started and WildFly Pod will continue to refer to all of them using MySQL Service.

Check the status/IP of the MySQL service:

kubernetes> ./cluster/kubectl.sh get services
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth get services
NAME                LABELS                                    SELECTOR            IP                  PORT
kubernetes          component=apiserver,provider=kubernetes   <none>              10.247.0.2          443
kubernetes-ro       component=apiserver,provider=kubernetes   <none>              10.247.0.1          80
mysql               name=mysql                                name=mysql          10.247.202.104      3306
skydns              k8s-app=skydns                            k8s-app=skydns      10.247.0.10         53

Start WildFly Kubernetes Pod

WildFly Pod must be started after MySQL service has started. This is because the environment variables used for creating JDBC resource in WildFly are only available after the service is up and running. Specifically, the JDBC resource is created as:

data-source add --name=mysqlDS --driver-name=mysql --jndi-name=java:jboss/datasources/ExampleMySQLDS --connection-url=jdbc:mysql://$MYSQL_SERVICE_HOST:$MYSQL_SERVICE_PORT/sample?useUnicode=true&characterEncoding=UTF-8 --user-name=mysql --password=mysql --use-ccm=false --max-pool-size=25 --blocking-timeout-wait-millis=5000 --enabled=true

$MYSQL_SERVICE_HOST and $MYSQL_SERVICE_PORT environment variables are populated by Kubernetes as explained here.

This is shown at github.com/arun-gupta/docker-images/blob/master/wildfly-mysql-javaee7/customization/execute.sh#L44.

Start WildFly pod:

kubernetes> ./cluster/kubectl.sh create -f ../kubernetes-java-sample/wildfly.json
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth create -f ../kubernetes-java-sample/wildfly.json
wildfly

The configuration file used is at github.com/arun-gupta/kubernetes-java-sample/blob/master/wildfly.json.

Check the status of pods:

KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth get pods
POD                 IP                  CONTAINER(S)        IMAGE(S)                                 HOST                    LABELS              STATUS
mysql               10.246.1.3          mysql               mysql:latest                             10.245.1.3/10.245.1.3   name=mysql          Running
wildfly             10.246.1.4          wildfly             arungupta/wildfly-mysql-javaee7:knetes   10.245.1.3/10.245.1.3   name=wildfly        Pending

Wait until WildFly pod’s status is changed to Running. This could be a few minutes, so may be time to grab another quick miler!

Once the container is up and running, you can check /opt/jboss/wildfly/standalone/configuration/standalone.xml in the WildFly container and verify that the connection URL indeed contains the correct IP address. Here is how it looks on my machine:

[jboss@wildfly ~]$ grep 3306 /opt/jboss/wildfly/standalone/configuration/standalone.xml 
                    <connection-url>jdbc:mysql://10.247.202.104:3306/sample?useUnicode=true&amp;characterEncoding=UTF-8</connection-url>

The updated status (after the container is running) would look like as shown:

kubernetes> ./cluster/kubectl.sh get pods
KUBE_MASTER_IP: 10.245.1.2
Running: ./cluster/../cluster/vagrant/../../_output/dockerized/bin/darwin/amd64/kubectl --auth-path=/Users/arungupta/.kubernetes_vagrant_auth get pods
POD                 IP                  CONTAINER(S)        IMAGE(S)                                 HOST                    LABELS              STATUS
mysql               10.246.1.3          mysql               mysql:latest                             10.245.1.3/10.245.1.3   name=mysql          Running
wildfly             10.246.1.4          wildfly             arungupta/wildfly-mysql-javaee7:knetes   10.245.1.3/10.245.1.3   name=wildfly        Running

Access the Java EE 7 Application

Note down the HOST IP address of the WildFly container and access the application as:

curl http://10.245.1.3:8080/employees/resources/employees

to see the output as:

<?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>

Or viewed in the browser as:

techtip72-kubernetes-wildfly-output

Debugging Kubernetes and Docker

Login to the Minion-1 VM:

kubernetes> vagrant ssh minion-1
Last login: Tue Feb 10 23:20:13 2015 from 10.0.2.2

Log in as root:

[vagrant@kubernetes-minion-1 ~]$ su -
Password: 
[root@kubernetes-minion-1 ~]#

Default root password for VM images created by Vagrant is “vagrant”.

List of Docker containers running on this VM can be seen as:

[root@kubernetes-minion-1 ~]# docker ps
CONTAINER ID        IMAGE                                    COMMAND                CREATED             STATUS              PORTS                                            NAMES
7fc1fca102bf        arungupta/wildfly-mysql-javaee7:knetes   "/opt/jboss/wildfly/   28 minutes ago      Up 28 minutes                                                        k8s_wildfly.6c5f240e_wildfly.default.api_1230e74a-b178-11e4-8464-0800279696e1_509268aa                                  
4aa49c0ebb70        kubernetes/pause:go                      "/pause"               43 minutes ago      Up 43 minutes       0.0.0.0:8080->8080/tcp, 0.0.0.0:9090->9090/tcp   k8s_POD.bca60d1a_wildfly.default.api_1230e74a-b178-11e4-8464-0800279696e1_0bff6efa                                      
c36e99cd4557        mysql:latest                             "/entrypoint.sh mysq   47 minutes ago      Up 47 minutes                                                        k8s_mysql.278e3c40_mysql.default.api_f3d07101-b175-11e4-8464-0800279696e1_ddbcaf62                                      
ed4611b5c276        google/cadvisor:0.8.0                    "/usr/bin/cadvisor"    56 minutes ago      Up 56 minutes                                                        k8s_cadvisor.8d424740_cadvisor-agent.file-6bb810db-kubernetes-minion-1.file_80331227d28e618b4cef459507a31796_36d83f7d   
50a3428612f0        kubernetes/pause:go                      "/pause"               58 minutes ago      Up 58 minutes       0.0.0.0:3306->3306/tcp                           k8s_POD.c783ea16_mysql.default.api_f3d07101-b175-11e4-8464-0800279696e1_e46a8424                                        
11a95eeda794        kubernetes/pause:go                      "/pause"               59 minutes ago      Up 59 minutes       0.0.0.0:4194->8080/tcp                           k8s_POD.252debe0_cadvisor-agent.file-6bb810db-kubernetes-minion-1.file_80331227d28e618b4cef459507a31796_734d54eb

Last 10 lines of the WildFly log (after application has been accessed a few times) can be seen as:

23:12:25,004 INFO  [stdout] (ServerService Thread Pool -- 50) Hibernate: INSERT INTO EMPLOYEE_SCHEMA(ID, NAME) VALUES (8, 'Priya')
23:12:27,928 INFO  [org.jboss.resteasy.spi.ResteasyDeployment] (MSC service thread 1-5) Deploying javax.ws.rs.core.Application: class org.javaee7.samples.employees.MyApplication
23:12:27,987 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-5) JBAS017534: Registered web context: /employees
23:12:28,073 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 28) JBAS018559: Deployed "employees.war" (runtime-name : "employees.war")
23:12:28,203 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.0.0.1:9990/management
23:12:28,203 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.0.0.1:9990
23:12:28,204 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 26772ms - Started 280 of 334 services (92 services are lazy, passive or on-demand)
23:23:16,091 INFO  [stdout] (default task-16) Hibernate: select employee0_.id as id1_0_, employee0_.name as name2_0_ from EMPLOYEE_SCHEMA employee0_
23:24:07,322 INFO  [stdout] (default task-17) Hibernate: select employee0_.id as id1_0_, employee0_.name as name2_0_ from EMPLOYEE_SCHEMA employee0_
23:35:07,030 INFO  [stdout] (default task-18) Hibernate: select employee0_.id as id1_0_, employee0_.name as name2_0_ from EMPLOYEE_SCHEMA employee0_

Similarly, MySQL log is seen as:

2015-02-10 22:52:55 1 [Note] Server hostname (bind-address): '*'; port: 3306
2015-02-10 22:52:55 1 [Note] IPv6 is available.
2015-02-10 22:52:55 1 [Note]   - '::' resolves to '::';
2015-02-10 22:52:55 1 [Note] Server socket created on IP: '::'.
2015-02-10 22:52:56 1 [Note] Event Scheduler: Loaded 0 events
2015-02-10 22:52:56 1 [Note] Execution of init_file '/tmp/mysql-first-time.sql' started.
2015-02-10 22:52:56 1 [Note] Execution of init_file '/tmp/mysql-first-time.sql' ended.
2015-02-10 22:52:56 1 [Note] mysqld: ready for connections.
Version: '5.6.23'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2015-02-10 23:12:21 1 [Warning] IP address '10.246.1.1' could not be resolved: Name or service not known

Enjoy!

Arun Gupta

Arun is a technology enthusiast, avid runner, author of a best-selling book, globe trotter, a community guy, Java Champion, JavaOne Rockstar, JUG Leader, Minecraft Modder, Devoxx4Kids-er, and a Red Hatter.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button