15 MINUTE EXERCISE
In this lab you will learn how to manage application configuration and how to provide environment specific configuration to the services.
Applications require configuration in order to tweak the application behavior or adapt it to a certain environment without the need to write code and repackage the application for every change. These configurations are sometimes specific to the application itself such as the number of products to be displayed on a product page and some other times they are dependent on the environment they are deployed in such as the database coordinates for the application.
The most common way to provide configurations to applications is using environment variables and external configuration files such as properties, JSON or YAML files, configuration files and command line arguments. These configuration artifacts should be externalized from the application and the container image content in order to keep the image portable across environments.
OpenShift provides a mechanism called {{OPENSHIFT_DOCS_BASE}}/dev_guide/configmaps.html[ConfigMaps^] in order to externalize configurations from the applications deployed within containers and provide them to the containers in a unified way as files and environment variables. OpenShift also offers a way to provide sensitive configuration data such as certificates, credentials, etc. to the application containers in a secure and encrypted mechanism called Secrets.
This allows developers to build the container image for their application only once, and reuse that image to deploy the application across various environments with different configurations that are provided to the application at runtime.
So far Catalog and Inventory services have been using an in-memory H2 database. Although H2 is a convenient database to run locally on your laptop, it’s in no way appropriate for production or even integration tests. Since it’s strongly recommended to use the same technology stack (operating system, JVM, middleware, database, etc.) that is used in production across all environments, you should modify Inventory and Catalog services to use PostgreSQL instead of the H2 in-memory database.
Fortunately, OpenShfit supports stateful applications such as databases which require access to a persistent storage that survives the container itself. You can deploy databases on OpenShift and regardless of what happens to the container itself, the data is safe and can be used by the next database container.
Let’s create a {{OPENSHIFT_DOCS_BASE}}/using_images/db_images/postgresql.html[PostgreSQL database^] for the Inventory Service using the PostgreSQL template that is provided out-of-the-box:
Tip
|
{{OPENSHIFT_DOCS_BASE}}/dev_guide/templates.html[OpenShift Templates^] use YAML/JSON to compose multiple containers and their configurations as a list of objects to be created and deployed at once, making it simple to re-create complex deployments by just deploying a single template. Templates can be parameterized to get input for fields like service names and generate values for fields like passwords. |
$ oc new-app postgresql-persistent \ --param=DATABASE_SERVICE_NAME=inventory-postgresql \ --param=POSTGRESQL_DATABASE=inventory \ --param=POSTGRESQL_USER=inventory \ --param=POSTGRESQL_PASSWORD=inventory \ --labels=app=inventory
Deploy another PostgreSQL database for the Catalog Service:
$ oc new-app postgresql-persistent \ --param=DATABASE_SERVICE_NAME=catalog-postgresql \ --param=POSTGRESQL_DATABASE=catalog \ --param=POSTGRESQL_USER=catalog \ --param=POSTGRESQL_PASSWORD=catalog \ --labels=app=catalog
Note
|
The |
Now you can move on to configure the Inventory and Catalog service to use these PostgreSQL databases.
Vert.x supports multiple mechanisms for externalizing configurations such as environment variables, Maven properties, command-line arguments and more. The recommend approach for the long-term for externalizing configuration is however using a YAML file which you have already packaged within the Inventory Maven project.
Note
|
Check out inventory-vertx/src/main/resources/config/app-config.yml which contains the default configuration. |
Create a YAML file with the PostgreSQL database credentials
. Note that you can give an arbitrary
name to this configuration (e.g. *prod
) in order to tell Thorntail which one to use:
$ cat <<EOF > /projects/labs/app-config.yml driverClassName: org.postgresql.Driver jdbcUrl: jdbc:postgresql://inventory-postgresql:5432/inventory (1) principal: inventory credential: inventory EOF
-
The hostname defined for the PostgreSQL connection-url (inventory-postgresql) corresponds to the PostgreSQL service name published on OpenShift. This name will be resolved by the internal DNS server exposed by OpenShift and accessible to containers running on OpenShift.
And then create a Config Map
that you will use to overlay on the default app-config.yml which is
packaged in the Inventory JAR archive:
oc create configmap inventory --from-file=/projects/labs/app-config.yml # (1) oc set volume dc/inventory --add --configmap-name=inventory --mount-path=/deployments/config # (2)
-
Config maps hold key-value pairs. An Inventory Config Map is created with app-config.yml as the key and the content of the app-config.yml as the value. Whenever a config map is injected into a container, it would appear as a file with the same name as the key, at specified path on the filesystem.
-
Mounts the content of the Inventory Config Map as a file inside the Inventory container at /deployments/config/app-config.yml
Tip
|
If you don’t like bash commands, go to the {{COOLSTORE_PROJECT}} project in {{OPENSHIFT_CONSOLE_URL}}[OpenShift Web Console^] and then on the left sidebar, Resources >> Config Maps. Click on Create Config Map button to create a config map with the following info:
|
Tip
|
You can see the content of the config map in the {{OPENSHIFT_CONSOLE_URL}}[OpenShift Web Console^] or by using |
The Inventory pod gets restarted automatically due to the configuration changes. Wait till it’s ready,
and then verify that the config map is in fact injected into the container by running
a shell command inside the Inventory Container
:
$ oc rsh -c thorntail-v2 dc/inventory cat /deployments/config/app-config.yml
Also verify that the PostgreSQL database is actually used by the Inventory service. Check the
Inventory pod logs
:
$ oc logs -c thorntail-v2 dc/inventory | grep database INFO: Will use database jdbc:postgresql://inventory-postgresql:5432/inventory
You can also connect to Inventory PostgreSQL database and check if the seed data is loaded into the database.
$ oc rsh dc/inventory-postgresql
Once connected to the PostgreSQL container, run the following
:
Important
|
Run this command inside the Inventory PostgreSQL container, after opening a remote shell to it. |
sh-4.2$ psql -U inventory -c "select * from inventory" itemid | quantity ---- 329299 | 35 329199 | 12 165613 | 45 165614 | 87 165954 | 43 444434 | 32 444435 | 53 (7 rows) sh-4.2$ exit
You have now created a config map that holds the configuration content for Inventory and can be updated at anytime for example when promoting the container image between environments without needing to modify the Inventory container image itself.
You should be quite familiar with config maps by now. Spring Boot application configuration is provided via a properties file called application.properties and can be overriden and overlayed via multiple mechanisms.
Note
|
Check out the default Spring Boot configuration in Catalog Maven project catalog-spring-boot/src/main/resources/application.properties. |
In this lab, you will configure the Catalog Service which is based on Spring Boot to override the default configuration using an alternative application.properties backed by a config map.
Create a Config Map
with the Spring Boot configuration content using the PostgreSQL database
credentials:
$ cat <<EOF > /projects/labs/application.properties spring.datasource.url=jdbc:postgresql://catalog-postgresql:5432/catalog#(1) spring.datasource.username=catalog spring.datasource.password=catalog spring.datasource.driver-class-name=org.postgresql.Driver spring.jpa.hibernate.ddl-auto=create EOF $ oc create configmap catalog --from-file=/projects/labs/application.properties
-
The hostname defined for the PostgreSQL connection-url (catalog-postgresql) corresponds to the PostgreSQL service name published on OpenShift. This name will be resolved by the internal DNS server exposed by OpenShift and accessible to containers running on OpenShift.
Tip
|
If you don’t like bash commands, go to the {{COOLSTORE_PROJECT}} project in {{OPENSHIFT_CONSOLE_URL}}[OpenShift Web Console^] and then on the left sidebar, Resources >> Config Maps. Click on Create Config Map button to create a config map with the following info:
|
The Spring Cloud Kubernetes plug-in implements the integration between Kubernetes and Spring Boot and is already added as a dependency to the Catalog Maven project. Using this dependency, Spring Boot would search for a config map (by default with the same name as the application) to use as the source of application configurations during application bootstrapping and if enabled, triggers hot reloading of beans or Spring context when changes are detected on the config map.
Delete the Catalog Pod
to make it start again and look for the config maps:
$ oc delete pod -l deploymentconfig=catalog
When the Catalog container is ready, verify that the PostgreSQL database is being used. Check the Catalog pod logs:
$ oc logs -c spring-boot dc/catalog | grep hibernate.dialect 2017-08-10 21:07:51.670 INFO 1 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL94Dialect
You can also connect to the Catalog PostgreSQL database and verify that the seed data is loaded:
$ oc rsh dc/catalog-postgresql
Once connected to the PostgreSQL container, run the following:
Important
|
Run this command inside the Catalog PostgreSQL container, after opening a remote shell to it. |
sh-4.2$ psql -U catalog -c "select item_id, name, price from product" item_id | name | price ---- 329299 | Red Fedora | 34.99 329199 | Forge Laptop Sticker | 8.5 165613 | Solid Performance Polo | 17.8 165614 | Ogio Caliber Polo | 28.75 165954 | 16 oz. Vortex Tumbler | 6 444434 | Pebble Smart Watch | 24 444435 | Oculus Rift | 106 444436 | Lytro Camera | 44.3 (8 rows) sh-4.2$ exit
Config maps are a superb mechanism for externalizing application configuration while keeping containers independent of in which environment or on what container platform they are running. Nevertheless, due to their clear-text nature, they are not suitable for sensitive data like database credentials, SSH certificates, etc. In the current lab, we used config maps for database credentials to simplify the steps; however, for production environments, you should opt for a more secure way to handle sensitive data.
Fortunately, OpenShift already provides a secure mechanism for handling sensitive data which is called {{OPENSHIFT_DOCS_BASE}}/dev_guide/secrets.html[Secrets^]. Secret objects act and are used similarly to config maps however with the difference that they are encrypted as they travel over the wire and also at rest when kept on a persistent disk. Like config maps, secrets can be injected into containers as environment variables or files on the filesystem using a temporary file-storage facility (tmpfs).
You won’t create any secrets in this lab; however, you have already created two secrets when you created the PostgreSQL databases for Inventory and Catalog services. The PostgreSQL template by default stores the database credentials in a secret in the project in which it’s being created:
$ oc describe secret catalog-postgresql Name: catalog-postgresql Namespace: coolstore Labels: app=catalog template=postgresql-persistent-template Annotations: openshift.io/generated-by=OpenShiftNewApp template.openshift.io/expose-database_name={.data['database-name']} template.openshift.io/expose-password={.data['database-password']} template.openshift.io/expose-username={.data['database-user']} Type: Opaque Data ==== database-name: 7 bytes database-password: 7 bytes database-user: 7 bytes
This secret has three encrypted properties defined as database-name, database-user and database-password which hold the PostgreSQL database name, username and password values. These values are injected in the PostgreSQL container as environment variables and used to initialize the database.
Go to '{{COOLSTORE_PROJECT}}' project
in the {{OPENSHIFT_CONSOLE_URL}}[OpenShift Web Console^] and click on the 'catalog-postgresql'
deployment
(blue text under the title Deployment) and then click on the 'Environment' tab
. Notice the values
from the secret are defined as env vars on the deployment:
That’s all for this lab! You are ready to move on to the next lab.