Skip to content

Docker w module Chat

Szczegółowa dokumentacja wykonania Chat w kontenerach Docker.

Przegląd

Docker executor w module Chat umożliwia:

  • ✅ Izolowane środowisko wykonania dla każdej sesji
  • ✅ Automatyczne klonowanie repozytoriów Git
  • ✅ Instalację Claude CLI w kontenerze
  • ✅ Pełny kontekst projektu bez modyfikacji lokalnego środowiska
  • ✅ Łatwe zarządzanie zależnościami i wersjami

Architektura

Konfiguracja Docker worker

config.yml

Worker musi mieć docker_compose_path:

yaml
workers:
  - name: "SemBot Chat"
    path: "/Users/user/Projects/sembot-chat"
    tags: ["sb"]
    docker_compose_path: "/path/to/docker/sembot-chat"
    git_repository: "[email protected]:org/sembot-angular.git,[email protected]:org/sembot-laravel.git"
    git_branch: "master,master"

Struktura katalogu Docker

embedded/resources/docker/sembot-chat/
├── docker-compose.yml
├── start.sh
└── scripts/               (opcjonalne, można współdzielić)

docker-compose.yml

Przykład kompletnego pliku

yaml
services:
  chat-worker:
    image: node:18-alpine
    container_name: chat-${SESSION_ID}
    working_dir: /app
    environment:
      SESSION_ID: ${SESSION_ID}
      WORKER_TAGS: ${WORKER_TAGS}
      WORKER_REPOS: ${WORKER_REPOS}
      WORKER_BRANCHES: ${WORKER_BRANCHES}
    volumes:
      # CONFIG - git credentials
      - ~/.ssh:/root/.ssh:ro
      - ~/.gitconfig:/root/.gitconfig:ro
      - ~/.git-credentials:/root/.git-credentials:ro

      # CLAUDE - global config from host
      - ~/.claude:/root/.claude
      - ~/.claude.json:/root/.claude.json
      - ~/.claude.json.backup:/root/.claude.json.backup

      # GEMINI - global config from host
      - ~/.gemini:/root/.gemini

      # CODEX
      - ~/.codex:/root/.codex

      # SCRIPTS
      - ./start.sh:/app/start.sh:ro
      - ./scripts:/app/scripts:ro

      # CHAT SESSION - only chat directory is mounted
      - ${CHAT_DIR}/${SESSION_ID}:/app/chat

      # CLAUDE CODE RESOURCES - worker-specific configs
      - ./resources/.claude:/app/worker/.claude:ro
      - ./resources/.gemini:/app/worker/.gemini:ro
    command: ["sh", "/app/start.sh"]
    restart: unless-stopped
    networks:
      - chat-network

networks:
  chat-network:
    driver: bridge

Zmienne środowiskowe

ZmiennaOpisPrzykład
SESSION_IDUUID sesji chatabc123-def456-...
WORKER_TAGSTagi workera (rozdzielone przecinkami)sbf,sb
WORKER_REPOSRepozytoria Git (rozdzielone przecinkami)[email protected]:org/repo1.git,[email protected]:org/repo2.git
WORKER_BRANCHESGałęzie Git (rozdzielone przecinkami)master
CHAT_DIRKatalog .chat/path/to/.chat

Volumes

Git credentials (read-only)

yaml
- ~/.ssh:/root/.ssh:ro
- ~/.gitconfig:/root/.gitconfig:ro
- ~/.git-credentials:/root/.git-credentials:ro

Montuje klucze SSH i konfigurację Git z hosta do kontenera (tylko do odczytu).

Claude config (read-write)

yaml
- ~/.claude:/root/.claude
- ~/.claude.json:/root/.claude.json
- ~/.claude.json.backup:/root/.claude.json.backup

Montuje globalną konfigurację Claude z hosta. Container może modyfikować historię sesji.

Gemini config (read-write)

yaml
- ~/.gemini:/root/.gemini

Montuje konfigurację Gemini (jeśli używany).

Scripts (read-only)

yaml
- ./start.sh:/app/start.sh:ro
- ./scripts:/app/scripts:ro

Montuje skrypty startowe i wspólne skrypty pomocnicze.

Chat session directory (read-write)

