infra/clusters/aprs/APRS_REPO_SETUP.md
2025-07-24 17:30:42 -05:00

7.8 KiB

APRS Repository Setup for Kubernetes Deployment

This document provides instructions for setting up your Elixir/Phoenix APRS repository to build Docker images and deploy to your K3s cluster on GitHub Actions push to main branch.

Prerequisites

  1. GitHub repository for your APRS application
  2. GitHub Container Registry (ghcr.io) access
  3. Tailscale installed on your K3s nodes
  4. Tailscale OAuth client for GitHub Actions

Steps to Configure Your APRS Repository

1. Create Dockerfile

Add this Dockerfile to the root of your APRS repository:

# Build stage
FROM elixir:1.15-alpine AS build

# Install build dependencies
RUN apk add --no-cache build-base git python3

# Set build env
ENV MIX_ENV=prod

# Install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# Set build directory
WORKDIR /app

# Copy mix files
COPY mix.exs mix.lock ./
COPY config config

# Install dependencies
RUN mix deps.get --only $MIX_ENV
RUN mix deps.compile

# Copy app files
COPY priv priv
COPY lib lib
COPY assets assets

# Compile assets
RUN mix assets.deploy

# Compile the release
RUN mix compile

# Build release
RUN mix release

# Runtime stage
FROM alpine:3.18 AS runtime

# Install runtime dependencies
RUN apk add --no-cache libstdc++ openssl ncurses-libs

WORKDIR /app

# Create non-root user
RUN addgroup -g 1000 -S aprs && \
    adduser -u 1000 -S aprs -G aprs

# Copy release from build stage
COPY --from=build --chown=aprs:aprs /app/_build/prod/rel/aprs ./

# Add health check endpoint if not already present
USER aprs

ENV HOME=/app
ENV MIX_ENV=prod
ENV PORT=4000
ENV PHX_SERVER=true

EXPOSE 4000

CMD ["bin/aprs", "start"]

2. Create GitHub Actions Workflow

Create .github/workflows/deploy.yml in your APRS repository:

name: Build and Deploy

on:
  push:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

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

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Log in to the Container registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=sha,prefix={{branch}}-
          type=raw,value=latest,enable={{is_default_branch}}

    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
    - name: Setup Tailscale
      uses: tailscale/github-action@v2
      with:
        oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
        oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
        tags: tag:ci
        
    - name: Set kubeconfig
      run: |
        mkdir -p ~/.kube
        echo "${{ secrets.TAILSCALE_KUBECONFIG }}" | base64 -d > ~/.kube/config
        # Replace the K3s master IP with its Tailscale IP
        sed -i 's/server: https:\/\/[0-9.]*:6443/server: https:\/\/${{ secrets.K3S_TAILSCALE_IP }}:6443/g' ~/.kube/config

    - name: Deploy to K3s
      run: |
        kubectl set image deployment/aprs aprs=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} -n aprs
    
    - name: Wait for rollout
      run: |
        kubectl rollout status deployment/aprs -n aprs

3. Configure Health Check Endpoint

Add a health check endpoint to your Phoenix application if not already present:

# In your router (lib/aprs_web/router.ex)
scope "/", AprsWeb do
  pipe_through :browser
  
  get "/health", HealthController, :index
  # ... other routes
end

Create the health controller:

# lib/aprs_web/controllers/health_controller.ex
defmodule AprsWeb.HealthController do
  use AprsWeb, :controller

  def index(conn, _params) do
    json(conn, %{status: "ok"})
  end
end

4. Update Production Configuration

Ensure your config/runtime.exs includes:

import Config

if config_env() == :prod do
  database_url =
    System.get_env("DATABASE_URL") ||
      raise """
      environment variable DATABASE_URL is missing.
      """

  config :aprs, Aprs.Repo,
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
    socket_options: [:inet6]

  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise """
      environment variable SECRET_KEY_BASE is missing.
      """

  host = System.get_env("PHX_HOST") || "example.com"
  port = String.to_integer(System.get_env("PORT") || "4000")

  config :aprs, AprsWeb.Endpoint,
    url: [host: host, port: 443, scheme: "https"],
    http: [
      ip: {0, 0, 0, 0},
      port: port
    ],
    secret_key_base: secret_key_base

  config :aprs, AprsWeb.Endpoint, server: true
end

5. Setup Tailscale OAuth Client

  1. Go to https://login.tailscale.com/admin/settings/oauth
  2. Create a new OAuth client with these settings:
    • Description: "GitHub Actions CI"
    • Tags: tag:ci
    • Capabilities: Read-only access
  3. Save the Client ID and Secret

6. Setup GitHub Secrets

In your APRS GitHub repository settings, add these secrets:

  1. TS_OAUTH_CLIENT_ID: Your Tailscale OAuth client ID
  2. TS_OAUTH_SECRET: Your Tailscale OAuth client secret
  3. K3S_TAILSCALE_IP: The Tailscale IP of your K3s master node (get it with tailscale ip -4 on the master)
  4. TAILSCALE_KUBECONFIG: Your K3s cluster kubeconfig file content (base64 encoded)
    # On your K3s server:
    sudo cat /etc/rancher/k3s/k3s.yaml | base64 -w0
    

7. Update Kubernetes Deployment Image

After your first GitHub Actions run, update the image in the Kubernetes deployment to use your actual GitHub username:

# In the infra repository
sed -i 's/YOUR_GITHUB_USERNAME/your-actual-username/g' clusters/aprs/aprs-deployment.yaml

8. Generate Secrets

Before deploying to Kubernetes, generate proper secrets:

# Generate a 64-character secret key base
mix phx.gen.secret

# Generate a strong PostgreSQL password
openssl rand -base64 32

Update the secrets.yaml file with these values before applying to your cluster.

9. Install Tailscale on K3s Nodes

Before deploying, ensure Tailscale is installed on your K3s nodes:

# From the ansible directory
export TAILSCALE_KEY="your-tailscale-auth-key"
ansible-playbook k3s-tailscale.yml

10. Apply Initial Deployment

# From the infra repository
kubectl apply -k clusters/aprs/

SSL Proxy Configuration

Since the APRS service is only accessible from the 10.0.16.0/22 network, you'll need to set up an external SSL proxy. Here's an example nginx configuration:

server {
    listen 443 ssl http2;
    server_name aprs.yourdomain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://10.0.16.x:4000;  # Replace with actual service IP
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Monitoring and Troubleshooting

Check deployment status:

kubectl get all -n aprs
kubectl logs -n aprs deployment/aprs
kubectl logs -n aprs deployment/postgis

Notes

  • The PostGIS database will persist data in the PersistentVolume
  • The APRS application runs with 2 replicas for high availability
  • Resource limits are set conservatively; adjust based on your needs
  • Remember to backup your PostGIS data regularly