From 52631751821682af0b49182a7bff788751e6aae5 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 9 Jan 2026 13:32:11 -0600 Subject: [PATCH] build docker image in ci --- .gitignore | 39 ++-- .gitlab-ci.yml | 138 +++++++++++++ DEPLOYMENT.md | 387 +++++++++++++++++++++++++++++++++++++ README.md | 10 +- docker-compose.example.yml | 41 ++++ 5 files changed, 594 insertions(+), 21 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 DEPLOYMENT.md create mode 100644 docker-compose.example.yml diff --git a/.gitignore b/.gitignore index e467c7c..4a93cb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,24 @@ -# Created by https://gitignore.org -# Rust.gitignore - -# Generated by Cargo -# will have compiled files and executables -debug -target - -# These are backup files generated by rustfmt +# Rust +/target/ +Cargo.lock **/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information *.pdb -# Generated by cargo mutants -# Contains mutation testing data -**/mutants.out*/ +# Database files +*.db +*.db-shm +*.db-wal -# RustRover -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ +# Docker +data/ + +# OS +.DS_Store +Thumbs.db diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..4b45bb8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,138 @@ +# GitLab CI/CD Configuration for Towerops Agent +# Builds and publishes Docker image to GitLab Container Registry + +stages: + - test + - build + - release + +variables: + # GitLab Container Registry + REGISTRY: registry.gitlab.com + IMAGE_NAME: graham/towerops-agent + # Docker BuildKit for better caching + DOCKER_BUILDKIT: 1 + DOCKER_TLS_CERTDIR: "/certs" + +# Test stage - compile and check code +test: + stage: test + image: rust:1.75-alpine + before_script: + - apk add --no-cache musl-dev + script: + - cargo check --release + - cargo fmt -- --check + - cargo clippy -- -D warnings + only: + - branches + - merge_requests + cache: + key: ${CI_COMMIT_REF_SLUG}-cargo + paths: + - target/ + - .cargo/ + +# Build Docker image for branches (test build) +build:test: + stage: build + image: docker:24-dind + services: + - docker:24-dind + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - | + docker build \ + --tag $REGISTRY/$IMAGE_NAME:$CI_COMMIT_REF_SLUG \ + --tag $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA \ + . + - docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_REF_SLUG + - docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA + only: + - branches + except: + - main + - tags + +# Build and push for main branch +build:main: + stage: build + image: docker:24-dind + services: + - docker:24-dind + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - | + docker build \ + --tag $REGISTRY/$IMAGE_NAME:latest \ + --tag $REGISTRY/$IMAGE_NAME:main-$CI_COMMIT_SHORT_SHA \ + . + - docker push $REGISTRY/$IMAGE_NAME:latest + - docker push $REGISTRY/$IMAGE_NAME:main-$CI_COMMIT_SHORT_SHA + - echo "IMAGE_TAG=latest" >> build.env + artifacts: + reports: + dotenv: build.env + only: + - main + +# Release build for tags (versioned releases) +release: + stage: release + image: docker:24-dind + services: + - docker:24-dind + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - | + # Extract version from tag (assumes tags like v0.1.0) + VERSION=${CI_COMMIT_TAG#v} + + docker build \ + --tag $REGISTRY/$IMAGE_NAME:$VERSION \ + --tag $REGISTRY/$IMAGE_NAME:$CI_COMMIT_TAG \ + --tag $REGISTRY/$IMAGE_NAME:latest \ + . + + docker push $REGISTRY/$IMAGE_NAME:$VERSION + docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_TAG + docker push $REGISTRY/$IMAGE_NAME:latest + + echo "Released version: $VERSION" + echo "IMAGE_TAG=$VERSION" >> release.env + artifacts: + reports: + dotenv: release.env + only: + - tags + except: + - branches + +# Optional: Multi-architecture build (amd64 + arm64) +# Uncomment if you need ARM support (e.g., for Raspberry Pi) +# +# build:multiarch: +# stage: build +# image: docker:24-dind +# services: +# - docker:24-dind +# before_script: +# - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY +# # Enable buildx for multi-arch +# - docker buildx create --use --name multiarch-builder +# - docker buildx inspect --bootstrap +# script: +# - | +# VERSION=${CI_COMMIT_TAG#v} +# +# docker buildx build \ +# --platform linux/amd64,linux/arm64 \ +# --tag $REGISTRY/$IMAGE_NAME:$VERSION \ +# --tag $REGISTRY/$IMAGE_NAME:latest \ +# --push \ +# . +# only: +# - tags diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..c7437e1 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,387 @@ +# Towerops Agent - Deployment Guide + +## GitLab CI/CD Pipeline + +The agent uses GitLab CI/CD to automatically build and publish Docker images to the GitLab Container Registry. + +### Pipeline Stages + +1. **Test** - Compile, format check, and lint +2. **Build** - Build Docker image +3. **Release** - Tag and publish versioned releases + +### Image Tags + +| Git Action | Image Tags | Example | +|------------|------------|---------| +| Push to branch | ``, `` | `feat-snmp`, `abc123` | +| Push to main | `latest`, `main-` | `latest`, `main-abc123` | +| Create tag | ``, ``, `latest` | `0.1.0`, `v0.1.0`, `latest` | + +### Registry Location + +All images are pushed to: +``` +registry.gitlab.com/graham/towerops-agent +``` + +## Creating a Release + +### 1. Update Version + +**In `Cargo.toml`**: +```toml +[package] +name = "towerops-agent" +version = "0.2.0" # ← Update this +edition = "2021" +``` + +### 2. Commit and Tag + +```bash +git add Cargo.toml +git commit -m "Release v0.2.0" +git tag v0.2.0 +git push origin main --tags +``` + +### 3. CI Pipeline Runs + +GitLab CI will automatically: +- Run tests (cargo check, fmt, clippy) +- Build Docker image +- Tag as: `0.2.0`, `v0.2.0`, `latest` +- Push to registry + +### 4. Verify Release + +Check the Container Registry: +``` +https://gitlab.com/graham/towerops-agent/container_registry +``` + +You should see: +- `latest` (updated) +- `0.2.0` (new) +- `v0.2.0` (new) + +## Using the Images + +### Pull Latest + +```bash +docker pull registry.gitlab.com/graham/towerops-agent:latest +``` + +### Pull Specific Version + +```bash +docker pull registry.gitlab.com/graham/towerops-agent:0.1.0 +``` + +### Docker Compose + +Update `docker-compose.yml`: +```yaml +services: + towerops-agent: + image: registry.gitlab.com/graham/towerops-agent:latest + # Or pin to specific version: + # image: registry.gitlab.com/graham/towerops-agent:0.1.0 + environment: + - TOWEROPS_API_URL=https://app.towerops.com + - TOWEROPS_AGENT_TOKEN=${AGENT_TOKEN} + volumes: + - ./data:/data +``` + +## Customer Deployment + +### Provide to Customers + +**Option 1: Docker Compose (Recommended)** + +Create `docker-compose.yml`: +```yaml +version: '3.8' +services: + towerops-agent: + image: registry.gitlab.com/graham/towerops-agent:latest + container_name: towerops-agent + restart: unless-stopped + environment: + - TOWEROPS_API_URL=https://app.towerops.com + - TOWEROPS_AGENT_TOKEN= + volumes: + - ./data:/data +``` + +Instructions for customer: +```bash +# 1. Get agent token from Towerops UI +# 2. Replace in docker-compose.yml +# 3. Start agent +docker-compose up -d + +# 4. Check logs +docker-compose logs -f + +# 5. Check status +docker-compose ps +``` + +**Option 2: Docker Run** + +```bash +docker run -d \ + --name towerops-agent \ + --restart unless-stopped \ + -e TOWEROPS_API_URL=https://app.towerops.com \ + -e TOWEROPS_AGENT_TOKEN= \ + -v $(pwd)/data:/data \ + registry.gitlab.com/graham/towerops-agent:latest +``` + +## CI/CD Configuration + +### Required GitLab Variables + +No additional CI/CD variables needed. GitLab provides these automatically: +- `CI_REGISTRY` - GitLab Container Registry URL +- `CI_REGISTRY_USER` - Username for registry login +- `CI_REGISTRY_PASSWORD` - Password for registry login + +These are automatically available in all GitLab CI/CD pipelines. + +### Pipeline Triggers + +**Automatic**: +- Push to any branch → test + build with branch tag +- Push to main → test + build + tag as `latest` +- Create tag (e.g., `v0.1.0`) → test + build + release + +**Manual**: No manual triggers configured (all automatic) + +### Viewing Pipeline Status + +1. Go to: https://gitlab.com/graham/towerops-agent/-/pipelines +2. Click on a pipeline to see detailed logs +3. Check job logs if build fails + +## Local Development + +### Build Locally + +```bash +# Development build +cargo build + +# Release build (optimized) +cargo build --release + +# Build Docker image locally +docker build -t towerops-agent:local . + +# Test local image +docker run --rm \ + -e TOWEROPS_API_URL=http://host.docker.internal:4000 \ + -e TOWEROPS_AGENT_TOKEN=test-token \ + -v $(pwd)/test-data:/data \ + towerops-agent:local +``` + +### Test Before Pushing + +```bash +# Check compilation +cargo check --release + +# Format check +cargo fmt -- --check + +# Lint check +cargo clippy -- -D warnings + +# All together (what CI runs) +cargo check --release && \ +cargo fmt -- --check && \ +cargo clippy -- -D warnings +``` + +## Rollback Procedure + +### If Latest Version Has Issues + +**Option 1: Revert Tag** +```bash +# Find previous working version +git tag -l + +# Delete bad tag locally and remotely +git tag -d v0.2.0 +git push origin :refs/tags/v0.2.0 + +# Customers can use previous version +docker pull registry.gitlab.com/graham/towerops-agent:0.1.0 +``` + +**Option 2: Quick Fix** +```bash +# Fix the issue +git commit -m "Hotfix: fix critical bug" + +# Create patch version +git tag v0.2.1 +git push origin main --tags + +# New pipeline builds v0.2.1 and updates latest +``` + +**Option 3: Pin Customers to Known Good Version** + +Update customer docker-compose.yml: +```yaml +image: registry.gitlab.com/graham/towerops-agent:0.1.0 # Pin to working version +``` + +## Multi-Architecture Support (Optional) + +To support ARM devices (Raspberry Pi, etc.), uncomment the multi-arch section in `.gitlab-ci.yml`. + +This will build for: +- `linux/amd64` (x86_64 servers) +- `linux/arm64` (ARM servers, Raspberry Pi 4+) + +**Note**: Multi-arch builds take longer (~2x build time). + +## Monitoring Deployments + +### Check Image Size + +```bash +docker images registry.gitlab.com/graham/towerops-agent +``` + +Expected size: 10-20 MB + +### Check Registry Usage + +GitLab provides 10 GB of free registry storage. Monitor usage at: +``` +https://gitlab.com/graham/towerops-agent/-/packages +``` + +### Cleanup Old Images + +GitLab has automatic cleanup policies. Configure at: +``` +Settings → Packages & Registries → Container Registry → Cleanup policies +``` + +Recommended settings: +- Keep most recent: 10 tags +- Keep tags matching: `^v\d+\.\d+\.\d+$` (versions) +- Remove tags older than: 90 days + +## Troubleshooting + +### Pipeline Fails at Test Stage + +**Symptom**: `cargo check` or `cargo clippy` fails + +**Fix**: +```bash +# Run locally to see errors +cargo check --release +cargo clippy -- -D warnings + +# Fix issues and push +git add . +git commit -m "Fix clippy warnings" +git push +``` + +### Pipeline Fails at Build Stage + +**Symptom**: Docker build fails + +**Fix**: +```bash +# Test Docker build locally +docker build -t test . + +# Check Dockerfile syntax +# Check .dockerignore isn't excluding needed files +``` + +### Image Too Large + +**Symptom**: Image is >50 MB + +**Fix**: +- Check release profile in Cargo.toml (should have `strip = true`) +- Verify multi-stage build is working +- Check for large files in context (review .dockerignore) + +### Can't Pull Image + +**Symptom**: `docker pull` fails with authentication error + +**Fix**: +```bash +# Login to GitLab registry +docker login registry.gitlab.com +# Username: your GitLab username +# Password: personal access token with read_registry scope + +# Or use deploy token (for customers) +# Create at: Settings → Repository → Deploy tokens +``` + +## Security + +### Container Scanning (Optional) + +Add to `.gitlab-ci.yml` for security scanning: + +```yaml +include: + - template: Security/Container-Scanning.gitlab-ci.yml + +container_scanning: + stage: test + variables: + CS_IMAGE: $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA +``` + +### Private Registry Access + +For customers needing private access: + +1. Create deploy token: + - Go to: Settings → Repository → Deploy tokens + - Name: `customer-deploy-token` + - Scopes: `read_registry` + - Copy username and token + +2. Provide to customer: +```bash +docker login registry.gitlab.com +# Username: +# Password: +``` + +## Support + +For issues with deployment: +1. Check pipeline logs in GitLab +2. Review CLAUDE.md for architecture details +3. Test locally with `docker build` +4. Check GitLab registry status page + +--- + +**Last Updated**: January 9, 2026 +**Registry**: registry.gitlab.com/graham/towerops-agent +**Current Version**: 0.1.0 (pre-release) diff --git a/README.md b/README.md index a6b45a9..10048ed 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,14 @@ The Towerops agent enables customers to deploy SNMP polling infrastructure on th ## Quick Start +### Using Pre-built Image + +Pull the latest image from GitLab Container Registry: + +```bash +docker pull registry.gitlab.com/graham/towerops-agent:latest +``` + ### Using Docker Compose 1. Create a `docker-compose.yml` file: @@ -25,7 +33,7 @@ The Towerops agent enables customers to deploy SNMP polling infrastructure on th version: '3.8' services: towerops-agent: - image: towerops/agent:latest + image: registry.gitlab.com/graham/towerops-agent:latest restart: unless-stopped environment: - TOWEROPS_API_URL=https://app.towerops.com diff --git a/docker-compose.example.yml b/docker-compose.example.yml new file mode 100644 index 0000000..f6c62f3 --- /dev/null +++ b/docker-compose.example.yml @@ -0,0 +1,41 @@ +version: '3.8' + +services: + towerops-agent: + image: registry.gitlab.com/graham/towerops-agent:latest + container_name: towerops-agent + restart: unless-stopped + + environment: + # Required: Your Towerops API URL + - TOWEROPS_API_URL=https://app.towerops.com + + # Required: Agent authentication token (from Towerops web UI) + # Get this from: Organization > Agents > Create New Agent + - TOWEROPS_AGENT_TOKEN=your-agent-token-here + + # Optional: How often to refresh configuration (seconds) + # Default: 300 (5 minutes) + # - CONFIG_REFRESH_SECONDS=300 + + # Optional: Database path for metrics buffering + # Default: /data/towerops-agent.db + # - DATABASE_PATH=/data/towerops-agent.db + + # Optional: Log level (error, warn, info, debug, trace) + # Default: info + # - RUST_LOG=info + + volumes: + # Persistent storage for metrics buffering + - ./data:/data + + # Optional: Resource limits + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.1' + memory: 128M