yaml
- ${CHAT_DIR}/${SESSION_ID}:/app/chat

Montuje katalog sesji chat:

.chat/abc123-def456-.../
├── chat.json
├── container.log
├── messages/
└── llm_outputs/

Container zapisuje tutaj:

  • Logi kontenera (container.log)
  • Q&A pairs (messages/msg_*.json)
  • Raw output LLM (llm_outputs/*.json)

Worker-specific resources (read-only)

yaml
- ./resources/.claude:/app/worker/.claude:ro
- ./resources/.gemini:/app/worker/.gemini:ro

Montuje zasoby Claude Code specyficzne dla workera (np. custom prompts, hooks).

Image

Używamy node:18-alpine jako bazowego image:

  • Lekki obraz Alpine Linux
  • Node.js 18 (wymagane dla Claude CLI)
  • Małe zużycie dysku i pamięci

Możesz użyć innych obrazów:

  • node:20-alpine - nowsza wersja Node
  • ubuntu:22.04 - pełny Ubuntu (większy)
  • Custom Dockerfile z dodatkowymi zależnościami

Network

yaml
networks:
  chat-network:
    driver: bridge

Każda sesja ma własną sieć Docker. Izolacja między sesjami.

start.sh

Główny skrypt startowy kontenera.

Przykład

bash
#!/bin/sh
set -e

# Redirect all output to log file in chat directory
LOG_FILE="/app/chat/container.log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo "$(date): Container started for session: $SESSION_ID"

SCRIPTS_DIR="/app/scripts"

# 1. Install dependencies
if [ -f "${SCRIPTS_DIR}/init_dependencies.sh" ]; then
    echo "$(date): 📦 Installing dependencies..."
    sh ${SCRIPTS_DIR}/init_dependencies.sh
else
    echo "$(date): ⚠️  init_dependencies.sh not found"
fi

# 2. Install Claude CLI
if [ -f "${SCRIPTS_DIR}/claude_install.sh" ]; then
    echo "$(date): ⬇️  Installing Claude CLI..."
    sh ${SCRIPTS_DIR}/claude_install.sh
else
    echo "$(date): ⚠️  claude_install.sh not found"
fi

# 3. Setup SSH
if [ -f "${SCRIPTS_DIR}/ssh_setup.sh" ]; then
    echo "$(date): 🔑 Setting up SSH..."
    sh ${SCRIPTS_DIR}/ssh_setup.sh
else
    echo "$(date): ⚠️  ssh_setup.sh not found"
fi

# 4. Validate and setup Claude config
if [ -f "/root/.claude.json.backup" ]; then
    echo "$(date): 🔧 Setting up Claude config..."
    if jq empty /root/.claude.json.backup 2>/dev/null; then
        echo "$(date): ✅ Claude config is valid"
        # Overwrite without deletion to avoid "Resource busy" error
        cat /root/.claude.json.backup > /root/.claude.json
        echo "$(date): ✅ Claude config copied"
    else
        echo "$(date): ❌ Claude config is invalid JSON"
        exit 1
    fi
else
    echo "$(date): ⚠️  No Claude config backup found"
fi

# 5. Clone repositories
if [ -f "${SCRIPTS_DIR}/project_repositories_multi.sh" ]; then
    echo "$(date): 📂 Cloning repositories..."
    sh ${SCRIPTS_DIR}/project_repositories_multi.sh
else
    echo "$(date): ⚠️  project_repositories_multi.sh not found"
fi

# 6. Verify Claude CLI installation
if command -v claude >/dev/null 2>&1; then
    CLAUDE_VERSION=$(claude --version 2>&1 || echo "unknown")
    echo "$(date): ✅ Claude CLI installed: $CLAUDE_VERSION"
else
    echo "$(date): ❌ Claude CLI not found in PATH"
    exit 1
fi

echo "$(date): ✅ Container ready for session: $SESSION_ID"
echo "$(date): 📁 Working directory: $(pwd)"
echo "$(date): 📋 Workers: $WORKER_TAGS"

# Keep container running
tail -f /dev/null

Logowanie

Wszystkie operacje są logowane do /app/chat/container.log:

bash
exec > >(tee -a "$LOG_FILE") 2>&1

To przekierowuje stdout i stderr do pliku i konsoli jednocześnie.

Kroki inicjalizacji

  1. Install dependencies - init_dependencies.sh

    • git, bash, curl, jq, openssh-client
  2. Install Claude CLI - claude_install.sh

    • Pobiera i instaluje Claude CLI
    • Dodaje do PATH
  3. Setup SSH - ssh_setup.sh

    • Uruchamia ssh-agent
    • Dodaje klucze SSH
    • Testuje połączenie z GitHub
  4. Validate Claude config

    • Sprawdza czy .claude.json.backup jest valid JSON
    • Kopiuje do .claude.json (bez usuwania, aby uniknąć "Resource busy")
  5. Clone repositories - project_repositories_multi.sh

    • Parsuje WORKER_REPOS i WORKER_BRANCHES
    • Klonuje każde repozytorium do /app/worker/{repo-name}
  6. Verify installation

    • Sprawdza czy claude jest dostępny
    • Pokazuje wersję
  7. Keep container running

    • tail -f /dev/null - utrzymuje kontener działającym

Wspólne skrypty

Skrypty w embedded/resources/docker/scripts/ są współdzielone między różnymi workerami.

init_dependencies.sh

bash
#!/bin/sh
# Install basic dependencies needed for chat worker

apk add --no-cache \
    git \
    bash \
    curl \
    jq \
    openssh-client

echo "✅ Dependencies installed"

claude_install.sh

bash
#!/bin/sh
# Install Claude CLI

CLAUDE_VERSION="latest"
INSTALL_DIR="/usr/local/bin"

# Download and install
curl -fsSL https://claude.ai/cli/install.sh | sh -s -- --install-dir "$INSTALL_DIR"

# Verify installation
if command -v claude >/dev/null 2>&1; then
    echo "✅ Claude CLI installed: $(claude --version)"
else
    echo "❌ Claude CLI installation failed"
    exit 1
fi

ssh_setup.sh

bash
#!/bin/sh
# Setup SSH agent and add keys

# Start ssh-agent
eval $(ssh-agent -s)

# Add all SSH keys
for key in /root/.ssh/id_*; do
    if [ -f "$key" ] && [ "${key##*.}" != "pub" ]; then
        ssh-add "$key" 2>/dev/null || true
    fi
done

# Test connection
ssh -T [email protected] 2>&1 | head -n 1

echo "✅ SSH setup complete"

project_repositories_multi.sh

bash
#!/bin/sh
# Clone multiple repositories (comma-separated)

if [ -z "$WORKER_REPOS" ]; then
    echo "No repositories to clone"
    exit 0
fi

# Convert comma-separated strings to arrays
IFS=',' read -ra REPO_ARRAY <<< "$WORKER_REPOS"
IFS=',' read -ra BRANCH_ARRAY <<< "$WORKER_BRANCHES"

INDEX=0
for REPO in "${REPO_ARRAY[@]}"; do
    # Get corresponding branch (default: main)
    BRANCH="${BRANCH_ARRAY[$INDEX]}"
    [ -z "$BRANCH" ] && BRANCH="main"

    # Extract repo name
    REPO_NAME=$(basename "$REPO" .git)
    TARGET_DIR="/app/worker/$REPO_NAME"

    echo "Cloning $REPO_NAME (branch: $BRANCH)..."

    # Clone repository
    git clone --depth 1 --branch "$BRANCH" --single-branch "$REPO" "$TARGET_DIR"

    if [ $? -eq 0 ]; then
        echo "✅ Cloned: $REPO_NAME"
    else
        echo "❌ Failed to clone: $REPO_NAME"
    fi

    INDEX=$((INDEX + 1))
done

echo "✅ All repositories cloned"

Workflow wykonania

1. Utworzenie sesji z Docker

2. Wysłanie wiadomości przez Docker

3. Zatrzymanie kontenera

Zarządzanie zasobami

CPU i pamięć

Domyślnie kontenery nie mają limitów. Możesz dodać:

yaml
services:
  chat-worker:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 1G

Dysk

Chat nie generuje dużo danych:

  • Katalog sesji: ~10-50 MB
  • Sklonowane repozytoria: zależnie od projektu (50-500 MB)
  • Claude CLI: ~100 MB

Razem: ~200-700 MB na sesję

Czyszczenie

Automatyczne

Kontenery są usuwane po docker-compose down.

Ręczne

bash
# Zatrzymaj wszystkie kontenery chat
docker ps | grep "chat-" | awk '{print $1}' | xargs docker stop

# Usuń wszystkie kontenery chat
docker ps -a | grep "chat-" | awk '{print $1}' | xargs docker rm

# Wyczyść volumes (UWAGA: usuwa dane!)
docker volume prune -f

Troubleshooting Docker

Problem: Kontener nie startuje

Debug:

bash
# Sprawdź logi Docker Compose
cd .docker/chat-{uuid}/
docker-compose logs

# Sprawdź logi kontenera
docker logs chat-{uuid}

Częste przyczyny:

  • Brak docker_compose_path w konfiguracji workera
  • Nieprawidłowe zmienne środowiskowe
  • Brak dostępu do SSH keys
  • Nieprawidłowy .claude.json

Problem: Claude CLI nie działa

Debug:

bash
# Wejdź do kontenera
docker exec -it chat-{uuid} sh

# Sprawdź instalację
which claude
claude --version

# Sprawdź PATH
echo $PATH

# Sprawdź config
cat /root/.claude.json | jq .

Fix:

bash
# Reinstaluj Claude CLI w kontenerze
docker exec -it chat-{uuid} sh -c "curl -fsSL https://claude.ai/cli/install.sh | sh"

Problem: Nie może sklonować repozytorium

Debug:

bash
# Sprawdź SSH w kontenerze
docker exec -it chat-{uuid} ssh -T [email protected]

# Sprawdź klucze SSH
docker exec -it chat-{uuid} ls -la /root/.ssh/

# Sprawdź zmienne środowiskowe
docker exec -it chat-{uuid} env | grep WORKER

Fix:

  • Upewnij się że klucze SSH są zamontowane: ~/.ssh:/root/.ssh:ro
  • Sprawdź uprawnienia kluczy: chmod 600 ~/.ssh/id_*
  • Dodaj klucze do ssh-agent w kontenerze

Problem: "Resource busy" przy kopiowaniu .claude.json

Przyczyna: Plik jest w użyciu przez montowanie volume

Fix: Użyj cat zamiast rm && cp:

bash
cat /root/.claude.json.backup > /root/.claude.json

Problem: Brak dostępu do plików w /app/chat

Debug:

bash
# Sprawdź volume mounts
docker inspect chat-{uuid} | jq '.[0].Mounts'

# Sprawdź uprawnienia
docker exec -it chat-{uuid} ls -la /app/chat/

Fix:

  • Sprawdź czy ${CHAT_DIR} jest poprawnie ustawiony
  • Sprawdź uprawnienia katalogu .chat/ na hoście

Najlepsze praktyki

1. Używaj Alpine images

node:18-alpine (100 MB) ❌ node:18 (900 MB)

2. Montuj tylko potrzebne katalogi

❌ Nie montuj całego home directory ✅ Montuj tylko .ssh, .claude, etc.

3. Używaj read-only volumes gdzie możliwe

yaml
- ~/.ssh:/root/.ssh:ro
- ./scripts:/app/scripts:ro

4. Loguj wszystko do pliku

bash
exec > >(tee -a "$LOG_FILE") 2>&1

Ułatwia debugging przez /docker/logs endpoint.

5. Weryfikuj instalację przed użyciem

bash
if command -v claude >/dev/null 2>&1; then
    echo "✅ Claude CLI ready"
else
    echo "❌ Claude CLI not found"
    exit 1
fi

6. Używaj shallow clone dla repozytoriów

bash
git clone --depth 1 --single-branch

Szybsze klonowanie, mniej miejsca.

7. Czyszcz stare kontenery regularnie

bash
# Cron job do czyszczenia kontenerów starszych niż 7 dni
docker ps -a --filter "name=chat-" --format "{{.ID}} {{.CreatedAt}}" | \
    awk '$2 < "'$(date -d '7 days ago' '+%Y-%m-%d')'" {print $1}' | \
    xargs docker rm -f

Następne kroki