Deploying a Java Open Liberty application with PostgreSQL
This tutorial illustrates deploying a Java Open Liberty application with odo and linking it to an in-cluster PostgreSQL service in a minikube environment.
There are two roles in this example:
- Cluster Admin - Prepare the cluster by installing the required Operators on the cluster.
- Application Developer - Imports a Java application, creates a Database instance, and connect the application to the Database instance.
#
Cluster admin#
Prerequisites- This section assumes that you have installed minikube and configured it.
We will be using Operators in this guide. An Operator helps in deploying the instances of a given service, for example PostgreSQL, MySQL, Redis.
Furthermore, these Operators are "bind-able". Meaning, if they expose information necessary to connect to them, odo can help connect your component to their instances.
See the Operator installation guide to install and configure an Operator on a minikube cluster.
The cluster admin must install two Operators into the cluster:
- PostgreSQL Operator
- Service Binding Operator
We will use Dev4Devs PostgreSQL Operator found on the OperatorHub to demonstrate a sample use case. This Operator will be installed in my-postgresql-operator-dev4devs-com
namespace by default, if you want to use another namespace, make sure that you add your namespace to .spec.targetNamespaces
list in the definition file before installing it.
Note: We will use the my-postgresql-operator-dev4devs-com
namespace for this guide.
#
Application Developer#
PrerequisitesThis section assumes that you have installed odo
.
Since the PostgreSQL Operator installed in above step is available only in my-postgresql-operator-dev4devs-com
namespace, ensure that odo uses this namespace to perform any tasks:
odo project set my-postgresql-operator-dev4devs-com
If you installed the Operator in a different namespace, ensure that odo uses it to perform any tasks:
odo project set <your-namespace>
#
Importing the JPA MicroServiceIn this example we will use odo to manage a sample Java JPA MicroService application.
Clone the sample application to your system:
git clone https://github.com/OpenLiberty/application-stack-samples.git
Go to the sample JPA app directory:
cd ./application-stack-samples/jpa
Initialize the application:
odo create java-openliberty mysboproj
java-openliberty
is the type of your application andmysboproj
is the name of your application.Deploy the application to the cluster:
odo push --show-log
Troubleshooting: The Open Liberty image used by this application is relatively large(~850 MB), and depending on your internet connection, it might fail to download within the BuildTimeout set by odo; default timeout is 300 seconds.
$ odo push Validationโ Validating the devfile [45508ns] Updating servicesโ Services and Links are in sync with the cluster, no changes are required Creating Kubernetes resources for component mysboprojโ Waiting for component to start [5m]โ Failed to start component with name "mysboproj". Error: Failed to create the component: error while waiting for deployment rollout: timeout while waiting for mysboproj-app deployment roll out
In case this step fails due to a timeout, consider increasing the Build Timeout:
odo preference set BuildTimeout 600
Deploy the application to the cluster again:
odo push --show-log -f
The application is now deployed to the cluster - you can view the status of the cluster, and the application test results by streaming the cluster logs of the component that we pushed to the cluster in the previous step.
odo log --follow
Notice the failing tests due to an
UnknownDatabaseHostException
:[INFO] [err] java.net.UnknownHostException: ${DATABASE_CLUSTERIP}[INFO] [err] at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:220)[INFO] [err] at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:403)[INFO] [err] at java.base/java.net.Socket.connect(Socket.java:609)[INFO] [err] at org.postgresql.core.PGStream.<init>(PGStream.java:68)[INFO] [err] at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:144)[INFO] [err] ... 86 more[ERROR] Tests run: 2, Failures: 1, Errors: 1, Skipped: 0, Time elapsed: 0.706 s <<< FAILURE! - in org.example.app.it.DatabaseIT[ERROR] testGetAllPeople Time elapsed: 0.33 s <<< FAILURE!org.opentest4j.AssertionFailedError: Expected at least 2 people to be registered, but there were only: [] ==> expected: <true> but was: <false>at org.example.app.it.DatabaseIT.testGetAllPeople(DatabaseIT.java:57) [ERROR] testGetPerson Time elapsed: 0.047 s <<< ERROR!java.lang.NullPointerExceptionat org.example.app.it.DatabaseIT.testGetPerson(DatabaseIT.java:41) [INFO][INFO] Results:[INFO][ERROR] Failures:[ERROR] DatabaseIT.testGetAllPeople:57 Expected at least 2 people to be registered, but there were only: [] ==> expected: <true> but was: <false>[ERROR] Errors:[ERROR] DatabaseIT.testGetPerson:41 NullPointer[INFO][ERROR] Tests run: 2, Failures: 1, Errors: 1, Skipped: 0[INFO][ERROR] Integration tests failed: There are test failures.
Note: This error will be fixed at a later stage in the tutorial when we connect a database instance to this application.
You can also create a URL with
odo
to access the application:odo url create --host $(minikube ip).nip.io
Push the URL to activate it:
odo push --show-log
Display the created URL:
odo url list
You will see a fully formed URL that can be used in a web browser:
$ odo url listFound the following URLs for component mysboprojNAME STATE URL PORT SECURE KINDmysboproj-9080 Pushed http://mysboproj-9080.192.168.49.2.nip.io 9080 false ingress
Use the URL to navigate to the
CreatePerson.xhtml
data entry page to use the application: In case of this tutorial, we will accesshttp://mysboproj-9080.192.168.49.2.nip.io/CreatePerson.xhtml
. Note that the URL could be different for you. Now, enter a name and age data using the form.Click on the Save button when complete
Note that the entry of any data does not result in the data being displayed when you click on the "View Persons Record List" link, until we connect the application to a database.
#
Creating a database to be used by the sample applicationYou can use the default configuration of the PostgreSQL Operator to start a Postgre database from it. But since our app uses few specific configuration values, lets make sure they are properly populated in the database service we start.
Store the YAML of the service in a file:
odo service create postgresql-operator.v0.1.1/Database --dry-run > db.yaml
Modify and add following values under
metadata:
section in thedb.yaml
file:name: sampledatabaseannotations: service.binding/db_name: 'path={.spec.databaseName}' service.binding/db_password: 'path={.spec.databasePassword}' service.binding/db_user: 'path={.spec.databaseUser}'
This configuration ensures that when a database service is started using this file, appropriate annotations are added to it. Annotations help the Service Binding Operator in injecting those values into the application. Hence, the above configuration will help Service Binding Operator inject the values for
databaseName
,databasePassword
anddatabaseUser
into the application.Change the following values under
spec:
section of the YAML file:databaseName: "sampledb"databasePassword: "samplepwd"databaseUser: "sampleuser"
Create the database from the YAML file:
odo service create --from-file db.yaml
odo push --show-log
This action will create a database instance pod in the
my-postgresql-operator-dev4devs-com
namespace. The application will be configured to use this database.
#
Binding the database and the applicationNow, the only thing that remains is to connect the DB and the application. We will use odo to create a link to the PostgreSQL Database Operator in order to access the database connection information.
List the service associated with the database created via the PostgreSQL Operator:
odo service list
Your output should look similar to the following:
$ odo service list NAME MANAGED BY ODO STATE AGE Database/sampledatabase Yes (mysboproj) Pushed 6m35s
Create a Service Binding Request between the application, and the database using the Service Binding Operator service created in the previous step:
odo link Database/sampledatabase
Push this link to the cluster:
odo push --show-log
After the link has been created and pushed, a secret will have been created containing the database connection data that the application requires.
You can inspect the new intermediate secret via the dashboard console in the
my-postgresql-operator-dev4devs-com
namespace by navigating to Secrets and clicking on the secret namedmysboproj-database-sampledatabase
: notice that it contains 4 pieces of data that are related to the connection information for your PostgreSQL database instance.Use
minikube dashboard
to launch the dashboard console.Note: Pushing the newly created link will terminate the existing application pod and start a new application pod that mounts this secret.
Once the new pod has initialized, you can see the secret database connection data as it is injected into the pod environment by executing the following:
odo exec -- bash -c 'export | grep DATABASE' \declare -x DATABASE_CLUSTERIP="10.106.182.173" \declare -x DATABASE_DB_NAME="sampledb" \declare -x DATABASE_DB_PASSWORD="samplepwd" \declare -x DATABASE_DB_USER="sampleuser"
Once the new version is up (there will be a slight delay until the application is available), navigate to the
CreatePerson.xhtml
using the URL created in a previous step. Enter the requested data and click the Save button.Notice that you are re-directed to the
PersonList.xhtml
page, where your data is displayed having been input to the postgreSQL database and retrieved for display purposes.You may inspect the database instance itself and query the table to see the data in place by using the postgreSQL command line tool,
psql
.Navigate to the pod containing your db from the dashboard console. Use
minikube dashboard
to start the console.Click on the terminal tab.
At the terminal prompt access
psql
for your databasesampledb
.psql sampledb
Your output should look similar to the following:
sh-4.2$ psql sampledbpsql (12.3)Type "help" for help. sampledb=#
Issue the following SQL statement from your :
SELECT * FROM person;
You can see the data that appeared in the results of the test run:
sampledb=# SELECT * FROM person; personid | age | name ----------+-----+---------5 | 52 | person1(1 row)