k3s
This commit is contained in:
parent
928a06659f
commit
bfdecc805b
10 changed files with 551 additions and 0 deletions
48
ansible/k3s-tailscale.yml
Normal file
48
ansible/k3s-tailscale.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
- name: Install Tailscale on K3s control plane
|
||||
hosts: k3s_server
|
||||
become: true
|
||||
roles:
|
||||
- role: artis3n.tailscale
|
||||
vars:
|
||||
# Pull Tailscale auth key from environment variable
|
||||
tailscale_authkey: "{{ lookup('ansible.builtin.env', 'TAILSCALE_KEY') }}"
|
||||
tailscale_args: "--ssh"
|
||||
pre_tasks:
|
||||
- name: Debug - Check Tailscale key (will be partially redacted)
|
||||
debug:
|
||||
msg:
|
||||
- "Key exists: {{ 'TAILSCALE_KEY' in ansible_env }}"
|
||||
- "Key length: {{ lookup('ansible.builtin.env', 'TAILSCALE_KEY') | length }}"
|
||||
- "Key prefix: {{ lookup('ansible.builtin.env', 'TAILSCALE_KEY')[0:10] }}..."
|
||||
when: lookup('ansible.builtin.env', 'TAILSCALE_KEY') | length > 0
|
||||
|
||||
- name: Fail if no Tailscale key
|
||||
fail:
|
||||
msg: "TAILSCALE_KEY environment variable is not set!"
|
||||
when: lookup('ansible.builtin.env', 'TAILSCALE_KEY') | length == 0
|
||||
|
||||
- name: Ensure artis3n.tailscale role is installed
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
ansible.builtin.command:
|
||||
cmd: ansible-galaxy install artis3n.tailscale
|
||||
run_once: true
|
||||
|
||||
post_tasks:
|
||||
- name: Wait for Tailscale to be connected
|
||||
ansible.builtin.command:
|
||||
cmd: tailscale status
|
||||
register: tailscale_status
|
||||
until: '"100.64" in tailscale_status.stdout'
|
||||
retries: 10
|
||||
delay: 5
|
||||
|
||||
- name: Get Tailscale IP
|
||||
ansible.builtin.command:
|
||||
cmd: tailscale ip -4
|
||||
register: tailscale_ip
|
||||
|
||||
- name: Display Tailscale IP
|
||||
debug:
|
||||
msg: "{{ inventory_hostname }} Tailscale IP: {{ tailscale_ip.stdout }}"
|
||||
328
clusters/aprs/APRS_REPO_SETUP.md
Normal file
328
clusters/aprs/APRS_REPO_SETUP.md
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
# 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:
|
||||
|
||||
```dockerfile
|
||||
# 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:
|
||||
|
||||
```yaml
|
||||
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:
|
||||
|
||||
```elixir
|
||||
# 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:
|
||||
|
||||
```elixir
|
||||
# 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:
|
||||
|
||||
```elixir
|
||||
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)
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# From the ansible directory
|
||||
export TAILSCALE_KEY="your-tailscale-auth-key"
|
||||
ansible-playbook k3s-tailscale.yml
|
||||
```
|
||||
|
||||
### 10. Apply Initial Deployment
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```nginx
|
||||
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:
|
||||
```bash
|
||||
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
|
||||
58
clusters/aprs/aprs-deployment.yaml
Normal file
58
clusters/aprs/aprs-deployment.yaml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: aprs
|
||||
namespace: aprs
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: aprs
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: aprs
|
||||
spec:
|
||||
containers:
|
||||
- name: aprs
|
||||
image: ghcr.io/gmcintire/aprs:latest
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
env:
|
||||
- name: PHX_HOST
|
||||
value: "aprs.me"
|
||||
- name: PORT
|
||||
value: "4000"
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aprs-secret
|
||||
key: database-url
|
||||
- name: SECRET_KEY_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aprs-secret
|
||||
key: secret-key-base
|
||||
- name: PHX_SERVER
|
||||
value: "true"
|
||||
- name: MIX_ENV
|
||||
value: "prod"
|
||||
resources:
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
12
clusters/aprs/aprs-service.yaml
Normal file
12
clusters/aprs/aprs-service.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: aprs
|
||||
namespace: aprs
|
||||
spec:
|
||||
selector:
|
||||
app: aprs
|
||||
ports:
|
||||
- port: 4000
|
||||
targetPort: 4000
|
||||
type: ClusterIP
|
||||
13
clusters/aprs/kustomization.yaml
Normal file
13
clusters/aprs/kustomization.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: aprs
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- secrets.yaml
|
||||
- postgis-pvc.yaml
|
||||
- postgis-deployment.yaml
|
||||
- postgis-service.yaml
|
||||
- aprs-deployment.yaml
|
||||
- aprs-service.yaml
|
||||
4
clusters/aprs/namespace.yaml
Normal file
4
clusters/aprs/namespace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: aprs
|
||||
46
clusters/aprs/postgis-deployment.yaml
Normal file
46
clusters/aprs/postgis-deployment.yaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgis
|
||||
namespace: aprs
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgis
|
||||
spec:
|
||||
containers:
|
||||
- name: postgis
|
||||
image: postgis/postgis:17-3.4
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: aprs_prod
|
||||
- name: POSTGRES_USER
|
||||
value: aprs
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgis-secret
|
||||
key: postgres-password
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
volumeMounts:
|
||||
- name: postgis-storage
|
||||
mountPath: /var/lib/postgresql/data
|
||||
resources:
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "250m"
|
||||
volumes:
|
||||
- name: postgis-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: postgis-pvc
|
||||
12
clusters/aprs/postgis-pvc.yaml
Normal file
12
clusters/aprs/postgis-pvc.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgis-pvc
|
||||
namespace: aprs
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: local-path
|
||||
12
clusters/aprs/postgis-service.yaml
Normal file
12
clusters/aprs/postgis-service.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgis
|
||||
namespace: aprs
|
||||
spec:
|
||||
selector:
|
||||
app: postgis
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
type: ClusterIP
|
||||
18
clusters/aprs/secrets.yaml
Normal file
18
clusters/aprs/secrets.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgis-secret
|
||||
namespace: aprs
|
||||
type: Opaque
|
||||
stringData:
|
||||
postgres-password: "mjBipckSZfNOoSOWLMRsfZEqJ9I5Of21"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: aprs-secret
|
||||
namespace: aprs
|
||||
type: Opaque
|
||||
stringData:
|
||||
database-url: "ecto://aprs:mjBipckSZfNOoSOWLMRsfZEqJ9I5Of21@postgis:5432/aprs"
|
||||
secret-key-base: "GJxzOUW3YXBAF7xB4mH5VcXY6BN1NYLnoJYadlZyiCxcRUGBw65w4Sco7+sNiW2t"
|
||||
Loading…
Add table
Reference in a new issue