Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up application startup times with AppCDS #558

Merged
merged 1 commit into from
Oct 22, 2024
Merged
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
2 changes: 1 addition & 1 deletion compose/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ services:
environment:
JAVA_OPTS: "${JAVA_OPTS_CONFIG}"
SPRING_PROFILES_ACTIVE: "${CONFIG_SERVER_DEFAULT_PROFILES}"
restart: unless-stopped
# restart: unless-stopped
volumes:
# override with the local copy to test config changes during development
- config:/etc/geoserver
Expand Down
15 changes: 14 additions & 1 deletion src/apps/base-images/jre/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@ FROM eclipse-temurin:21-jre

LABEL maintainer="GeoServer PSC <[email protected]>"

ENV JAVA_TOOL_OPTIONS=
# default JVM parameters https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html
# to add JVM parameters use the JAVA_OPTS env variable preferrably
ENV DEFAULT_JAVA_TOOL_OPTIONS="\
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.util=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
--add-opens=java.base/java.text=ALL-UNNAMED \
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED \
--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED \
--add-opens=java.desktop/sun.java2d.pipe=ALL-UNNAMED \
--add-opens=java.naming/com.sun.jndi.ldap=ALL-UNNAMED \
-Djava.awt.headless=true"

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS}"
ENV JAVA_OPTS=

# Install the system CA certificates for the JVM :wqnow that we're root
Expand Down
14 changes: 0 additions & 14 deletions src/apps/base-images/spring-boot/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,6 @@ RUN mkdir -p /opt/app/bin

WORKDIR /opt/app/bin

# default JVM parameters https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html
# to add JVM parameters use the JAVA_OPTS env variable preferrably
ENV JAVA_TOOL_OPTIONS="\
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.util=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
--add-opens=java.base/java.text=ALL-UNNAMED \
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED \
--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED \
--add-opens=java.desktop/sun.java2d.pipe=ALL-UNNAMED \
--add-opens=java.naming/com.sun.jndi.ldap=ALL-UNNAMED \
-Djava.awt.headless=true"

ENV JAVA_OPTS=
EXPOSE 8080
EXPOSE 8081

Expand Down
12 changes: 12 additions & 0 deletions src/apps/geoserver/gwc/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/restconfig/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/wcs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/webui/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/wfs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/wms/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
12 changes: 12 additions & 0 deletions src/apps/geoserver/wps/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN mkdir /tmp/tmpdatadir
RUN java \
-XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,datadir,offline \
-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
9 changes: 9 additions & 0 deletions src/apps/infrastructure/admin/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@ HEALTHCHECK \
CMD curl -f -s -o /dev/null localhost:8080/actuator/health || exit 1

CMD exec env USER_ID="$(id -u)" USER_GID="$(id -g)" java $JAVA_OPTS org.springframework.boot.loader.JarLauncher

# Execute the CDS training run
RUN java -XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone,offline \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
6 changes: 5 additions & 1 deletion src/apps/infrastructure/config/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ RUN true
COPY --from=builder application/ ./

# Either 'git' or 'native'.
ENV SPRING_PROFILES_ACTIVE=native
ENV SPRING_PROFILES_ACTIVE=native,standalone
# 'native' profile config, use the default config embedded in the Docker image.
# Feel free to override with a mounted volume
ENV CONFIG_NATIVE_PATH=/etc/geoserver
Expand All @@ -36,4 +36,8 @@ ENV CONFIG_GIT_BASEDIR: /tmp/git_config
# avoid stack trace due to jgit not being able of creating a .config dir at $HOME
ENV XDG_CONFIG_HOME: /tmp

# Execute the CDS training run
RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefreshed org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
9 changes: 9 additions & 0 deletions src/apps/infrastructure/discovery/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN java -XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
9 changes: 9 additions & 0 deletions src/apps/infrastructure/gateway/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ COPY --from=builder spring-boot-loader/ ./
#see https://github.com/moby/moby/issues/37965
RUN true
COPY --from=builder application/ ./

