Improve Docker Build and Deployment for Arcade Worker (#205)

This PR enhances the Docker build and deployment process for the Arcade
Worker by:

- **Modularizing Docker Builds:**
- Introduces a new `INSTALL_TOOLKITS` build argument in the `Dockerfile`
to conditionally include toolkits. this enables the creation of a
`arcadeai/worker-base` which can be used to build custom containers in a
multi-stage build. an example of this is included in the example dir.
- Adds `docker-base` Makefile target to build a lightweight base image
without toolkits.

- **Publishing to GitHub Container Registry (GHCR):**
- Adds Makefile targets `publish-ghcr` and `gh-login` for pushing images
to GHCR.
  - Supports publishing both base and full images with toolkits to GHCR.

- **Docker Compose:**
  - Add Docker compose file and setup
  - Renames the `actor` service to `worker`
- Adds an `nginx` service in `docker-compose.yml` to proxy requests to
the Arcade Engine.
  - Introduces an `nginx.conf` file for the Nginx service.

- **Streamlining Toolkit Installation:**
- Moves toolkit installation from the `start.sh` script to the Docker
build process.
- Creates a `toolkits.txt` file to manage toolkit dependencies which can
be edited easily when we want to add a new toolkit. Developers can also
use this approach as shown in the example.

- **Improving Configuration Files:**
- Updates `docker.engine.yaml` and `env.example` to align with the new
setup.


TODO:
- CI/CD needs to be adjusted so that images are pushed to ghcr on
release.
- AWS resources need to be renamed actor -> worker @EricGustin 

This can go in after the above two items are resolved.

---------

Co-authored-by: Wils Dawson <wils@arcade-ai.com>
Co-authored-by: Eric Gustin <34000337+EricGustin@users.noreply.github.com>
Co-authored-by: sdreyer <sterling@arcade-ai.com>
Co-authored-by: Sterling Dreyer <sdreyer21@gmail.com>
Co-authored-by: Nate Barbettini <nathanaelb@gmail.com>
This commit is contained in:
Sam Partee 2025-01-23 12:57:24 -08:00 committed by GitHub
parent 6d8e943c96
commit 09a0784cd5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 637 additions and 54 deletions

View file

@ -100,11 +100,11 @@ jobs:
id: build
run: |
make docker VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
make docker-base VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
make publish-ecr VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
- name: Push GHCR
if: github.event_name != 'push' || startsWith(github.ref, 'refs/tags/')
working-directory: ./docker
run: |
make publish-ghcr VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
@ -144,12 +144,16 @@ jobs:
- name: Push manifest to ECR
working-directory: ./docker
run: make ecr-manifest VERSION=${{ needs.set-version.outputs.version }}
run: |
make ecr-manifest VERSION=${{ needs.set-version.outputs.version }}
make ecr-manifest INSTALL_TOOLKITS=false VERSION=${{ needs.set-version.outputs.version }}
- name: Push manifest to GHCR
if: github.event_name != 'push' || startsWith(github.ref, 'refs/tags/')
working-directory: ./docker
run: make ghcr-manifest VERSION=${{ needs.set-version.outputs.version }}
run: |
make ghcr-manifest VERSION=${{ needs.set-version.outputs.version }}
make ghcr-manifest INSTALL_TOOLKITS=false VERSION=${{ needs.set-version.outputs.version }}
# Currently broken: workflow deploy requires all versions
# deploy:

View file

@ -91,15 +91,35 @@ build-and-publish: build publish ## Build and publish.
docker: ## Build and run the Docker container
@echo "🚀 Building arcade and toolkit wheels..."
@make full-dist
@echo "Writing extras [fastapi, evals] to requirements.txt"
@cd arcade && poetry export --extras "fastapi evals" --output ../dist/requirements.txt
@echo "Writing extras requirements.txt"
@cd arcade && poetry export --extras "fastapi" --output ../dist/requirements.txt
@echo "🚀 Building Docker image"
@cd docker && make docker-build
@cd docker && make docker-run
.PHONY: docker-base
docker-base: ## Build and run the Docker container
@echo "🚀 Building arcade and toolkit wheels..."
@make full-dist
@echo "Writing extras requirements.txt"
@cd arcade && poetry export --extras "fastapi" --output ../dist/requirements.txt
@echo "🚀 Building Docker image"
@cd docker && INSTALL_TOOLKITS=false make docker-build
@cd docker && INSTALL_TOOLKITS=false make docker-run
.PHONY: publish-ecr
publish-ecr: ## Publish to the ECR
@cd docker && make publish-ecr
# Publish the base image - <ECR_ENDPOINT>/arcadeai/worker-base
@cd docker && INSTALL_TOOLKITS=false make publish-ecr
# Publish the image with toolkits - <ECR_ENDPOINT>/arcadeai/worker
@cd docker && INSTALL_TOOLKITS=true make publish-ecr
.PHONY: publish-ghcr
publish-ghcr: ## Publish to the GHCR
# Publish the base image - ghcr.io/arcadeai/worker-base
@cd docker && INSTALL_TOOLKITS=false make publish-ghcr
# Publish the image with toolkits - ghcr.io/arcadeai/worker
@cd docker && INSTALL_TOOLKITS=true make publish-ghcr
.PHONY: full-dist
full-dist: clean-dist ## Build all projects and copy wheels to ./dist
@ -121,19 +141,6 @@ full-dist: clean-dist ## Build all projects and copy wheels to ./dist
@echo "Reset version to default (0.1.0)"
@make unset-version
@echo "🛠️ Building all projects and copying wheels to ./dist"
# Build and copy wheels for each toolkit
# @for toolkit_dir in toolkits/*; do \
# if [ -d "$$toolkit_dir" ]; then \
# toolkit_name=$$(basename "$$toolkit_dir"); \
# echo "Building $$toolkit_name project..."; \
# poetry build; \
# cp dist/*.whl ../../dist/toolkits; \
# cd -; \
# fi; \
# done
@echo "✅ All toolkits built and wheels copied to ./dist"
.PHONY: clean-dist
clean-dist: ## Clean all built distributions

View file

@ -1,25 +1,20 @@
# Use a lightweight Python image
FROM python:3.10-slim
# Define build arguments with default values
ARG PORT=8001
ARG HOST=0.0.0.0
ARG VERSION=0.1.0
ARG VERSION=${VERSION:-0.1.0}
ARG INSTALL_TOOLKITS=true
# Set environment variables using the build arguments
ENV PORT=${PORT}
ENV HOST=${HOST}
ENV VERSION=${VERSION}
ENV OTEL_ENABLE=false
ENV ARCADE_WORK_DIR=/app
ENV TOOLKITS="arcade-code-sandbox,arcade-github,arcade-google,arcade-linkedin,arcade-math,arcade-search,arcade-slack,arcade-spotify,arcade-web,arcade-x,arcade-zoom"
# Install system dependencies
RUN apt-get update && apt-get install -y \
apt-utils \
build-essential \
libssl-dev \
libffi-dev \
python3-dev \
curl \
&& apt-get clean \
@ -30,6 +25,12 @@ WORKDIR /app/arcade
# Copy the parent directory contents into the container
COPY ./dist ./arcade /app/arcade/
# Copy the toolkits.txt file into the container
COPY ./docker/toolkits.txt /app/arcade/
# Expose the port
EXPOSE $PORT
# List files for debugging purposes
RUN ls -la /app/arcade/
@ -49,8 +50,10 @@ RUN if [ ! "$(echo ${VERSION} | grep -E '\.dev0$')" ]; then \
pip install .; \
fi
# Expose the port
EXPOSE $PORT
# Conditionally install toolkits.txt dependencies
RUN if [ "$INSTALL_TOOLKITS" = "true" ] ; then \
python -m pip install -r ./toolkits.txt ; \
fi
# Run the arcade workerup (hidden cli command)
COPY docker/start.sh /app/start.sh

View file

@ -2,24 +2,35 @@ VENDOR ?= ArcadeAI
PROJECT ?= ArcadeAI
SOURCE ?= https://github.com/ArcadeAI/arcade-ai
LICENSE ?= MIT
DESCRIPTION ?= "Arcade AI for LLM Tool Serving"
DESCRIPTION ?= "Arcade Worker for LLM Tool Serving"
REPOSITORY ?= arcadeai/worker
ECR_ENDPOINT ?= 471112909428.dkr.ecr.us-east-1.amazonaws.com
ARCH ?= arm64
ARCH ?= $(shell uname -m)
VERSION ?= 0.1.0.dev0
COMMIT ?= $(shell git describe --dirty --always --abbrev=15)
BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
IMAGE_NAME ?= worker
PORT ?= 8002
INSTALL_TOOLKITS ?= true
# If INSTALL_TOOLKITS is true, we are building the worker image with all toolkits
# Otherwise, we are building the worker image with no toolkits
ifeq ($(INSTALL_TOOLKITS), true)
REPOSITORY := $(REPOSITORY)
else
REPOSITORY := $(REPOSITORY)-base
endif
VERSION_TAG := $(VERSION)-$(ARCH)
.PHONY: docker-build
docker-build: ## Build the Docker container
@echo "🛠️ Building Docker image ($(VERSION)).."
@echo "🛠️ Building Docker image ($(VERSION_TAG)).."
@echo "- Commit: $(COMMIT)"
@echo "- Build Date: $(BUILD_DATE)"
@docker build --build-arg PORT=$(PORT) -f Dockerfile -t $(REPOSITORY):$(VERSION) .. \
@docker build --build-arg PORT=$(PORT) -f Dockerfile -t $(REPOSITORY):$(VERSION_TAG) \
--build-arg INSTALL_TOOLKITS=$(INSTALL_TOOLKITS) \
--build-arg PORT=$(PORT) \
--build-arg VERSION="$(VERSION)" \
--build-arg COMMIT="$(COMMIT)" \
@ -27,14 +38,16 @@ docker-build: ## Build the Docker container
--label=org.opencontainers.image.vendor="$(VENDOR)" \
--label=org.opencontainers.image.title="$(PROJECT)" \
--label=org.opencontainers.image.revision="$(COMMIT)" \
--label=org.opencontainers.image.version="$(VERSION)" \
--label=org.opencontainers.image.version="$(VERSION_TAG)" \
--label=org.opencontainers.image.created="$(BUILD_DATE)" \
--label=org.opencontainers.image.source="$(SOURCE)" \
--label=org.opencontainers.image.licenses="$(LICENSE)" \
--label=org.opencontainers.image.description=$(DESCRIPTION)
--label=org.opencontainers.image.description=$(DESCRIPTION) \
..
ecr-manifest: ## Make a manifest file for the image
@echo "🛠️ Build manifest file ($(VERSION)).."
@echo "🛠️ Build manifest file for $(REPOSITORY):$(VERSION).."
@echo "- Commit: $(COMMIT)"
@echo "- Build Date: $(BUILD_DATE)"
@export DOCKER_CLI_EXPERIMENTAL=enabled
@ -56,7 +69,7 @@ ecr-manifest: ## Make a manifest file for the image
@docker manifest push $(ECR_ENDPOINT)/$(REPOSITORY):latest
ghcr-manifest: ## Make a manifest file for the image
@echo "🛠️ Build manifest file ($(VERSION)).."
@echo "🛠️ Build manifest file for $(REPOSITORY):$(VERSION).."
@echo "- Commit: $(COMMIT)"
@echo "- Build Date: $(BUILD_DATE)"
@export DOCKER_CLI_EXPERIMENTAL=enabled
@ -80,17 +93,16 @@ ghcr-manifest: ## Make a manifest file for the image
.PHONY: docker-run
docker-run: ## Run the Docker container
@echo "\n🚀 Run the container with the following ..."
@echo ">>> docker run -d -p $(PORT):$(PORT) $(REPOSITORY):$(VERSION)"
@echo ">>> docker run -d -p $(PORT):$(PORT) $(REPOSITORY):$(VERSION_TAG)"
.PHONY: publish-ecr
publish-ecr:
@echo "🚚 Pushing the Agent image to ECR.."
@docker tag $(REPOSITORY):$(VERSION) $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)
@echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)"
@docker push $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)
@echo $(VERSION) | grep -q $(RC_PART) || { \
docker tag $(REPOSITORY):$(VERSION) $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
@docker tag $(REPOSITORY):$(VERSION_TAG) $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)
@echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)"
@docker push $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)
@echo $(VERSION_TAG) | grep -q $(RC_PART) || { \
docker tag $(REPOSITORY):$(VERSION_TAG) $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH)"; \
docker push $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
}
@ -98,20 +110,30 @@ publish-ecr:
.PHONY: publish-ghcr
publish-ghcr:
@echo "🚚 Pushing the Agent image to GHCR.."
@docker tag $(REPOSITORY):$(VERSION) ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)
@echo "- pushing ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)"
@docker push ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)
@docker tag $(REPOSITORY):$(VERSION_TAG) ghcr.io/$(REPOSITORY):$(VERSION_TAG)
@echo "- pushing ghcr.io/$(REPOSITORY):$(VERSION_TAG)"
@docker push ghcr.io/$(REPOSITORY):$(VERSION_TAG)
@echo $(VERSION) | grep -q $(RC_PART) || { \
docker tag $(REPOSITORY):$(VERSION) ghcr.io/$(REPOSITORY):latest-$(ARCH); \
docker tag $(REPOSITORY):$(VERSION_TAG) ghcr.io/$(REPOSITORY):latest-$(ARCH); \
echo "- pushing ghcr.io/$(REPOSITORY):latest-$(ARCH)"; \
docker push ghcr.io/$(REPOSITORY):latest-$(ARCH); \
}
.PHONY: ecr-login
ecr-login: # Login to ECR
ecr-login: ## Login to ECR
@aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(ECR_ENDPOINT)
.PHONY: gh-login
gh-login: ## Login to GHCR
@echo "🚚 Logging in to GHCR..."
@if [ -z "$(GHCR_PAT)" ]; then \
echo "Error: GHCR_PAT environment variable is not set"; \
exit 1; \
fi
@echo $(GHCR_PAT) | docker login ghcr.io -u arcadeai --password-stdin
.PHONY: help
help:
@echo "🛠️ Worker Docker Commands:\n"

