feat: add podman support and Docker Hub push to base image builder

Enhanced base image build system with:

Container Engine Support:
- Auto-detect podman or docker (prefers podman if both installed)
- All scripts work with either engine seamlessly
- No configuration needed

Docker Hub Integration:
- Push to both GitLab and Docker Hub registries
- GitLab: registry.gitlab.com/towerops/towerops/elixir-runtime
- Docker Hub: docker.io/gmcintire/elixir-runtime
- Both versioned and :latest tags pushed to each

Build Script:
- Detect and use $CONTAINER_CMD for all operations
- Show which container engine is being used in output
- Tag and push to both registries automatically

Makefile:
- Add CONTAINER_CMD variable with auto-detection
- Add DOCKERHUB_REGISTRY configuration
- Update all targets to use detected container engine
- Add login-dockerhub target for Docker Hub authentication
- Update push target to push to both registries

Documentation:
- Document podman/docker auto-detection
- Update login instructions for both registries
- Update troubleshooting with examples for both engines

This makes the build system more flexible and accessible to users
who prefer podman over docker, while also publishing to the public
Docker Hub registry for easier access.
This commit is contained in:
Graham McIntire 2026-01-25 09:18:03 -06:00
parent a9405f6818
commit d92443d91b
No known key found for this signature in database
4 changed files with 96 additions and 33 deletions

View file

@ -1,6 +1,10 @@
.PHONY: build push test update clean help
# Detect container engine (podman or docker)
CONTAINER_CMD := $(shell command -v podman 2>/dev/null || command -v docker 2>/dev/null)
REGISTRY ?= registry.gitlab.com/towerops/towerops
DOCKERHUB_REGISTRY ?= docker.io/gmcintire
IMAGE_NAME ?= elixir-runtime
ERLANG_VERSION ?= 28.3
ELIXIR_VERSION ?= 1.19.5
@ -18,34 +22,46 @@ update: build ## Alias for build (rebuilds with latest packages)
test: ## Test the built image
@echo "Testing Erlang..."
@docker run --rm $(REGISTRY)/$(IMAGE_NAME):latest erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell
@$(CONTAINER_CMD) run --rm $(REGISTRY)/$(IMAGE_NAME):latest erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell
@echo "Testing Elixir..."
@docker run --rm $(REGISTRY)/$(IMAGE_NAME):latest elixir --version
@$(CONTAINER_CMD) run --rm $(REGISTRY)/$(IMAGE_NAME):latest elixir --version
@echo "Testing locale..."
@docker run --rm $(REGISTRY)/$(IMAGE_NAME):latest locale
@$(CONTAINER_CMD) run --rm $(REGISTRY)/$(IMAGE_NAME):latest locale
@echo "✅ All tests passed!"
push: ## Push image to registry
@docker push $(REGISTRY)/$(IMAGE_NAME):$(TAG)
@docker push $(REGISTRY)/$(IMAGE_NAME):latest
push: ## Push image to GitLab and Docker Hub
@echo "Pushing to GitLab registry..."
@$(CONTAINER_CMD) push $(REGISTRY)/$(IMAGE_NAME):$(TAG)
@$(CONTAINER_CMD) push $(REGISTRY)/$(IMAGE_NAME):latest
@echo "Tagging for Docker Hub..."
@$(CONTAINER_CMD) tag $(REGISTRY)/$(IMAGE_NAME):$(TAG) $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):$(TAG)
@$(CONTAINER_CMD) tag $(REGISTRY)/$(IMAGE_NAME):latest $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):latest
@echo "Pushing to Docker Hub..."
@$(CONTAINER_CMD) push $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):$(TAG)
@$(CONTAINER_CMD) push $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):latest
pull: ## Pull latest image from registry
@docker pull $(REGISTRY)/$(IMAGE_NAME):latest
@$(CONTAINER_CMD) pull $(REGISTRY)/$(IMAGE_NAME):latest
inspect: ## Show image details (size, layers, labels)
@echo "Image size:"
@docker images $(REGISTRY)/$(IMAGE_NAME):latest --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
@$(CONTAINER_CMD) images $(REGISTRY)/$(IMAGE_NAME):latest --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
@echo ""
@echo "Image labels:"
@docker inspect $(REGISTRY)/$(IMAGE_NAME):latest --format '{{range $$k, $$v := .Config.Labels}}{{$$k}}={{$$v}}{{println}}{{end}}'
@$(CONTAINER_CMD) inspect $(REGISTRY)/$(IMAGE_NAME):latest --format '{{range $$k, $$v := .Config.Labels}}{{$$k}}={{$$v}}{{println}}{{end}}'
clean: ## Remove local images
@docker rmi $(REGISTRY)/$(IMAGE_NAME):$(TAG) || true
@docker rmi $(REGISTRY)/$(IMAGE_NAME):latest || true
@$(CONTAINER_CMD) rmi $(REGISTRY)/$(IMAGE_NAME):$(TAG) || true
@$(CONTAINER_CMD) rmi $(REGISTRY)/$(IMAGE_NAME):latest || true
@$(CONTAINER_CMD) rmi $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):$(TAG) || true
@$(CONTAINER_CMD) rmi $(DOCKERHUB_REGISTRY)/$(IMAGE_NAME):latest || true
@echo "✅ Cleaned local images"
login: ## Login to GitLab container registry
@docker login registry.gitlab.com
@$(CONTAINER_CMD) login registry.gitlab.com
login-dockerhub: ## Login to Docker Hub
@$(CONTAINER_CMD) login docker.io
# Quick commands
b: build ## Shortcut for build

View file