# Execute the CDS training run
RUN java -XX:ArchiveClassesAtExit=application.jsa \
-Dspring.context.exit=onRefreshed \
-Dspring.profiles.active=standalone \
org.springframework.boot.loader.JarLauncher
RUN rm -rf /tmp/*

ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa"
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.app;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment;

/**
* Allows to pass a JVM argument to exit the application upon specific {@link
* ApplicationContextEvent application events}, mostly useful to start up an application during the
* Docker image build process to create the AppCDS archive.
*
* <p>Usage: run the application with {@code -Dspring.context.exit=<event>}, where {@code <event>}
* is one of
*
* <ul>
* <li>{@link ExitOn#onPrepared onPrepared}
* <li>{@link ExitOn#onRefreshed onRefreshed}
* <li>{@link ExitOn#onStarted onStarted}
* <li>{@link ExitOn#onReady onReady}
* </ul>
*
* <p>Note Spring Boot 3.2 supports {@code spring.context.exit=onRefresh} as of <a
* href="https://github.com/spring-projects/spring-framework/commit/eb3982b6c25d6c3dd49f6c4cc000c40364916a83">this
* commit</a>, and when we migrate from Spring Boot 2.7 to 3.2+ this will not be necessary most
* probably, although we've added additional events because some applications may fail to start
* without all the machinery in place at different stages. Nonetheless, the new {@code offline}
* embedded spring profile should allow them all to start without spring cloud bus, ACL, etc.
*
* @since 1.9.0
*/
@AutoConfiguration
@ConditionalOnProperty("spring.context.exit")
@Slf4j
public class ExitOnApplicationEventAutoConfiguration {

public enum ExitOn {
/**
* The {@link SpringApplication} is starting up and the {@link ApplicationContext} is fully
* prepared but not refreshed. The bean definitions will be loaded and the {@link
* Environment} is ready for use at this stage.
*
* @see ApplicationPreparedEvent
*/
onPrepared,
/**
* {@code ApplicationContext} gets initialized or refreshed
*
* @see ContextRefreshedEvent
*/
onRefreshed,
/**
* {@code ApplicationContext} has been refreshed but before any {@link ApplicationRunner
* application} and {@link CommandLineRunner command line} runners have been called.
*
* @see ApplicationStartedEvent
*/
onStarted,
/**
* Published as late as conceivably possible to indicate that the application is ready to
* service requests. The source of the event is the {@link SpringApplication} itself, but
* beware all initialization steps will have been completed by then.
*
* @see ApplicationReadyEvent
*/
onReady
}

@Autowired private ApplicationContext appContext;

@Value("${spring.context.exit}")
ExitOn exitOn;

@EventListener(ApplicationPreparedEvent.class)
void exitOnApplicationPreparedEvent(ApplicationPreparedEvent event) {
exit(ExitOn.onStarted, event.getApplicationContext());
}

@EventListener(ContextRefreshedEvent.class)
void exitOnContextRefreshedEvent(ContextRefreshedEvent event) {
exit(ExitOn.onRefreshed, event.getApplicationContext());
}

@EventListener(ApplicationStartedEvent.class)
void exitOnApplicationStartedEvent(ApplicationStartedEvent event) {
exit(ExitOn.onStarted, event.getApplicationContext());
}

@EventListener(ApplicationReadyEvent.class)
void exitOnApplicationReadyEvent(ApplicationReadyEvent event) {
exit(ExitOn.onStarted, event.getApplicationContext());
}

private void exit(ExitOn ifGiven, ApplicationContext applicationContext) {
if (this.exitOn == ifGiven && applicationContext == this.appContext) {
log.warn("Exiting application, spring.context.exit={}", ifGiven);
try {
((ConfigurableApplicationContext) applicationContext).close();
} finally {
System.exit(0);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.geoserver.cloud.app.StartupLoggerAutoConfiguration,\
org.geoserver.cloud.app.ServiceIdFilterAutoConfiguration
org.geoserver.cloud.app.ServiceIdFilterAutoConfiguration,\
org.geoserver.cloud.app.ExitOnApplicationEventAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ eureka:
defaultZone: ${eureka.server.url}
healthcheck:
enabled: false # must only be set to true in application.yml, not bootstrap

---
spring.config.activate.on-profile: offline
spring:
cloud.config.enabled: false
cloud.config.discovery.enabled: false
cloud.discovery.enabled: false
cloud.bus.enabled: false
eureka.client.enabled: false

geoserver.acl.enabled: false
---
spring.config.activate.on-profile: local
# Profile used for local development, so an app launched from the IDE can participate in the cluster.
Expand Down
Loading