nex-speech: Sprachdienste für NEX (whisper.cpp CUDA 12 + Piper) als Compose und Ansible-Rolle
ci / test (push) Successful in 29s

- deploy/compose/nex-speech: whisper-server v1.9.4 (sm_52/sm_61, Port 8178, -l de -bs 5
  --convert, tmpfs, Aufwärmen mit 1 s Stille) und OpenAI-kompatibler Piper-Wrapper
  (Port 8179, mp3/opus/aac/flac/pcm gestreamt, wav, PCM 24 kHz mit Rate-Header)
- Host-Netz, damit ufw greift; keine Texte in Logs, kein Access-Log
- Rolle nex-speech + Playbook (nur gpu-pve52): ufw, Modelle mit sha256, Compose bauen/starten
- compose-check um nex-speech erweitert, community.docker in requirements.yml

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Rapp
2026-10-12 20:41:11 +02:00
co-authored by Claude Opus 5.5
parent b66c4c37cc
commit afc5fa0360
13 changed files with 643 additions and 1 deletions
@@ -0,0 +1,102 @@
# --- Firewall in der VM: SSH aus dem LAN, Sprachdienste nur von appsrv08, sonst nichts ---
# Reihenfolge wichtig: SSH-Regel vor dem Einschalten, sonst sperrt sich Ansible aus.
- name: ufw installieren
ansible.builtin.apt:
name: ufw
state: present
- name: ufw – SSH aus dem LAN erlauben
community.general.ufw:
rule: allow
from_ip: "{{ nex_speech_ssh_from }}"
port: "22"
proto: tcp
comment: ssh-lan
- name: ufw – Sprachdienste nur von appsrv08
community.general.ufw:
rule: allow
from_ip: "{{ nex_speech_allow_from }}"
port: "{{ item.port | string }}"
proto: tcp
comment: "{{ item.name }}"
loop:
- {name: nex-stt, port: "{{ nex_speech_stt_port }}"}
- {name: nex-tts, port: "{{ nex_speech_tts_port }}"}
- name: ufw – eingehend sonst ablehnen, ausgehend erlauben
community.general.ufw:
direction: "{{ item.direction }}"
default: "{{ item.policy }}"
loop:
- {direction: incoming, policy: deny}
- {direction: outgoing, policy: allow}
- name: ufw einschalten
community.general.ufw:
state: enabled
# Compose baut nur mit buildx (BuildKit); der Altbau-Builder ist in Docker 29 veraltet
- name: docker-buildx installieren
ansible.builtin.apt:
name: docker-buildx
state: present
# --- Modelle ---
- name: Modellverzeichnisse
ansible.builtin.file:
path: "{{ nex_speech_models_dir }}/{{ item }}"
state: directory
mode: "0755"
loop: [whisper, piper]
- name: Modelle laden (Prüfsumme gegen Hugging Face)
ansible.builtin.get_url:
url: "{{ item.url }}"
dest: "{{ nex_speech_models_dir }}/{{ item.dest }}"
checksum: "{{ item.checksum }}"
mode: "0644"
timeout: 120
loop: "{{ nex_speech_models }}"
loop_control:
label: "{{ item.dest }}"
# --- Compose-Projekt ---
- name: Compose-Dateien kopieren
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../../../deploy/compose/nex-speech/"
dest: "{{ nex_speech_dir }}/"
mode: preserve
directory_mode: "0755"
register: nex_speech_files
- name: .env schreiben (keine Geheimnisse)
ansible.builtin.copy:
dest: "{{ nex_speech_dir }}/.env"
mode: "0644"
content: |
# Verwaltet von Ansible (Rolle nex-speech)
{% for k, v in nex_speech_env.items() %}
{{ k }}={{ v }}
{% endfor %}
register: nex_speech_envfile
# Bauen dauert beim ersten Mal lange (whisper.cpp mit CUDA auf 4 Kernen, ca. 20–40 min)
- name: Images bauen und Dienste starten
community.docker.docker_compose_v2:
project_src: "{{ nex_speech_dir }}"
build: "{{ 'always' if (nex_speech_files.changed or nex_speech_envfile.changed) else 'policy' }}"
state: present
wait: false
async: 3600
poll: 30
- name: Warten, bis beide Dienste gesund sind
ansible.builtin.uri:
url: "http://127.0.0.1:{{ item }}/health"
status_code: 200
register: nex_speech_health
until: nex_speech_health.status == 200
retries: 60
delay: 10
loop: ["{{ nex_speech_stt_port }}", "{{ nex_speech_tts_port }}"]