Skip to content
This repository was archived by the owner on Jul 11, 2025. It is now read-only.

Commit 0e26ddb

Browse files
Merge pull request #9 from open-AIMS/dockerisation
feat: add Dockerfile and GitHub workflow to publish to ghcr
2 parents 1d1ac91 + 774b456 commit 0e26ddb

File tree

7 files changed

+331
-4
lines changed

7 files changed

+331
-4
lines changed

.dockerignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
input/
2+
output/
3+
.git*
4+
assets/
5+
docs/
6+
.config.toml
7+
Dockerfile
8+
.gitignore
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# This workflow builds and publishes the ReefGuideAPI base Docker image to GitHub Container Registry (ghcr.io)
2+
# It is triggered when a push is made to the main branch.
3+
4+
# Additional notes:
5+
# - The workflow uses the github.repository context to name the image, ensuring it's tied to your repository
6+
# - The GITHUB_TOKEN is automatically provided by GitHub Actions, no need to set it up manually
7+
# - The Docker metadata action automatically generates appropriate tags based on the release version
8+
# - The Julia version can be easily updated by changing the JULIA_VERSION environment variable at the top of the workflow
9+
10+
name: Build and Publish ReefGuideAPI.jl Docker Image
11+
12+
on:
13+
push:
14+
branches:
15+
- main
16+
release:
17+
types: [published]
18+
19+
env:
20+
REGISTRY: ghcr.io
21+
IMAGE_NAME: ${{ github.repository }}/reefguide-src
22+
JULIA_VERSION: 1.10.5
23+
# Set to true to use a fixed ReefGuideAPI version for debugging, false to use the release version
24+
USE_FIXED_REEFGUIDEAPI_VERSION: true
25+
# TODO this doesn't make sense until the releasing is sorted out
26+
# The fixed ReefGuideAPI version to use when USE_FIXED_REEFGUIDEAPI_VERSION is true
27+
FIXED_REEFGUIDEAPI_VERSION: "0.1.0"
28+
29+
jobs:
30+
build-and-push-image:
31+
runs-on: ubuntu-latest
32+
permissions:
33+
contents: read
34+
packages: write
35+
36+
steps:
37+
# Step 1: Checkout the repository code
38+
- name: Checkout repository
39+
uses: actions/checkout@v3
40+
41+
# Step 2: Extract the version number from the release tag
42+
# This removes the 'v' prefix from the tag (e.g., 'v1.2.3' becomes '1.2.3')
43+
- name: Extract version from tag
44+
id: get_version
45+
run: |
46+
if ${{ env.USE_FIXED_REEFGUIDEAPI_VERSION == 'true' }}; then
47+
echo "Using fixed ReefGuideAPI version: ${{ env.FIXED_REEFGUIDEAPI_VERSION }}"
48+
echo "VERSION=${{ env.FIXED_REEFGUIDEAPI_VERSION }}" >> $GITHUB_OUTPUT
49+
elif [ "${{ github.event_name }}" = "release" ]; then
50+
RELEASE_VERSION=${GITHUB_REF#refs/tags/v}
51+
echo "Using release version: $RELEASE_VERSION"
52+
echo "VERSION=$RELEASE_VERSION" >> $GITHUB_OUTPUT
53+
else
54+
echo "No version specified and not a release event. Using default version."
55+
echo "VERSION=${{ env.FIXED_REEFGUIDEAPI_VERSION }}" >> $GITHUB_OUTPUT
56+
fi
57+
58+
# Step 3: Log in to the GitHub Container Registry
59+
# This uses the provided GitHub token for authentication
60+
- name: Log in to the Container registry
61+
uses: docker/login-action@v2
62+
with:
63+
registry: ${{ env.REGISTRY }}
64+
username: ${{ github.actor }}
65+
password: ${{ secrets.GITHUB_TOKEN }}
66+
67+
# Step 4: Extract metadata for Docker
68+
# This step generates tags and labels for the Docker image
69+
- name: Extract metadata (tags, labels) for Docker
70+
id: meta
71+
uses: docker/metadata-action@v4
72+
with:
73+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
74+
tags: |
75+
type=semver,pattern={{version}}
76+
type=semver,pattern={{major}}.{{minor}}
77+
type=semver,pattern={{major}}.{{minor}}.{{patch}}
78+
type=semver,pattern={{major}}
79+
type=ref,event=branch
80+
type=sha,format=long
81+
type=sha,format=short
82+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
83+
84+
# Step 5: Build and push the Docker image
85+
# This step builds the reefguide-src image and pushes it to the registry
86+
- name: Build and push Docker image
87+
uses: docker/build-push-action@v4
88+
with:
89+
context: .
90+
target: reefguide-src # Specifies which stage of the Dockerfile to build
91+
push: true # Pushes the image to the registry
92+
tags: ${{ steps.meta.outputs.tags }} # Uses the tags generated in the metadata step
93+
labels: ${{ steps.meta.outputs.labels }} # Uses the labels generated in the metadata step
94+
# Passes the Julia and ReefGuideAPI versions to the Dockerfile
95+
# TODO bring back this build arg when releasing is ready
96+
# REEFGUIDE_VERSION=${{ steps.get_version.outputs.VERSION }}
97+
build-args: |
98+
JULIA_VERSION=${{ env.JULIA_VERSION }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ docs/site/
2222
sandbox/
2323

2424
/.config.toml
25+
26+
data

Dockerfile

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# See https://hub.docker.com/_/julia for valid versions.
2+
ARG JULIA_VERSION="1.10.4"
3+
4+
#------------------------------------------------------------------------------
5+
# internal-base build target: julia with OS updates and an empty @reefguide
6+
# Julia environment prepared for use. NOT intended for standalone use.
7+
#------------------------------------------------------------------------------
8+
FROM julia:${JULIA_VERSION}-bookworm AS internal-base
9+
10+
# Record the actual base image used from the FROM command as label in the compiled image
11+
ARG BASE_IMAGE=$BASE_IMAGE
12+
LABEL org.opencontainers.image.base.name=${BASE_IMAGE}
13+
14+
# Update all pre-installed OS packages (to get security updates)
15+
# and add a few extra utilities
16+
RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
17+
--mount=target=/var/cache/apt,type=cache,sharing=locked \
18+
apt-get update \
19+
&& apt-get -y upgrade \
20+
&& apt-get install --no-install-recommends -y \
21+
git \
22+
less \
23+
nano \
24+
&& apt-get clean \
25+
&& apt-get autoremove --purge \
26+
&& rm -rf /var/lib/apt/lists/*
27+
28+
# Tweak the JULIA_DEPOT_PATH setting so that our shared environments will end up
29+
# in a user-agnostic location, not in ~/.julia => /root/.julia which is the default.
30+
# See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_DEPOT_PATH
31+
# This allows apps derived from this image to drop privileges and run as non-root
32+
# user accounts, but still activate environments configured by this dockerfile.
33+
ENV JULIA_DEPOT_PATH="/usr/local/share/julia"
34+
35+
# Prepare an empty @reefguide Julia environment for derived images to use - this is created in the shared depot path
36+
RUN mkdir -p "${JULIA_DEPOT_PATH}" && \
37+
chmod 0755 "${JULIA_DEPOT_PATH}" && \
38+
julia -e 'using Pkg; Pkg.activate("reefguide", shared=true)'
39+
40+
# Ensure the @reefguide environment is in the load path for Julia, so that apps derived
41+
# from this image can access any packages installed to there.
42+
# (See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_LOAD_PATH)
43+
ENV JULIA_LOAD_PATH="@:@reefguide:@v#.#:@stdlib"
44+
45+
# Run Julia commands by default as the container launches.
46+
# Derived applications should override the command.
47+
ENTRYPOINT ["julia", "--project=@reefguide"]
48+
49+
50+
#------------------------------------------------------------------------------
51+
# reefguide-base build target: ReefGuideAPI.jl preinstalled as a non-dev package.
52+
# Use `--target=reefguide-base` on your `docker build command to build *just* this.
53+
#------------------------------------------------------------------------------
54+
55+
# TODO enable and update this once the package is available from registry
56+
# FROM internal-base AS reefguide-base
57+
#
58+
# # What version of ReefGuideAPI.jl from package registry to install in reefguide-base
59+
# # TODO this doesn't make sense yet
60+
# ARG REEFGUIDE_VERSION="0.1.0"
61+
#
62+
# # Which julia package registry version to install
63+
# ENV REEFGUIDE_VERSION=$REEFGUIDE_VERSION
64+
#
65+
# # Try to coerce Julia to build across multiple targets
66+
# ENV JULIA_CPU_TARGET=x86_64;haswell;skylake;skylake-avx512;tigerlake
67+
#
68+
# # Install ReefGuideAPI.jl into the @reefguide shared environment as an unregistered package.
69+
# # - Allow the package source and version to be overridden at build-time.
70+
# # - Include citation information for ReefGuideAPI.jl in the image labels.
71+
# RUN mkdir -p "${JULIA_DEPOT_PATH}" && \
72+
# chmod 0755 "${JULIA_DEPOT_PATH}" && \
73+
# julia --project=@reefguide -e "using Pkg; Pkg.add(name=\"ReefGuideAPI\", version=\"${REEFGUIDE_VERSION}\"); Pkg.instantiate(); Pkg.precompile(); using ReefGuideAPI;"
74+
# LABEL au.gov.aims.reefguideapi.source="https://github.com/open-AIMS/ReefGuideAPI.jl/releases/tag/v${REEFGUIDE_VERSION}" \
75+
# au.gov.aims.reefguideapi.version="${REEFGUIDE_VERSION}" \
76+
# au.gov.aims.reefguideapi.vendor="Australian Institute of Marine Science" \
77+
# au.gov.aims.reefguideapi.licenses=MIT
78+
#
79+
# # Expect to include the prepped data at /data/reefguide and the config at
80+
# # /data/.config.toml
81+
# VOLUME ["/data/reefguide"]
82+
#
83+
# EXPOSE 8000
84+
#
85+
# # Run Julia commands by default as the container launches.
86+
# # Derived applications should override the command.
87+
# ENTRYPOINT ["julia", "--project=@reefguide", "-t", "1,auto", "-e", "using ReefGuideAPI; ReefGuideAPI.start_server(\"/data/reefguide/config.toml\")"]
88+
89+
#------------------------------------------------------------------------------
90+
# reefguide-src build target: installs directly from source files in this repo.
91+
#------------------------------------------------------------------------------
92+
FROM internal-base AS reefguide-src
93+
94+
ENV REEFGUIDE_ENV_DIR="${JULIA_DEPOT_PATH}/environments/reefguide" \
95+
REEFGUIDE_SRC_DIR="/usr/local/src/reefguide"
96+
97+
# Try to coerce Julia to build across multiple targets
98+
ENV JULIA_CPU_TARGET=x86_64;haswell;skylake;skylake-avx512;tigerlake
99+
100+
# Install the versioned .toml file(s) into the shared reefguide environment and use
101+
# those to set up the ReefGuideAPI source code as a development package in the
102+
# shared @reefguide environment, pre-installing and precompiling dependencies.
103+
WORKDIR "${REEFGUIDE_SRC_DIR}"
104+
COPY ./Project.toml ./Project.toml
105+
COPY ./Manifest.toml ./Manifest.toml
106+
RUN julia --project=@reefguide -e 'using Pkg; Pkg.instantiate(verbose=true)'
107+
108+
# Install the ReefGuideAPI source code and configure it as a development
109+
# package in the @reefguide shared environment.
110+
# Should be v speedy if the .toml file is unchanged, because all the
111+
# dependencies *should* already be installed.
112+
COPY ./src src
113+
RUN julia --project=@reefguide \
114+
-e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.precompile(); using ReefGuideAPI;'
115+
116+
# Expect to include the prepped data at /data/reefguide and the config at
117+
# /data/.config.toml
118+
VOLUME ["/data/reefguide"]
119+
120+
EXPOSE 8000
121+
122+
# Run Julia commands by default as the container launches.
123+
# Derived applications should override the command.
124+
ENTRYPOINT ["julia", "--project=@reefguide", "-t", "1,auto", "-e", "using ReefGuideAPI; ReefGuideAPI.start_server(\"/data/reefguide/config.toml\")"]

README.md

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,71 @@ Higher values do seem to reduce write times but with diminishing returns (tested
9494
Locally, write times with four threads configured range from 10 to 15 seconds.
9595

9696
## Reef edge alignment for site searching
97+
9798
`identify_potential_sites_edges()` can be used to identify potential sites that only align with
9899
the nearest reef edge (or specified rotations away from this angle).
99100
This method works by identifying the closest edge of reef polygon geometries that have been
100101
converted into lines.
101102

102103
The following processing is required before use:
104+
103105
- Reef polygons should be simplified (`GO.simplify()`) and buffered to avoid matching possibly inaccurate reef edges.
104106
- Simplified reef polygons should be provided as vertex-vertex lines with `polygon_to_lines()`.
105107
- Require raster of target pixels to search, and their indices (currently a vector of `CartesianIndices` for identifying search pixels). Use `findall(bool_search_raster)` to return pixel indices.
106108
- Raster of search pixels should be masked by reef polygons or simplified reef polygons.
107-
The column used for masking should be the same as the column specified as geometry_col in
108-
`identify_potential_sites_edges` (default = `:geometry`).
109+
The column used for masking should be the same as the column specified as geometry_col in
110+
`identify_potential_sites_edges` (default = `:geometry`).
111+
112+
## Docker build and run
113+
114+
The ReefGuideAPI.jl package has an associated `Dockerfile` and build/publish process. This means you can run an instance of the ReefGuideAPI.jl package without needing to compile/build it with a local `Julia` installation. You will be able to view the latest published versions of the Docker image on the repository packages page.
115+
116+
### Mounting files and required data
117+
118+
As mentioned above, the `ReefGuideAPI.jl` package currently requires
119+
120+
- a `config.toml` file and
121+
- a set of input data files
122+
123+
Please include these in a folder called `data` in your working directory.
124+
125+
When running the below commands, it is assumed you have `data` available locally with the required files.
126+
127+
**Note**: Due to how Docker excludes `.` files, we have named the config file `config.toml` in the data folder. This is required to launch the server.
128+
129+
### To build from src files using Docker
130+
131+
```
132+
docker build . --target reefguide-src -t reefguide
133+
```
134+
135+
### To build from src files using Docker Compose
136+
137+
```
138+
docker compose up --build reefguide-src
139+
```
140+
141+
### To run with mounted files (launch server) using Docker
142+
143+
```
144+
docker run -p 8000:8000 -v ./data:/data/reefguide reefguide
145+
```
146+
147+
### To run with mounted files (launch server) using Docker Compose
148+
149+
```
150+
docker compose up reefguide-src
151+
```
152+
153+
### To run with mounted files (interactive shell) using Docker
154+
155+
This will start a Julia shell where `ReefGuideAPI` is compiled and ready for use e.g.
156+
157+
```
158+
using ReefGuideAPI
159+
ReefGuideAPI.start_server("/data/reefguide/config.toml")
160+
```
161+
162+
```
163+
docker run --rm --interactive --entrypoint="julia" --tty -v ./data:/data/reefguide reefguide
164+
```

docker-compose.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
#-------------------------------------------------
3+
# Docker compose for ReefGuideAPI.jl docker image
4+
#--------------------------------------------------
5+
version: "3.8"
6+
7+
services:
8+
# TODO enable and update this once the release version is ready
9+
# reefguide-base:
10+
# build:
11+
# args:
12+
# # TODO when versioning is setup improve this
13+
# REEFGUIDE_VERSION: "v0.1.0"
14+
# JULIA_VERSION: "1.10.4"
15+
# context: .
16+
# target: reefguide-base
17+
# image: ReefGuideAPI.jl/reefguide-base:latest
18+
# volumes:
19+
# - ./data:/data/reefguide
20+
reefguide-src:
21+
build:
22+
context: .
23+
target: reefguide-src
24+
image: ReefGuideAPI.jl/reefguide-base:latest
25+
volumes:
26+
- ./data:/data/reefguide
27+
ports:
28+
- 8000:8000

src/ReefGuideAPI.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,26 @@ function tile_size(config)::Tuple
139139
end
140140

141141
function start_server(config_path)
142+
println("Launching server...please wait")
143+
144+
println("Parsing configuration from $(config_path)...")
142145
config = TOML.parsefile(config_path)
146+
println("Successfully parsed configuration.")
147+
148+
println("Setting up region routes...")
143149
setup_region_routes(config)
150+
println("Completed region routes setup.")
144151

152+
println("Setting up tile routes...")
145153
setup_tile_routes()
154+
println("Completed tile routes setup.")
146155

156+
println("Initialisation complete, starting server on port 8000.")
157+
println("Starting with $(Threads.nthreads()) threads...")
147158
if Threads.nthreads() > 1
148-
serveparallel(port=8000)
159+
serveparallel(host="0.0.0.0", port=8000)
149160
else
150-
serve(port=8000)
161+
serve(host="0.0.0.0", port=8000)
151162
end
152163
end
153164

0 commit comments

Comments
 (0)