Skip to content

Commit 3dacdb7

Browse files
authored
feat: embed agent docker files (#584)
1 parent 6dc1946 commit 3dacdb7

File tree

8 files changed

+166
-50
lines changed

8 files changed

+166
-50
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ lk token create --join \
289289
290290
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.
291291
292-
![Load tester screenshot](misc/load-test-screenshot.jpg?raw=true)
292+
![Load tester screenshot](.github/load-test-screenshot.jpg?raw=true)
293293
294294
### Running on a cloud VM
295295

pkg/agentfs/docker.go

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ package agentfs
1616

1717
import (
1818
"bytes"
19+
"embed"
1920
"encoding/json"
2021
"fmt"
21-
"io"
22-
"net/http"
2322
"os"
2423
"path/filepath"
2524
"strings"
@@ -30,6 +29,9 @@ import (
3029
"github.com/livekit/protocol/logger"
3130
)
3231

32+
//go:embed examples/*
33+
var fs embed.FS
34+
3335
func HasDockerfile(dir string) (bool, error) {
3436
entries, err := os.ReadDir(dir)
3537
if err != nil {
@@ -61,35 +63,14 @@ func CreateDockerfile(dir string, settingsMap map[string]string) error {
6163
var dockerfileContent []byte
6264
var dockerIgnoreContent []byte
6365
var err error
64-
switch projectType {
65-
case "python":
66-
err = validateSettingsMap(settingsMap, []string{"python_docker_file", "python_docker_ignore"})
67-
if err != nil {
68-
return err
69-
}
70-
dockerfileContent, err = downloadFile(settingsMap["python_docker_file"])
71-
if err != nil {
72-
return err
73-
}
7466

75-
dockerIgnoreContent, err = downloadFile(settingsMap["python_docker_ignore"])
76-
if err != nil {
77-
return err
78-
}
79-
case "node":
80-
err = validateSettingsMap(settingsMap, []string{"node_docker_file", "node_docker_ignore"})
81-
if err != nil {
82-
return err
83-
}
84-
85-
dockerfileContent, err = downloadFile(settingsMap["node_docker_file"])
86-
if err != nil {
87-
return err
88-
}
89-
dockerIgnoreContent, err = downloadFile(settingsMap["node_docker_ignore"])
90-
if err != nil {
91-
return err
92-
}
67+
dockerfileContent, err = fs.ReadFile("examples/" + projectType + ".Dockerfile")
68+
if err != nil {
69+
return err
70+
}
71+
dockerIgnoreContent, err = fs.ReadFile("examples/" + projectType + ".dockerignore")
72+
if err != nil {
73+
return err
9374
}
9475

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

115-
func downloadFile(url string) ([]byte, error) {
116-
resp, err := http.Get(url)
117-
if err != nil {
118-
return nil, err
119-
}
120-
defer resp.Body.Close()
121-
122-
if resp.StatusCode != http.StatusOK {
123-
return nil, fmt.Errorf("failed to fetch file: HTTP %d", resp.StatusCode)
124-
}
125-
126-
data, err := io.ReadAll(resp.Body)
127-
if err != nil {
128-
return nil, err
129-
}
130-
131-
return data, nil
132-
}
133-
13496
func validateEntrypoint(dir string, dockerfileContent []byte, projectType string, settingsMap map[string]string) ([]byte, error) {
13597
fileList := make(map[string]bool)
13698
entries, err := os.ReadDir(dir)

pkg/agentfs/docker_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package agentfs
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestLoadDockerFiles(t *testing.T) {
8+
expectedFiles := []string{
9+
"examples/node.Dockerfile",
10+
"examples/node.dockerignore",
11+
"examples/python.Dockerfile",
12+
"examples/python.dockerignore",
13+
}
14+
15+
for _, file := range expectedFiles {
16+
bytes, err := fs.ReadFile(file)
17+
if err != nil {
18+
t.Fatalf("failed to read Dockerfile: %v", err)
19+
}
20+
if len(bytes) == 0 {
21+
t.Fatalf("Dockerfile empty: %s", file)
22+
}
23+
}
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# This is an example Dockerfile that builds a minimal container for running LK Agents
2+
# syntax=docker/dockerfile:1
3+
FROM node:20-slim AS base
4+
5+
WORKDIR /app
6+
7+
RUN npm install -g [email protected]
8+
9+
# throw away build stage to reduce size of final image
10+
FROM base AS build
11+
12+
RUN apt-get update -qq && apt-get install --no-install-recommends -y ca-certificates
13+
COPY --link . .
14+
15+
RUN pnpm install --frozen-lockfile
16+
RUN npm run build
17+
18+
FROM base
19+
COPY --from=build /app /app
20+
COPY --from=build /etc/ssl/certs /etc/ssl/certs
21+
22+
# start the server by default, this can be overwritten at runtime
23+
EXPOSE 8081
24+
25+
CMD [ "node", "./dist/agent.js", "start" ]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Node.js dependencies
2+
node_modules
3+
npm-debug.log
4+
yarn-error.log
5+
pnpm-debug.log
6+
7+
# Build outputs
8+
dist
9+
build
10+
coverage
11+
12+
# Local environment & config files
13+
.env
14+
.env.local
15+
.DS_Store
16+
17+
# Logs & temp files
18+
*.log
19+
*.gz
20+
*.tgz
21+
.tmp
22+
.cache
23+
24+
# Docker artifacts
25+
Dockerfile*
26+
.dockerignore
27+
28+
# Git & Editor files
29+
.git
30+
.gitignore
31+
.idea
32+
.vscode
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# This is an example Dockerfile that builds a minimal container for running LK Agents
2+
# syntax=docker/dockerfile:1
3+
ARG PYTHON_VERSION=3.11.6
4+
FROM python:${PYTHON_VERSION}-slim
5+
6+
# Keeps Python from buffering stdout and stderr to avoid situations where
7+
# the application crashes without emitting any logs due to buffering.
8+
ENV PYTHONUNBUFFERED=1
9+
10+
# Create a non-privileged user that the app will run under.
11+
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
12+
ARG UID=10001
13+
RUN adduser \
14+
--disabled-password \
15+
--gecos "" \
16+
--home "/home/appuser" \
17+
--shell "/sbin/nologin" \
18+
--uid "${UID}" \
19+
appuser
20+
21+
22+
# Install gcc and other build dependencies.
23+
RUN apt-get update && \
24+
apt-get install -y \
25+
gcc \
26+
python3-dev \
27+
&& rm -rf /var/lib/apt/lists/*
28+
29+
USER appuser
30+
31+
RUN mkdir -p /home/appuser/.cache
32+
RUN chown -R appuser /home/appuser/.cache
33+
34+
WORKDIR /home/appuser
35+
36+
COPY requirements.txt .
37+
RUN python -m pip install --user --no-cache-dir -r requirements.txt
38+
39+
COPY . .
40+
41+
# ensure that any dependent models are downloaded at build-time
42+
RUN python main.py download-files
43+
44+
# expose healthcheck port
45+
EXPOSE 8081
46+
47+
# Run the application.
48+
CMD ["python", "main.py", "start"]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Python artifacts
2+
venv/
3+
__pycache__/
4+
5+
# Local environment & config files
6+
.env
7+
.env.local
8+
.DS_Store
9+
10+
# Logs & temp files
11+
*.log
12+
*.gz
13+
*.tgz
14+
.tmp
15+
.cache
16+
17+
# Docker artifacts
18+
Dockerfile*
19+
.dockerignore
20+
21+
# Git & Editor files
22+
.git
23+
.gitignore
24+
.idea
25+
.vscode

0 commit comments

Comments
 (0)