Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
_build
deps
cover
doc
.elixir_ls
.git
.gitignore
.formatter.exs
.terraform
.terraform.lock.hcl
terraform
k8s
tutorial
test
.expert
.claude
*.md
*.ez
erl_crash.dump
output
80 changes: 80 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# syntax=docker/dockerfile:1.7

ARG ELIXIR_VERSION=1.17.3
ARG OTP_VERSION=27.1.2
ARG UBUNTU_VERSION=jammy-20240808

FROM hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-ubuntu-${UBUNTU_VERSION} AS builder

ENV MIX_ENV=prod \
LANG=C.UTF-8 \
DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
git \
ca-certificates \
nasm \
yasm \
glslang-tools \
libvulkan-dev \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

RUN mix local.hex --force && mix local.rebar --force

COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get --only prod && mix deps.compile

COPY lib lib

RUN mix compile && mix release


FROM nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04 AS runtime

ENV LANG=C.UTF-8 \
DEBIAN_FRONTEND=noninteractive \
NVIDIA_DRIVER_CAPABILITIES=video,compute,utility,graphics \
NVIDIA_VISIBLE_DEVICES=all

RUN apt-get update && apt-get install -y --no-install-recommends \
libstdc++6 \
libssl3 \
libncurses6 \
libsctp1 \
ca-certificates \
locales \
libvulkan1 \
vulkan-tools \
libavcodec58 \
libavformat58 \
libavutil56 \
libswscale5 \
libswresample3 \
libavfilter7 \
&& rm -rf /var/lib/apt/lists/* \
&& locale-gen en_US.UTF-8

RUN groupadd --system app && useradd --system --gid app --create-home --home /app app

WORKDIR /app

COPY --from=builder --chown=app:app /app/_build/prod/rel/ex_broadcaster ./

USER app

EXPOSE 1935 8080

ENTRYPOINT ["/app/bin/ex_broadcaster"]
CMD ["start"]
10 changes: 10 additions & 0 deletions k8s/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ex-broadcaster-config
data:
AWS_REGION: "eu-north-1"
# Set to the value of `terraform output hls_bucket_name`
# (format: ex-broadcaster-hls-<aws-account-id>)
S3_BUCKET: "ex-broadcaster-hls-REPLACE_WITH_ACCOUNT_ID"
S3_PREFIX: "hls"
65 changes: 65 additions & 0 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ex-broadcaster
labels:
app: ex-broadcaster
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: ex-broadcaster
template:
metadata:
labels:
app: ex-broadcaster
spec:
nodeSelector:
node.kubernetes.io/accelerator: nvidia-l4
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: "true"
effect: NoSchedule
containers:
- name: ex-broadcaster
image: REPLACE_ME_REGISTRY/ex-broadcaster:latest
imagePullPolicy: IfNotPresent
ports:
- name: rtmp
containerPort: 1935
protocol: TCP
- name: http
containerPort: 8080
protocol: TCP
env:
- name: NVIDIA_DRIVER_CAPABILITIES
value: "video,compute,utility,graphics"
- name: NVIDIA_VISIBLE_DEVICES
value: "all"
envFrom:
- configMapRef:
name: ex-broadcaster-config
- secretRef:
name: ex-broadcaster-aws
resources:
limits:
nvidia.com/gpu: 1
memory: "4Gi"
cpu: "2"
requests:
nvidia.com/gpu: 1
memory: "2Gi"
cpu: "1"
readinessProbe:
tcpSocket:
port: rtmp
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
tcpSocket:
port: rtmp
initialDelaySeconds: 30
periodSeconds: 15
8 changes: 8 additions & 0 deletions k8s/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: ex-broadcaster-aws
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "REPLACE_ME"
AWS_SECRET_ACCESS_KEY: "REPLACE_ME"
31 changes: 31 additions & 0 deletions k8s/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: v1
kind: Service
metadata:
name: ex-broadcaster-rtmp
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: ex-broadcaster
ports:
- name: rtmp
port: 1935
targetPort: rtmp
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: ex-broadcaster-http
spec:
type: ClusterIP
selector:
app: ex-broadcaster
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
105 changes: 105 additions & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions terraform/ecr.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
resource "aws_ecr_repository" "ex_broadcaster" {
name = "ex-broadcaster"
image_tag_mutability = "MUTABLE"

image_scanning_configuration {
scan_on_push = true
}
}

resource "aws_ecr_lifecycle_policy" "ex_broadcaster" {
repository = aws_ecr_repository.ex_broadcaster.name

policy = jsonencode({
rules = [
{
rulePriority = 1
description = "Expire untagged images after 7 days"
selection = {
tagStatus = "untagged"
countType = "sinceImagePushed"
countUnit = "days"
countNumber = 7
}
action = { type = "expire" }
}
]
})
}

output "ecr_repository_url" {
value = aws_ecr_repository.ex_broadcaster.repository_url
description = "Use this as the image: prefix in k8s/deployment.yaml"
}
Loading