The goal of this lab is to learn the tools we will use during the course:
- Docker
- Run an image with Docker
- Map ports between the container and the host
- Use the basic Docker commands such as
docker ps
,docker exec
,docker build
,docker images
,docker logs
, etc.
- Docker Compose
- Create a
docker-compose.yml
file - Start and stop a Docker Compose infrastructure
- Inspect a running Docker Compose infrastructure using commands such as
docker compose ps
,docker compose top
,docker compose logs
, etc.
- Create a
- Dockerfile
- Containerize an application using a Dockerfile
- Java and Maven
- Modify a Maven project using its
pom.xml
file - Compile a Java project with Maven
- Run a Java application from the command line
- Modify a Maven project using its
You will need Git installed on your computer. If it is not yet installed, go to https://git-scm.com/downloads and follow the instructions for your operating system.
Then clone this repository on your computer with the command:
git clone <urL of the repository>
Docker and Docker Compose are two of the main tools that we will be using for many labs. Docker allows you to package software into containers that can be easily run without compatibility problems or missing libraries. We will use Docker Compose to create complete infrastructures with multiple servers.
Docker is available for Windows, Mac and Linux. You can find the installation instructions for your operating system here: https://docs.docker.com/install/. This will install Docker Desktop. Docker Desktop is free for education and personal use, so you can use it for this course.
Docker Desktop includes Docker Compose, so you don't need to install it separately.
The Docker documentation is very good, so you should read it. Here we will give you a quick introduction to the most important commands. For a complete overview of Docker, you will find more information here.
We've provided a custom docker image heigvddai/chucknorris
that you can use for your tests. It is publicly available on Docker hub.
Go to Docker hub and search for this image.
Using this image, work through the section "Working with containers" of the document "The Top 10 Docker commands" on Cyberlearn. You should:
- run the image
heigvddai/chucknorris
, - run the image as background process,
- run the image, which uses TCP port 80 and map it to another port, for example 8080 (you can then open a browser and connect to
localhost:8080
to see as Chuck Norris joke and refresh the page several times), - open a shell in the running container,
- list the running containers with
docker ps
, - stop the running container,
- remove the downloaded image.
Write down the commands you used for each of these steps.
Of the examples above, the port mapping merits some more information.
A docker image may contain a server which uses a specific port (e.g., port 80). However, when you run the image, you may want the container to use a different port, to avoid conflicts. This is where the syntax -p <host port>:<container port>
comes in. If you want to run the chucknorris image on port 3000, use the option -p 3000:80
.
Try different ports and connect to the container using your browser with the URL http://localhost:<port>
.
While running a single container is nice, Docker shows it's real power when you use Docker Compose to deploy complete infrastructures such as a Web server with a database and a cache.
It should already be installed with Docker Desktop. Just type docker compose version
in a terminal to check.
Create a docker-compose.yml
file with the following content:
version: '3.8'
services:
chucknorris:
image: heigvddai/chucknorris
ports:
- "8080:80"
web:
image: nginx
ports:
- "80:80"
In the same directory, run the command:
docker compose up -d
to start the infrastructure. The option -d
tells Docker Compose to run the containers in the background.
Check that the containers are running with the command docker compose ps
. You should see two containers, one for the chucknorris image and one for the nginx image.
Then go to your browser and open http://localhost to access the nginx server. Open http://localhost:8080 to access the chucknorris server. It should show a Chuck Norris joke on each refresh.
Shut down the infrastructure with the command
docker compose down
Throughout the course, we will use Java 21, which came out on September 19, 2023.
To install it on Windows and MacOS, got the the Oracle Java Website and download the JDK (Java Development Kit) version 21 for your operating system.
For Linux and WSL2/Windows, the easiest way is to use SDKMAN. Follow the instructions on the website to install sdkman. Then use it install Java 21.
After the installation, check if it works in a new terminal:
java --version
> java 21.0...
As IDE, you can use:
- IntelliJ IDEA Community Edition, which is free. Download it from https://www.jetbrains.com/idea/download/ and install it.
- VS Code, which is also free. Download it from https://code.visualstudio.com/download and install it. Then install the Java Extension Pack from https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack.
This repository contains a simple Java project under the directory ./java/demo-app
. Open it in your IDE and try to run it. You should see "Hello world!" in the console output.
Maven is a build tool for Java. It is used to compile, test and package Java projects. It also manages dependencies between libraries.
You will need Maven since the code you will develop in the following labs will be containerized with Docker. You therefore need to provide a .jar
file that contain the compiled code of your application and that can be run in a Docker container.
To install Maven, follow the instructions on https://maven.apache.org/install.html. You can also install it with your package manager (e.g., sudo apt install maven
on Ubuntu).
Check if it works in a terminal:
mvn -v
> Apache Maven 3.6.3...
To understand the basics of Maven, read the Maven in 5 minutes tutorial before moving on.
Maven uses a configuration file called pom.xml
to manage the project. It contains the project name, version, dependencies, etc. The demo-app already contains an almost minimal pom.xml
file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ch.heig.dai.lab.tools</groupId>
<artifactId>demo-app</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
</build>
<dependencies>
</dependencies>
</project>
Besides the standard template, there are only three important elements:
<groupId>
: the groupId gives the package name. This is usually the name of the organization that created the project. The source code must follow this structure. In our case, the source code is in the directory.../src/main/java/ch/heig/dai/lab/tools/
, which corresponds to the groupIdch.heig.dai.lab.tools
. In theMain.java
file you will see the package declarationpackage ch.heig.dai.lab.tools;
.<artifactId>
and<version
: together they define the name of the.jar
file that will be generated. In our case, the.jar
file will be nameddemo-app-1.0.jar
.
Other elements to check are maven.compiler.source
and maven.compiler.target
. Here we use Java 17 for the compiler and the generated .jar
file.
As described in the Maven in 5 minutes tutorial, you can compile the project with the command mvn clean package
(in the directory with the pom.xml
file). This will create a target
directory with the compiled .class
files and the .jar
file.
An executable .jar
file can be run with the command java -jar <jar file>
. Try it with the .jar
file generated by Maven.
Unfortunately, java will complain with an error message: no main manifest attribute, in demo-app-1.0.jar
. This is because the .jar
file does not contain the information about the main class to run. To fix this, we need to add the maven-jar-plugin
to the pom.xml
file:
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>ch.heig.dai.lab.tools.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
After this change, run mvn clean package
again and try to run the .jar
file with java -jar target/demo-app-1.0.jar
. It should work now.
Now that we can compile the Java application, we would like to containerize it, that is run it in a Docker container.
We need an application that runs continuously. Otherwise the container will stop once the application stops.
Modify the Main.java
file of the demo app to print "Hello world!" in an infinite loop.
You can use the following code to sleep for 1 second between the iterations:
...
try {
Thread.sleep(1000);
} catch (InterruptedException e) { }
...
Docker uses a file called Dockerfile
to build the image. Create a Dockerfile
in the directory ./demo-app
with the following content:
FROM alpine:latest
RUN apk add --no-cache openjdk17
WORKDIR /app
COPY target/*.jar /app/app.jar
CMD ["java", "-jar", "app.jar"]
Read the Dockerfile documentation to understand each of the lines.
To build the image, run the command:
docker build -t dai/demo-app .
in the directory ./demo-app
. Be careful to not forget the dot at the end of the command. It is important. It tells Docker to use the current directory (./
) as the build context. That means the command will be run in this directory.
The option -t
gives a name to the Docker image. If the build was successful, you can check that the image is present with the command docker images
.
Now try to run the image with the normal docker run
command that you already know. It should work and print "Hello world!" in an infinite loop. Quit with Ctrl-C.
In a final step, we will use Docker Compose to run the application. Create a docker-compose.yml
file with the image as the only service. Run it with docker compose
to check that it works.