This is a Spring Boot Native Image app that connects to a General Transit Feed Specification and writes the VehiclePosition data from the feed to a file. Specifically it connects to a GTFS Realtime
A list of GTFS Realtime feeds can be found here
The application has been deployed to fly.io running on a VM with 1 shared CPU and 256mb of ram. This fits under the Free allowances of fly.io
Below are the various environment properties that control the application.
Property Name | Description | Required | Default | Example |
---|---|---|---|---|
GTFS_VP_FEED_URL | The URL of the GTFS RT feed | Yes | N/A | https://api.actransit.org/transit/gtfsrt/vehicles?token=api_token |
GTFS_VP_FILE | The absolute path to the file | No | Default temp directory | /data/gtfs-vp-out-20240513.txt |
GTFS_VP_ROUTE_IDS | Comma seperated list of route ids that will be written to file | No | ALL | 51A,51B,6,7,10,F |
Spring Boot has introduced GraalVM Native Image support. Documentation can be found here.
There are quite a bit of limitation when working with Spring Boot and GraalVM. The current known limitation are tracked here
- GraalVM installed
The easiest way is to use SDKMAN!. GraalVM Download page - Gradle
Can also be installed using SDKMAN! - Docker
Building the Docker image uses Cloud Native Buildpacks
The command below will build a "distroless" Docker image. This means there is no bash, mkdir, chown and so on...
./gradlew bootBuildImage
To build a Docker image that is not a "distroless" Docker image we have to specify a different builder to use.
./gradlew bootBuildImage --builder paketobuildpacks/builder-jammy-base:latest
- Run the application with the agent to collect metadata
While the above command is running we can send a graceful shutdown command via a POST call to end the collection of metadata.
./gradlew -Pagent bootRun --args='--GTFS_VP_FEED_URL=https://api.actransit.org/transit/gtfsrt/vehicles?token=F2... --GTFS_VP_FILE=/home/../../gp-vp-out.txt'
POST https://localhost:8080/actuator/shutdown
- Copy the generated metadata for the native image compile
./gradlew metadataCopy --task bootRun --dir src/main/resources/META-INF/native-image
- Compile the native image executable
./gradlew nativeCompile
You might encounter the error running build exit status 137. This usually means Docker ran out memory when trying to build the application.
On Windows with WSL2 installed create %UserProfile%\.wslconfig
and add the following to increase the memory allocated.
[wsl2]
memory=12GB
fly.io is an app hosting platform that can run Docker images for us in what they call "micro-vms". fly.io
They have a $5 monthly hobby plan. Pricing can be found here. The plan includes some free allowances. As long as the application stays within the free allowances you will not be billed.
-
Sign up for their Hobby plan.
-
Install their CLI tool
curl -L https://fly.io/install.sh | sh
-
Generate the 'fly.toml' configuration file used by fly.io
fly launch --no-deploy
The settings can be tweaked by answers Yes and a browser will be launched. If the browser fails to open, just copy the url.
-
Edit the fly.toml to add a mounts section so that we can have persistent storage.
[mounts] source="myapp_data" destination="/data"
-
Edit the fly.toml to add a env section to add our environment variables
[env] GTFS_VP_FEED_URL = "https://api.actransit.org/transit/gtfsrt/vehicles?token=api_token_here" GTFS_VP_FILE = "/data/gtfs-vp-out-20240514.txt" GTFS_VP_ROUTE_IDS = "51A,51B,6,7,10,F" SERVER_HOST = "0.0.0.0" LOG_LEVEL_APP = "WARN" LOG_PATH = "/data/"
-
Add a metrics section so fly.io knows where to get metrics data
[metrics] port = 8080
-
Make sure to set auto_stop_machines to false in the http_service section to stop fly.io's proxy from stopping our application once it detects there is no activity.
[http_service] nternal_port = 8080 force_https = true auto_stop_machines = false
-
Add a build section to point to `./Dockerfiles/Dockerfile.fly.io'
[build] dockerfile = "./Dockerfiles/Dockerfile.flyio"
- Build the Docker image
./gradlew bootBuildImage
- Deploy to fly.io
fly deploy --local-only