Skip to content

Commit

Permalink
Merge pull request #9 from open-AIMS/dockerisation
Browse files Browse the repository at this point in the history
feat: add Dockerfile and GitHub workflow to publish to ghcr
  • Loading branch information
ConnectedSystems authored Sep 22, 2024
2 parents 1d1ac91 + 774b456 commit 0e26ddb
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 4 deletions.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
input/
output/
.git*
assets/
docs/
.config.toml
Dockerfile
.gitignore
98 changes: 98 additions & 0 deletions .github/workflows/PublishDockerImage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This workflow builds and publishes the ReefGuideAPI base Docker image to GitHub Container Registry (ghcr.io)
# It is triggered when a push is made to the main branch.

# Additional notes:
# - The workflow uses the github.repository context to name the image, ensuring it's tied to your repository
# - The GITHUB_TOKEN is automatically provided by GitHub Actions, no need to set it up manually
# - The Docker metadata action automatically generates appropriate tags based on the release version
# - The Julia version can be easily updated by changing the JULIA_VERSION environment variable at the top of the workflow

name: Build and Publish ReefGuideAPI.jl Docker Image

on:
push:
branches:
- main
release:
types: [published]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/reefguide-src
JULIA_VERSION: 1.10.5
# Set to true to use a fixed ReefGuideAPI version for debugging, false to use the release version
USE_FIXED_REEFGUIDEAPI_VERSION: true
# TODO this doesn't make sense until the releasing is sorted out
# The fixed ReefGuideAPI version to use when USE_FIXED_REEFGUIDEAPI_VERSION is true
FIXED_REEFGUIDEAPI_VERSION: "0.1.0"

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
# Step 1: Checkout the repository code
- name: Checkout repository
uses: actions/checkout@v3

