Files
bluejay-infra/apps/asterisk/deployment.yaml
Andrew Stoltz d3ffad9190 fix(telephony): PiperUrl 10.0.57.15 → .17 + shared-tts hostPath for TTS playback
Piper was never reachable on 10.0.57.15 — edge1's actual address is
10.0.57.17 (SSH config, project_edge1_sdcard memory). Every telephony
prompt hit the 8s HttpClient timeout and fell back to the built-in sound
map (vm-advopts, vm-goodbye, beep) instead of speaking the real workflow
text. Verified from noc1: `curl http://10.0.57.17:8500/health` returns
HTTP 200 in 6ms, `POST /tts` returns a 16kHz mono WAV in 606ms.

Changes:

- apps/telephony/telephony.yaml
  - `Tts.PiperUrl` → `http://10.0.57.17:8500`
  - NetworkPolicy egress allow → `10.0.57.17/32:8500`
  - Header comment now documents the POST /tts {"text":"..."} contract
  - telephony-web pod mounts `/shared-tts` from hostPath `/tmp/tts-audio`
    (rke2-agent1). This is where `AsteriskProvider.SpeakTextAsync` writes
    the synthesized .sln16 before calling ARI `Play sound:tts/<name>`.

- apps/asterisk/deployment.yaml
  - Asterisk pod mounts the same hostPath at
    `/var/lib/asterisk/sounds/tts` so it can read and play what
    telephony-web wrote. Both deployments have
    `nodeSelector: kubernetes.io/hostname: rke2-agent1` so the hostPath
    is guaranteed to be the same directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 16:19:48 -05:00

189 lines
6.5 KiB
YAML

apiVersion: apps/v1
kind: Deployment
metadata:
name: asterisk
namespace: telephony
labels:
app: asterisk
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: asterisk
template:
metadata:
labels:
app: asterisk
spec:
nodeSelector:
kubernetes.io/hostname: rke2-agent1
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
securityContext:
fsGroup: 0
# CoreDNS in this cluster has an iamworkin.lan wildcard that catches
# any unresolved name and returns 10.0.56.200 (Traefik VIP), which
# means downloads.asterisk.org inside the pod resolves to Traefik and
# returns 404. Pin the real address so the init container can fetch
# the sounds tarball.
hostAliases:
- ip: 165.22.184.19
hostnames:
- downloads.asterisk.org
initContainers:
- name: install-sounds
# Downloads Asterisk core sounds (en, ulaw) into the sounds emptyDir
# volume so the base Asterisk image (which ships no sounds) can play
# vm-advopts, vm-goodbye, digits/*, characters/*, beep, etc. Skips
# the download if the directory already contains sound files —
# re-running the pod after a hot image reload reuses the unpack.
image: alpine:3.20
command:
- sh
- -c
- |
set -eu
if [ -f /sounds/en/vm-goodbye.ulaw ] || [ -f /sounds/en/vm-goodbye.gsm ]; then
echo "Sounds already present — skipping download."
exit 0
fi
echo "Installing curl + tar..."
apk add --no-cache curl tar gzip >/dev/null
cd /tmp
echo "Downloading Asterisk core sounds (en, ulaw) 1.6.1..."
# -k: cluster egress goes through a step-ca MITM for outbound TLS
# that this pod does not trust. The tarball is a public artifact —
# integrity is checked downstream by Asterisk at playback time.
curl -fksSLO https://downloads.asterisk.org/pub/telephony/sounds/releases/asterisk-core-sounds-en-ulaw-1.6.1.tar.gz
echo "Extracting to /sounds/en ..."
mkdir -p /sounds/en
tar -xzf asterisk-core-sounds-en-ulaw-1.6.1.tar.gz -C /sounds/en
echo "Done — $(ls /sounds/en | wc -l) files installed."
volumeMounts:
- name: sounds
mountPath: /sounds/en
containers:
- name: asterisk
image: localhost/andrius/asterisk:latest
imagePullPolicy: Never
ports:
- name: sip-udp
containerPort: 5060
protocol: UDP
- name: sip-tcp
containerPort: 5060
protocol: TCP
- name: ari
containerPort: 8088
protocol: TCP
volumeMounts:
- name: config-modules
mountPath: /etc/asterisk/modules.conf
subPath: modules.conf
- name: config-http
mountPath: /etc/asterisk/http.conf
subPath: http.conf
- name: config-ari
mountPath: /etc/asterisk/ari.conf
subPath: ari.conf
- name: config-manager
mountPath: /etc/asterisk/manager.conf
subPath: manager.conf
- name: config-pjsip
mountPath: /etc/asterisk/pjsip.conf
subPath: pjsip.conf
- name: config-extensions
mountPath: /etc/asterisk/extensions.conf
subPath: extensions.conf
- name: config-rtp
mountPath: /etc/asterisk/rtp.conf
subPath: rtp.conf
- name: asterisk-data
mountPath: /var/spool/asterisk
- name: asterisk-logs
mountPath: /var/log/asterisk
- name: sounds
mountPath: /var/lib/asterisk/sounds/en
# Shared TTS audio — telephony-web writes .sln16 files here (as
# /shared-tts), Asterisk plays them via `sound:tts/<name>` which
# resolves to this mount. Both pods are pinned to rke2-agent1.
- name: shared-tts
mountPath: /var/lib/asterisk/sounds/tts
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
livenessProbe:
tcpSocket:
port: 8088
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ari/asterisk/info
port: 8088
httpHeaders:
- name: Authorization
value: "Basic Zmxvd2VyY29yZTpibHVlamF5LWFzdGVyaXNrLWFyaQ=="
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: config-modules
configMap:
name: asterisk-config
items:
- key: modules.conf
path: modules.conf
- name: config-http
configMap:
name: asterisk-config
items:
- key: http.conf
path: http.conf
- name: config-ari
configMap:
name: asterisk-config
items:
- key: ari.conf
path: ari.conf
- name: config-manager
configMap:
name: asterisk-config
items:
- key: manager.conf
path: manager.conf
- name: config-pjsip
configMap:
name: asterisk-config
items:
- key: pjsip.conf
path: pjsip.conf
- name: config-extensions
configMap:
name: asterisk-config
items:
- key: extensions.conf
path: extensions.conf
- name: config-rtp
configMap:
name: asterisk-config
items:
- key: rtp.conf
path: rtp.conf
- name: asterisk-data
persistentVolumeClaim:
claimName: asterisk-data
- name: asterisk-logs
emptyDir: {}
- name: sounds
emptyDir: {}
- name: shared-tts
hostPath:
path: /tmp/tts-audio
type: DirectoryOrCreate