Gain more understanding on how to build cloud native applications.
Learning outcomes
After completing the lab, you will be able to:
- Summarize some of the ways to configure a Spring application
- Use environment variables to configure an application running
locally - Use
cf logs
to view the logs of an application running on
Tanzu Application Service - Use environment variables to configure an application running on
Tanzu Application Service - Explain when to use the CLI versus a manifest for
application configuration
12 factor applications
If you completed the
Cloud Native Development learning path,
you should already be familiar with 12 factors
guidelines.
In practice, you will have to decide which ones to use and if it makes
sense to adhere to all of them.
In the previous lab, you covered the first two factors by setting up
your codebase in GitHub and using Gradle to explicitly declare your
dependencies.
This lab will focus on the third factor:
storing configuration in
the environment.
There are many options for how to externalize configuration for a cloud
native application.
Our first choice is to use environment variables.
Another choice, externalizing configuration using a Config Server,
will be introduced in later lessons.
Get started
-
Review the
Environment
slides. -
You must have completed (or fast-forwarded to) the
Deploying a Spring Boot Application lab.
You must have yourpal-tracker
application associated with the
spring-boot-solution
codebase deployed and running on
Tanzu Application Service. -
In a terminal window,
make sure you start in the~/workspace/pal-tracker
directory. -
Pull in pre-authored tests to your codebase:
git cherry-pick configuration-start
You will see the following tests:
[main 95c54c1] Add tests for configuration lab Author: <redacted> Date: Wed Jan 8 11:09:04 2020 -0500 3 files changed, 67 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java
Your goal is to get your tests to pass by the end of the lab,
as well as deploy the updated code to Tanzu Application Service.
If you get stuck
If you get stuck within this lab,
you can either
view the solution,
or you can
fast-forward
to the configuration-solution
tag.
Add test dependencies
Since you now have added tests to your codebase,
you need to set up the build to use a testing framework.
You will use Junit 5.
Add the following to your build.gradle
file:
-
Add the following line to the
dependencies
closure in your
build.gradle
file to enable the necessary test dependencies:testImplementation 'org.springframework.boot:spring-boot-starter-test'
-
Add the following closure to the end of the
build.gradle
file:test { useJUnitPlatform() }
-
Take a look at the solution if you get stuck
git show configuration-solution:build.gradle
Environment variables
You will use the environment variable mechanism to provide configuration
to a process.
It does not matter if your application is written in
Java,
Ruby,
Golang, or
some other language,
they all have the capability of accessing environment variables during
their process start up time.
Tanzu Application Service can be configured to provide your application
with environment variables when it is executed.
In fact, it provides
several environment variables
that your application can use to uniquely identify its execution
environment.
You will extend your application to:
- Configure the “hello” message from the environment.
- Add a RESTful endpoint that returns some of the system-provided
variables.
Externalize configuration
Spring Boot includes a mechanism to get configuration values.
-
Extract the
hello
message to a field in the controller. -
Create a constructor that accepts a
message
parameter and assigns
it to the field. -
Annotate that constructor parameter with
@Value
.@Value
takes a specific format to reference an environment
variable, for example:@Value("${welcome.message}")
Take time to read about annotation-based configuration
if you are new to Spring or if you need a refresher.
There is more
detailed documentation
on using Spring expressions in bean definitions and annotations,
if you are interested. -
If you get stuck,
review the solution:git show configuration-solution:src/main/java/io/pivotal/pal/tracker/WelcomeController.java
Verify it works
Run your app with the bootRun
Gradle task.
It will fail to start because Spring Boot is unable to find a value for
the requested variable.
From now on the WELCOME_MESSAGE
environment variable must be set to
run your app.
One way to do this is to add the environment variable assignment before
the Gradle command.
WELCOME_MESSAGE=howdy ./gradlew bootRun
Notice you are using a different welcome message than the previous
statically configured message.
Why is the @Value
name welcome.message
?
You may be wondering why the name given in the @Value
annotation
is welcome.message
and not the name of the environment variable,
WELCOME_MESSAGE
.
Spring can take configuration information from
many different sources.
In order to support this, and to have a consistent way of naming
configuration items from different sources, Spring uses what is known as
relaxed binding.
Among other things, this defines the rules that convert environment
variable names, such as WELCOME_MESSAGE
,
to the common format represented by welcome.message
.
This means that you could later change the source of the welcome message
text to, for example, a Java property without having to modify the code.
Manage local configuration
Running your application like this with environment variables in the
command line every time is tedious.
You can configure your Gradle build
to make this easier.
-
Extend the
bootRun
andtest
tasks to set the required
environment variable by adding the following to the end of your
build.gradle
file:bootRun.environment([ "WELCOME_MESSAGE": "howdy", ]) test.environment([ "WELCOME_MESSAGE": "Hello from test", ])
-
You can review the solution here:
git show configuration-solution:build.gradle
-
This will instruct Gradle to set that environment variable for you
when you run thebootRun
task, so you can go back to just using:./gradlew bootRun
This has the added benefit of documenting required environment variables
and supporting multiple operating systems.
Tanzu Application Service environment variables
When Tanzu Application Service starts your application,
it will be running with a port provided by the runtime environment.
It will also have an instance ID set which will be distinct for
each instance, if the app has multiple instances.
Create an endpoint to see some of that information.
-
Create another controller called
EnvController
. -
Annotate it with
@RestController
. -
Implement a method called
getEnv
as described in the
EnvControllerTest
. -
Annotate the
getEnv
method with@GetMapping("/env")
. -
Declare
@Value
annotated constructor arguments to be populated
with values of the following environment variables:PORT
MEMORY_LIMIT
CF_INSTANCE_INDEX
CF_INSTANCE_ADDR
-
Add a default value to the variables above so that the app will
still run correctly locally.
For example:@Value("${cf.instance.index:NOT SET}") String cfInstanceIndex,
-
Implement
getEnv
so that the test passes.Take a look at our solution if you get stuck:
git show configuration-solution:src/main/java/io/pivotal/pal/tracker/EnvController.java
-
Run your app and navigate to
localhost:8080/env.
Make sure you are getting a response.
The variables will showNOT SET
,
which ensures the controller is valid. -
Navigate to localhost:8080/.
Make sure the root route still has the welcome message you set above. -
Make sure all your tests pass before moving on by running the
Gradlebuild
task.
Deploy
-
Push the app to Tanzu Application Service.
The app will fail to start and will be reported as a crash.To see what the problem is, use the
cf logs
command.
Logging in Tanzu Application Service works by taking everything
from standard output and error
(System.out
andSystem.err
in Java)
and storing it in a small buffer. -
View the logs from your app through the
cf logs
command.
By default, this will show you a stream of logs.
To see logs from the recent past, use the--recent
flag. -
Check the output and find the problem.
You will see something about Spring not having a value for the
welcome message. -
To fix this issue, use the
cf set-env
command to set the
WELCOME_MESSAGE
environment variable to
Howdy from Tanzu Application Service in your app’s container on
Tanzu Application Service.
The CLI will give you a helpful tip:TIP: Use 'cf restage pal-tracker' to ensure your env variable changes take effect
Actually, the CLI does not know whether you need to restage,
or simply restart.
Restaging is necessary if the environment variable is used in the
buildpack’s compile stage.
Restarting is sufficient if the environment variable is only used by
the application at runtime.
Restarting is usually faster,
since the droplet does not need to be re-created.
In this case, restarting is sufficient, so save yourself some time:cf restart pal-tracker
-
Once your app is running on Tanzu Application Service,
navigate to the root endpoint and check that it displays
Howdy from Tanzu Application Service. -
Navigate to the
/env
endpoint and verify actual values are
displayed instead of NOT SET.
Manifest
The initial deployment failure could have been avoided by using a
manifest.yml
.
This file documents requirements for the application and configures
variables in the
environment
such as the WELCOME_MESSAGE
and setting the JDK version to use when
running the application.
A Tanzu Application Service manifest is also an appropriate place to
describe Backing Services dependencies that your app requires.
You will see more about backing services in the
Backing Services and Database Migrations and
Data Access labs.
-
Checkout the solution
manifest.yml
file:git checkout configuration-solution manifest.yml
-
Notice the
WELCOME_MESSAGE
must be
Hello from Tanzu Application Service
.
To see the effect of using a manifest:
-
Delete your app with the
cf delete
command. -
Push your app using your new manifest with
push
.
Thepush
command will automatically use your manifest file if you
push from the same directory. -
Visit the root endpoint and
/env
endpoint.
Be aware that the app URL has changed as a result of the
random-route: true
line in the manifest. -
Make a commit and push your code to GitHub once you are sure
everything is working.
Wrap up
Review the
Tanzu Application Service Environment
slides about environment variable configuration on
Tanzu Application Service.
Now that you have completed the lab, you should be able to:
- Summarize some of the ways to configure a Spring application
- Use environment variables to configure an application running
locally - Use
cf logs
to view the logs of an application running on Cloud
Foundry - Use environment variables to configure an application running on
Tanzu Application Service - Explain when to use the command line interface (CLI)
versus a manifest for application configuration
Extras
Tanzu Application Service command line interface (CLI)
If you have additional time, explore the cf
CLI by reading the
documentation or by
running cf help
and cf <command> --help
.
Explore the pal-tracker application environment
-
Review the
cf env
command. -
Run the
cf env
command for thepal-tracker
application.
Review the system provided environment variables,
as well as the user provided environment variables.
Explore the pal-tracker container
-
Review the
cf ssh
command. -
Run the
cf ssh
command to create a secure shell connection to the
only running instance (index0
) of yourpal-tracker
. -
Run the following and review the running
pal-tracker
container:-
Tanzu Application Service environment variables:
env | grep CF_
Notice that Tanzu Application Service sets the IP address,
ports,
and host name (GUID) of the containers for you. -
Review the running processes in the container:
ps -ef
Notice the Java process,
how did Tanzu Application Service know what the run command is
for thepal-tracker
application?Notice the
envoy
andhealthcheck
processes.
You will see discussion of these in later labs. -
Review the following directory and its subdirectories:
ll app/
Where did the directory and files originate from?
-