115
docker/README.md Normal file
View file

@ -0,0 +1,115 @@
# Arcade Docker Compose Guide
This guide provides detailed instructions on how to set up and run Arcade using Docker Compose.
## Prerequisites
- **Docker** installed on your system. [Install Docker](https://docs.docker.com/get-docker/)
- **Docker Compose** installed. It comes bundled with Docker Desktop on Windows and macOS. For Linux, follow the [Docker Compose installation guide](https://docs.docker.com/compose/install/).
## Getting Started
### 1. Clone the Repository
Begin by cloning the Arcade repository:
```bash
git clone https://github.com/ArcadeAI/arcade-ai.git
```
Change to the `docker` directory:
```bash
cd arcade-ai/docker
```
### 2. Copy and Configure Environment Variables
Copy the example environment file to `.env`:
```bash
cp env.example .env
```
Open the `.env` file in your preferred text editor and fill in the required values. At a minimum, you **must** provide the `OPENAI_API_KEY`:
```env:.env
### LLM ###
OPENAI_API_KEY=your_openai_api_key_here
```
If you plan to use other Large Language Model (LLM) providers, add their API keys as well:
```env:.env
ANTHROPIC_API_KEY=your_anthropic_api_key_here
```
### 3. Run Docker Compose
Start the Arcade services using Docker Compose:
```bash
docker compose up -p arcade
```
This command will build and start all the services defined in the `docker-compose.yml` file and make their ports available to your host machine.
### 4. Verify the Engine is Running
In a separate terminal window, check if the engine is running:
```bash
curl http://localhost:9099/v1/health
```
You should receive a response indicating that the engine is healthy:
```json
{ "status": "healthy" }
```
You can also view the swagger docs at `http://localhost:9099/swagger/index.html`
## Adding Authentication Providers
Arcade supports various authentication providers. To add an auth provider, follow these steps:
### 1. Enable the Auth Provider in the Configuration
Edit the `docker.engine.yaml` file to enable the desired auth provider. For example, to enable Google authentication, modify the file as follows:
```yaml:docker.engine.yaml
auth:
providers:
- id: google
enabled: true # Change from false to true
```
### 2. Add Client ID and Secret to the `.env` File
Obtain the client ID and client secret from your auth provider and add them to the `.env` file:
```env:.env
GOOGLE_CLIENT_ID="your_google_client_id"
GOOGLE_CLIENT_SECRET="your_google_client_secret"
```
Repeat this step for any other auth providers you wish to enable.
### 3. Restart the Docker Compose Services
After making changes to the configuration, restart the services:
```bash
docker compose down
docker compose up -p arcade
```
## Troubleshooting
- **Engine Health Check Fails**: Ensure that all environment variables are correctly set in the `.env` file and that the services have started without errors.
- **Port Conflicts**: If the default ports are already in use, modify the ports in the `docker-compose.yml` file.
- **Authentication Errors**: Double-check the client IDs and secrets provided for auth providers.
NOTE: `arcade login` will not work within a docker container, you must copy your credentials into the container if you would like to use it.

62
docker/docker-compose.yml Normal file
View file

@ -0,0 +1,62 @@
version: '3.8'
services:
engine:
image: ghcr.io/arcadeai/engine:latest
container_name: arcade-engine
volumes:
- ./docker.engine.yaml:/bin/engine.yaml
- ./.env:/bin/.env
- ./arcade-engine.sqlite3:/app/arcade-engine.sqlite3
ports:
- "9099:9099"
depends_on:
redis:
condition: service_healthy
worker:
condition: service_started
networks:
arcade-network:
command: /bin/arcade-engine --config /bin/engine.yaml --env /bin/.env --migrate
worker:
# image: ghcr.io/arcadeai/arcade-ai:latest
image: arcade-worker:0.1.0
container_name: arcade-worker
ports:
- "8002:8002"
networks:
arcade-network:
redis:
image: redis/redis-stack:latest
container_name: arcade-redis
ports:
- "6379:6379"
- "8004:8002"
depends_on:
worker:
condition: service_started
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 3s
timeout: 3s
retries: 5
networks:
arcade-network:
nginx:
image: nginx:stable-alpine
container_name: arcade-nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- engine
networks:
- arcade-network
networks:
arcade-network:
driver: bridge

173
docker/docker.engine.yaml Normal file
View file

@ -0,0 +1,173 @@
telemetry:
environment: local
version: ${env:VERSION}
logging:
level: debug # debug, info, warn, error
encoding: console
api:
development: true
host: 0.0.0.0
port: 9099
analytics:
enabled: false
# Uncomment to enable rate limiter
# rate_limit:
# redis:
# addr: "localhost:6379"
# password: ""
# db: 2
# time_unit: m
# limit: 2
# write_timeout: 10
# read_timeout: 10
llm:
models:
- id: oai
openai:
api_key: ${env:OPENAI_API_KEY}
# - id: anthropic
# anthropic:
# api_key: ${env:ANTHROPIC_API_KEY}
# # model: "claude-3-5-sonnet-20240620"
#- id: ollama
# openai:
# base_url: "http://localhost:11434/v1"
# api_key: "ollama"
tools:
directors:
- id: default
enabled: true
workers:
- id: "localworker"
enabled: true
http:
uri: ${env:WORKER_URL}
timeout: 30
retry: 3
secret: ${env:ARCADE_WORKER_SECRET} # If not set, defaults to "dev" in development mode only
# Uncomment mock and comment http to start engine without live worker
# mock:
# enabled: true
security:
root_keys:
- id: key1
default: true
value: ${env:ROOT_KEY_1}
# - id: key2
# value: ${env:ROOT_KEY_2}
storage:
# postgres:
# user: postgres
# password: 123456
# host: localhost
# port: 5432
# db: arcade_engine
# sslmode: disable
sqlite: # Default for local development
connection_string: "/app/arcade-engine.sqlite3"
cache:
api_key_ttl: "10s"
redis:
addr: ${env:REDIS_HOST}:${env:REDIS_PORT}
password: ${env:REDIS_PASSWORD}
auth:
token_store:
redis:
addr: ${env:REDIS_HOST}:${env:REDIS_PORT}
password: ${env:REDIS_PASSWORD}
providers:
- id: default-atlassian
description: "The default Atlassian provider"
enabled: false
type: oauth2
provider_id: atlassian
client_id: ${env:ATLASSIAN_CLIENT_ID}
client_secret: ${env:ATLASSIAN_CLIENT_SECRET}
- id: default-discord
description: "The default Discord provider"
enabled: false
type: oauth2
provider_id: discord
client_id: ${env:DISCORD_CLIENT_ID}
client_secret: ${env:DISCORD_CLIENT_SECRET}
- id: default-dropbox
description: "The default Dropbox provider"
enabled: false
type: oauth2
provider_id: dropbox
client_id: ${env:DROPBOX_CLIENT_ID}
client_secret: ${env:DROPBOX_CLIENT_SECRET}
- id: default-github
description: "The default GitHub provider"
enabled: false
type: oauth2
provider_id: github
client_id: ${env:GITHUB_CLIENT_ID}
client_secret: ${env:GITHUB_CLIENT_SECRET}
- id: default-google
description: "The default Google provider"
enabled: false
type: oauth2
provider_id: google
client_id: ${env:GOOGLE_CLIENT_ID}
client_secret: ${env:GOOGLE_CLIENT_SECRET}
- id: default-linkedin
description: "The default LinkedIn provider"
enabled: false
type: oauth2
provider_id: linkedin
client_id: ${env:LINKEDIN_CLIENT_ID}
client_secret: ${env:LINKEDIN_CLIENT_SECRET}
- id: default-microsoft
description: "The default Microsoft provider"
enabled: false
type: oauth2
provider_id: microsoft
client_id: ${env:MICROSOFT_CLIENT_ID}
client_secret: ${env:MICROSOFT_CLIENT_SECRET}
- id: default-slack
description: "The default Slack provider"
enabled: false
type: oauth2
provider_id: slack
client_id: ${env:SLACK_CLIENT_ID}
client_secret: ${env:SLACK_CLIENT_SECRET}
- id: default-spotify
description: "The default Spotify provider"
enabled: false
type: oauth2
provider_id: spotify
client_id: ${env:SPOTIFY_CLIENT_ID}
client_secret: ${env:SPOTIFY_CLIENT_SECRET}
- id: default-x
description: "The default X provider"
enabled: false
type: oauth2
provider_id: x
client_id: ${env:X_CLIENT_ID}
client_secret: ${env:X_CLIENT_SECRET}
- id: default-zoom
description: "The default Zoom provider"
enabled: false
type: oauth2
provider_id: zoom
client_id: ${env:ZOOM_CLIENT_ID}
client_secret: ${env:ZOOM_CLIENT_SECRET}

48
docker/env.example Normal file
View file

@ -0,0 +1,48 @@
### LLM ###
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
### Auth providers ###
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=
LINKEDIN_CLIENT_ID=
LINKEDIN_CLIENT_SECRET=""
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=""
SLACK_CLIENT_ID=""
SLACK_CLIENT_SECRET=
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
X_CLIENT_ID=
X_CLIENT_SECRET=
ZOOM_CLIENT_ID=
ZOOM_CLIENT_SECRET=
### WORKER ###
WORKER_URL="http://arcade-worker:8002"
### Redis ###
REDIS_HOST=arcade-redis
REDIS_PORT=6379
REDIS_PASSWORD=""
RATE_LIMIT_REQS_PER_MIN=100
### ARCADE ###
ARCADE_HOME=/app
ARCADE_WORKER_SECRET=dev
ROOT_KEY_1=supersecret

47
docker/nginx.conf Normal file
View file

@ -0,0 +1,47 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
client_max_body_size 5M;
client_body_buffer_size 5M;
gzip on;
gzip_comp_level 2;
gzip_types text/plain text/css text/javascript application/javascript application/xml image/jpeg image/gif image/png;
gzip_vary on;
keepalive_timeout 300;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name arcade-engine:9099;
root /app;
client_max_body_size 10m;
location / {
proxy_pass http://arcade-engine:9099;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
}
}

View file

@ -1,10 +1,4 @@
#!/bin/bash
echo "Installing toolkits..."
for toolkit in $(echo $TOOLKITS | tr "," " "); do
echo "Installing $toolkit..."
pip install $toolkit
done
echo "Starting arcade..."
arcade workerup --host $HOST --port $PORT $([ "$OTEL_ENABLE" = "true" ] && echo "--otel-enable")

11
docker/toolkits.txt Normal file
View file

@ -0,0 +1,11 @@
arcade-code-sandbox
arcade-github
arcade-google
arcade-linkedin
arcade-math
arcade-search
arcade-slack
arcade-spotify
arcade-web
arcade-x
arcade-zoom

View file

@ -0,0 +1,10 @@
ARG VERSION=latest
# Base worker image
FROM ghcr.io/arcadeai/worker-base:${VERSION}
# Copy requirements and constraints
COPY toolkits.txt ./
# Install toolkits from file
RUN pip install -r toolkits.txt

View file

@ -0,0 +1,30 @@
## Custom Worker Image
This example shows how to build a custom worker image with toolkits.
### Requirements
- Docker
### Build
```
docker build -t custom-worker:0.1.0 .
```
### Run
```
docker run -p 8002:8002 custom-worker:0.1.0
```
### Change the Toolkits
To change the toolkits, edit the `toolkits.txt` file.
```
arcade-google==0.1.0
arcade-web==0.1.0
arcade-zoom==0.1.2
...
```

View file

@ -0,0 +1 @@
arcade-google

View file

@ -0,0 +1,17 @@
## Deploy a Custom Arcade Worker on Modal
### Requirements
- Python 3.10+
- Modal CLI
### Deploy
```bash
cd examples/serving-tools
modal deploy run-arcade-worker.py
```
### Changing the Toolkits
To change the toolkits, edit the `toolkits` list in the `run-arcade-worker.py` file.

View file

@ -0,0 +1,39 @@
import os
from modal import App, Image, asgi_app
# Define the FastAPI app
app = App("arcade-worker")
toolkits = ["arcade-google", "arcade-slack"]
image = (
Image.debian_slim()
.pip_install("arcade-ai[fastapi]")
.pip_install(toolkits)
.pip_install("fastapi>=0.115.3")
.pip_install("uvicorn>=0.24.0")
)
@app.function(image=image)
@asgi_app()
def fastapi_app():
from fastapi import FastAPI
from arcade.sdk import Toolkit
from arcade.worker.fastapi.worker import FastAPIWorker
web_app = FastAPI()
# Initialize app and Arcade FastAPIWorker
worker_secret = os.environ.get("ARCADE_WORKER_SECRET", "dev")
worker = FastAPIWorker(web_app, secret=worker_secret)
# Register toolkits we've installed
installed_toolkits = Toolkit.find_all_arcade_toolkits()
for toolkit in toolkits:
if toolkit in installed_toolkits:
worker.register_toolkit(toolkit)
return web_app