diff --git a/.dockerignore b/.dockerignore index 3e3fec2..ae63c8c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,4 @@ -** -!target/*.jar \ No newline at end of file +.* +examples +target +#!target/*.jar \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 48cc740..ea41fe2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ env: script: - mvn clean verify -Drevisison=$(date +%Y-%V) -- docker build --build-arg MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} -t observabilitystack/geoip-api:latest -t observabilitystack/geoip-api:$(date +%Y-%V) . +- docker build --build-arg MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} -t observabilitystack/geoip-api:latest -t observabilitystack/geoip-api:native -t observabilitystack/geoip-api:$(date +%Y-%V) . +- ./test-native-image.sh before_deploy: - export TRAVIS_TAG=${TRAVIS_TAG:-$(date +%Y-%V)} @@ -23,7 +24,7 @@ before_deploy: deploy: - provider: script - script: bash push-to-dockerhub.sh + script: ./push-to-dockerhub.sh on: branch: master - provider: releases @@ -34,4 +35,4 @@ deploy: skip_cleanup: true body: Updated MaxMind GeoLite database as of ${TRAVIS_TAG} on: - branch: master \ No newline at end of file + branch: master diff --git a/Dockerfile b/Dockerfile index 85009be..afd230e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,41 @@ -FROM openjdk:11.0.8 -ARG MAXMIND_LICENSE_KEY +# build stage +FROM oracle/graalvm-ce:20.2.0-java11 AS builder + +ADD . /build +WORKDIR /build + +# For SDKMAN to work we need unzip & zip +RUN yum -y install unzip zip +RUN \ + # Install SDKMAN + curl -s "https://get.sdkman.io" | bash; \ + source "$HOME/.sdkman/bin/sdkman-init.sh" && \ + sdk install maven && \ + # Install GraalVM Native Image + gu install native-image && \ + native-image --version && \ + mvn --version && \ + java -version && \ + ls -la . -# place app -COPY target/*.jar /opt/geoip-api.jar +# build image +RUN source "$HOME/.sdkman/bin/sdkman-init.sh" && mvn -P native clean package + +# run stage +FROM centos:7.8.2003 +ARG MAXMIND_LICENSE_KEY # download current maxmind databases WORKDIR /srv -RUN curl -sSL "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz&license_key=${MAXMIND_LICENSE_KEY}" | tar -xz && \ +#RUN apk add curl && \ +RUN yum install -y tar gzip && \ + curl -sfSL "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz&license_key=${MAXMIND_LICENSE_KEY}" | tar -xz && \ ln -s GeoLite2-City_*/GeoLite2-City.mmdb . +# place app +COPY --from=builder "/build/target/geoip-api" /srv/geoip-api + ENV CITY_DB_FILE /srv/GeoLite2-City.mmdb -HEALTHCHECK --interval=5s --timeout=2s CMD curl -f http://localhost:8080/actuator/health +HEALTHCHECK --interval=5s --timeout=1s CMD curl -f http://localhost:8080/actuator/health EXPOSE 8080 -CMD exec java ${JAVA_GC_OPTS} ${JAVA_OPTS} -jar /opt/geoip-api.jar +CMD exec /srv/geoip-api diff --git a/pom.xml b/pom.xml index e652b7b..7dd8915 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.3.RELEASE + 2.3.4.RELEASE @@ -63,8 +63,26 @@ true true + + com.s24.geoip.GeoIpApi + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + com.google.guava @@ -74,12 +92,14 @@ org.springframework.boot spring-boot-starter-web + org.springframework.boot @@ -89,10 +109,22 @@ io.micrometer micrometer-registry-prometheus + + + org.springframework.experimental + spring-graalvm-native + 0.7.1 + + + org.springframework + spring-context-indexer + true + com.maxmind.geoip2 geoip2 @@ -108,15 +140,51 @@ - - - org.springframework.boot - spring-boot-maven-plugin - - - maven-failsafe-plugin - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + maven-failsafe-plugin + + + + + + native + + + + org.graalvm.nativeimage + native-image-maven-plugin + 20.2.0 + + + -J-Xmx4G + -H:+TraceClassInitialization + -H:+ReportExceptionStackTraces + -H:ReflectionConfigurationFiles=/build/src/main/resources/reflection-config.json + -Dspring.graal.remove-unused-autoconfig=true + -Dspring.graal.remove-yaml-support=true + + ${project.artifactId} + + + + + native-image + + package + + + + + + + + diff --git a/push-to-dockerhub.sh b/push-to-dockerhub.sh old mode 100644 new mode 100755 diff --git a/src/main/java/com/s24/geoip/GeoIpApi.java b/src/main/java/com/s24/geoip/GeoIpApi.java index 0a88c71..8d8e0cb 100644 --- a/src/main/java/com/s24/geoip/GeoIpApi.java +++ b/src/main/java/com/s24/geoip/GeoIpApi.java @@ -17,7 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@SpringBootApplication +@SpringBootApplication(proxyBeanMethods = false) public class GeoIpApi { private static final Logger logger = LoggerFactory.getLogger(GeoIpApi.class); diff --git a/src/main/java/com/s24/geoip/web/GeoIpRestController.java b/src/main/java/com/s24/geoip/web/GeoIpRestController.java index f411f62..4f07037 100644 --- a/src/main/java/com/s24/geoip/web/GeoIpRestController.java +++ b/src/main/java/com/s24/geoip/web/GeoIpRestController.java @@ -53,8 +53,9 @@ public ResponseEntity handleKnownNotFounds() { /** * Lookup the geolocation information for an ip address. */ + @GetMapping("/{address:.+}") - public ResponseEntity lookup(@PathVariable InetAddress address) { + public ResponseEntity lookup(@PathVariable("address") InetAddress address) { return ResponseEntity.of(geolocations.lookup(address)); } diff --git a/src/main/resources/reflection-config.json b/src/main/resources/reflection-config.json new file mode 100644 index 0000000..4d02793 --- /dev/null +++ b/src/main/resources/reflection-config.json @@ -0,0 +1,102 @@ +[ + { + "name" : "com.s24.geoip.GeoIpEntry", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.model.CityResponse", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.City", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Continent", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Location", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Postal", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Country", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.RepresentedCountry", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Subdivision", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.record.Traits", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + }, + { + "name" : "com.maxmind.geoip2.NetworkDeserializer", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredFields" : true, + "allPublicFields" : true + } +] + \ No newline at end of file diff --git a/test-native-image.sh b/test-native-image.sh new file mode 100755 index 0000000..74093d1 --- /dev/null +++ b/test-native-image.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# clean up after test +function shutdown { + docker rm -f geoip-native-test +} +trap shutdown EXIT + +# launch docker +docker run -dp 18080:8080 --name geoip-native-test observabilitystack/geoip-api:native +sleep 1 + +# execute some curls +curl -fso /dev/null "http://localhost:18080/actuator" +curl -fso /dev/null "http://localhost:18080/actuator/health" +curl -fso /dev/null "http://localhost:18080/actuator/prometheus" +curl -fso /dev/null "http://localhost:18080/$(curl -s https://ifconfig.me/ip)" +curl -fso /dev/null "http://localhost:18080/8.8.4.4" +curl -fso /dev/null "http://localhost:18080/8.8.8.8" +curl -fso /dev/null "http://localhost:18080/206.80.238.253" +curl -fso /dev/null "http://localhost:18080/2.161.45.64" +curl -fso /dev/null "http://localhost:18080/5.4.55.34"