@ -2,11 +2,14 @@
## One-Time Setup
Works with both **Podman** and **Docker** - the scripts auto-detect which one you have installed.
```bash
cd k8s/base-image
# Login to GitLab registry
docker login registry.gitlab.com
# Login to registries
make login # GitLab registry
make login-dockerhub # Docker Hub
# Build the base image
make build
@ -14,7 +17,7 @@ make build
# Test it works
make test
# Push to registry
# Push to both registries
make push
```
@ -81,14 +84,17 @@ make clean # Remove local images
**Build fails:**
```bash
# Try with verbose output
# Try with verbose output (use podman or docker)
podman build --progress=plain --no-cache -f Dockerfile .
# or
docker build --progress=plain --no-cache -f Dockerfile .
```
**Can't push to registry:**
```bash
# Re-login
docker login registry.gitlab.com
make login
make login-dockerhub
```
**Application crashes with missing library:**

View file

@ -2,6 +2,10 @@
This directory contains scripts to build a minimal Debian 13 (Trixie) base image with Erlang/OTP and Elixir runtime pre-installed.
## Container Engine Support
The build scripts automatically detect and use either **Podman** or **Docker**, preferring Podman if both are installed. No configuration needed - just install your preferred container engine and the scripts will use it.
## Why a Custom Base Image?
Building a custom base image provides several benefits:
@ -66,13 +70,22 @@ Verifies:
- Elixir is installed and working
- Locale is configured correctly
### Pushing to Registry
### Pushing to Registries
```bash
# Login to registries (one-time setup)
make login # GitLab Container Registry
make login-dockerhub # Docker Hub
# Push to both GitLab and Docker Hub
make push
```
Pushes both versioned and `:latest` tags to GitLab Container Registry.
Automatically pushes to both:
- **GitLab Container Registry**: `registry.gitlab.com/towerops/towerops/elixir-runtime`
- **Docker Hub**: `docker.io/gmcintire/elixir-runtime`
Both versioned and `:latest` tags are pushed to each registry.
## Image Tags

View file

@ -4,6 +4,16 @@ set -euo pipefail
# Build script for minimal Debian base image with Erlang/OTP and Elixir
# This script can be re-run to rebuild with latest packages
# Detect container engine (podman or docker)
if command -v podman &> /dev/null; then
CONTAINER_CMD="podman"
elif command -v docker &> /dev/null; then
CONTAINER_CMD="docker"
else
echo "Error: Neither podman nor docker found. Please install one of them."
exit 1
fi
# Configuration
REGISTRY="${REGISTRY:-registry.gitlab.com/towerops/towerops}"
IMAGE_NAME="${IMAGE_NAME:-elixir-runtime}"
@ -23,6 +33,7 @@ TAG_DATED="${TAG_FULL}-${BUILD_DATE//[:-]*/}" # YYYY-MM-DD format
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Building Minimal Debian Base Image"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Container: ${CONTAINER_CMD}"
echo "Registry: ${REGISTRY}"
echo "Image: ${IMAGE_NAME}"
echo "Erlang: ${ERLANG_VERSION}"
@ -35,7 +46,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
# Build the image with no cache to ensure latest packages
echo ""
echo "🔨 Building image (no cache, pulling latest packages)..."
docker build \
$CONTAINER_CMD build \
--no-cache \
--pull \
--build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \
@ -62,33 +73,50 @@ echo ""
# Show image size
echo "📦 Image size:"
docker images "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"
$CONTAINER_CMD images "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"
echo ""
# Verify image works
echo "🔍 Verifying image..."
docker run --rm "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" erl -eval 'io:format("Erlang/OTP: ~s~n", [erlang:system_info(otp_release)]), halt().' -noshell
docker run --rm "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" elixir --version
$CONTAINER_CMD run --rm "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" erl -eval 'io:format("Erlang/OTP: ~s~n", [erlang:system_info(otp_release)]), halt().' -noshell
$CONTAINER_CMD run --rm "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" elixir --version
echo ""
# Ask if user wants to push
read -p "Push to registry? (y/N) " -n 1 -r
read -p "Push to registries? (y/N) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🚀 Pushing to registry..."
docker push "${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
docker push "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
docker push "${REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo "🚀 Pushing to GitLab registry..."
$CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
$CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
$CONTAINER_CMD push "${REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo ""
echo "✅ Pushed successfully!"
echo "🚀 Tagging for Docker Hub..."
DOCKERHUB_REGISTRY="docker.io/gmcintire"
$CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
$CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
$CONTAINER_CMD tag "${REGISTRY}/${IMAGE_NAME}:${TAG_DATED}" "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo "🚀 Pushing to Docker Hub..."
$CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
$CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
$CONTAINER_CMD push "${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo ""
echo "✅ Pushed successfully to both registries!"
echo ""
echo "Update your main Dockerfile to use:"
echo " ARG RUNNER_IMAGE=\"${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}\""
echo " GitLab: ARG RUNNER_IMAGE=\"${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}\""
echo " Docker Hub: ARG RUNNER_IMAGE=\"${DOCKERHUB_REGISTRY}/${IMAGE_NAME}:${TAG_FULL}\""
else
echo "⏭️ Skipping push. To push later, run:"
echo " docker push ${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
echo " docker push ${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
echo " docker push ${REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:${TAG_FULL}"
echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:${TAG_LATEST}"
echo " $CONTAINER_CMD push ${REGISTRY}/${IMAGE_NAME}:${TAG_DATED}"
echo " $CONTAINER_CMD push docker.io/gmcintire/${IMAGE_NAME}:${TAG_FULL}"
echo " $CONTAINER_CMD push docker.io/gmcintire/${IMAGE_NAME}:${TAG_LATEST}"
echo " $CONTAINER_CMD push docker.io/gmcintire/${IMAGE_NAME}:${TAG_DATED}"
fi
echo ""