359 lines
13 KiB
Docker
359 lines
13 KiB
Docker
# Optimized Multi-stage Dockerfile for Attune workers
|
|
# This Dockerfile minimizes layer invalidation by selectively copying only required crates
|
|
#
|
|
# Key optimizations:
|
|
# 1. Copy only Cargo.toml files first to cache dependency downloads
|
|
# 2. Build dummy binaries to cache compiled dependencies
|
|
# 3. Copy only worker and common crates (not all crates)
|
|
# 4. Use BuildKit cache mounts for cargo registry and build artifacts
|
|
#
|
|
# Supports building different worker variants with different runtime capabilities
|
|
#
|
|
# Usage:
|
|
# docker build --target worker-base -t attune-worker:base -f docker/Dockerfile.worker.optimized .
|
|
# docker build --target worker-python -t attune-worker:python -f docker/Dockerfile.worker.optimized .
|
|
# docker build --target worker-node -t attune-worker:node -f docker/Dockerfile.worker.optimized .
|
|
# docker build --target worker-full -t attune-worker:full -f docker/Dockerfile.worker.optimized .
|
|
|
|
ARG RUST_VERSION=1.92
|
|
ARG DEBIAN_VERSION=bookworm
|
|
ARG PYTHON_VERSION=3.11
|
|
ARG NODE_VERSION=20
|
|
|
|
# ============================================================================
|
|
# Stage 1: Planner - Extract dependency information
|
|
# ============================================================================
|
|
FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS planner
|
|
|
|
# Install build dependencies
|
|
RUN apt-get update && apt-get install -y \
|
|
pkg-config \
|
|
libssl-dev \
|
|
ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /build
|
|
|
|
# Copy only Cargo.toml and Cargo.lock
|
|
COPY Cargo.toml Cargo.lock ./
|
|
|
|
# Copy all crate manifests (required for workspace resolution)
|
|
COPY crates/common/Cargo.toml ./crates/common/Cargo.toml
|
|
COPY crates/api/Cargo.toml ./crates/api/Cargo.toml
|
|
COPY crates/executor/Cargo.toml ./crates/executor/Cargo.toml
|
|
COPY crates/sensor/Cargo.toml ./crates/sensor/Cargo.toml
|
|
COPY crates/core-timer-sensor/Cargo.toml ./crates/core-timer-sensor/Cargo.toml
|
|
COPY crates/worker/Cargo.toml ./crates/worker/Cargo.toml
|
|
COPY crates/notifier/Cargo.toml ./crates/notifier/Cargo.toml
|
|
COPY crates/cli/Cargo.toml ./crates/cli/Cargo.toml
|
|
|
|
# Create dummy source files to satisfy cargo
|
|
RUN mkdir -p crates/common/src && echo "fn main() {}" > crates/common/src/lib.rs
|
|
RUN mkdir -p crates/api/src && echo "fn main() {}" > crates/api/src/main.rs
|
|
RUN mkdir -p crates/executor/src && echo "fn main() {}" > crates/executor/src/main.rs
|
|
RUN mkdir -p crates/executor/benches && echo "fn main() {}" > crates/executor/benches/context_clone.rs
|
|
RUN mkdir -p crates/sensor/src && echo "fn main() {}" > crates/sensor/src/main.rs
|
|
RUN mkdir -p crates/core-timer-sensor/src && echo "fn main() {}" > crates/core-timer-sensor/src/main.rs
|
|
RUN mkdir -p crates/worker/src && echo "fn main() {}" > crates/worker/src/main.rs
|
|
RUN mkdir -p crates/notifier/src && echo "fn main() {}" > crates/notifier/src/main.rs
|
|
RUN mkdir -p crates/cli/src && echo "fn main() {}" > crates/cli/src/main.rs
|
|
|
|
# Copy SQLx metadata
|
|
COPY .sqlx/ ./.sqlx/
|
|
|
|
# Build dependencies only (with dummy source)
|
|
# This layer is cached and only invalidated when dependencies change
|
|
# - registry/git use sharing=shared (cargo handles concurrent access safely)
|
|
# - target uses private cache for planner stage
|
|
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=shared \
|
|
--mount=type=cache,target=/usr/local/cargo/git,sharing=shared \
|
|
--mount=type=cache,target=/build/target,id=target-worker-planner \
|
|
cargo build --release --bin attune-worker || true
|
|
|
|
# ============================================================================
|
|
# Stage 2: Builder - Compile the worker binary
|
|
# ============================================================================
|
|
FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS builder
|
|
|
|
# Install build dependencies
|
|
RUN apt-get update && apt-get install -y \
|
|
pkg-config \
|
|
libssl-dev \
|
|
ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /build
|
|
|
|
# Copy workspace configuration
|
|
COPY Cargo.toml Cargo.lock ./
|
|
|
|
# Copy all crate manifests (required for workspace resolution)
|
|
COPY crates/common/Cargo.toml ./crates/common/Cargo.toml
|
|
COPY crates/api/Cargo.toml ./crates/api/Cargo.toml
|
|
COPY crates/executor/Cargo.toml ./crates/executor/Cargo.toml
|
|
COPY crates/sensor/Cargo.toml ./crates/sensor/Cargo.toml
|
|
COPY crates/core-timer-sensor/Cargo.toml ./crates/core-timer-sensor/Cargo.toml
|
|
COPY crates/worker/Cargo.toml ./crates/worker/Cargo.toml
|
|
COPY crates/notifier/Cargo.toml ./crates/notifier/Cargo.toml
|
|
COPY crates/cli/Cargo.toml ./crates/cli/Cargo.toml
|
|
|
|
# Create dummy source files for workspace members that won't be built
|
|
# This satisfies workspace resolution without copying full source
|
|
RUN mkdir -p crates/api/src && echo "fn main() {}" > crates/api/src/main.rs
|
|
RUN mkdir -p crates/executor/src && echo "fn main() {}" > crates/executor/src/main.rs
|
|
RUN mkdir -p crates/executor/benches && echo "fn main() {}" > crates/executor/benches/context_clone.rs
|
|
RUN mkdir -p crates/sensor/src && echo "fn main() {}" > crates/sensor/src/main.rs
|
|
RUN mkdir -p crates/core-timer-sensor/src && echo "fn main() {}" > crates/core-timer-sensor/src/main.rs
|
|
RUN mkdir -p crates/notifier/src && echo "fn main() {}" > crates/notifier/src/main.rs
|
|
RUN mkdir -p crates/cli/src && echo "fn main() {}" > crates/cli/src/main.rs
|
|
|
|
# Copy SQLx metadata
|
|
COPY .sqlx/ ./.sqlx/
|
|
|
|
# Copy migrations (required by common crate)
|
|
COPY migrations/ ./migrations/
|
|
|
|
# Copy ONLY the crates needed for worker
|
|
# This is the key optimization: changes to api/executor/sensor/notifier/cli won't invalidate this layer
|
|
COPY crates/common/ ./crates/common/
|
|
COPY crates/worker/ ./crates/worker/
|
|
|
|
# Build the worker binary
|
|
# Dependencies are already cached from planner stage
|
|
# - registry/git use sharing=shared (concurrent builds are safe)
|
|
# - target uses dedicated cache for worker builds (all workers share same binary)
|
|
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=shared \
|
|
--mount=type=cache,target=/usr/local/cargo/git,sharing=shared \
|
|
--mount=type=cache,target=/build/target,id=target-worker-builder \
|
|
cargo build --release --bin attune-worker && \
|
|
cp /build/target/release/attune-worker /build/attune-worker
|
|
|
|
# Verify the binary was built
|
|
RUN ls -lh /build/attune-worker && \
|
|
file /build/attune-worker && \
|
|
/build/attune-worker --version || echo "Version check skipped"
|
|
|
|
# ============================================================================
|
|
# Stage 3a: Base Worker (Shell only)
|
|
# Runtime capabilities: shell
|
|
# Use case: Lightweight workers for shell scripts and basic automation
|
|
# ============================================================================
|
|
FROM debian:${DEBIAN_VERSION}-slim AS worker-base
|
|
|
|
# Install runtime dependencies
|
|
RUN apt-get update && apt-get install -y \
|
|
ca-certificates \
|
|
libssl3 \
|
|
curl \
|
|
bash \
|
|
procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create worker user and directories
|
|
# Note: /opt/attune/packs is mounted as a volume at runtime, not copied in
|
|
RUN useradd -m -u 1000 attune && \
|
|
mkdir -p /opt/attune/packs /opt/attune/logs && \
|
|
chown -R attune:attune /opt/attune
|
|
|
|
WORKDIR /opt/attune
|
|
|
|
# Copy worker binary from builder
|
|
COPY --from=builder /build/attune-worker /usr/local/bin/attune-worker
|
|
|
|
# Copy configuration template
|
|
COPY config.docker.yaml ./config.yaml
|
|
|
|
# Note: Packs are NOT copied into the image
|
|
# They are mounted as a volume at runtime from the packs_data volume
|
|
# The init-packs service populates the packs_data volume from ./packs directory
|
|
|
|
# Switch to non-root user
|
|
USER attune
|
|
|
|
# Environment variables
|
|
ENV ATTUNE_WORKER_RUNTIMES="shell"
|
|
ENV ATTUNE_WORKER_TYPE="container"
|
|
ENV RUST_LOG=info
|
|
ENV ATTUNE_CONFIG=/opt/attune/config.yaml
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
CMD pgrep -f attune-worker || exit 1
|
|
|
|
# Run the worker
|
|
CMD ["/usr/local/bin/attune-worker"]
|
|
|
|
# ============================================================================
|
|
# Stage 3b: Python Worker (Shell + Python)
|
|
# Runtime capabilities: shell, python
|
|
# Use case: Python actions and scripts with dependencies
|
|
# ============================================================================
|
|
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_VERSION} AS worker-python
|
|
|
|
# Install system dependencies
|
|
RUN apt-get update && apt-get install -y \
|
|
ca-certificates \
|
|
libssl3 \
|
|
curl \
|
|
build-essential \
|
|
procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install common Python packages
|
|
# These are commonly used in automation scripts
|
|
RUN pip install --no-cache-dir \
|
|
requests>=2.31.0 \
|
|
pyyaml>=6.0 \
|
|
jinja2>=3.1.0 \
|
|
python-dateutil>=2.8.0
|
|
|
|
# Create worker user and directories
|
|
# Note: /opt/attune/packs is mounted as a volume at runtime, not copied in
|
|
RUN useradd -m -u 1001 attune && \
|
|
mkdir -p /opt/attune/packs /opt/attune/logs && \
|
|
chown -R attune:attune /opt/attune
|
|
|
|
WORKDIR /opt/attune
|
|
|
|
# Copy worker binary from builder
|
|
COPY --from=builder /build/attune-worker /usr/local/bin/attune-worker
|
|
|
|
# Copy configuration template
|
|
COPY config.docker.yaml ./config.yaml
|
|
|
|
# Note: Packs are NOT copied into the image
|
|
# They are mounted as a volume at runtime from the packs_data volume
|
|
|
|
# Switch to non-root user
|
|
USER attune
|
|
|
|
# Environment variables
|
|
ENV ATTUNE_WORKER_RUNTIMES="shell,python"
|
|
ENV ATTUNE_WORKER_TYPE="container"
|
|
ENV RUST_LOG=info
|
|
ENV ATTUNE_CONFIG=/opt/attune/config.yaml
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
CMD pgrep -f attune-worker || exit 1
|
|
|
|
# Run the worker
|
|
CMD ["/usr/local/bin/attune-worker"]
|
|
|
|
# ============================================================================
|
|
# Stage 3c: Node Worker (Shell + Node.js)
|
|
# Runtime capabilities: shell, node
|
|
# Use case: JavaScript/TypeScript actions and npm packages
|
|
# ============================================================================
|
|
FROM node:${NODE_VERSION}-slim AS worker-node
|
|
|
|
# Install system dependencies
|
|
RUN apt-get update && apt-get install -y \
|
|
ca-certificates \
|
|
libssl3 \
|
|
curl \
|
|
procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create worker user and directories
|
|
# Note: Node base image has 'node' user at UID 1000, so we use UID 1001
|
|
# Note: /opt/attune/packs is mounted as a volume at runtime, not copied in
|
|
RUN useradd -m -u 1001 attune && \
|
|
mkdir -p /opt/attune/packs /opt/attune/logs && \
|
|
chown -R attune:attune /opt/attune
|
|
|
|
WORKDIR /opt/attune
|
|
|
|
# Copy worker binary from builder
|
|
COPY --from=builder /build/attune-worker /usr/local/bin/attune-worker
|
|
|
|
# Copy configuration template
|
|
COPY config.docker.yaml ./config.yaml
|
|
|
|
# Note: Packs are NOT copied into the image
|
|
# They are mounted as a volume at runtime from the packs_data volume
|
|
|
|
# Switch to non-root user
|
|
USER attune
|
|
|
|
# Environment variables
|
|
ENV ATTUNE_WORKER_RUNTIMES="shell,node"
|
|
ENV ATTUNE_WORKER_TYPE="container"
|
|
ENV RUST_LOG=info
|
|
ENV ATTUNE_CONFIG=/opt/attune/config.yaml
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
CMD pgrep -f attune-worker || exit 1
|
|
|
|
# Run the worker
|
|
CMD ["/usr/local/bin/attune-worker"]
|
|
|
|
# ============================================================================
|
|
# Stage 3d: Full Worker (All runtimes)
|
|
# Runtime capabilities: shell, python, node, native
|
|
# Use case: General-purpose automation with multi-language support
|
|
# ============================================================================
|
|
FROM debian:${DEBIAN_VERSION} AS worker-full
|
|
|
|
# Install system dependencies including Python and Node.js
|
|
RUN apt-get update && apt-get install -y \
|
|
ca-certificates \
|
|
libssl3 \
|
|
curl \
|
|
build-essential \
|
|
python3 \
|
|
python3-pip \
|
|
python3-venv \
|
|
procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install Node.js from NodeSource
|
|
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
apt-get install -y nodejs && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create python symlink for convenience
|
|
RUN ln -s /usr/bin/python3 /usr/bin/python
|
|
|
|
# Install common Python packages
|
|
# Use --break-system-packages for Debian 12+ pip-in-system-python restrictions
|
|
RUN pip3 install --no-cache-dir --break-system-packages \
|
|
requests>=2.31.0 \
|
|
pyyaml>=6.0 \
|
|
jinja2>=3.1.0 \
|
|
python-dateutil>=2.8.0
|
|
|
|
# Create worker user and directories
|
|
# Note: /opt/attune/packs is mounted as a volume at runtime, not copied in
|
|
RUN useradd -m -u 1001 attune && \
|
|
mkdir -p /opt/attune/packs /opt/attune/logs && \
|
|
chown -R attune:attune /opt/attune
|
|
|
|
WORKDIR /opt/attune
|
|
|
|
# Copy worker binary from builder
|
|
COPY --from=builder /build/attune-worker /usr/local/bin/attune-worker
|
|
|
|
# Copy configuration template
|
|
COPY config.docker.yaml ./config.yaml
|
|
|
|
# Note: Packs are NOT copied into the image
|
|
# They are mounted as a volume at runtime from the packs_data volume
|
|
|
|
# Switch to non-root user
|
|
USER attune
|
|
|
|
# Environment variables
|
|
ENV ATTUNE_WORKER_RUNTIMES="shell,python,node,native"
|
|
ENV ATTUNE_WORKER_TYPE="container"
|
|
ENV RUST_LOG=info
|
|
ENV ATTUNE_CONFIG=/opt/attune/config.yaml
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
CMD pgrep -f attune-worker || exit 1
|
|
|
|
# Run the worker
|
|
CMD ["/usr/local/bin/attune-worker"]
|