|
| 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