Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions grails-doc/src/en/guide/deployment.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ under the License.
////

Grails applications can be deployed in a number of ways, each of which has its pros and cons.

This section covers:

* Traditional WAR file deployment for servlet containers
* Standalone deployment using executable JAR/WAR files
* Container-based deployment with layered builds for optimized Docker images
* Deployment configuration tasks including SSL setup
102 changes: 102 additions & 0 deletions grails-doc/src/en/guide/deployment/deploymentContainer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,105 @@ testImplementation "org.springframework.boot:spring-boot-starter-tomcat"
== Application servers

The Grails framework requires that runtime containers support Servlet 6.0.0 and above. By default, Grails framework applications are bundled with an embeddable Tomcat and testing is primarily done with Tomcat. Any servlet container meeting the minimum requirements should be able to run Grails framework applications, but some workarounds may be required for container-specific bugs or configurations.

== Container Deployments with Layered Builds

For optimized container deployments, Grails applications can take advantage of Spring Boot's layered JAR functionality. This approach splits the application into different layers (dependencies, snapshot dependencies, and application code), which allows for more efficient Docker image builds and better layer caching.

To enable layered builds, you can configure your `build.gradle` file:

[source,gradle]
----
springBoot {
layout = 'ZIP'
}

jar {
enabled = true
archiveClassifier = 'plain'
}

bootJar {
enabled = true
archiveClassifier = 'exec'
}

// Configure layered JAR for container optimization
bootBuildImage {
builder = 'docker.io/paketobuildpacks/builder-jammy-base:latest'
}
----

This configuration creates layered builds that are ideal for container deployments, as Docker can cache unchanged layers (like dependencies) and only rebuild the layers that have changed.

=== Docker Build Best Practices

When building Docker images for Grails applications, consider using multi-stage builds to optimize the final image size:

[source,Dockerfile]
----
# Multi-stage build for Grails application
FROM docker.io/library/eclipse-temurin:17-jdk-jammy AS builder
WORKDIR /workspace/app

COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
RUN chmod +x ./gradlew
RUN ./gradlew --version

COPY src src
RUN ./gradlew assemble -x test

