aprs.me/k8s/deployment.yaml
Graham McIntire 24796f98d4
Optimize prod: scale deployment, drop expensive metric queries, fix slow callsign and nearby-stations queries
- k8s: 2 replicas, POOL_SIZE 25, cpu 2000m, mem 1Gi (was 1 replica, 5, 500m, 512Mi)
- telemetry poller 10s -> 60s; drop pg_database_size, pg_stat_statements,
  pg_table_size and pg_indexes_size (these were ~7100s of cumulative DB time
  on their own)
- get_latest_packet_for_callsign: bound by :packet_retention_days so partition
  pruning kicks in
- get_nearby_stations_knn: ST_DWithin spatial pre-filter (default 500km) so
  the GiST geography index cuts candidates before DISTINCT ON sort
  (EXPLAIN: 5793ms -> 400ms)
- New migration: partial functional indexes on upper(object_name) and
  upper(item_name), built per-partition CONCURRENTLY then attached to parent.
  Lets get_latest_packet_for_callsign use BitmapOr across all 3 upper()
  indexes instead of falling back to a scan
2026-05-12 17:48:58 -05:00

192 lines
5.3 KiB
YAML

apiVersion: apps/v1
kind: Deployment
metadata:
name: aprs
namespace: aprs
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
minReadySeconds: 30
progressDeadlineSeconds: 600
selector:
matchLabels:
app: aprs
template:
metadata:
labels:
app: aprs
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "4000"
prometheus.io/path: "/metrics"
spec:
priorityClassName: system-cluster-critical
terminationGracePeriodSeconds: 90
imagePullSecrets:
- name: forgejo-registry
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
initContainers:
- name: migrate
image: codeberg.org/gmcintire/aprs.me:main-1777477809-208401e
imagePullPolicy: IfNotPresent
command: ["/app/bin/migrate"]
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1001
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
env:
- name: MIX_ENV
value: "prod"
- name: SKIP_DB_CREATE
value: "true"
- name: PHX_HOST
value: "aprs.me"
- name: PORT
value: "4000"
- name: APRS_PORT
value: "10152"
- name: APRS_HOST
value: "204.110.191.232"
- name: APRS_CALLSIGN
value: "w5isp-1"
- name: APRS_PASSWORD
value: "15748"
- name: RELEASE_COOKIE
valueFrom:
secretKeyRef:
name: aprs-secrets
key: RELEASE_COOKIE
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: aprs-secrets
key: SECRET_KEY_BASE
envFrom:
- secretRef:
name: aprs-db
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
containers:
- name: aprs
image: codeberg.org/gmcintire/aprs.me:main-1777477809-208401e
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1001
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
ports:
- containerPort: 4000
name: http
- containerPort: 4369
name: epmd
- containerPort: 9000
name: dist
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 20
env:
- name: CLUSTER_ENABLED
value: "true"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: PORT
value: "4000"
- name: PHX_HOST
value: "aprs.me"
- name: MIX_ENV
value: "prod"
- name: POOL_SIZE
value: "25"
- name: PACKET_RETENTION_DAYS
value: "1"
- name: APRS_CALLSIGN
value: "w5isp-1"
- name: APRS_PORT
value: "10152"
- name: APRS_HOST
value: "204.110.191.232"
- name: APRS_PASSWORD
value: "15748"
- name: RELEASE_COOKIE
valueFrom:
secretKeyRef:
name: aprs-secrets
key: RELEASE_COOKIE
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: aprs-secrets
key: SECRET_KEY_BASE
- name: RESEND_API_KEY
valueFrom:
secretKeyRef:
name: aprs-secrets
key: RESEND_API_KEY
envFrom:
- secretRef:
name: aprs-db
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "2000m"
startupProbe:
httpGet:
path: /health
port: 4000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12
livenessProbe:
httpGet:
path: /health
port: 4000
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 4000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
successThreshold: 1
failureThreshold: 3