Skip to content
Merged
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
File renamed without changes
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ lk token create --join \

Head over to the [example web client](https://meet.livekit.io/?tab=custom) and paste in the token, you can see the simulated tracks published by the load tester.

![Load tester screenshot](misc/load-test-screenshot.jpg?raw=true)
![Load tester screenshot](.github/load-test-screenshot.jpg?raw=true)

### Running on a cloud VM

Expand Down
60 changes: 11 additions & 49 deletions pkg/agentfs/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ package agentfs

import (
"bytes"
"embed"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
Expand All @@ -30,6 +29,9 @@ import (
"github.com/livekit/protocol/logger"
)

//go:embed examples/*
var fs embed.FS

func HasDockerfile(dir string) (bool, error) {
entries, err := os.ReadDir(dir)
if err != nil {
Expand Down Expand Up @@ -61,35 +63,14 @@ func CreateDockerfile(dir string, settingsMap map[string]string) error {
var dockerfileContent []byte
var dockerIgnoreContent []byte
var err error
switch projectType {
case "python":
err = validateSettingsMap(settingsMap, []string{"python_docker_file", "python_docker_ignore"})
if err != nil {
return err
}
dockerfileContent, err = downloadFile(settingsMap["python_docker_file"])
if err != nil {
return err
}

dockerIgnoreContent, err = downloadFile(settingsMap["python_docker_ignore"])
if err != nil {
return err
}
case "node":
err = validateSettingsMap(settingsMap, []string{"node_docker_file", "node_docker_ignore"})
if err != nil {
return err
}

dockerfileContent, err = downloadFile(settingsMap["node_docker_file"])
if err != nil {
return err
}
dockerIgnoreContent, err = downloadFile(settingsMap["node_docker_ignore"])
if err != nil {
return err
}
dockerfileContent, err = fs.ReadFile("examples/" + projectType + ".Dockerfile")
if err != nil {
return err
}
dockerIgnoreContent, err = fs.ReadFile("examples/" + projectType + ".dockerignore")
if err != nil {
return err
}

if projectType == "python" {
Expand All @@ -112,25 +93,6 @@ func CreateDockerfile(dir string, settingsMap map[string]string) error {
return nil
}

func downloadFile(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch file: HTTP %d", resp.StatusCode)
}

data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

return data, nil
}

func validateEntrypoint(dir string, dockerfileContent []byte, projectType string, settingsMap map[string]string) ([]byte, error) {
fileList := make(map[string]bool)
entries, err := os.ReadDir(dir)
Expand Down
24 changes: 24 additions & 0 deletions pkg/agentfs/docker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package agentfs

import (
"testing"
)

func TestLoadDockerFiles(t *testing.T) {
expectedFiles := []string{
"examples/node.Dockerfile",
"examples/node.dockerignore",
"examples/python.Dockerfile",
"examples/python.dockerignore",
}

for _, file := range expectedFiles {
bytes, err := fs.ReadFile(file)
if err != nil {
t.Fatalf("failed to read Dockerfile: %v", err)
}
if len(bytes) == 0 {
t.Fatalf("Dockerfile empty: %s", file)
}
}
}
25 changes: 25 additions & 0 deletions pkg/agentfs/examples/node.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This is an example Dockerfile that builds a minimal container for running LK Agents
# syntax=docker/dockerfile:1
FROM node:20-slim AS base

WORKDIR /app

RUN npm install -g pnpm@9.7.0

# throw away build stage to reduce size of final image
FROM base AS build

RUN apt-get update -qq && apt-get install --no-install-recommends -y ca-certificates
COPY --link . .

RUN pnpm install --frozen-lockfile
RUN npm run build

FROM base
COPY --from=build /app /app
COPY --from=build /etc/ssl/certs /etc/ssl/certs

# start the server by default, this can be overwritten at runtime
EXPOSE 8081

CMD [ "node", "./dist/agent.js", "start" ]
32 changes: 32 additions & 0 deletions pkg/agentfs/examples/node.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Node.js dependencies
node_modules
npm-debug.log
yarn-error.log
pnpm-debug.log

# Build outputs
dist
build
coverage

# Local environment & config files
.env
.env.local
.DS_Store

# Logs & temp files
*.log
*.gz
*.tgz
.tmp
.cache

# Docker artifacts
Dockerfile*
.dockerignore

# Git & Editor files
.git
.gitignore
.idea
.vscode
48 changes: 48 additions & 0 deletions pkg/agentfs/examples/python.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This is an example Dockerfile that builds a minimal container for running LK Agents
# syntax=docker/dockerfile:1
ARG PYTHON_VERSION=3.11.6
FROM python:${PYTHON_VERSION}-slim

# Keeps Python from buffering stdout and stderr to avoid situations where
# the application crashes without emitting any logs due to buffering.
ENV PYTHONUNBUFFERED=1

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/home/appuser" \
--shell "/sbin/nologin" \
--uid "${UID}" \
appuser


# Install gcc and other build dependencies.
RUN apt-get update && \
apt-get install -y \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*

USER appuser

RUN mkdir -p /home/appuser/.cache
RUN chown -R appuser /home/appuser/.cache

WORKDIR /home/appuser

COPY requirements.txt .
RUN python -m pip install --user --no-cache-dir -r requirements.txt

COPY . .

# ensure that any dependent models are downloaded at build-time
RUN python main.py download-files

# expose healthcheck port
EXPOSE 8081

# Run the application.
CMD ["python", "main.py", "start"]
25 changes: 25 additions & 0 deletions pkg/agentfs/examples/python.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Python artifacts
venv/
__pycache__/

# Local environment & config files
.env
.env.local
.DS_Store

# Logs & temp files
*.log
*.gz
*.tgz
.tmp
.cache

# Docker artifacts
Dockerfile*
.dockerignore

# Git & Editor files
.git
.gitignore
.idea
.vscode
Loading