Skip to content

Commit 8c802f8

Browse files
first draft
1 parent cd8f724 commit 8c802f8

File tree

2 files changed

+389
-0
lines changed

2 files changed

+389
-0
lines changed

articles/container-apps/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@
378378
- name: Overview
379379
href: java-overview.md
380380
displayName: java
381+
- name: Introduction to containers
382+
href: java-containers-intro.md
381383
- name: Turn on Java features
382384
href: java-feature-switch.md
383385
displayName: java
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
---
2+
title: Introduction to containers for Java applications
3+
description: Learn the basics of using containers for Java applications.
4+
services: container-apps
5+
author: craigshoemaker
6+
ms.service: azure-container-apps
7+
ms.custom: devx-track-azurecli, devx-track-extended-java
8+
ms.topic: tutorial
9+
ms.date: 03/07/2025
10+
ms.author: cshoe
11+
---
12+
13+
# Introduction to containers for Java applications
14+
15+
TODO
16+
17+
## Understanding containers for Java applications
18+
19+
Containers package applications with their dependencies, ensuring consistency across environments. For Java developers, this means bundling the application, its dependencies, JRE/JDK, and configuration files into a single, portable unit.
20+
21+
Containers solve the "it works on my machine" problem by providing the same runtime environment everywhere.
22+
23+
Containerization differs from virtualization in that containers share the host OS kernel, making them more lightweight and efficient. This approach is beneficial for Java applications, which already run in a virtual machine (JVM). Containerizing Java applications adds minimal overhead while providing significant deployment benefits.
24+
25+
The container ecosystem includes several key components:
26+
27+
- images (the blueprints)
28+
- containers (running instances)
29+
- registries (where images are stored)
30+
- orchestrators (systems that manage containers at scale).
31+
32+
Docker is the most popular containerization platform, and is well-supported in the Azure ecosystem through Azure Container Apps.
33+
34+
## Set up your development environment
35+
36+
### Install required tools
37+
38+
To containerize Java applications, you need several tools installed on your development machine:
39+
40+
1. **[Docker Desktop](https://www.docker.com/products/docker-desktop/)**: Provides the Docker engine, CLI, and Docker Compose for Windows or macOS.
41+
42+
1. **[Visual Studio Code](https://code.visualstudio.com/download)**: A lightweight but powerful code editor.
43+
44+
1. **Essential Visual Studio Code Extensions**:
45+
- [Docker extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) for managing containers
46+
- [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) for Java development
47+
- [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension for developing inside containers.
48+
49+
After installing Docker Desktop, verify the installation with:
50+
51+
```bash
52+
docker --version
53+
docker-compose --version
54+
```
55+
56+
### Configure Visual Studio Code for container development
57+
58+
Visual Studio Code can dramatically improve your container development experience. The Docker extension provides syntax highlighting for Dockerfiles, command completion, and an explorer interface for managing containers.
59+
60+
For Java development in containers, configure Visual Studio Code by installing the Java Extension Pack and setting up your Java development kit. The *Dev Containers* extension enables you to open any folder inside a container and use Visual Studio Code's full feature set while inside that container.
61+
62+
Creating a `.devcontainer/devcontainer.json` file in your project allows Visual Studio Code to automatically build and connect to a development container.
63+
64+
For instance, the following configuration defines a Java build:
65+
66+
```json
67+
{
68+
"name": "Java Development",
69+
"image": "mcr.microsoft.com/devcontainers/java:11",
70+
"customizations": {
71+
"vscode": {
72+
"extensions": [
73+
"vscjava.vscode-java-pack",
74+
"ms-azuretools.vscode-docker"
75+
]
76+
}
77+
},
78+
"forwardPorts": [8080, 5005],
79+
"remoteUser": "vscode"
80+
}
81+
```
82+
83+
This configuration uses Microsoft's Java development container image, adds essential extensions, and forwards both the application port (`8080`) and debugging port (`5005`).
84+
85+
## Create a Dockerfile
86+
87+
A Dockerfile contains instructions for building a Docker image. For Java applications, the Dockerfile typically includes:
88+
89+
1. A base image with the Java Development Kit (JDK) or Java Runtime Environment (JRE)
90+
1. Instructions to copy application files
91+
1. Setting environment variables
92+
1. Configuring entry points
93+
94+
### Select a base image
95+
96+
Choosing the right base image is crucial. Consider these options:
97+
98+
| Description | Name | Remarks |
99+
|---|---|---|
100+
| **Microsoft Java development Image** | `mcr.microsoft.com/java/jdk:11-zulu-ubuntu` | Full JDK and optimized for Azure |
101+
| **Microsoft Java production Image** | `mcr.microsoft.com/java/jre:11-zulu-ubuntu` | Runtime only and optimized for Azure |
102+
| **Official OpenJDK development Image** | `openjdk:11-jdk` | Full JDK |
103+
| **Official OpenJDK production Image** | `openjdk:11-jre` | Runtime only |
104+
105+
For development environments, use a full JDK image. For production, use JRE or distroless images to minimize size and attack surface.
106+
107+
The Microsoft Java images come with Azure-specific optimizations and are regularly updated with security patches, making them ideal for applications targeting Azure Container Apps.
108+
109+
### Basic Dockerfile examples
110+
111+
The following example shows a simple Dockerfile for a Java application:
112+
113+
```dockerfile
114+
FROM mcr.microsoft.com/java/jdk:11-zulu-ubuntu
115+
WORKDIR /app
116+
COPY target/myapp.jar app.jar
117+
EXPOSE 8080
118+
ENTRYPOINT ["java", "-jar", "app.jar"]
119+
```
120+
121+
For Spring Boot applications, which are commonly used in microservices architecture, you can use setup your Dockerfile with the following base:
122+
123+
```dockerfile
124+
FROM mcr.microsoft.com/java/jdk:11-zulu-ubuntu
125+
WORKDIR /app
126+
COPY target/*.jar app.jar
127+
EXPOSE 8080
128+
ENTRYPOINT ["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"]
129+
```
130+
131+
For production deployments, use the JRE image to reduce the size and minimize the attack surface of your application:
132+
133+
```dockerfile
134+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
135+
WORKDIR /app
136+
COPY target/*.jar app.jar
137+
EXPOSE 8080
138+
ENV JAVA_OPTS="-Dserver.port=8080"
139+
ENTRYPOINT ["java", ${JAVA_OPTS}, "-jar", "app.jar"]
140+
```
141+
142+
## Local development with containers
143+
144+
Containers are meant to execute in various contexts. In this section, you learn a local development flow while using containers.
145+
146+
### Use Docker Compose for multi-container applications
147+
148+
Most Java applications interact with databases, caches, or other services.
149+
150+
Docker Compose is a tool used to define and manage multi-container Docker applications. It allows you to configure your application's services, networks, and volumes using a simple YAML file named `docker-compose.yml`. With Docker Compose, you can start, stop, and manage all the containers in your application as a single unit.
151+
152+
The following example demonstrates how to configure Docker Compose to prepare a database connection to your application.
153+
154+
```yaml
155+
version: '3.8'
156+
services:
157+
app:
158+
build: .
159+
ports:
160+
- "8080:8080"
161+
- "5005:5005"
162+
environment:
163+
- SPRING_PROFILES_ACTIVE=dev
164+
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
165+
volumes:
166+
- ./target:/app/target
167+
depends_on:
168+
- db
169+
170+
db:
171+
image: postgres:13
172+
environment:
173+
- POSTGRES_USER=postgres
174+
- POSTGRES_PASSWORD=postgres
175+
- POSTGRES_DB=myapp
176+
ports:
177+
- "5432:5432"
178+
volumes:
179+
- postgres-data:/var/lib/postgresql/data
180+
181+
volumes:
182+
postgres-data:
183+
```
184+
185+
This configuration creates two containers: one for your Java application and one for a PostgreSQL database. It also sets up networking between them, mounts volumes for persistence, and exposes necessary ports.
186+
187+
## Debugging containerized applications
188+
189+
TODO
190+
191+
### Setting Up Remote Debugging
192+
193+
Debugging containerized Java applications requires exposing a debug port and configuring your IDE to connect to it:
194+
195+
1. To enable debugging, modify your Dockerfile or container startup command:
196+
197+
```dockerfile
198+
FROM mcr.microsoft.com/java/jdk:11-zulu-ubuntu
199+
WORKDIR /app
200+
COPY target/*.jar app.jar
201+
EXPOSE 8080 5005
202+
ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
203+
```
204+
205+
1. Configure Visual Studio Code's `launch.json` to connect to the debug port:
206+
207+
```json
208+
{
209+
"version": "0.2.0",
210+
"configurations": [
211+
{
212+
"type": "java",
213+
"name": "Debug in Container",
214+
"request": "attach",
215+
"hostName": "localhost",
216+
"port": 5005
217+
}
218+
]
219+
}
220+
```
221+
222+
1. Start your container with port `5005` mapped to your host, then launch the debugger in Visual Studio Code.
223+
224+
### Troubleshooting Container Issues
225+
226+
When containers don't behave as expected, use these commands for troubleshooting:
227+
228+
```bash
229+
# View logs
230+
docker logs <CONTAINER_ID>
231+
232+
# Follow logs in real-time
233+
docker logs -f <CONTAINER_ID>
234+
235+
# Inspect container details
236+
docker inspect <CONTAINER_ID>
237+
238+
# Get a shell in the container
239+
docker exec -it <CONTAINER_ID> bash
240+
```
241+
242+
For Java-specific issues, enable JVM flags for better diagnostics:
243+
244+
```dockerfile
245+
ENTRYPOINT ["java", "-XX:+PrintFlagsFinal", "-XX:+PrintGCDetails", "-jar", "app.jar"]
246+
```
247+
248+
Common issues include:
249+
250+
| Error | Task |
251+
|---|---|
252+
| Out of memory | Increase container memory limits |
253+
| Connection time-outs | Check network configuration |
254+
| Permission problems | Verify file system permissions |
255+
| Classpath issues | Check JAR structure and dependencies |
256+
257+
## Optimizing Java Containers
258+
259+
TODO
260+
261+
### JVM Memory Configuration in Containers
262+
263+
The JVM doesn't automatically detect container memory limits in Java 8. For Java 9+, container awareness is enabled by default. Configure your JVM to respect container limits:
264+
265+
```dockerfile
266+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
267+
WORKDIR /app
268+
COPY target/*.jar app.jar
269+
EXPOSE 8080
270+
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
271+
```
272+
273+
Key JVM flags for containerized applications:
274+
275+
- `-XX:+UseContainerSupport`: Makes JVM container-aware (default in Java 10+)
276+
- `-XX:MaxRAMPercentage=75.0`: Sets maximum heap as a percentage of available memory
277+
- `-XX:InitialRAMPercentage=50.0`: Sets initial heap size
278+
- `-Xmx` and `-Xms`: Are also available, but requires fixed values
279+
280+
## Prepare for production deployment
281+
282+
TODO
283+
284+
### Security best practices
285+
286+
Secure your containerized Java applications with these practices:
287+
288+
1. Run as nonroot user:
289+
290+
```dockerfile
291+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
292+
WORKDIR /app
293+
COPY target/*.jar app.jar
294+
RUN addgroup --system javauser && adduser --system --ingroup javauser javauser
295+
USER javauser
296+
ENTRYPOINT ["java", "-jar", "app.jar"]
297+
```
298+
299+
1. Scan images for vulnerabilities:
300+
301+
```bash
302+
docker scan myapp:latest
303+
```
304+
305+
1. Use up-to-date base images
306+
307+
1. Implement proper secrets management (don't hardcode sensitive data)
308+
309+
1. Apply the principle of least privilege
310+
311+
1. Use read-only file systems where possible
312+
313+
### Health Checks and Monitoring
314+
315+
Implement health checks to ensure your application is running correctly:
316+
317+
```dockerfile
318+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
319+
WORKDIR /app
320+
COPY target/*.jar app.jar
321+
EXPOSE 8080
322+
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8080/health || exit 1
323+
ENTRYPOINT ["java", "-jar", "app.jar"]
324+
```
325+
326+
For Spring Boot applications, include the Actuator dependency for comprehensive health endpoints:
327+
328+
```xml
329+
<dependency>
330+
<groupId>org.springframework.boot</groupId>
331+
<artifactId>spring-boot-starter-actuator</artifactId>
332+
</dependency>
333+
```
334+
335+
Configure your application to output logs in a format suitable for container environments (JSON format works well).
336+
337+
### Configuration management
338+
339+
Use environment variables for configuration rather than hardcoding values:
340+
341+
```dockerfile
342+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
343+
WORKDIR /app
344+
COPY target/*.jar app.jar
345+
EXPOSE 8080
346+
ENV JAVA_OPTS=""
347+
ENV SPRING_PROFILES_ACTIVE="prod"
348+
ENTRYPOINT java $JAVA_OPTS -jar app.jar
349+
```
350+
351+
Pass configuration settings when running:
352+
353+
```bash
354+
docker run -p 8080:8080 \
355+
-e "SPRING_PROFILES_ACTIVE=prod" \
356+
-e "DB_HOST=my-db-host" \
357+
myapp:latest
358+
```
359+
360+
For more complex configurations, consider using Azure App Configuration or environment variable mapping in Azure Container Apps.
361+
362+
## Deploying to Azure Container Apps
363+
364+
TODO
365+
366+
### Preparing Your Container for Azure
367+
368+
1. Ensure your container listens on the port provided by Azure:
369+
370+
```dockerfile
371+
FROM mcr.microsoft.com/java/jre:11-zulu-ubuntu
372+
WORKDIR /app
373+
COPY target/*.jar app.jar
374+
ENV PORT=8080
375+
EXPOSE ${PORT}
376+
CMD java -jar app.jar --server.port=${PORT}
377+
```
378+
379+
1. Implement health probes for Azure's liveness and readiness checks
380+
381+
1. Configure logging to output to `stdout`/`stderr`
382+
383+
1. Set up proper graceful shutdown handling with time out configuration
384+
385+
## Related content
386+
387+
TODO

0 commit comments

Comments
 (0)