Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/openliberty/guide-docker

A guide on how to use Docker containers for iterative development of microservices: https://openliberty.io/guides/docker.html
https://github.com/openliberty/guide-docker

Last synced: 2 months ago
JSON representation

A guide on how to use Docker containers for iterative development of microservices: https://openliberty.io/guides/docker.html

Awesome Lists containing this project

README

        

// Copyright (c) 2017, 2024 IBM Corporation and others.
// Licensed under Creative Commons Attribution-NoDerivatives
// 4.0 International (CC BY-ND 4.0)
// https://creativecommons.org/licenses/by-nd/4.0/
//
// Contributors:
// IBM Corporation
:projectid: docker
:page-layout: guide-multipane
:page-duration: 20 minutes
:page-description: Learn how to use Docker containers for iterative development.
:page-releasedate: 2017-12-27
:page-tags: ['docker']
:page-related-guides: ['rest-intro', 'containerize']
:page-guide-category: basic
:page-essential: true
:page-essential-order: 4
:page-permalink: /guides/{projectid}
:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod
:source-highlighter: prettify
:page-seo-title: Learn how to use Docker containers for iterative development
:page-seo-description: A getting started tutorial with examples of how to run a simple RESTful Java microservice in a Docker container built from a Dockerfile.
:guide-author: Open Liberty
= Using Docker containers to develop microservices

[.hidden]
NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].

Learn how to use Docker containers for iterative development.

:linux: LINUX
:win: WINDOWS
:mac: MAC

// =================================================================================================
// Introduction
// =================================================================================================

== What you'll learn

You will learn how to set up, run, and iteratively develop a simple REST application in a container with Open Liberty and Docker.

Open Liberty is a lightweight open framework for building fast and efficient cloud-native Java microservices. It’s small, lightweight, and designed with modern cloud-native application development in mind. Open Liberty simplifies the development process for these applications by automating the repetitive actions associated with running applications inside containers, like rebuilding the image and stopping and starting the container.

You'll also learn how to create and run automated tests for your application and container.

The implementation of the REST application can be found in the `start/src` directory. To learn more about this application and how to build it, check out the https://openliberty.io/guides/rest-intro.html[Creating a RESTful web service^] guide.

=== What is Docker?

Docker is a tool that you can use to deploy and run applications with containers. You can think of Docker like a virtual machine that runs various applications. However, unlike a typical virtual machine, you can run these applications simultaneously on a single system and independent of one another.

Learn more about Docker on the https://www.docker.com/what-docker[official Docker website^].

=== What is a container?

A container is a lightweight, stand-alone package that contains a piece of software that is bundled together with the entire environment that it needs to run. Containers are small compared to regular images and can run on any environment where Docker is set up. Moreover, you can run multiple containers on a single machine at the same time in isolation from each other.

Learn more about containers on the https://www.docker.com/what-container[official Docker website^].

=== Why use a container to develop?

Consider a scenario where you need to deploy your application on another environment. Your application works on your local machine, but when you try to run it on your cloud production environment, it breaks. You do some debugging and discover that you built your application with Java 8, but this cloud production environment has only Java 11 installed. Although this issue is generally easy to fix, you don't want your application to be missing dozens of version-specific dependencies. You can develop your application in this cloud environment, but that requires you to rebuild and repackage your application every time you update your code and wish to test it.

To avoid this kind of problem, you can instead choose to develop your application in a container locally, bundled together with the entire environment that it needs to run. By doing this, you know that at any point in your iterative development process, the application can run inside that container. This helps avoid any unpleasant surprises when you go to test or deploy your application down the road. Containers run quickly and do not have a major impact on the speed of your iterative development.

== Additional prerequisites

Before you begin, you need to install Docker if it is not already installed. For installation instructions, refer to the https://docs.docker.com/get-docker/[official Docker documentation^]. You will build and run the application in Docker containers.

Make sure to start your Docker daemon before you proceed.

// =================================================================================================
// Getting started
// =================================================================================================
[role='command']
include::{common-includes}/gitclone.adoc[]

// Cloud hosted guide instruction
ifdef::cloud-hosted[]
In this IBM Cloud environment, you need to change the user home to ***/home/project*** by running the following command:
```bash
sudo usermod -d /home/project theia
```
endif::[]

// =================================================================================================
// Creating the Dockerfile
// =================================================================================================

