diff --git a/.env.template b/.env.template index d4010f1..8238a78 100644 --- a/.env.template +++ b/.env.template @@ -6,6 +6,9 @@ LETSENCRYPT_ACME_EMAIL= # Set this to the exact version you want to use. Example: 42.3.1 DHIS2_VERSION=42 +# CPU architecture for healthcheck binary (amd64 or arm64) +ARCH=amd64 + DHIS2_ADMIN_USERNAME=admin DHIS2_ADMIN_PASSWORD= @@ -13,7 +16,9 @@ DHIS2_ADMIN_PASSWORD= DHIS2_MONITOR_USERNAME=monitor DHIS2_MONITOR_PASSWORD= -POSTGRES_VERSION=16-master +# PostgreSQL image. For ARM compatibility use baosystems image: +# POSTGRES_IMAGE=ghcr.io/baosystems/postgis:16 +POSTGRES_IMAGE=postgis/postgis:16-master POSTGRES_PASSWORD= POSTGRES_DB=dhis POSTGRES_DB_USERNAME=dhis diff --git a/docker-compose.yml b/docker-compose.yml index 13e9674..8f03c64 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,23 +1,52 @@ x-database-image: &database-image - image: postgis/postgis:${POSTGRES_VERSION:-16-master} + image: ${POSTGRES_IMAGE:-postgis/postgis:16-master} x-file-storage-image: &file-storage-image image: rclone/rclone:${RCLONE_VERSION:-1.68} services: + healthcheck-init: + image: curlimages/curl:${CURL_VERSION:-8.10.1} + volumes: + - healthcheck:/healthcheck + environment: + ARCH: ${ARCH:-amd64} + HEALTHCHECK_VERSION: ${HEALTHCHECK_VERSION:-1.0.0} + HEALTHCHECK_SHA256_AMD64: ${HEALTHCHECK_SHA256_AMD64:-d4542505894c0f5e1692ea282fecf75290a3893538a165d571e4c1a15724eb1b} + HEALTHCHECK_SHA256_ARM64: ${HEALTHCHECK_SHA256_ARM64:-df6382f871f02f48fac81893500aa8578ae21fb985819f6a19af21918c352f92} + command: + - sh + - -c + - | + if [ ! -f /healthcheck/healthcheck ]; then + if [ "$${ARCH}" = "arm64" ]; then + EXPECTED_SHA="$${HEALTHCHECK_SHA256_ARM64}" + else + EXPECTED_SHA="$${HEALTHCHECK_SHA256_AMD64}" + fi + curl -sL -o /healthcheck/healthcheck "https://github.com/netroms/simple_healthcheck/releases/download/v$${HEALTHCHECK_VERSION}/healthcheck-$${ARCH}" + echo "$${EXPECTED_SHA} /healthcheck/healthcheck" | sha256sum -c - + chmod +x /healthcheck/healthcheck + fi + user: root + security_opt: + - no-new-privileges:true + app: image: dhis2/core:${DHIS2_VERSION:-42} depends_on: database: condition: service_healthy + healthcheck-init: + condition: service_completed_successfully volumes: - dhis2:/opt/dhis2/ - #- ./config/dhis2/log4j2.xml:/opt/dhis2/log4j2.xml:ro - ./config/dhis2/dhis.conf:/opt/dhis2/dhis.conf:ro + - healthcheck:/opt/dhis2/healthcheck-bin:ro - type: tmpfs target: /tmp tmpfs: - size: 1000000 + size: 300000000 - type: tmpfs target: /usr/local/tomcat/temp tmpfs: @@ -43,7 +72,7 @@ services: - database restart: unless-stopped healthcheck: - test: [ "CMD", "curl", "-f", "http://127.0.0.1:8080/dhis-web-login/" ] + test: [ "CMD", "/opt/dhis2/healthcheck-bin/healthcheck", "http://localhost:8080/api/ping" ] interval: 10s timeout: 3s retries: 3 @@ -309,3 +338,4 @@ volumes: dhis2: {} postgres: {} cert: {} + healthcheck: {} diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 5682627..01295cb 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -33,7 +33,7 @@ | Variable | Description | Default Value | |----------|-------------|---------------| -| `POSTGRES_VERSION` | | `16-master` | +| `POSTGRES_IMAGE` | | `postgis/postgis:16-master` | ### Service: database @@ -52,7 +52,7 @@ | Variable | Description | Default Value | |----------|-------------|---------------| -| `POSTGRES_VERSION` | | `16-master` | +| `POSTGRES_IMAGE` | | `postgis/postgis:16-master` | ### Service: traefik @@ -82,7 +82,7 @@ | Variable | Description | Default Value | |----------|-------------|---------------| -| `POSTGRES_VERSION` | | `16-master` | +| `POSTGRES_IMAGE` | | `postgis/postgis:16-master` | ### Service: backup-file-storage @@ -116,7 +116,7 @@ | Variable | Description | Default Value | |----------|-------------|---------------| -| `POSTGRES_VERSION` | | `16-master` | +| `POSTGRES_IMAGE` | | `postgis/postgis:16-master` | ### Service: restore-file-storage diff --git a/scripts/generate-env.sh b/scripts/generate-env.sh index 9f5e92b..ebed396 100755 --- a/scripts/generate-env.sh +++ b/scripts/generate-env.sh @@ -44,6 +44,17 @@ generate_password() { echo "$password" | fold -w1 | shuf | tr -d '\n' } +# Detect CPU architecture for healthcheck binary +detect_arch() { + local arch + arch=$(uname -m) + case "$arch" in + x86_64) echo "amd64" ;; + aarch64|arm64) echo "arm64" ;; + *) echo "amd64" ;; # fallback to amd64 + esac +} + # Validate required inputs for ungeneratable values : "${GEN_APP_HOSTNAME:?Environment variable GEN_APP_HOSTNAME must be set}" : "${GEN_LETSENCRYPT_ACME_EMAIL:?Environment variable GEN_LETSENCRYPT_ACME_EMAIL must be set}" @@ -54,6 +65,7 @@ POSTGRES_DB_PASSWORD=$(generate_password) POSTGRES_METRICS_PASSWORD=$(generate_password) GRAFANA_ADMIN_PASSWORD=$(generate_password) DHIS2_MONITOR_PASSWORD=$(generate_password) +ARCH=$(detect_arch) # Detect GNU vs BSD sed if sed --version >/dev/null 2>&1; then @@ -81,6 +93,7 @@ update_env_var "GRAFANA_ADMIN_PASSWORD" "$GRAFANA_ADMIN_PASSWORD" update_env_var "DHIS2_MONITOR_PASSWORD" "$DHIS2_MONITOR_PASSWORD" update_env_var "APP_HOSTNAME" "$GEN_APP_HOSTNAME" update_env_var "LETSENCRYPT_ACME_EMAIL" "$GEN_LETSENCRYPT_ACME_EMAIL" +update_env_var "ARCH" "$ARCH" chmod u+rw,go-rwx "$OUTPUT_FILE" diff --git a/scripts/update-admin-password.sh b/scripts/update-admin-password.sh index 06af8af..0d8dec4 100755 --- a/scripts/update-admin-password.sh +++ b/scripts/update-admin-password.sh @@ -3,6 +3,9 @@ set -o errexit set -o nounset +# Install curl (not included in baosystems/postgis image) +apt update && apt install -y --no-install-recommends curl + URL="https://github.com/shoenig/bcrypt-tool/releases/download/v1.1.7/bcrypt-tool_1.1.7_linux_amd64.tar.gz" EXPECTED_SHA="7eb3f0cc159a54e4a1efb0fc56c11a5eb80efa5c88b482d0034eceea4cdf936e" diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..9006576 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,61 @@ +# Healthcheck Binary + +Lightweight static binary for Docker container health checks. Works in minimal/distroless images that lack curl, wget, or bash. + +## Source + +The binary is from: + +## Download at Runtime + +The healthcheck binary is downloaded at container startup by the `healthcheck-init` service. This avoids storing large binary files in the repository. + +### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `ARCH` | `amd64` | CPU architecture (`amd64` or `arm64`) | +| `HEALTHCHECK_VERSION` | `1.0.0` | Version to download | +| `HEALTHCHECK_SHA256_AMD64` | (see docker-compose.yml) | SHA256 for amd64 binary | +| `HEALTHCHECK_SHA256_ARM64` | (see docker-compose.yml) | SHA256 for arm64 binary | + +The `ARCH` variable is auto-detected by `scripts/generate-env.sh`. + +## Usage + +```bash +healthcheck [options] +``` + +### Options + +| Flag | Default | Description | +|------|---------|-------------| +| `-timeout` | 5s | Request timeout | +| `-status` | 200 | Expected HTTP status code (0 = accept any 2xx) | +| `-json-field` | | JSON field to check (dot notation) | +| `-json-value` | | Expected value for the JSON field | + +### Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | Health check passed | +| 1 | Health check failed | + +## Docker Compose Integration + +The binary is downloaded to a volume and mounted into the DHIS2 container: + +```yaml +healthcheck: + test: [ "CMD", "/opt/dhis2/healthcheck-bin/healthcheck", "http://localhost:8080/api/ping" ] +``` + +## Updating + +To update to a new version: + +1. Find the latest release at +2. Get the SHA256 checksums for both architectures +3. Update `HEALTHCHECK_VERSION` and the SHA256 variables in your `.env` file or docker-compose.yml