Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
Explore how to externalize configuration using MicroProfile Config and configure your microservices using Kubernetes ConfigMaps and Secrets.
You will learn how and why to externalize your microservice’s configuration. Externalized configuration is useful because configuration usually changes depending on your environment. You will also learn how to configure the environment by providing required values to your application using Kubernetes. Using environment variables allows for easier deployment to different environments.
MicroProfile Config provides useful annotations that you can use to inject configured values into your code. These values can come from any config sources, such as environment variables. To learn more about MicroProfile Config, read the Configuring microservices guide.
Furthermore, you’ll learn how to set these environment variables with ConfigMaps and Secrets. These resources are provided by Kubernetes and act as a data source for your environment variables. You can use a ConfigMap or Secret to set environment variables for any number of containers.
The two microservices you will deploy are called system
and inventory
. The system
microservice
returns the JVM system properties of the running container and it returns the pod’s name in the HTTP header
making replicas easy to distinguish from each other. The inventory
microservice
adds the properties from the system
microservice to the inventory. This demonstrates
how communication can be established between pods inside a cluster.
To build these applications, navigate to the start
directory and run the following command.
cd start
mvn clean package
Next, run the docker build
commands to build container images for your application:
docker build -t system:1.0-SNAPSHOT system/.
docker build -t inventory:1.0-SNAPSHOT inventory/.
The -t
flag in the docker build
command allows the Docker image to be labeled (tagged) in the name[:tag]
format.
The tag for an image describes the specific image version. If the optional [:tag]
tag is not specified, the latest
tag is created by default.
Run the following command to deploy the necessary Kubernetes resources to serve the applications.
kubectl apply -f kubernetes.yaml
When this command finishes, wait for the pods to be in the Ready state. Run the following command to view the status of the pods.
kubectl get pods
When the pods are ready, the output shows 1/1
for READY and Running
for STATUS.
NAME READY STATUS RESTARTS AGE
system-deployment-6bd97d9bf6-6d2cj 1/1 Running 0 34s
inventory-deployment-645767664f-7gnxf 1/1 Running 0 34s
After the pods are ready, you will make requests to your services.
The default host name for Docker Desktop is localhost
.
The default host name for minikube is 192.168.99.100. Otherwise it can be found using the minikube ip
command.
Navigate to http://[hostname]:31000/system/properties
and use the user name bob
and the password bobpwd
to authenticate.
Replace [hostname]
with the IP address or host name of your Kubernetes cluster.
Open your browser’s developer console and examine the response headers.
You can also run the curl
command to make requests to your microservices.
To view the header, you may use the -I
option in the curl
command when making a request to http://[hostname]:31000/system/properties
.
Use the -u
option to pass in the user name bob
and the password bobpwd
.
curl http://localhost:31000/system/properties -u bob:bobpwd -I
curl http://`minikube ip`:31000/system/properties -u bob:bobpwd -I
Observe that the X-App-Name
header currently has the value system
:
X-App-Name: system
Similarly, navigate to http://[hostname]:32000/inventory/systems/system-service
, or use the following curl
command, to add the system to your inventory.
curl http://localhost:32000/inventory/systems/system-service
curl http://`minikube ip`:32000/inventory/systems/system-service
The system
service is hardcoded to have system
as the app name. You’ll make this name configurable by adding the appName
member and X-App-Name
header.
Replace theSystemResource
class.system/src/main/java/io/openliberty/guides/system/SystemResource.java
SystemResource.java
link:finish/system/src/main/java/io/openliberty/guides/system/SystemResource.java[role=include]
The appName
member was added to the header
value in getProperties()
.
These changes use MicroProfile Config and CDI to inject the value of an environment variable called APP_NAME
into the appName
member of the SystemResource
class.
The inventory
service is hardcoded to use bob
and bobpwd
as the credentials to authenticate against the system
service. You’ll make these credentials configurable.
Replace theSystemClient
class.inventory/src/main/java/io/openliberty/guides/inventory/client/SystemClient.java
SystemClient.java
link:finish/inventory/src/main/java/io/openliberty/guides/inventory/client/SystemClient.java[role=include]
The changes introduced here use MicroProfile Config and CDI to inject the value of the environment variables SYSTEM_APP_USERNAME
and SYSTEM_APP_PASSWORD
into the SystemClient
class.
There are several ways to configure an environment variable in a Docker container. You can set it directly in the Dockerfile
with the ENV
command. You can also set it in your kubernetes.yaml
file by specifying a name and a value for the environment variable you want to set for a specific container. With these options in mind, you’re going to use a ConfigMap and Secret to set these values. These are resources provided by Kubernetes that are used as a way to provide configuration values to your containers. A benefit is that they can be reused across many different containers, even if they all require different environment variables to be set with the same value.
Create a ConfigMap to configure the app name with the following kubectl
command.
kubectl create configmap sys-app-name --from-literal name=my-system
This command deploys a ConfigMap named sys-app-name
to your cluster. It has a key called name
with a value of my-system
. The --from-literal
flag allows you to specify individual key-value pairs to store in this ConfigMap. Other available options, such as --from-file
and --from-env-file
, provide more versatility as to what you want to configure. Details about these options can be found in the Kubernetes CLI documentation.
Create a Secret to configure the credentials that inventory
will use to authenticate against system
with the following kubectl
command.
kubectl create secret generic sys-app-credentials --from-literal username=bob --from-literal password=bobpwd
This command looks very similar to the command to create a ConfigMap, one difference is the word generic
. It means you’re creating a Secret that is generic
, in other words it stores information that isn’t specialized in any way. There are different types of secrets, such as secrets to store Docker credentials and secrets to store public and private key pairs.
A Secret is similar to a ConfigMap. A key difference is that a Secret is used for confidential information such as credentials. One of the main differences is that you must explicitly tell kubectl
to show you the contents of a Secret. Additionally, when it does show you the information, it only shows you a Base64 encoded version so that a casual onlooker doesn’t accidentally see any sensitive data. Secrets don’t provide any encryption by default, that is something you’ll either need to do yourself or find an alternate option to configure.
kubernetes.yaml
link:finish/kubernetes.yaml[role=include]
Dockerfile
link:finish/system/Dockerfile[role=include]
Next, you will update your Kubernetes deployments to set the environment variables in your containers based on the values configured in the ConfigMap and Secret created previously.
The env
section under the system-container
is where the APP_NAME
environment variable will be set.
The env
section under the inventory-container
is where the SYSTEM_APP_USERNAME
and SYSTEM_APP_PASSWORD
environment variables will be set.
Replace the kubernetes file.
kubernetes.yaml
kubernetes.yaml
link:finish/kubernetes.yaml[role=include]
In the kubernetes.yaml
file where the containers are defined, you can see the valueFrom
field that allows you to specify the value of an environment variable from various sources. These sources include a ConfigMap, a Secret, and information about the cluster. In this example configMapKeyRef
gets the value name
from the ConfigMap sys-app-name
. Similarly, secretKeyRef
gets the values username
and password
from the Secret sys-app-credentials
.
Rebuild the application using mvn clean package
.
mvn clean package
Run the docker build
commands to rebuild container images for your application:
docker build -t system:1.0-SNAPSHOT system/.
docker build -t inventory:1.0-SNAPSHOT inventory/.
Run the following command to deploy your changes to the Kubernetes cluster.
kubectl replace --force -f kubernetes.yaml
Navigate to http://[hostname]:31000/system/properties
and examine the response headers in your developer console,
or run the following curl
command:
curl http://localhost:31000/system/properties -u bob:bobpwd -I
curl http://`minikube ip`:31000/system/properties -u bob:bobpwd -I
You will see that the app name has changed from system
to my-system
.
X-App-Name: my-system
Verify that http://[hostname]:32000/inventory/systems/system-service
is working as intended.
If it is not, then check the configuration of the credentials.
Run the integration tests against a cluster running with appName my-system
:
mvn failsafe:integration-test -Dsystem.appName=my-system
Run the integration tests against a cluster running at Minikube’s IP address and with appName my-system
:
mvn failsafe:integration-test -Dcluster.ip=`minikube ip` -Dsystem.appName=my-system
The tests check that the system
service responds with a header containing the configured name. The tests for inventory
verify that the service may communicate with system
using the configured credentials. If the credentials are misconfigured, then the inventory
test will fail, so the inventory
test indirectly verifies the credentials are correctly configured.
After the tests succeed, you should see output similar to the following in your console.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointIT
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.709 s - in it.io.openliberty.guides.system.SystemEndpointIT
Results:
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.696 s - in it.io.openliberty.guides.inventory.InventoryEndpointIT
Results:
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
Run the following commands to delete all the resources that you created.
kubectl delete -f kubernetes.yaml
kubectl delete configmap sys-app-name
kubectl delete secret sys-app-credentials
You have used MicroProfile Config to externalize the configuration of two microservices, and then you configured them by creating a ConfigMap and Secret in your Kubernetes cluster.