FROM docker.io/library/eclipse-temurin:17-jre-jammy
VOLUME /tmp
COPY --from=builder /workspace/app/build/libs/*.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
----

=== Using Spring Boot's Layered JAR Feature

For even more optimized Docker builds, you can create layered JARs that separate application code from dependencies:

[source,gradle]
----
// In build.gradle, add the layered plugin
plugins {
id 'org.springframework.boot' version '3.x.x' apply false
id 'io.spring.dependency-management' version '1.x.x'
id 'org.grails.grails-web' version '7.x.x'
id 'org.grails.plugins:hibernate5' version '7.x.x'
}

// Configure layered JAR
jar {
enabled = true
archiveClassifier = 'plain'
}

bootJar {
enabled = true
archiveClassifier = 'exec'
layered {
enabled = true
}
}

// Add the Spring Boot layered plugin dependency
configurations.all {
resolutionStrategy {
force 'org.springframework.boot:spring-boot-gradle-plugin:3.x.x'
}
}
----

Then you can use the `org.springframework.boot:jarmap` tool to inspect and work with the layers:

[source,bash]
----
# Extract layers for Docker multi-stage build
java -jar build/libs/myapp-1.0.0-exec.jar extract --layers --destination build/extracted/
----

This approach significantly reduces Docker image build times and image sizes by enabling layer caching for unchanged dependencies.
136 changes: 136 additions & 0 deletions grails-doc/src/en/guide/deployment/deploymentLayeredBuilds.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
////

== Layered Builds for Container Deployments

Grails applications can leverage Spring Boot's layered JAR functionality to optimize container deployments. This approach significantly reduces Docker image sizes and build times by separating the application into distinct layers that can be cached independently.

=== Understanding Layered JARs

A layered JAR splits your application into different layers:

* **Dependencies**: Third-party libraries that rarely change
* **Snapshot Dependencies**: Libraries that are under active development
* **Resources**: Static resources like configuration files
* **Application**: Your application code

This separation enables Docker to cache unchanged layers, resulting in faster builds and reduced storage requirements.

=== Configuring Layered Builds

To enable layered builds in your Grails application, add the following to your `build.gradle`:

[source,gradle]
----
// Enable layered JAR plugin
plugins {
id 'org.springframework.boot' version '3.x.x'
id 'io.spring.dependency-management' version '1.x.x'
id 'org.grails.grails-web' version '7.x.x'
}

// Configure layered JAR
jar {
enabled = true
archiveClassifier = 'plain'
}

bootJar {
enabled = true
archiveClassifier = 'exec'
layered {
enabled = true
}
}
----

=== Working with Layers

You can extract and work with individual layers using the jarmode:

[source,bash]
----
# Extract layers to separate directories
java -Djarmode=layertools -jar build/libs/myapp-0.1-exec.jar extract --destination build/extracted/

# The extracted layers will be in separate directories:
# - build/extracted/dependencies
# - build/extracted/snapshot-dependencies
# - build/extracted/resources
# - build/extracted/application
----

=== Optimized Dockerfile

Using the extracted layers, you can create an optimized Dockerfile:

[source,Dockerfile]
----
# syntax=docker/dockerfile:1
FROM eclipse-temurin:17-jre-jammy as builder
WORKDIR /app
COPY build/libs/myapp-0.1-exec.jar application.jar
RUN java -Djarmode=layertools -jar application.jar extract

# Create final image with only necessary layers
FROM eclipse-temurin:17-jre-jammy
VOLUME /tmp
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
----

=== Benefits of Layered Builds

1. **Faster Builds**: Docker can cache unchanged layers, only rebuilding what's necessary
2. **Smaller Images**: Reduced storage requirements when running multiple applications
3. **Efficient Push/Pull**: Less data transferred when pushing to registries
4. **Improved CI/CD Performance**: Faster pipeline execution times

=== Docker Buildpacks Integration

Grails applications work well with Docker buildpacks for automated layered builds:

[source,gradle]
----
// Configure buildpack settings in build.gradle
bootBuildImage {
builder = 'docker.io/paketobuildpacks/builder-jammy-base:latest'
environment = [
'BP_JVM_VERSION': '17',
'BP_SPRING_CLOUD_BINDINGS_ENABLED': 'true'
]
}
----

Run with:
[source,bash]
----
./gradlew bootBuildImage --imageName=myapp:latest
----

=== Best Practices for Container Deployments

1. **Use Alpine-based base images** when possible to reduce image size
2. **Enable container support** in JVM settings: `-XX:+UseContainerSupport`
3. **Set appropriate memory limits** using `-XX:MaxRAMPercentage`
4. **Use multi-stage builds** to keep the final image small
5. **Regularly update base images** to include security patches
75 changes: 75 additions & 0 deletions grails-doc/src/en/guide/deployment/deploymentStandalone.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,81 @@ java -Dgrails.env=prod -jar build/libs/mywar-0.1.war (or .jar)
WARNING: Note: TAR/ZIP distribution assembly has been removed from Grails 3.1.


=== Advanced Container Deployment with Layered Builds

For production deployments in containerized environments, Grails applications can leverage Spring Boot's layered JAR functionality. This approach separates the application into distinct layers (dependencies, snapshots, and application code), optimizing Docker image builds and storage.

==== Creating Layered JARs

To create a layered executable JAR, configure your `build.gradle`:

[source,gradle]
----
bootJar {
enabled = true
archiveClassifier = 'exec'
layered {
enabled = true
}
}

jar {
enabled = true
archiveClassifier = 'plain'
}
----

==== Building Optimized Docker Images

Once you have a layered JAR, you can create an optimized Docker image using the extracted layers:

[source,Dockerfile]
----
FROM eclipse-temurin:17-jre-jammy

VOLUME /tmp

ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app

ENTRYPOINT ["java","-cp","app:app/lib/*","example.Application"]
----

To prepare the layers for Docker:

[source,bash]
----
grails package
java -Djarmode=layertools -jar build/libs/myapp-*-exec.jar extract
----

==== Benefits of Layered Builds

* **Reduced image sizes**: Only changed layers need to be rebuilt
* **Faster deployments**: Docker layer caching improves build times
* **Optimized storage**: Shared layers across applications reduce total storage requirements

==== Production Deployment Commands

For production deployments, consider using these optimized approaches:

[source,bash]
----
# Create layered JAR for container deployment
grails package

# Run with specific memory settings
java -server -Xmx768M -Dgrails.env=prod -jar build/libs/myapp-*-exec.jar

# Or with specific JVM options for container environments
java -XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-Dgrails.env=prod \
-jar build/libs/myapp-*-exec.jar
----

=== "./gradlew bootRun"


Expand Down
5 changes: 5 additions & 0 deletions grails-doc/src/en/guide/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,11 @@ include::deployment/deploymentStandalone.adoc[]

include::deployment/deploymentContainer.adoc[]

[[deploymentLayeredBuilds]]
=== Layered Builds for Container Deployments

include::deployment/deploymentLayeredBuilds.adoc[]

[[deploymentTasks]]
=== Deployment Configuration Tasks

Expand Down