git clone https://github.com/openliberty/guide-containerize-podman.git
cd guide-containerize-podman
Contents
Tags
Containerizing microservices with Podman
Prerequisites:
Learn how to containerize and run your microservices on Open Liberty using Podman.
What you’ll learn
You can easily deploy your microservices in different environments in a lightweight and portable manner by using containers.
From development to production and across your DevOps environments, you can deploy your microservices consistently and
efficiently with containers. You can run a container from a container image, which can be defined by a Containerfile
file or a Dockerfile
file.
Each container image is a package of what you need to run your microservice or application, from the code to its dependencies and configuration.
You’ll learn how to build container images and run containers using the Pod Manager tool (Podman) for your microservices.
You’ll construct Containerfile
files, create container images by using the podman build
command, and run the image as containers
by using podman run
command.
Podman and Buildah are related open-source container tools built to run on most Linux platforms and more.
Buildah is designed specifically for building container images from either a Containerfile
file or the command line.
You can use Podman to maintain those images, and to create and run containers.
Podman incorporates Buildah functions to create the container image that it uses.
The two microservices that you’ll be working with are called system
and inventory
. The system
microservice returns the JVM system properties
of the running container. The inventory
microservice adds the properties from the system
microservice to the inventory. This guide demonstrates how both microservices can run and communicate
with each other in different containers.
Additional prerequisites
Before you begin, Podman needs to be installed. For installation instructions, refer to the official Podman documentation. You will build and run the microservices in containers.
If you are running Mac or Windows, make sure to start your Podman-managed VM before you proceed.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Packaging your microservices
Navigate to the start
directory to begin.
You can find the starting Java project in the start
directory.
This project is a multi-module Maven project that is made up of the system
and inventory
microservices. Each microservice is located in its own corresponding directory, system
and inventory
.
To try out the microservices by using Maven, run the following Maven goal to build the system
microservice and run it inside Open Liberty:
mvn -pl system liberty:run
Open another command-line session and run the following Maven goal to build the inventory
microservice and run it inside Open Liberty:
mvn -pl inventory liberty:run
After you see the following message in both command-line sessions, both of your services are ready:
The defaultServer server is ready to run a smarter planet.
To access the inventory
service, which displays the current contents of the inventory, see http://localhost:9081/inventory/systems.
To access the system
service, which shows the system properties of the running JVM, see http://localhost:9080/system/properties.
You can add the system properties of your localhost to the inventory
service at http://localhost:9081/inventory/systems/localhost.
After you are finished checking out the microservices, stop the Open Liberty instances by pressing CTRL+C
in the command-line sessions where you ran the system
and inventory
services. Alternatively, you can run the liberty:stop
goal in another command-line session:
mvn -pl system liberty:stop
mvn -pl inventory liberty:stop
To package your microservices, run the Maven package goal to build the application .war
files from the start directory so that the .war
files are in the system/target
and inventory/target
directories.
mvn package
To learn more about RESTful web services and how to build them, see
Creating a RESTful web service for details about how to build the system
service.
The inventory
service is built in a similar way.
Building your container images
A container image is a binary file. It is made up of multiple layers and is used to run code in a container. Images are built from
instructions in Containerfile
files to create a containerized version of the application.
Containerfile
and Dockerfile
files use the same syntax.
Podman can build your container image by using either Containerfile
files or Dockerfile
files. Containerfile
files are used in this guide.
A Containerfile
file is a collection of instructions for building a container image that can then be run as a container. These files can be interpreted by Buildah directly or through Podman.
The podman build
command uses Buildah to build your container image. As each instruction in a Containerfile
file runs, a new image layer is created.
These layers, which are known as intermediate images, are created when a change is made to your container image.
Learn more about Podman on the official Podman page.
Creating your Containerfile files
You will be creating two container images to run the inventory
service and system
service. The first step is to create Containerfile
files for both services.
In this guide, you’re using an official image from the IBM Container Registry (ICR), icr.io/appcafe/open-liberty:full-java11-openj9-ubi
, as your parent image. This image is tagged with the word full
, meaning it includes all Liberty features.
full
images include all available features and are recommended for development only because they contain features that may not be required by your application and will significantly expand the image size.
To minimize your image footprint in production, you can use one of the kernel-slim
images, such as icr.io/appcafe/open-liberty:kernel-slim-java11-openj9-ubi
.
This image installs the basic runtime. You can then add all the necessary features for your application with the usage pattern that is detailed in the Open Liberty container image documentation.
To use the default image available for Open Liberty, define the FROM
instruction as FROM icr.io/appcafe/open-liberty
.
You can find all official images on the Open Liberty container image repository.
Create theContainerfile
file for the inventory service.inventory/Containerfile
inventory/Containerfile
1# tag::from[]
2FROM icr.io/appcafe/open-liberty:full-java11-openj9-ubi
3# end::from[]
4
5ARG VERSION=1.0
6ARG REVISION=SNAPSHOT
7
8# tag::label[]
9LABEL \
10 org.opencontainers.image.authors="Your Name" \
11 org.opencontainers.image.vendor="Open Liberty" \
12 org.opencontainers.image.url="local" \
13 org.opencontainers.image.source="https://github.com/OpenLiberty/guide-containerize-podman" \
14 org.opencontainers.image.version="$VERSION" \
15 org.opencontainers.image.revision="$REVISION" \
16 vendor="Open Liberty" \
17 name="inventory" \
18 version="$VERSION-$REVISION" \
19 summary="The inventory microservice from the Containerizing microservices with Podman guide" \
20 description="This image contains the inventory microservice running with the Open Liberty runtime."
21# end::label[]
22
23# tag::copy-config[]
24# tag::config-userID[]
25COPY --chown=1001:0 \
26# end::config-userID[]
27 # tag::inventory-config[]
28 src/main/liberty/config \
29 # end::inventory-config[]
30 # tag::config[]
31 /config/
32 # end::config[]
33# end::copy-config[]
34
35# tag::copy-war[]
36# tag::war-userID[]
37COPY --chown=1001:0 \
38# end::war-userID[]
39 # tag::inventory-war[]
40 target/inventory.war \
41 # end::inventory-war[]
42 # tag::config-apps[]
43 /config/apps
44 # end::config-apps[]
45# end::copy-war[]
46
47# tag::configure-sh[]
48RUN configure.sh
49# end::configure-sh[]
The FROM
instruction initializes a new build stage, which indicates the parent image of the built image. If you don’t need a parent image, then you can use FROM scratch
, which makes your image a base image.
Furthermore, you can label your container images with the LABEL
command. The label information can help you manage your images.
The COPY
instructions are structured as COPY
[--chown=<user>:<group>]
<source>
<destination>
.
They copy local files into the specified destination within your container image.
In this case, the inventory
Liberty configuration files that are located at src/main/liberty/config
are copied to the /config/
destination directory.
The inventory
application WAR file inventory.war
, which was created from running mvn package
, is copied to the /config/apps
destination directory.
The COPY
instructions use the 1001
user ID and 0
group because all official Open Liberty base images,
including icr.io/appcafe/open-liberty:full-java11-openj9-ubi
used in this case, run by default with the USER 1001
(non-root) user for security purposes. Otherwise, the files and directories that are copied over are owned by the root user and a non-root user will be unable to access them.
Place the RUN configure.sh
command at the end to get a pre-warmed container image. It improves the startup time of running your container especially for production deployment.
The Containerfile
file for the system
service follows the same instructions as the inventory
service, except that some labels
are updated, and the system.war
archive is copied into /config/apps
.
Create theContainerfile
file for the system service.system/Containerfile
system/Containerfile
1FROM icr.io/appcafe/open-liberty:full-java11-openj9-ubi
2
3ARG VERSION=1.0
4ARG REVISION=SNAPSHOT
5
6LABEL \
7 org.opencontainers.image.authors="Your Name" \
8 org.opencontainers.image.vendor="Open Liberty" \
9 org.opencontainers.image.url="local" \
10 org.opencontainers.image.source="https://github.com/OpenLiberty/guide-containerize-podman" \
11 org.opencontainers.image.version="$VERSION" \
12 org.opencontainers.image.revision="$REVISION" \
13 vendor="Open Liberty" \
14# tag::name[]
15 name="system" \
16# end::name[]
17 version="$VERSION-$REVISION" \
18# tag::summary[]
19 summary="The system microservice from the Containerizing microservices with Podman guide" \
20 description="This image contains the system microservice running with the Open Liberty runtime."
21# end::summary[]
22
23COPY --chown=1001:0 src/main/liberty/config /config/
24
25# tag::copy-war[]
26COPY --chown=1001:0 target/system.war /config/apps
27# end::copy-war[]
28
29RUN configure.sh
Building your container image
Now that your microservices are packaged and your Containerfile
files are written, you will build your container images by using the podman build
command.
Run the following commands to build container images for your application:
podman build -t system:1.0-SNAPSHOT system/.
podman build -t inventory:1.0-SNAPSHOT inventory/.
The -t
flag in the podman build
command tags the image 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.
To verify that the images are built, run the podman images
command to list all local container images:
podman images
Or, run the podman images
command with --filter
option to list your images:
podman images -f "label=org.opencontainers.image.authors=Your Name"
Your inventory
and system
images appear in the list of all container images:
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/inventory 1.0-SNAPSHOT 9d991299725c 4 minutes ago 933 MB localhost/system 1.0-SNAPSHOT a9b29bc94afd 5 minutes ago 931 MB
Running your microservices in containers
Now that your two images are built, you will run your microservices in containers:
podman run -d --name system -p 9080:9080 system:1.0-SNAPSHOT
podman run -d --name inventory -p 9081:9081 inventory:1.0-SNAPSHOT
The following table describes the flags in these commands:
Flag | Description |
---|---|
-d |
Runs the container in the background. |
--name |
Specifies a name for the container. |
-p |
Maps the host ports to the container ports. For example: |
Next, run the podman ps
command to verify that your containers are started:
podman ps
Make sure that your containers are running and show Up
as their status:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2b584282e0f5 localhost/inventory:1.0-SNAPSHOT /opt/ol/wlp/bin/s... 2 seconds ago Up 1 second 9080/tcp, 9443/tcp, 0.0.0.0:9081->9081/tcp inventory 99a98313705f localhost/system:1.0-SNAPSHOT /opt/ol/wlp/bin/s... 3 seconds ago Up 2 seconds 0.0.0.0:9080->9080/tcp, 9443/tcp system
If a problem occurs and your containers exit prematurely, the containers don’t appear in the container
list that the podman ps
command displays.
Instead, your containers appear with an Exited
status when you run the podman ps -a
command.
Run the podman logs system
and podman logs inventory
commands to view the container logs for any potential problems.
Run the podman stats system
and podman stats inventory
commands to display a live stream of usage statistics for your containers.
You can also double-check that your Containerfile
files are correct.
When you find the cause of the issues, remove the faulty containers with the podman rm system
and podman rm inventory
commands.
Rebuild your images, and start the containers again.
To access the application, go to the http://localhost:9081/inventory/systems URL. An empty list is expected because no system properties are stored in the inventory yet.
Next, retrieve the system
container’s IP address by running the following:
podman inspect -f "{{.NetworkSettings.IPAddress }}" system
The command returns the system container IP address:
10.88.0.2
In this case, the IP address for the system
service is 10.88.0.2
.
Take note of this IP address to construct the URL to view the system properties.
Go to the http://localhost:9081/inventory/systems/[system-ip-address]
URL by replacing [system-ip-address]
with the IP address that you obtained earlier.
You see a result in JSON format with the system properties of your local JVM. When you go to this URL, these system
properties are automatically stored in the inventory. Go back to the http://localhost:9081/inventory/systems URL and
you see a new entry for [system-ip-address]
.
Externalizing Liberty’s configuration
inventory/server.xml
1<server description="Sample Liberty server">
2
3 <featureManager>
4 <feature>restfulWS-3.1</feature>
5 <feature>jsonb-3.0</feature>
6 <feature>jsonp-2.1</feature>
7 <feature>cdi-4.0</feature>
8 <feature>mpConfig-3.1</feature>
9 </featureManager>
10
11 <!-- tag::httpPort[] -->
12 <variable name="http.port" defaultValue="9081" />
13 <!-- end::httpPort[] -->
14 <variable name="https.port" defaultValue="9444" />
15
16 <!-- tag::httpEndpoint[] -->
17 <httpEndpoint httpPort="${http.port}" httpsPort="${https.port}"
18 id="defaultHttpEndpoint" host="*" />
19 <!-- end::httpEndpoint[] -->
20
21 <webApplication location="inventory.war" contextRoot="/">
22
23 </webApplication>
24
25</server>
As mentioned at the beginning of this guide, one of the advantages of using containers is that they are portable and can be moved and deployed efficiently across all of your DevOps environments. Configuration often changes across different environments, and by externalizing your Liberty’s configuration, you can simplify the development process.
Imagine a scenario where you are developing an Open Liberty application on
port 9081
but to deploy it to production, it must be available
on port 9091
. To manage this scenario, you can keep two different versions of the
server.xml
file; one for production and one for development. However, trying to
maintain two different versions of a file might lead to mistakes. A better
solution would be to externalize the configuration of the port number and use the
value of an environment variable that is stored in each environment.
In this example, you will use an environment variable to externally configure the
HTTP port number of the inventory
service.
In the inventory/server.xml
file,
the http.port
variable is declared and is used in the
httpEndpoint
element to define the service
endpoint. The default value of the http.port
variable is 9081
. However, this value is only used if no other value is specified.
You can replace this value in the container by using the -e flag for the podman run command.
Run the following commands to stop and remove the inventory
container and rerun it with the http.port
environment variable set:
podman stop inventory
podman rm inventory
podman run -d --name inventory -e http.port=9091 -p 9091:9091 inventory:1.0-SNAPSHOT
The -e
flag can be used to create and set the values of environment variables
in a container. In this case, you are setting the http.port
environment
variable to 9091
for the inventory
container. The -p
flag then maps the local port
to the new container port that was specified via the environment variable.
Now, when the service is starting up, Open Liberty finds the
http.port
environment variable and uses it to set the value of the
http.port
variable to be used in the HTTP
endpoint.
The inventory
service is now available on the new port number that you
specified. You can see the contents of the inventory at the
http://localhost:9091/inventory/systems URL.
You can add your local system properties at
http://localhost:9091/inventory/systems/[system-ip-address]
by
replacing [system-ip-address]
with the IP address that you obtained in the previous
section. The system
service remains unchanged and is available at the
http://localhost:9080/system/properties URL.
You can externalize the configuration of more than just the port numbers. To learn more about Open Liberty configuration, check out the Server Configuration Overview docs.
Optimizing the image size
As mentioned previously, the parent image that is used in each Containerfile
contains the full
tag, which includes all of the Liberty features. This parent image with the full
tag is recommended for development, but while deploying to production it is recommended to use a parent image with the kernel-slim
tag. The kernel-slim
tag provides a bare minimum Liberty runtime with the ability to add the features required by the application.
Replace theContainerfile
for the inventory service.inventory/Containerfile
inventory/Containerfile
1# tag::kernel-slim[]
2FROM icr.io/appcafe/open-liberty:kernel-slim-java11-openj9-ubi
3# end::kernel-slim[]
4
5ARG VERSION=1.0
6ARG REVISION=SNAPSHOT
7
8# tag::label[]
9LABEL \
10 org.opencontainers.image.authors="Your Name" \
11 org.opencontainers.image.vendor="Open Liberty" \
12 org.opencontainers.image.url="local" \
13 org.opencontainers.image.source="https://github.com/OpenLiberty/guide-containerize-podman" \
14 org.opencontainers.image.version="$VERSION" \
15 org.opencontainers.image.revision="$REVISION" \
16 vendor="Open Liberty" \
17 name="inventory" \
18 version="$VERSION-$REVISION" \
19 summary="The inventory microservice from the Containerizing microservices with Podman guide" \
20 description="This image contains the inventory microservice running with the Open Liberty runtime."
21# end::label[]
22
23# tag::copy-config[]
24# tag::config-userID[]
25COPY --chown=1001:0 \
26# end::config-userID[]
27 # tag::inventory-config[]
28 src/main/liberty/config \
29 # end::inventory-config[]
30 # tag::config[]
31 /config/
32 # end::config[]
33# end::copy-config[]
34
35# tag::features[]
36RUN features.sh
37# end::features[]
38
39# tag::copy-war[]
40# tag::war-userID[]
41COPY --chown=1001:0 \
42# end::war-userID[]
43 # tag::inventory-war[]
44 target/inventory.war \
45 # end::inventory-war[]
46 # tag::config-apps[]
47 /config/apps
48 # end::config-apps[]
49# end::copy-war[]
50
51# tag::configure-sh[]
52RUN configure.sh
53# end::configure-sh[]
Replace the parent image with icr.io/appcafe/open-liberty:kernel-slim-java11-openj9-ubi
at the top of your Containerfile
. This image contains the kernel-slim
tag that is recommended when deploying to production.
Place RUN features.sh
command after the COPY command that the configuration file is copied to the /config/
destination directory. The features.sh
script adds the Liberty features that your application is required to operate.
Ensure that you repeat these instructions for the system
service.
Replace theContainerfile
for the system service.system/Containerfile
system/Containerfile
1# tag::kernel-slim[]
2FROM icr.io/appcafe/open-liberty:kernel-slim-java11-openj9-ubi
3# end::kernel-slim[]
4
5ARG VERSION=1.0
6ARG REVISION=SNAPSHOT
7
8LABEL \
9 org.opencontainers.image.authors="Your Name" \
10 org.opencontainers.image.vendor="Open Liberty" \
11 org.opencontainers.image.url="local" \
12 org.opencontainers.image.source="https://github.com/OpenLiberty/guide-containerize-podman" \
13 org.opencontainers.image.version="$VERSION" \
14 org.opencontainers.image.revision="$REVISION" \
15 vendor="Open Liberty" \
16# tag::name[]
17 name="system" \
18# end::name[]
19 version="$VERSION-$REVISION" \
20# tag::summary[]
21 summary="The system microservice from the Containerizing microservices with Podman guide" \
22 description="This image contains the system microservice running with the Open Liberty runtime."
23# end::summary[]
24
25COPY --chown=1001:0 src/main/liberty/config /config/
26
27# tag::features[]
28RUN features.sh
29# end::features[]
30
31# tag::copy-war[]
32COPY --chown=1001:0 target/system.war /config/apps
33# end::copy-war[]
34
35RUN configure.sh
Continue by running the following commands to stop and remove your current containers that are using the full
parent image:
podman stop inventory system
podman rm inventory system
Next, build your new images with the kernel-slim
parent image:
podman build -t system:1.0-SNAPSHOT system/.
podman build -t inventory:1.0-SNAPSHOT inventory/.
Verify that the images have been built by executing the following command to list all the local images:
podman images
Notice that the images for the inventory
and system
services now have a reduced image size.
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/inventory 1.0-SNAPSHOT eee0d13f0e80 3 minutes ago 674 MB localhost/system 1.0-SNAPSHOT 5b8f87d6f0d5 4 minutes ago 726 MB
After confirming that the images have been built, run the following commands to start the containers:
podman run -d --name system -p 9080:9080 system:1.0-SNAPSHOT
podman run -d --name inventory -p 9081:9081 inventory:1.0-SNAPSHOT
Once your containers are running, run the following command to see the list of the required features installed by features.sh
:
podman exec -it inventory /opt/ol/wlp/bin/productInfo featureInfo
Your list of Liberty features should be similar to the following:
jndi-1.0 cdi-4.0 jsonb-3.0 jsonp-2.1 mpConfig-3.1 restfulWS-3.1 restfulWSClient-3.1
To ensure that your containers are working properly, try accessing the system
service to show the system properties of the running JVM.
See http://localhost:9080/system/properties
Next, replace [system-ip-address]
with the IP address that you obtained earlier and add your localhost system properties to the inventory
service by visiting:
http://localhost:9081/inventory/systems/[system-ip-address]
Then, verify the addition of your localhost system properties to your inventory
service.
See http://localhost:9081/inventory/systems
Testing the microservices
You can test your microservices manually by hitting the endpoints or with automated tests that check your running containers.
Create theSystemEndpointIT
class.system/src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java
SystemEndpointIT.java
The testGetProperties()
method checks for a 200
response code from the system
service endpoint.
Create theInventoryEndpointIT
class.inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java
InventoryEndpointIT.java
-
The
testEmptyInventory()
method checks that theinventory
service has a total of 0 systems before anything is added to it. -
The
testHostRegistration()
method checks that thesystem
service was added toinventory
properly. -
The
testSystemPropertiesMatch()
checks that thesystem
properties match what was added into theinventory
service. -
The
testUnknownHost()
method checks that an error is raised if an unknown host name is being added into theinventory
service. -
The
systemServiceIp
variable has the same value as the IP address that you retrieved in the previous section when you manually added thesystem
service into theinventory
service. This value of the IP address is passed in when you run the tests.
Running the tests
Run the Maven package
goal to compile the test classes. Run the Maven failsafe
goal to test the services that are running in the containers by replacing the [system-ip-address]
with the IP address that you determined previously.
mvn package
mvn failsafe:integration-test -Dsystem.ip=[system-ip-address] -Dinventory.http.port=9081 -Dsystem.http.port=9080
If the tests pass, you see output similar to the following example:
------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.io.openliberty.guides.system.SystemEndpointIT Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.653 s - in it.io.openliberty.guides.system.SystemEndpointIT Results: Tests run: 1, 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: 0.935 s - in it.io.openliberty.guides.inventory.InventoryEndpointIT Results: Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
When you are finished with the services, run the following commands to stop and remove your containers:
podman stop inventory system
podman rm inventory system
Great work! You’re done!
You have just built container images and run two microservices on Open Liberty in containers using Podman.
Guide Attribution
Containerizing microservices with Podman by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow