feat(livekit): add role

This commit is contained in:
Tobias Zenk 2024-08-16 11:59:20 +02:00
parent 2b7d24f767
commit e246d3dfde
No known key found for this signature in database
GPG key ID: 953FFB020465E686
4 changed files with 276 additions and 0 deletions

124
roles/livekit/README.md Normal file
View file

@ -0,0 +1,124 @@
# LiveKit WebRTC SFU
Deploys [LiveKit](https://docs.livekit.io/home/self-hosting/vm) and configures it,
optionally in combination with a redis database.
## Requirements
A working docker installation `enable_docker: true` is required in order to generate
the config files for LiveKit, as well as Let's Encrypt (`enable_lego: true`) and Traefik
(`enable_traefik: true`).
## Variables
See [defaults](defaults/main.yml) for default variables.
The following mandatory variables have to be declared in the inventory on a per-host basis:
```yaml
enable_livekit: true
livekit_turnserver_domain: # the fqdn for the livekit TURN server
livekit_redis_enabled: # boolean value, defining if a redis database shall be created for livekit
```
A second domain record for the TURN server is needed,
additionally to the `livekit_domain` (in the host_vars):
```yaml
livekit_domain: "{{ famedly_instance_domain }}"
livekit_turnserver_domain: "turn.dev.famedly.de"
```
Both domains must be included in the SAN list of the Let's Encrypt TLS
certificate (in the group_vars):
```yaml
lego_certificate:
domains:
- "{{ famedly_instance_domain }}"
- "{{ livekit_turnserver_domain }}"
email: "{{ famedly_acme_email }}"
```
Also, there needs to be a CNAME record for the additional
domain (which can be found in group_vars/all/dns.yml):
```yaml
dns_cnames:
- type: CNAME
name: "{{ livekit_turnserver_domain }}."
content: "{{ famedly_instance_domain }}."
ttl: 3600
when: "{{ enable_livekit|default(false) }}"
```
Additional `ufw` ACLs are defined in the group vars:
```yaml
famedly_firewall_per_host_allowlist:
- service: webrtc-tcp
port: 7881
proto: tcp
- service: turn-udp
port: 3478
proto: udp
- service: webrtc-udp
port: '50000:60000'
proto: udp
```
"traefik" is required as a reverse proxy by setting `enable_traefik: true` (in the host_vars)
and including the correct router configuration in the group_vars:
```yaml
traefik_dynamic_extra_configs:
- "livekit-router" # the livekit-router is always required for traefik to work properly
```
If one decides to use Redis in combination with LiveKit by setting `livekit_redis_enabled: true` as
a host variable, the redis role will be imported as a task in the `livekit.yml` playbook.
The following variables are defined in the group_vars for that case:
```yaml
redis_config_bind_ip: "127.0.0.1"
redis_docker_networks:
- name: host
```
The group_vars also must contain the port variables:
```yaml
livekit_ws_port: 7880
livekit_rtc_port: 7881
livekit_rtc_udp_port_start: 50000
livekit_rtc_udp_port_end: 60000
livekit_turns_port: 5349
livekit_turn_port: 3478
livekit_redis_port: 6379
livekit_jwt_service_external_port: 8888
```
## Dependencies
- famedly.base.redis
## Example Playbook
```yaml
- name: Deploy Livekit Server - an SFU for video calls
hosts: [ livekit ]
become: true
tasks:
- name: Set up a redis container for livekit
ansible.builtin.import_role:
name: famedly.base.redis
when: livekit_redis_enabled | bool
- name: Set up livekit and lk-jwt-service containers
ansible.builtin.import_role:
name: famedly.matrix.livekit
```
Author Information
------------------
[blueorca363](https://github.com/blueorca363)

View file

@ -0,0 +1,78 @@
---
livekit_version: "v1.7"
livekit_jwt_service_version: "sha-4a29504"
livekit_domain: "{{ famedly_instance_domain }}"
livekit_turnserver_domain: ~
livekit_redis_enabled: false
livekit_ws_port: 7880
livekit_rtc_port: 7881
livekit_rtc_udp_port_start: 50000
livekit_rtc_udp_port_end: 60000
livekit_turns_port: 5349
livekit_turn_port: 3478
livekit_redis_port: 6379
livekit_jwt_service_external_port: 8888
livekit_user: "livekit"
livekit_container_image_reference: "livekit/livekit-server:{{ livekit_version }}"
livekit_config_path: "/opt/livekit"
livekit_config_file: "livekit.yaml"
livekit_container_config: "/etc/livekit.yaml"
livekit_container_command: "--config {{ livekit_container_config }}"
livekit_config_basic:
port: "{{ livekit_ws_port }}"
bind_addresses:
- "127.0.0.1"
room:
enabled_codecs:
- mime: audio/opus
- mime: video/vp8
- mime: video/h264
- mime: video/vp9
- mime: video/av1
rtc:
tcp_port: "{{ livekit_rtc_port }}"
port_range_start: "{{ livekit_rtc_udp_port_start }}"
port_range_end: "{{ livekit_rtc_udp_port_end }}"
use_external_ip: true
# enable_loopback_candidate: false
congestion_control:
enabled: true
allow_pause: true
turn:
enabled: true
domain: "{{ livekit_turnserver_domain }}"
tls_port: "{{ livekit_turns_port }}"
udp_port: "{{ livekit_turn_port }}"
external_tls: true
keys:
secret: "{{ livekit_secret_key }}"
livekit_config_redis:
redis:
address: "127.0.0.1:{{ livekit_redis_port }}"
username: ""
password: ""
db: 0
use_tls: false
sentinel_master_name: ""
sentinel_username: ""
sentinel_password: ""
sentinel_addresses: []
cluster_addresses: []
max_redirects: null
livekit_config_complete: >-
{{ livekit_config_basic | combine(livekit_config_redis if (livekit_redis_enabled | bool) else []) }}
livekit_container_name: "livekit"
livekit_container_volumes: []
livekit_container_preset_volumes:
- "{{ livekit_config_path }}/{{ livekit_config_file }}:{{ livekit_container_config }}:ro"
livekit_container_combined_volumes: >-
{{ livekit_container_preset_volumes + livekit_container_volumes }}
livekit_container_network_mode: "host"
livekit_jwt_service_container_name: "jwt-service"
livekit_jwt_service_container_image_reference: "ghcr.io/element-hq/lk-jwt-service:{{ livekit_jwt_service_version }}"
livekit_jwt_service_container_env:
LIVEKIT_KEY: "secret"
LIVEKIT_SECRET: "{{ livekit_secret_key }}"
LIVEKIT_URL: "wss://{{ livekit_domain }}"
livekit_jwt_service_container_ports:
- "127.0.0.1:{{ livekit_jwt_service_external_port }}:8080"

View file

@ -0,0 +1,16 @@
---
- name: Ensure container for livekit is restarted
community.docker.docker_container:
name: "{{ livekit_container_name }}"
image: "{{ livekit_container_image_reference }}"
state: started
restart: true
listen: "restart-livekit"
- name: Ensure container for jwt-service is restarted
community.docker.docker_container:
name: "{{ livekit_jwt_service_container_name }}"
image: "{{ livekit_jwt_service_container_image_reference }}"
state: started
restart: true
listen: "restart-jwt-service"

View file

@ -0,0 +1,58 @@
---
- name: 'Ensure livekit user exists: {{ livekit_user }}'
ansible.builtin.user:
name: "{{ livekit_user }}"
state: present
system: true
register: livekit_user_info
- name: Ensure livekit container image is present
community.docker.docker_image:
name: "{{ livekit_container_image_reference }}"
state: present
source: pull
force_source: true
- name: Ensure lk-jwt-service container image is present
community.docker.docker_image:
name: "{{ livekit_jwt_service_container_image_reference }}"
state: present
source: pull
force_source: true
- name: Ensure livekit config directory exists
file:
path: "{{ livekit_config_path }}"
state: directory
mode: "0744"
- name: Ensure livekit config.json is up to date
ansible.builtin.copy:
content: "{{ livekit_config_complete | to_nice_yaml }}"
dest: "{{ livekit_config_path }}/{{ livekit_config_file }}"
owner: "{{ livekit_user_info.uid | default(livekit_user) }}"
group: "{{ livekit_user_info.group | default(livekit_user) }}"
mode: "0644"
notify:
- restart-livekit
- restart-jwt-service
- name: 'Ensure livekit container is running: {{ livekit_container_name }}'
community.docker.docker_container:
name: "{{ livekit_container_name }}"
image: "{{ livekit_container_image_reference }}"
command: "{{ livekit_container_command }}"
env: "{{ livekit_container_env | default(omit, true) }}"
user: "{{ livekit_container_user | default(omit, true) }}"
volumes: "{{ livekit_container_combined_volumes | default(omit, true) }}"
network_mode: "{{ livekit_container_network_mode | default(omit, true) }}"
restart_policy: unless-stopped
- name: 'Ensure lk-jwt-service container is running: {{ livekit_jwt_service_container_name }}'
community.docker.docker_container:
name: "{{ livekit_jwt_service_container_name }}"
image: "{{ livekit_jwt_service_container_image_reference }}"
env: "{{ livekit_jwt_service_container_env | default(omit, true) }}"
ports: "{{ livekit_jwt_service_container_ports | default(omit, true) }}"
network_mode: "{{ livekit_jwt_service_container_network_mode | default(omit, true) }}"
restart_policy: unless-stopped