feat(templates): add Docker deployment templates
This commit is contained in:
parent
5136411258
commit
efbdbd82ed
4 changed files with 208 additions and 0 deletions
33
scripts/templates/docker/Dockerfile
Normal file
33
scripts/templates/docker/Dockerfile
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Agent Factory — Docker deployment template
|
||||||
|
# Runs a Claude Code agent system in an isolated container.
|
||||||
|
# Replace {{PROJECT_NAME}} and {{ANTHROPIC_API_KEY}} with real values
|
||||||
|
# (or pass them at runtime via .env / docker compose env_file).
|
||||||
|
|
||||||
|
FROM node:22-slim
|
||||||
|
|
||||||
|
# Install Claude Code globally
|
||||||
|
RUN npm install -g @anthropic-ai/claude-code
|
||||||
|
|
||||||
|
# Create a non-root agent user for security
|
||||||
|
RUN useradd -m -s /bin/bash agent
|
||||||
|
|
||||||
|
WORKDIR /home/agent/project
|
||||||
|
|
||||||
|
# Copy project files (adjust to your project structure)
|
||||||
|
COPY --chown=agent:agent . .
|
||||||
|
|
||||||
|
# Set up entrypoint script
|
||||||
|
COPY --chown=agent:agent docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
USER agent
|
||||||
|
|
||||||
|
# API key injected at runtime — never bake into image
|
||||||
|
ENV ANTHROPIC_API_KEY={{ANTHROPIC_API_KEY}}
|
||||||
|
|
||||||
|
# Health check — entrypoint writes /tmp/agent-health on each beat
|
||||||
|
HEALTHCHECK --interval=60s --timeout=10s --start-period=30s --retries=3 \
|
||||||
|
CMD test -f /tmp/agent-health && \
|
||||||
|
test $(( $(date +%s) - $(date +%s -r /tmp/agent-health 2>/dev/null || echo 0) )) -lt 300
|
||||||
|
|
||||||
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
77
scripts/templates/docker/README.md
Normal file
77
scripts/templates/docker/README.md
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Docker Deployment
|
||||||
|
|
||||||
|
Run your agent system in an isolated Docker container.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- `.env` file with your API key (see below)
|
||||||
|
- Agent system built with `/agent-factory:build`
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Copy these files to your project root:
|
||||||
|
- `Dockerfile`
|
||||||
|
- `docker-compose.yml`
|
||||||
|
- `docker-entrypoint.sh`
|
||||||
|
|
||||||
|
2. Create `.env` in your project root:
|
||||||
|
```
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
AGENT_BEAT_INTERVAL=3600
|
||||||
|
```
|
||||||
|
Add `.env` to `.gitignore` — never commit API keys.
|
||||||
|
|
||||||
|
3. Replace `{{PROJECT_NAME}}` in `docker-compose.yml` with your project name.
|
||||||
|
|
||||||
|
## Build and run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the image
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
# Start in background
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
## Volume mounts
|
||||||
|
|
||||||
|
| Host path | Container path | Purpose |
|
||||||
|
|-----------|---------------|---------|
|
||||||
|
| `./data` | `/home/agent/project/data` | Run state, outputs |
|
||||||
|
| `./memory` | `/home/agent/project/memory` | Long-term memory files |
|
||||||
|
| `./budget` | `/home/agent/project/budget` | Budget tracking |
|
||||||
|
| `./logs` | `/home/agent/project/logs` | Agent activity logs |
|
||||||
|
|
||||||
|
These directories are created automatically on first run.
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
| Variable | Required | Default | Description |
|
||||||
|
|----------|----------|---------|-------------|
|
||||||
|
| `ANTHROPIC_API_KEY` | Yes | — | Your Anthropic API key |
|
||||||
|
| `AGENT_BEAT_INTERVAL` | No | `3600` | Seconds between heartbeat runs |
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- **Never bake the API key into the image.** Always pass it via `.env` or `--env-file`.
|
||||||
|
- **Never mount the Docker socket** (`/var/run/docker.sock`) — the agent does not need Docker control.
|
||||||
|
- The container runs as a non-root `agent` user.
|
||||||
|
- `no-new-privileges:true` prevents privilege escalation.
|
||||||
|
- `restart: unless-stopped` ensures the agent recovers from crashes automatically.
|
||||||
|
|
||||||
|
## Health check
|
||||||
|
|
||||||
|
The entrypoint writes a timestamp to `/tmp/agent-health` on each beat.
|
||||||
|
Docker's `HEALTHCHECK` verifies this file is updated within 5 minutes.
|
||||||
|
|
||||||
|
Check health status:
|
||||||
|
```bash
|
||||||
|
docker inspect --format='{{.State.Health.Status}}' {{PROJECT_NAME}}-agent
|
||||||
|
```
|
||||||
28
scripts/templates/docker/docker-compose.yml
Normal file
28
scripts/templates/docker/docker-compose.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Agent Factory — docker-compose deployment template
|
||||||
|
# Usage: docker compose up -d
|
||||||
|
# Requires: .env file with ANTHROPIC_API_KEY=...
|
||||||
|
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
agent:
|
||||||
|
container_name: {{PROJECT_NAME}}-agent
|
||||||
|
build: .
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
# Persistent data directories — survive container restarts
|
||||||
|
- ./data:/home/agent/project/data
|
||||||
|
- ./memory:/home/agent/project/memory
|
||||||
|
- ./budget:/home/agent/project/budget
|
||||||
|
- ./logs:/home/agent/project/logs
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
read_only: false
|
||||||
|
# No Docker socket mount — agent cannot control the Docker daemon
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
70
scripts/templates/docker/docker-entrypoint.sh
Normal file
70
scripts/templates/docker/docker-entrypoint.sh
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Agent Factory — Docker container entrypoint
|
||||||
|
# Validates environment, then runs the heartbeat runner in a loop.
|
||||||
|
# Bash 3.2 compatible.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
HEALTH_FILE="/tmp/agent-health"
|
||||||
|
LOG_DIR="/home/agent/project/logs"
|
||||||
|
HEARTBEAT_SCRIPT="/home/agent/project/automation/heartbeat-runner.sh"
|
||||||
|
|
||||||
|
# Graceful shutdown handler
|
||||||
|
shutdown_handler() {
|
||||||
|
echo "[entrypoint] SIGTERM received — shutting down gracefully"
|
||||||
|
if [ -n "$RUNNER_PID" ]; then
|
||||||
|
kill "$RUNNER_PID" 2>/dev/null || true
|
||||||
|
wait "$RUNNER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -f "$HEALTH_FILE"
|
||||||
|
echo "[entrypoint] Shutdown complete"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
trap shutdown_handler TERM INT
|
||||||
|
|
||||||
|
# Validate required environment variables
|
||||||
|
if [ -z "$ANTHROPIC_API_KEY" ]; then
|
||||||
|
echo "[entrypoint] ERROR: ANTHROPIC_API_KEY is not set" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create required directories
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
mkdir -p "/home/agent/project/data"
|
||||||
|
mkdir -p "/home/agent/project/memory"
|
||||||
|
mkdir -p "/home/agent/project/budget"
|
||||||
|
mkdir -p "/home/agent/project/pipeline-output"
|
||||||
|
|
||||||
|
echo "[entrypoint] Starting agent container at $(date)"
|
||||||
|
echo "[entrypoint] Project: $(basename /home/agent/project)"
|
||||||
|
|
||||||
|
# Verify Claude Code is available
|
||||||
|
if ! command -v claude >/dev/null 2>&1; then
|
||||||
|
echo "[entrypoint] ERROR: claude command not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run heartbeat runner in loop
|
||||||
|
while true; do
|
||||||
|
# Write health check timestamp
|
||||||
|
date > "$HEALTH_FILE"
|
||||||
|
|
||||||
|
if [ -f "$HEARTBEAT_SCRIPT" ]; then
|
||||||
|
echo "[entrypoint] Running heartbeat at $(date)"
|
||||||
|
bash "$HEARTBEAT_SCRIPT" >> "$LOG_DIR/agent.log" 2>&1 &
|
||||||
|
RUNNER_PID=$!
|
||||||
|
wait $RUNNER_PID
|
||||||
|
RUNNER_PID=""
|
||||||
|
else
|
||||||
|
echo "[entrypoint] WARNING: $HEARTBEAT_SCRIPT not found — sleeping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sleep between beats (default 3600s = 1 hour)
|
||||||
|
BEAT_INTERVAL="${AGENT_BEAT_INTERVAL:-3600}"
|
||||||
|
echo "[entrypoint] Sleeping ${BEAT_INTERVAL}s until next beat"
|
||||||
|
sleep "$BEAT_INTERVAL" &
|
||||||
|
RUNNER_PID=$!
|
||||||
|
wait $RUNNER_PID
|
||||||
|
RUNNER_PID=""
|
||||||
|
done
|
||||||
Loading…
Add table
Add a link
Reference in a new issue