== Creating the Dockerfile

The first step to running your application inside of a Docker container is creating a Dockerfile. A Dockerfile is a collection of instructions for building a Docker image that can then be run as a container. Every Dockerfile begins with a parent or base image on top of which various commands are run. For example, you can start your image from scratch and run commands that download and install Java, or you can start from an image that already contains a Java installation.

Navigate to the `start` directory to begin.
// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-docker/start
```
endif::[]

[role="code_command hotspot file=0",subs="quotes"]
----
#Create the `Dockerfile` in the `start` directory.#
`Dockerfile`
----

// File 0
Dockerfile
[source, text, linenums, role="code_column"]
----
include::finish/Dockerfile[]
----

The [hotspot=from file=0]`FROM` instruction initializes a new build stage and indicates the parent image from which your image is built. If you don't need a parent image, then use `FROM scratch`, which makes your image a base image.

In this case, you’re using the `icr.io/appcafe/open-liberty:kernel-slim-java11-openj9-ubi` image as your parent image, which comes with the latest Open Liberty runtime.

The [hotspot=copy file=0]`COPY` instructions are structured as `COPY` `[--chown=:]` `` ``. They copy local files into the specified destination within your Docker image. In this case, the Liberty configuration file that is located at `src/main/liberty/config/server.xml` is copied to the `/config/` destination directory.

=== Writing a .dockerignore file

// File 0
`.dockerignore`
[source, text, linenums, role="code_column"]
----
include::finish/.dockerignore[]
----

When Docker runs a build, it sends all of the files and directories that are located in the same directory as the Dockerfile to its build context, making them available for use in instructions like `ADD` and `COPY`. If there are files or directories you wish to exclude from the build context, you can add them to a `.dockerignore` file. By adding files that aren't nessecary for building your image to the `.dockerignore` file, you can decrease the image's size and speed up the building process. You may also want to exclude files that contain sensitive information, such as a `.git` folder or private keys, from the build context.

A [hotspot file=0]`.dockerignore` file is available to you in the `start` directory. This file includes the `pom.xml` file and some system files.

// =================================================================================================
// Launching Open Liberty in dev mode
// =================================================================================================

== Launching Open Liberty in dev mode

The Open Liberty Maven plug-in includes a `devc` goal that builds a Docker image, mounts the required directories, binds the required ports, and then runs the application inside of a container. This https://openliberty.io/docs/latest/development-mode.html[dev mode^], also listens for any changes in the application source code or configuration and rebuilds the image and restarts the container as necessary.

ifdef::cloud-hosted[]
In this IBM Cloud environment, you need to pre-create the ***logs*** directory by running the following commands:

```bash
mkdir -p /home/project/guide-docker/start/target/liberty/wlp/usr/servers/defaultServer/logs
chmod 777 /home/project/guide-docker/start/target/liberty/wlp/usr/servers/defaultServer/logs
```
endif::[]

Build and run the container by running the `devc` goal from the `start` directory:

[role='command']
```
mvn liberty:devc
```

After you see the following message, your Liberty instance is ready in dev mode:
[role="no_copy"]
----
**************************************************************
* Liberty is running in dev mode.
----

Open another command-line session and run the following command to make sure that your container is running and didn’t crash:

[role='command']
```
docker ps
```

You should see something similar to the following output:

[role="no_copy"]
----
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee2daf0b33e1 guide-docker-dev-mode "/opt/ol/helpers/run…" 2 minutes ago Up 2 minutes 0.0.0.0:7777->7777/tcp, 0.0.0.0:9080->9080/tcp, 0.0.0.0:9443->9443/tcp liberty-dev
----

To view a full list of all available containers, you can run the `docker ps -a` command.

// Static guide instruction
ifndef::cloud-hosted[]
If your container runs without problems, go to the http://localhost:9080/system/properties[http://localhost:9080/system/properties^] URL swhere you can see a JSON response that contains the system properties of the JVM in your container.
endif::[]

// Cloud hosted guide instruction
ifdef::cloud-hosted[]
If your container runs without problems, run the following ***curl*** command to get a JSON response that contains the system properties of the JVM in your container.

```bash
curl -s http://localhost:9080/system/properties | jq
```
endif::[]

// =================================================================================================
// Updating the application while the container is running
// =================================================================================================

== Updating the application while the container is running

With your container running, make the following update to the source code:

[role="code_command hotspot hotspot file=0", subs="quotes"]
----
#Update the `PropertiesResource` class.#
`src/main/java/io/openliberty/guides/rest/PropertiesResource.java`
----

// File 0
PropertiesResource.java
[source, java, linenums, role='code_column hide_tags=comment,copyright']
----
include::finish/src/main/java/io/openliberty/guides/rest/PropertiesResource.java[]
----

[role="edit_command_text"]
Change the endpoint of your application from `properties` to `properties-new` by changing the [hotspot=Path]`@Path` annotation to `"properties-new"`.

// Static guide instruction
ifndef::cloud-hosted[]
After you make the file changes, Open Liberty automatically updates the application. To see these changes reflected in the application, go to the http://localhost:9080/system/properties-new[http://localhost:9080/system/properties-new^] URL.
endif::[]

// Cloud hosted guide instruction
ifdef::cloud-hosted[]
After you make the file changes, Open Liberty automatically updates the application. To see the changes reflected in the application, run the following command in a terminal:

```bash
curl -s http://localhost:9080/system/properties-new | jq
```
endif::[]

// =================================================================================================
// Testing the container
// =================================================================================================

== Testing the container

// Static guide version
ifndef::cloud-hosted[]
You can test this service manually by starting a Liberty instance and going to the http://localhost:9080/system/properties-new[http://localhost:9080/system/properties-new^] URL. However, automated tests are a much better approach because they trigger a failure if a change introduces a bug. JUnit and the JAX-RS Client API provide a simple environment to test the application. You can write tests for the individual units of code outside of a running Liberty instance, or you can write them to call the instance directly. In this example, you will create a test that calls the instance directly.
endif::[]

// Cloud hosted guide version
ifdef::cloud-hosted[]
You can test this service manually by starting a Liberty instance and going to the ***http://localhost:9080/system/properties-new*** URL.
However, automated tests are a much better approach because they trigger a failure if a change introduces a bug. JUnit and the JAX-RS Client API provide a simple environment to test the application. You can write tests for the individual units of code outside of a running Liberty instance, or you can write them to call the instance directly. In this example, you will create a test that calls the instance directly.
endif::[]

[role="code_command hotspot file=0", subs="quotes"]
----
#Create the `EndpointIT` test class.#
`src/test/java/it/io/openliberty/guides/rest/EndpointIT.java`
----

// File 0
EndpointIT.java
[source, java, linenums, role='code_column hide_tags=comment,copyright']
----
include::finish/src/test/java/it/io/openliberty/guides/rest/EndpointIT.java[]
----

This test makes a request to the `/system/properties-new` endpoint and checks to make sure that the response has a valid status code, and that the information in the response is correct.

[role='command']
include::{common-includes}/devmode-test.adoc[]

You will see the following output:

[source,role="no_copy"]
----
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.rest.EndpointIT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.884 sec - in it.io.openliberty.guides.rest.EndpointIT

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
----

When you are finished, press `CTRL+C` in the session that the dev mode was
started from to stop and remove the container.

// =================================================================================================
// Starting dev mode with run options
// =================================================================================================

== Starting dev mode with run options

Another useful feature of dev mode with a container is the ability to pass additional options to the `docker run` command. You can do this by adding the `dockerRunOpts` tag to the `pom.xml` file under the `configuration` tag of the Liberty Maven Plugin. Here is an example of an environment variable being passed in:

[role="no_copy"]
----
io.openliberty.tools
liberty-maven-plugin
3.10

-e ENV_VAR=exampleValue

----

If the Dockerfile isn't located in the directory that the `devc` goal is being run from, you can add the `dockerfile` tag to specify the location. Using this parameter sets the context for building the Docker image to the directory that contains this file.

Additionally, both of these options can be passed from the command line when running the `devc` goal by adding `-D` as such:

[role="no_copy"]
----
mvn liberty:devc \
-DdockerRunOpts="-e ENV_VAR=exampleValue" \
-Ddockerfile="./path/to/file"
----

To learn more about dev mode with a container and its different features, check out the http://github.com/OpenLiberty/ci.maven/blob/main/docs/dev.md#devc-container-mode[Documentation^].

== Great work! You're done!

You just iteratively developed a simple REST application in a container with Open Liberty and Docker.

include::{common-includes}/attribution.adoc[]