# Step 2: Extract the version number from the release tag
# This removes the 'v' prefix from the tag (e.g., 'v1.2.3' becomes '1.2.3')
- name: Extract version from tag
id: get_version
run: |
if ${{ env.USE_FIXED_REEFGUIDEAPI_VERSION == 'true' }}; then
echo "Using fixed ReefGuideAPI version: ${{ env.FIXED_REEFGUIDEAPI_VERSION }}"
echo "VERSION=${{ env.FIXED_REEFGUIDEAPI_VERSION }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" = "release" ]; then
RELEASE_VERSION=${GITHUB_REF#refs/tags/v}
echo "Using release version: $RELEASE_VERSION"
echo "VERSION=$RELEASE_VERSION" >> $GITHUB_OUTPUT
else
echo "No version specified and not a release event. Using default version."
echo "VERSION=${{ env.FIXED_REEFGUIDEAPI_VERSION }}" >> $GITHUB_OUTPUT
fi
# Step 3: Log in to the GitHub Container Registry
# This uses the provided GitHub token for authentication
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Step 4: Extract metadata for Docker
# This step generates tags and labels for the Docker image
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=semver,pattern={{major}}
type=ref,event=branch
type=sha,format=long
type=sha,format=short
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
# Step 5: Build and push the Docker image
# This step builds the reefguide-src image and pushes it to the registry
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
target: reefguide-src # Specifies which stage of the Dockerfile to build
push: true # Pushes the image to the registry
tags: ${{ steps.meta.outputs.tags }} # Uses the tags generated in the metadata step
labels: ${{ steps.meta.outputs.labels }} # Uses the labels generated in the metadata step
# Passes the Julia and ReefGuideAPI versions to the Dockerfile
# TODO bring back this build arg when releasing is ready
# REEFGUIDE_VERSION=${{ steps.get_version.outputs.VERSION }}
build-args: |
JULIA_VERSION=${{ env.JULIA_VERSION }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ docs/site/
sandbox/

/.config.toml

data
124 changes: 124 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# See https://hub.docker.com/_/julia for valid versions.
ARG JULIA_VERSION="1.10.4"

#------------------------------------------------------------------------------
# internal-base build target: julia with OS updates and an empty @reefguide
# Julia environment prepared for use. NOT intended for standalone use.
#------------------------------------------------------------------------------
FROM julia:${JULIA_VERSION}-bookworm AS internal-base

# Record the actual base image used from the FROM command as label in the compiled image
ARG BASE_IMAGE=$BASE_IMAGE
LABEL org.opencontainers.image.base.name=${BASE_IMAGE}

# Update all pre-installed OS packages (to get security updates)
# and add a few extra utilities
RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
apt-get update \
&& apt-get -y upgrade \
&& apt-get install --no-install-recommends -y \
git \
less \
nano \
&& apt-get clean \
&& apt-get autoremove --purge \
&& rm -rf /var/lib/apt/lists/*

# Tweak the JULIA_DEPOT_PATH setting so that our shared environments will end up
# in a user-agnostic location, not in ~/.julia => /root/.julia which is the default.
# See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_DEPOT_PATH
# This allows apps derived from this image to drop privileges and run as non-root
# user accounts, but still activate environments configured by this dockerfile.
ENV JULIA_DEPOT_PATH="/usr/local/share/julia"

# Prepare an empty @reefguide Julia environment for derived images to use - this is created in the shared depot path
RUN mkdir -p "${JULIA_DEPOT_PATH}" && \
chmod 0755 "${JULIA_DEPOT_PATH}" && \
julia -e 'using Pkg; Pkg.activate("reefguide", shared=true)'

# Ensure the @reefguide environment is in the load path for Julia, so that apps derived
# from this image can access any packages installed to there.
# (See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_LOAD_PATH)
ENV JULIA_LOAD_PATH="@:@reefguide:@v#.#:@stdlib"

# Run Julia commands by default as the container launches.
# Derived applications should override the command.
ENTRYPOINT ["julia", "--project=@reefguide"]


#------------------------------------------------------------------------------
# reefguide-base build target: ReefGuideAPI.jl preinstalled as a non-dev package.
# Use `--target=reefguide-base` on your `docker build command to build *just* this.
#------------------------------------------------------------------------------

# TODO enable and update this once the package is available from registry
# FROM internal-base AS reefguide-base
#
# # What version of ReefGuideAPI.jl from package registry to install in reefguide-base
# # TODO this doesn't make sense yet
# ARG REEFGUIDE_VERSION="0.1.0"
#
# # Which julia package registry version to install
# ENV REEFGUIDE_VERSION=$REEFGUIDE_VERSION
#
# # Try to coerce Julia to build across multiple targets
# ENV JULIA_CPU_TARGET=x86_64;haswell;skylake;skylake-avx512;tigerlake
#
# # Install ReefGuideAPI.jl into the @reefguide shared environment as an unregistered package.
# # - Allow the package source and version to be overridden at build-time.
# # - Include citation information for ReefGuideAPI.jl in the image labels.
# RUN mkdir -p "${JULIA_DEPOT_PATH}" && \
# chmod 0755 "${JULIA_DEPOT_PATH}" && \
# julia --project=@reefguide -e "using Pkg; Pkg.add(name=\"ReefGuideAPI\", version=\"${REEFGUIDE_VERSION}\"); Pkg.instantiate(); Pkg.precompile(); using ReefGuideAPI;"
# LABEL au.gov.aims.reefguideapi.source="https://github.com/open-AIMS/ReefGuideAPI.jl/releases/tag/v${REEFGUIDE_VERSION}" \
# au.gov.aims.reefguideapi.version="${REEFGUIDE_VERSION}" \
# au.gov.aims.reefguideapi.vendor="Australian Institute of Marine Science" \
# au.gov.aims.reefguideapi.licenses=MIT
#
# # Expect to include the prepped data at /data/reefguide and the config at
# # /data/.config.toml
# VOLUME ["/data/reefguide"]
#
# EXPOSE 8000
#
# # Run Julia commands by default as the container launches.
# # Derived applications should override the command.
# ENTRYPOINT ["julia", "--project=@reefguide", "-t", "1,auto", "-e", "using ReefGuideAPI; ReefGuideAPI.start_server(\"/data/reefguide/config.toml\")"]

#------------------------------------------------------------------------------
# reefguide-src build target: installs directly from source files in this repo.
#------------------------------------------------------------------------------
FROM internal-base AS reefguide-src

ENV REEFGUIDE_ENV_DIR="${JULIA_DEPOT_PATH}/environments/reefguide" \
REEFGUIDE_SRC_DIR="/usr/local/src/reefguide"

# Try to coerce Julia to build across multiple targets
ENV JULIA_CPU_TARGET=x86_64;haswell;skylake;skylake-avx512;tigerlake

# Install the versioned .toml file(s) into the shared reefguide environment and use
# those to set up the ReefGuideAPI source code as a development package in the
# shared @reefguide environment, pre-installing and precompiling dependencies.
WORKDIR "${REEFGUIDE_SRC_DIR}"
COPY ./Project.toml ./Project.toml
COPY ./Manifest.toml ./Manifest.toml
RUN julia --project=@reefguide -e 'using Pkg; Pkg.instantiate(verbose=true)'

# Install the ReefGuideAPI source code and configure it as a development
# package in the @reefguide shared environment.
# Should be v speedy if the .toml file is unchanged, because all the
# dependencies *should* already be installed.
COPY ./src src
RUN julia --project=@reefguide \
-e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.precompile(); using ReefGuideAPI;'

# Expect to include the prepped data at /data/reefguide and the config at
# /data/.config.toml
VOLUME ["/data/reefguide"]

EXPOSE 8000

# Run Julia commands by default as the container launches.
# Derived applications should override the command.
ENTRYPOINT ["julia", "--project=@reefguide", "-t", "1,auto", "-e", "using ReefGuideAPI; ReefGuideAPI.start_server(\"/data/reefguide/config.toml\")"]
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,71 @@ Higher values do seem to reduce write times but with diminishing returns (tested
Locally, write times with four threads configured range from 10 to 15 seconds.

## Reef edge alignment for site searching

`identify_potential_sites_edges()` can be used to identify potential sites that only align with
the nearest reef edge (or specified rotations away from this angle).
This method works by identifying the closest edge of reef polygon geometries that have been
converted into lines.

The following processing is required before use:

- Reef polygons should be simplified (`GO.simplify()`) and buffered to avoid matching possibly inaccurate reef edges.
- Simplified reef polygons should be provided as vertex-vertex lines with `polygon_to_lines()`.
- 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.
- Raster of search pixels should be masked by reef polygons or simplified reef polygons.
The column used for masking should be the same as the column specified as geometry_col in
`identify_potential_sites_edges` (default = `:geometry`).
The column used for masking should be the same as the column specified as geometry_col in
`identify_potential_sites_edges` (default = `:geometry`).

## Docker build and run

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.

### Mounting files and required data

As mentioned above, the `ReefGuideAPI.jl` package currently requires

- a `config.toml` file and
- a set of input data files

Please include these in a folder called `data` in your working directory.

When running the below commands, it is assumed you have `data` available locally with the required files.

**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.

### To build from src files using Docker

```
docker build . --target reefguide-src -t reefguide
```

### To build from src files using Docker Compose

```
docker compose up --build reefguide-src
```

### To run with mounted files (launch server) using Docker

```
docker run -p 8000:8000 -v ./data:/data/reefguide reefguide
```

### To run with mounted files (launch server) using Docker Compose

```
docker compose up reefguide-src
```

### To run with mounted files (interactive shell) using Docker

This will start a Julia shell where `ReefGuideAPI` is compiled and ready for use e.g.

```
using ReefGuideAPI
ReefGuideAPI.start_server("/data/reefguide/config.toml")
```

```
docker run --rm --interactive --entrypoint="julia" --tty -v ./data:/data/reefguide reefguide
```
28 changes: 28 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
#-------------------------------------------------
# Docker compose for ReefGuideAPI.jl docker image
#--------------------------------------------------
version: "3.8"

services:
# TODO enable and update this once the release version is ready
# reefguide-base:
# build:
# args:
# # TODO when versioning is setup improve this
# REEFGUIDE_VERSION: "v0.1.0"
# JULIA_VERSION: "1.10.4"
# context: .
# target: reefguide-base
# image: ReefGuideAPI.jl/reefguide-base:latest
# volumes:
# - ./data:/data/reefguide
reefguide-src:
build:
context: .
target: reefguide-src
image: ReefGuideAPI.jl/reefguide-base:latest
volumes:
- ./data:/data/reefguide
ports:
- 8000:8000
15 changes: 13 additions & 2 deletions src/ReefGuideAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,26 @@ function tile_size(config)::Tuple
end

function start_server(config_path)
println("Launching server...please wait")

println("Parsing configuration from $(config_path)...")
config = TOML.parsefile(config_path)
println("Successfully parsed configuration.")

println("Setting up region routes...")
setup_region_routes(config)
println("Completed region routes setup.")

println("Setting up tile routes...")
setup_tile_routes()
println("Completed tile routes setup.")

println("Initialisation complete, starting server on port 8000.")
println("Starting with $(Threads.nthreads()) threads...")
if Threads.nthreads() > 1
serveparallel(port=8000)
serveparallel(host="0.0.0.0", port=8000)
else
serve(port=8000)
serve(host="0.0.0.0", port=8000)
end
end

Expand Down

0 comments on commit 0e26ddb

Please sign in to comment.