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; this 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 name
and ping
. The name
microservice displays a brief greeting and the name of the container that it runs in. The ping
microservice pings the Kubernetes Service that encapsulates the pod running the name
microservice. The ping
microservice 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.
mvn package
When the build succeeds, 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
name-deployment-6bd97d9bf6-6d2cj 1/1 Running 0 34s
ping-deployment-645767664f-7gnxf 1/1 Running 0 34s
After the pods are ready, you will make requests to your services.
Windows | Mac
The default hostname for Docker Desktop is localhost
.
Linux
The default hostname for minikube is 192.168.99.100. Otherwise it can be found using the minikube ip
command.
Navigate to http://[hostname]:31000/api/name
and use the username bob
and the password bobpwd
to authenticate. Replace [hostname]
with the IP address or hostname of your Kubernetes cluster. You will see something similar to Hello! I’m container name-deployment-6bd97d9bf6-qbhbc
.
Similarly, navigate to http://[hostname]:32000/api/ping/name-service
and you will see pong
.
The name
service is hardcoded to have Hello!
as the greeting message. You’ll make this message configurable. Replace the NameResource
class in the name/src/main/java/io/openliberty/guides/name/NameResource.java
file with the following:
link:finish/name/src/main/java/io/openliberty/guides/name/NameResource.java[role=include]
These changes use MicroProfile Config and CDI to inject the value of an environment variable called GREETING
into the greeting
member of the NameResource
class.
link:finish/name/src/main/java/io/openliberty/guides/name/NameResource.java[role=include]
Another change uses the new greeting
member to create a response with a custom greeting message in the getContainerName
method.
link:finish/name/src/main/java/io/openliberty/guides/name/NameResource.java[role=include]
The ping
service is hardcoded to use bob
and bobpwd
as the credentials to authenticate against the name
service. You’ll make these credentials configurable. Replace the PingResource
class in the ping/src/main/java/io/openliberty/guides/ping/PingResource.java
file with the following:
link:finish/ping/src/main/java/io/openliberty/guides/ping/PingResource.java[role=include]
The changes introduced here use MicroProfile Config and CDI to inject the value of the environment variables USERNAME
and PASSWORD
into the PingResource
class. Then the hardcoded values are removed from those same properties.
link:finish/ping/src/main/java/io/openliberty/guides/ping/PingResource.java[role=include]
There are several ways to configure an environment variable in a Docker container. You can set it directly in 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 are 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 re-used 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 greeting with the following kubectl
command.
kubectl create configmap greeting-config --from-literal message=Greetings...
This command deploys a ConfigMap named greeting-config
to your cluster. It has a key called message
with a value of Greetings…
. 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 ping
will use to authenticate against name
with the following kubectl
command.
kubectl create secret generic name-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 that you’re creating a Secret that is generic
, in other words it stores information that is not specialized in any way. There are different types of secrets, such as secrets to store Docker credentials and secrets to store public/private key pairs.
A Secret is similar to a ConfigMap, except a Secret is used for confidential information such as credentials. One of the main differences is that you have to 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.
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
sections under the name-container
and ping-container
containers are where the environment variables will be set.
Replace the contents of the kubernetes.yaml
file with the following:
link:finish/kubernetes.yaml[role=include]
In the kubernetes.yaml
file where the containers are defined, you can see the valueFrom
field which allows you to specify the value of an environment variable from a variety of sources. These sources include a ConfigMap, a Secret, and information about the cluster. In this example configMapKeyRef
gets the value message
from the ConfigMap greeting-config
. Similarly, secretKeyRef
gets the values username
and password
from the Secret name-credentials
.
Rebuild the application using mvn package
.
mvn package
Run the following commands to deploy your changes to the Kubernetes cluster.
kubectl delete -f kubernetes.yaml
kubectl apply -f kubernetes.yaml
Navigate to http://[hostname]:31000/api/name
and you will see that the greeting message has changed from Hello!
to Greetings…
. Verify that http://[hostname]:32000/api/ping/name-service
is working as intended. If it is not, then check the configuration of the credentials.
Windows | Mac
Run the integration tests against a cluster running with a hostname of localhost:
mvn verify -Ddockerfile.skip=true -Dcluster.ip=localhost
Linux
Run the integration tests against a cluster running at Minikube’s IP address:
mvn verify -Ddockerfile.skip=true -Dcluster.ip=`minikube ip`
The tests check that the name
service responds with a container name, and they check that the ping
service returns a pong
response on success and that it gracefully handles a bad response.
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.name.NameEndpointTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.542 sec - in it.io.openliberty.guides.name.NameEndpointTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.ping.PingEndpointTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.761 sec - in it.io.openliberty.guides.ping.PingEndpointTest
Results :
Tests run: 2, 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 greeting-config
kubectl delete secret name-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.