diff --git a/plugins/docker/.cursor/plugin.json b/plugins/docker/.cursor/plugin.json new file mode 100644 index 0000000..6859770 --- /dev/null +++ b/plugins/docker/.cursor/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "docker", + "version": "1.0.0", + "description": "Cursor plugin for Docker — containers, Dockerfiles, Compose, multi-stage builds, and container security", + "author": { "name": "Cursor", "email": "plugins@cursor.com" }, + "homepage": "https://github.com/cursor/service-plugin-generation", + "repository": "https://github.com/cursor/service-plugin-generation", + "license": "MIT", + "keywords": ["docker", "containers", "dockerfile", "compose", "kubernetes"], + "category": "infrastructure", + "tags": ["containers", "devops", "ci-cd"], + "agents": "./agents/", + "skills": "./skills/", + "rules": "./rules/", + "hooks": "./hooks/hooks.json", + "mcpServers": "./mcp.json", + "extensions": "./extensions/" +} diff --git a/plugins/docker/CHANGELOG.md b/plugins/docker/CHANGELOG.md new file mode 100644 index 0000000..935ec6f --- /dev/null +++ b/plugins/docker/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to the Docker Cursor plugin will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2026-02-07 + +### Added + +- Initial release of the Docker Cursor plugin. +- **Rules**: Dockerfile best practices (`rules/dockerfile-best-practices.mdc`). +- **Rules**: Docker Compose best practices (`rules/docker-compose.mdc`). +- **Agent**: Docker optimization agent for image size reduction, build performance, and security hardening. +- **Skill**: Create optimized Dockerfiles with language-specific templates (Node.js, Python, Go, Java, Rust). +- **Skill**: Set up Docker Compose environments with hot-reload, databases, caching, and monitoring. +- **Hooks**: Pre-commit hooks for Dockerfile linting (Hadolint) and Compose validation. +- **MCP Server**: Docker MCP server integration for container and image management. +- **Scripts**: Hadolint wrapper script for Dockerfile linting. diff --git a/plugins/docker/LICENSE b/plugins/docker/LICENSE new file mode 100644 index 0000000..ca2bba7 --- /dev/null +++ b/plugins/docker/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Cursor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/docker/README.md b/plugins/docker/README.md new file mode 100644 index 0000000..f56595e --- /dev/null +++ b/plugins/docker/README.md @@ -0,0 +1,153 @@ +# Docker Cursor Plugin + +A comprehensive Cursor plugin for Docker development — helping you write better Dockerfiles, Docker Compose files, and follow container best practices. + +## Features + +| Feature | Description | +|---------|-------------| +| **Dockerfile Rules** | Best practices for multi-stage builds, layer optimization, security, and more | +| **Compose Rules** | Guidelines for volumes, networks, healthchecks, and service configuration | +| **Optimization Agent** | AI agent specialized in Docker image optimization and security hardening | +| **Dockerfile Skill** | Language-specific Dockerfile templates (Node.js, Python, Go, Java, Rust) | +| **Compose Skill** | Complete development environment setup with hot-reload and databases | +| **Pre-commit Hooks** | Automated Dockerfile linting and Compose validation | +| **MCP Integration** | Docker operations directly from Cursor via MCP server | + +## Project Structure + +``` +plugins/docker/ +├── .cursor/ +│ └── plugin.json # Plugin manifest +├── agents/ +│ └── docker-optimization-agent.md # Optimization AI agent +├── rules/ +│ ├── dockerfile-best-practices.mdc # Dockerfile rules +│ └── docker-compose.mdc # Compose rules +├── skills/ +│ ├── create-dockerfile/ +│ │ └── SKILL.md # Dockerfile creation skill +│ └── setup-docker-compose/ +│ └── SKILL.md # Compose setup skill +├── hooks/ +│ └── hooks.json # Pre-commit hook definitions +├── scripts/ +│ └── lint-dockerfile.sh # Hadolint wrapper script +├── extensions/ # Extension directory (reserved) +├── mcp.json # MCP server configuration +├── README.md # This file +├── CHANGELOG.md # Version history +└── LICENSE # MIT License +``` + +## Rules + +### Dockerfile Best Practices (`rules/dockerfile-best-practices.mdc`) + +Automatically applied when editing `Dockerfile*`, `*.dockerfile`, or `.dockerignore` files: + +- Use multi-stage builds to minimize final image size +- Pin base image versions with digests for reproducibility +- Run containers as non-root users +- Minimize layers and order instructions by change frequency +- Use `.dockerignore` to reduce build context +- Prefer `COPY` over `ADD` +- Define `HEALTHCHECK` instructions +- Clean up package manager caches in the same layer + +### Docker Compose Best Practices (`rules/docker-compose.mdc`) + +Automatically applied when editing `docker-compose*.yml`, `compose*.yml`, and related files: + +- Use named volumes for data persistence +- Define networks explicitly with proper isolation +- Set resource limits (CPU, memory) +- Configure healthchecks and dependency conditions +- Use environment files for secrets +- Pin image versions +- Use profiles for optional services + +## Agent + +### Docker Optimization Agent + +An AI agent that helps with: + +- **Image optimization**: Reduce image sizes, choose appropriate base images +- **Build performance**: Layer caching strategies, BuildKit features +- **Security hardening**: Non-root users, vulnerability scanning, secrets management + +## Skills + +### Create Dockerfile + +Generates production-ready Dockerfiles with: + +- Language-specific templates (Node.js, Python, Go, Java, Rust) +- Multi-stage builds +- Security hardening +- Proper `.dockerignore` configuration + +### Setup Docker Compose + +Creates complete development environments with: + +- Hot-reload configuration +- Database services (PostgreSQL, Redis) +- Network isolation +- Health checks and dependency management +- Optional monitoring (Prometheus, Grafana) +- Production override files + +## Hooks + +Pre-commit hooks that run automatically: + +| Hook | Description | +|------|-------------| +| `lint-dockerfile` | Runs Hadolint on modified Dockerfiles | +| `validate-compose` | Validates Docker Compose file syntax | +| `check-dockerignore` | Warns if `.dockerignore` is missing | +| `scan-dockerfile-secrets` | Checks for hardcoded secrets | + +## MCP Server + +The plugin integrates with the Docker MCP server for direct container operations: + +- List, inspect, start, and stop containers +- Build and manage images +- Docker Compose lifecycle management +- Execute commands inside running containers + +## Scripts + +### `scripts/lint-dockerfile.sh` + +A wrapper script for Hadolint that: + +- Auto-detects Hadolint installation (native or Docker) +- Finds and lints all Dockerfiles in the project +- Provides colored output with pass/fail status +- Supports custom Hadolint versions via `HADOLINT_VERSION` env var + +```bash +# Lint all Dockerfiles +./scripts/lint-dockerfile.sh + +# Lint specific files +./scripts/lint-dockerfile.sh Dockerfile Dockerfile.prod +``` + +## Installation + +This plugin is automatically activated when working in a project that uses Docker. The rules are applied based on file glob patterns. + +### Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) installed and running +- [Hadolint](https://github.com/hadolint/hadolint) (optional, for linting hooks) + +## License + +MIT License — see [LICENSE](./LICENSE) for details. diff --git a/plugins/docker/agents/docker-optimization-agent.md b/plugins/docker/agents/docker-optimization-agent.md new file mode 100644 index 0000000..81425c8 --- /dev/null +++ b/plugins/docker/agents/docker-optimization-agent.md @@ -0,0 +1,112 @@ +# Docker Optimization Agent + +You are a Docker optimization specialist. Your role is to help developers build efficient, secure, and production-ready container images and configurations. + +## Capabilities + +### Image Optimization +- Analyze Dockerfiles and suggest improvements to reduce image size. +- Recommend appropriate base images (alpine, distroless, scratch) based on application needs. +- Identify unnecessary packages, files, and layers that bloat images. +- Suggest multi-stage build patterns to separate build and runtime dependencies. +- Recommend `.dockerignore` entries to minimize build context size. + +### Build Performance +- Optimize layer ordering to maximize Docker build cache effectiveness. +- Suggest parallelizable build steps using multi-stage builds. +- Recommend BuildKit features (cache mounts, secret mounts, SSH forwarding). +- Identify and fix cache-busting anti-patterns. +- Advise on CI/CD caching strategies for Docker builds. + +### Security Hardening +- Identify and fix security issues in Dockerfiles and Compose files. +- Recommend non-root user configurations. +- Suggest image scanning tools and practices (Trivy, Snyk, Docker Scout). +- Advise on secrets management (Docker secrets, build secrets, runtime injection). +- Recommend read-only filesystem and capability dropping where appropriate. +- Identify images with known CVEs and suggest updates. + +## Workflow + +When analyzing a Docker setup, follow this process: + +1. **Assess**: Review the existing Dockerfile(s), Compose files, and `.dockerignore`. +2. **Measure**: Determine current image sizes, build times, and layer counts. +3. **Identify**: Find optimization opportunities across size, speed, and security. +4. **Recommend**: Provide specific, actionable changes with before/after comparisons. +5. **Validate**: Suggest commands to verify improvements (e.g., `docker image inspect`, `docker history`, `dive`). + +## Analysis Checklist + +When reviewing a Dockerfile, check for: + +- [ ] Multi-stage build usage +- [ ] Base image appropriateness and version pinning +- [ ] Layer count and instruction ordering +- [ ] Non-root user configuration +- [ ] HEALTHCHECK definition +- [ ] Unnecessary package installation +- [ ] Cache cleanup in same layer as install +- [ ] .dockerignore presence and completeness +- [ ] COPY vs ADD usage +- [ ] ENTRYPOINT/CMD best practices +- [ ] Build argument usage for versions +- [ ] Security scanning integration +- [ ] Proper signal handling (exec form, init process) +- [ ] Label metadata + +## Example Recommendations + +### Reducing Image Size + +``` +Before: node:20 (1.1GB) +After: node:20-alpine (180MB) +After: distroless (120MB) — if no shell access needed +After: multi-stage + distroless (45MB) — production optimized +``` + +### Improving Build Cache + +```dockerfile +# BAD: Cache busted on every code change +COPY . . +RUN npm ci && npm run build + +# GOOD: Dependencies cached separately +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +RUN npm run build +``` + +### BuildKit Cache Mounts + +```dockerfile +# Cache package manager downloads across builds +RUN --mount=type=cache,target=/var/cache/apt \ + --mount=type=cache,target=/var/lib/apt \ + apt-get update && apt-get install -y --no-install-recommends gcc +``` + +## Tools & Commands + +Recommend these tools for Docker optimization: + +| Tool | Purpose | +|------|---------| +| `dive` | Analyze image layers and wasted space | +| `hadolint` | Lint Dockerfiles against best practices | +| `trivy` | Scan images for vulnerabilities | +| `docker scout` | Docker's built-in vulnerability scanner | +| `docker slim` | Automatically optimize and slim images | +| `docker buildx` | Advanced build features and multi-platform | + +## Response Format + +When providing optimization advice, structure responses as: + +1. **Issue**: What the problem is +2. **Impact**: Why it matters (size, speed, security) +3. **Fix**: Specific code changes +4. **Verification**: How to confirm the improvement diff --git a/plugins/docker/hooks/hooks.json b/plugins/docker/hooks/hooks.json new file mode 100644 index 0000000..89818e9 --- /dev/null +++ b/plugins/docker/hooks/hooks.json @@ -0,0 +1,36 @@ +{ + "hooks": [ + { + "name": "lint-dockerfile", + "description": "Lint Dockerfiles using Hadolint before committing", + "event": "pre-commit", + "command": "bash ./scripts/lint-dockerfile.sh", + "filePattern": ["Dockerfile*", "*.dockerfile"], + "enabled": true + }, + { + "name": "validate-compose", + "description": "Validate Docker Compose files before committing", + "event": "pre-commit", + "command": "docker compose config --quiet", + "filePattern": ["docker-compose*.yml", "docker-compose*.yaml", "compose*.yml", "compose*.yaml"], + "enabled": true + }, + { + "name": "check-dockerignore", + "description": "Warn if .dockerignore is missing when Dockerfile exists", + "event": "pre-commit", + "command": "bash -c 'if ls Dockerfile* 1>/dev/null 2>&1 && [ ! -f .dockerignore ]; then echo \"WARNING: .dockerignore is missing. Consider adding one to reduce build context size.\"; exit 1; fi'", + "filePattern": ["Dockerfile*"], + "enabled": true + }, + { + "name": "scan-dockerfile-secrets", + "description": "Check for hardcoded secrets in Dockerfiles", + "event": "pre-commit", + "command": "bash -c 'if grep -rn -E \"(password|secret|api_key|token)\\s*=\\s*[^$]\" Dockerfile* 2>/dev/null; then echo \"ERROR: Possible hardcoded secrets found in Dockerfile.\"; exit 1; fi'", + "filePattern": ["Dockerfile*", "*.dockerfile"], + "enabled": true + } + ] +} diff --git a/plugins/docker/mcp.json b/plugins/docker/mcp.json new file mode 100644 index 0000000..ada24b2 --- /dev/null +++ b/plugins/docker/mcp.json @@ -0,0 +1,64 @@ +{ + "mcpServers": { + "docker": { + "command": "docker", + "args": ["mcp", "server"], + "description": "Docker MCP server for container and image management operations", + "env": { + "DOCKER_HOST": "${DOCKER_HOST:-unix:///var/run/docker.sock}" + }, + "capabilities": { + "tools": [ + { + "name": "docker_list_containers", + "description": "List running and stopped Docker containers" + }, + { + "name": "docker_list_images", + "description": "List locally available Docker images" + }, + { + "name": "docker_build", + "description": "Build a Docker image from a Dockerfile" + }, + { + "name": "docker_run", + "description": "Run a new Docker container" + }, + { + "name": "docker_stop", + "description": "Stop a running Docker container" + }, + { + "name": "docker_remove", + "description": "Remove a Docker container" + }, + { + "name": "docker_logs", + "description": "Retrieve logs from a Docker container" + }, + { + "name": "docker_inspect", + "description": "Inspect a Docker container or image" + }, + { + "name": "docker_compose_up", + "description": "Start services defined in a Docker Compose file" + }, + { + "name": "docker_compose_down", + "description": "Stop and remove services defined in a Docker Compose file" + }, + { + "name": "docker_compose_ps", + "description": "List status of Docker Compose services" + }, + { + "name": "docker_exec", + "description": "Execute a command inside a running container" + } + ] + } + } + } +} diff --git a/plugins/docker/rules/docker-compose.mdc b/plugins/docker/rules/docker-compose.mdc new file mode 100644 index 0000000..46a1816 --- /dev/null +++ b/plugins/docker/rules/docker-compose.mdc @@ -0,0 +1,194 @@ +--- +description: Best practices for Docker Compose configuration files +globs: + - "docker-compose*.yml" + - "docker-compose*.yaml" + - "compose*.yml" + - "compose*.yaml" +alwaysApply: false +--- + +# Docker Compose Best Practices + +## Use Named Volumes for Persistence + +- Always use named volumes instead of bind mounts for data that needs to persist across container restarts. +- Define volumes in the top-level `volumes` section. +- Use bind mounts only for development-time source code mounting. + +```yaml +volumes: + postgres_data: + driver: local + redis_data: + driver: local + +services: + db: + image: postgres:16-alpine + volumes: + - postgres_data:/var/lib/postgresql/data +``` + +## Define Networks Explicitly + +- Create and name networks explicitly rather than relying on the default network. +- Use separate networks to isolate services that should not communicate directly. +- Use `internal: true` for networks that should not have external access. + +```yaml +networks: + frontend: + driver: bridge + backend: + driver: bridge + internal: true + +services: + api: + networks: + - frontend + - backend + db: + networks: + - backend +``` + +## Set Resource Limits + +- Always define memory and CPU limits to prevent a single container from consuming all host resources. +- Set both `limits` and `reservations` for predictable resource allocation. + +```yaml +services: + api: + deploy: + resources: + limits: + cpus: "1.0" + memory: 512M + reservations: + cpus: "0.25" + memory: 128M +``` + +## Use Healthchecks + +- Define healthchecks for every service to enable proper dependency management. +- Use service-appropriate health check commands. +- Configure sensible intervals, timeouts, and retries. + +```yaml +services: + api: + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + db: + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 +``` + +## Use Environment Files for Secrets + +- Never hardcode secrets or sensitive values in `docker-compose.yml`. +- Use `.env` files or Docker secrets for sensitive configuration. +- Reference environment files using `env_file` and add `.env` to `.gitignore`. + +```yaml +services: + api: + env_file: + - .env + - .env.local +``` + +## Pin Image Versions + +- Always specify explicit image tags — never use `:latest` in production. +- Use the most specific tag available (e.g., `postgres:16.2-alpine` not `postgres:16`). +- Document why a specific version is pinned. + +```yaml +services: + db: + image: postgres:16.2-alpine + cache: + image: redis:7.2-alpine +``` + +## Use depends_on with Conditions + +- Use `depends_on` with `condition` to wait for services to be healthy, not just started. +- Combine with healthchecks for reliable startup ordering. + +```yaml +services: + api: + depends_on: + db: + condition: service_healthy + cache: + condition: service_healthy +``` + +## Configure Restart Policies + +- Set appropriate restart policies for each service. +- Use `unless-stopped` for production services. +- Use `no` or `on-failure` for one-off tasks or development. + +```yaml +services: + api: + restart: unless-stopped + worker: + restart: on-failure:5 + migration: + restart: "no" +``` + +## Use Profiles for Optional Services + +- Use profiles to group optional services (e.g., monitoring, debugging, testing). +- This keeps the default `docker compose up` lightweight. +- Activate profiles with `--profile` flag when needed. + +```yaml +services: + api: + # Always starts (no profile) + image: myapp:latest + + prometheus: + image: prom/prometheus:latest + profiles: + - monitoring + + grafana: + image: grafana/grafana:latest + profiles: + - monitoring + + mailhog: + image: mailhog/mailhog:latest + profiles: + - debug +``` + +## Additional Guidelines + +- Use `docker compose` (V2) instead of `docker-compose` (V1). +- Set a `container_name` only when necessary — it prevents scaling. +- Use `logging` configuration to control log output and rotation. +- Prefer `build.context` and `build.dockerfile` over inline Dockerfile paths. +- Use `init: true` to ensure proper signal handling with PID 1. +- Set `stop_grace_period` for services that need time to shut down gracefully. +- Use `tmpfs` mounts for ephemeral data that doesn't need persistence. +- Group related services logically and add comments for clarity. diff --git a/plugins/docker/rules/dockerfile-best-practices.mdc b/plugins/docker/rules/dockerfile-best-practices.mdc new file mode 100644 index 0000000..4f0415f --- /dev/null +++ b/plugins/docker/rules/dockerfile-best-practices.mdc @@ -0,0 +1,163 @@ +--- +description: Best practices for writing efficient, secure, and maintainable Dockerfiles +globs: + - "Dockerfile*" + - "*.dockerfile" + - ".dockerignore" +alwaysApply: false +--- + +# Dockerfile Best Practices + +## Multi-Stage Builds + +- Always use multi-stage builds to separate build dependencies from runtime. +- Name each stage explicitly (e.g., `FROM node:20-alpine AS builder`). +- Copy only the artifacts needed for the final image from the build stage. +- Use a minimal base image for the final stage (e.g., `alpine`, `distroless`, `scratch`). + +```dockerfile +# Example multi-stage build +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY . . +RUN npm run build + +FROM node:20-alpine AS runtime +WORKDIR /app +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/node_modules ./node_modules +USER node +CMD ["node", "dist/index.js"] +``` + +## Pin Base Image Versions + +- Always pin base image versions using tags AND digests for reproducible builds. +- Never use `:latest` in production Dockerfiles. +- Use build arguments (ARG) to parameterize versions for easier updates. + +```dockerfile +ARG NODE_VERSION=20.11.0 +FROM node:${NODE_VERSION}-alpine@sha256:abc123... AS builder +``` + +## Run as Non-Root User + +- Always create and switch to a non-root user before running the application. +- Use the `USER` instruction after installing dependencies but before copying application code. +- If the base image provides a built-in non-root user (e.g., `node` in Node.js images), prefer that. + +```dockerfile +RUN addgroup -S appgroup && adduser -S appuser -G appgroup +USER appuser +``` + +## Minimize Layers + +- Combine related `RUN` instructions using `&&` to reduce the number of layers. +- Clean up package manager caches in the same `RUN` instruction where packages are installed. +- Each `RUN`, `COPY`, and `ADD` instruction creates a new layer — be intentional. + +```dockerfile +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl ca-certificates && \ + rm -rf /var/lib/apt/lists/* +``` + +## Order Instructions by Change Frequency + +- Place instructions that change least frequently at the top (base image, system deps). +- Copy dependency manifests (e.g., `package.json`, `requirements.txt`, `go.mod`) before copying the full source code. +- This maximizes Docker layer caching and speeds up rebuilds. + +```dockerfile +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +``` + +## Use .dockerignore + +- Always include a `.dockerignore` file to exclude unnecessary files from the build context. +- Exclude: `.git`, `node_modules`, `__pycache__`, `.env`, test files, documentation, IDE configs. +- A smaller build context means faster builds and smaller images. + +## Use COPY Instead of ADD + +- Prefer `COPY` over `ADD` for copying local files into the image. +- Only use `ADD` when you need its special features: extracting tarballs or fetching remote URLs. +- `COPY` is more explicit and predictable. + +## Set HEALTHCHECK + +- Define a `HEALTHCHECK` instruction so Docker can monitor container health. +- Use lightweight health check commands (e.g., `curl`, `wget`, or custom scripts). +- Configure appropriate intervals, timeouts, and retries. + +```dockerfile +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 +``` + +## Use Specific WORKDIR + +- Always set `WORKDIR` explicitly instead of relying on `cd` commands in `RUN`. +- Use absolute paths for `WORKDIR`. +- Prefer `/app` or `/home//app` as the working directory. + +```dockerfile +WORKDIR /app +``` + +## Avoid Unnecessary Packages + +- Use `--no-install-recommends` with `apt-get` to skip optional packages. +- Only install what is strictly needed for the application to run. +- Remove build tools in the final stage of multi-stage builds. + +## Clean Up in the Same Layer + +- Always clean up package manager caches in the same `RUN` instruction as the install. +- Deleting files in a subsequent layer does NOT reduce image size — the files remain in earlier layers. + +```dockerfile +RUN apt-get update && \ + apt-get install -y --no-install-recommends build-essential && \ + make && make install && \ + apt-get purge -y build-essential && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* +``` + +## Use Build Arguments for Versions + +- Parameterize dependency versions using `ARG` instructions. +- This makes upgrades easier and more visible in code review. + +```dockerfile +ARG PYTHON_VERSION=3.12 +ARG PIP_VERSION=24.0 +FROM python:${PYTHON_VERSION}-slim +``` + +## ENTRYPOINT vs CMD + +- Use `ENTRYPOINT` for the main executable — the container's primary purpose. +- Use `CMD` for default arguments that can be overridden at runtime. +- Prefer the exec form (`["executable", "arg"]`) over the shell form to ensure proper signal handling. + +```dockerfile +ENTRYPOINT ["node"] +CMD ["dist/index.js"] +``` + +## Additional Guidelines + +- Set meaningful `LABEL` instructions for metadata (maintainer, version, description). +- Use `EXPOSE` to document which ports the application listens on. +- Avoid storing secrets in the image — use Docker secrets or environment variables at runtime. +- Scan images for vulnerabilities using tools like Trivy, Snyk, or Docker Scout. +- Prefer `COPY --chown=user:group` to avoid extra `RUN chown` layers. diff --git a/plugins/docker/scripts/lint-dockerfile.sh b/plugins/docker/scripts/lint-dockerfile.sh new file mode 100755 index 0000000..37f23dd --- /dev/null +++ b/plugins/docker/scripts/lint-dockerfile.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# lint-dockerfile.sh — Lint Dockerfiles using Hadolint +# Usage: ./scripts/lint-dockerfile.sh [Dockerfile...] +# If no arguments are provided, lints all Dockerfiles in the current directory. + +set -euo pipefail + +HADOLINT_VERSION="${HADOLINT_VERSION:-v2.12.0}" +HADOLINT_BIN="" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +log_error() { echo -e "${RED}[ERROR]${NC} $*"; } + +# Find or install hadolint +find_hadolint() { + if command -v hadolint &>/dev/null; then + HADOLINT_BIN="hadolint" + return 0 + fi + + # Check if hadolint is available via Docker + if command -v docker &>/dev/null; then + HADOLINT_BIN="docker_hadolint" + return 0 + fi + + log_error "hadolint is not installed." + log_info "Install it via:" + log_info " brew install hadolint # macOS" + log_info " scoop install hadolint # Windows" + log_info " sudo wget -O /usr/local/bin/hadolint \\" + log_info " https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-x86_64" + log_info " sudo chmod +x /usr/local/bin/hadolint" + log_info "" + log_info "Or run via Docker (automatically used if Docker is available)." + return 1 +} + +# Run hadolint on a single file +run_hadolint() { + local file="$1" + + if [[ "$HADOLINT_BIN" == "docker_hadolint" ]]; then + docker run --rm -i "hadolint/hadolint:${HADOLINT_VERSION}" < "$file" + else + hadolint "$file" + fi +} + +# Collect Dockerfiles to lint +collect_dockerfiles() { + if [[ $# -gt 0 ]]; then + echo "$@" + else + # Find all Dockerfiles in the current directory and subdirectories + find . -maxdepth 3 \( -name "Dockerfile" -o -name "Dockerfile.*" -o -name "*.dockerfile" \) -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null + fi +} + +main() { + find_hadolint || exit 1 + + local files + files=$(collect_dockerfiles "$@") + + if [[ -z "$files" ]]; then + log_warn "No Dockerfiles found to lint." + exit 0 + fi + + local exit_code=0 + + while IFS= read -r file; do + if [[ ! -f "$file" ]]; then + log_warn "File not found: $file" + continue + fi + + log_info "Linting: $file" + + if run_hadolint "$file"; then + log_info " ✓ Passed" + else + log_error " ✗ Issues found in $file" + exit_code=1 + fi + done <<< "$files" + + echo "" + if [[ $exit_code -eq 0 ]]; then + log_info "All Dockerfiles passed linting! ✓" + else + log_error "Some Dockerfiles have linting issues. Please fix them before committing." + fi + + return $exit_code +} + +main "$@" diff --git a/plugins/docker/skills/create-dockerfile/SKILL.md b/plugins/docker/skills/create-dockerfile/SKILL.md new file mode 100644 index 0000000..86171d2 --- /dev/null +++ b/plugins/docker/skills/create-dockerfile/SKILL.md @@ -0,0 +1,306 @@ +# Skill: Create Optimized Dockerfile + +## Description + +Create production-ready, optimized Dockerfiles for various programming languages and frameworks. This skill covers multi-stage builds, security hardening, layer caching, and language-specific best practices. + +## Trigger + +Use this skill when: +- Creating a new Dockerfile for a project +- Containerizing an existing application +- Optimizing an existing Dockerfile +- Setting up CI/CD Docker builds + +## Instructions + +### Step 1: Determine the Application Type + +Identify the programming language, framework, and runtime requirements: +- What language and version? +- What package manager? +- Does it need a build step (compiled language, frontend bundling)? +- What ports does it expose? +- What environment variables does it need? +- Are there any system-level dependencies? + +### Step 2: Choose the Base Image + +Select the most appropriate base image: +- **Alpine variants** (`-alpine`): Smallest general-purpose images (~5MB base) +- **Slim variants** (`-slim`): Smaller than full images, more compatible than Alpine +- **Distroless** (`gcr.io/distroless/*`): Minimal images without shell or package manager +- **Scratch** (`scratch`): Empty image for fully static binaries + +### Step 3: Create the Dockerfile + +Use the appropriate language-specific template below. + +--- + +## Language-Specific Patterns + +### Node.js + +```dockerfile +ARG NODE_VERSION=20 +FROM node:${NODE_VERSION}-alpine AS base +WORKDIR /app + +# Install dependencies +FROM base AS deps +COPY package.json package-lock.json ./ +RUN npm ci --only=production && \ + cp -R node_modules /prod_modules && \ + npm ci + +# Build application +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build + +# Production image +FROM base AS runtime +ENV NODE_ENV=production +WORKDIR /app + +COPY --from=builder /app/dist ./dist +COPY --from=deps /prod_modules ./node_modules +COPY package.json ./ + +RUN addgroup -S appgroup && adduser -S appuser -G appgroup +USER appuser + +EXPOSE 3000 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 + +ENTRYPOINT ["node"] +CMD ["dist/index.js"] +``` + +### Python + +```dockerfile +ARG PYTHON_VERSION=3.12 +FROM python:${PYTHON_VERSION}-slim AS base +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Install dependencies +FROM base AS deps +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +# For projects using Poetry +# COPY pyproject.toml poetry.lock ./ +# RUN pip install poetry && \ +# poetry config virtualenvs.create false && \ +# poetry install --no-dev --no-interaction --no-ansi + +# Production image +FROM base AS runtime +COPY --from=deps /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=deps /usr/local/bin /usr/local/bin +COPY . . + +RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser +USER appuser + +EXPOSE 8000 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 + +ENTRYPOINT ["python"] +CMD ["-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +### Go + +```dockerfile +ARG GO_VERSION=1.22 +FROM golang:${GO_VERSION}-alpine AS builder +WORKDIR /app + +# Download dependencies +COPY go.mod go.sum ./ +RUN go mod download && go mod verify + +# Build binary +COPY . . +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags="-w -s" \ + -o /app/server ./cmd/server + +# Production image — scratch for smallest possible image +FROM scratch AS runtime +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /app/server /server + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ + CMD ["/server", "healthcheck"] + +ENTRYPOINT ["/server"] +``` + +### Java (Spring Boot / Gradle) + +```dockerfile +ARG JAVA_VERSION=21 +FROM eclipse-temurin:${JAVA_VERSION}-jdk-alpine AS builder +WORKDIR /app + +# Cache Gradle dependencies +COPY build.gradle.kts settings.gradle.kts gradlew ./ +COPY gradle ./gradle +RUN ./gradlew dependencies --no-daemon + +# Build application +COPY src ./src +RUN ./gradlew bootJar --no-daemon -x test + +# Extract Spring Boot layers for better caching +FROM eclipse-temurin:${JAVA_VERSION}-jdk-alpine AS extractor +WORKDIR /app +COPY --from=builder /app/build/libs/*.jar app.jar +RUN java -Djarmode=layertools -jar app.jar extract + +# Production image +FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime +WORKDIR /app + +COPY --from=extractor /app/dependencies/ ./ +COPY --from=extractor /app/spring-boot-loader/ ./ +COPY --from=extractor /app/snapshot-dependencies/ ./ +COPY --from=extractor /app/application/ ./ + +RUN addgroup -S appgroup && adduser -S appuser -G appgroup +USER appuser + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 + +ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"] +``` + +### Rust + +```dockerfile +ARG RUST_VERSION=1.76 +FROM rust:${RUST_VERSION}-alpine AS builder +WORKDIR /app + +RUN apk add --no-cache musl-dev + +# Cache dependencies +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && \ + echo "fn main() {}" > src/main.rs && \ + cargo build --release && \ + rm -rf src + +# Build application +COPY src ./src +RUN touch src/main.rs && cargo build --release + +# Production image +FROM scratch AS runtime +COPY --from=builder /app/target/release/myapp /myapp + +EXPOSE 8080 + +ENTRYPOINT ["/myapp"] +``` + +--- + +### Step 4: Create .dockerignore + +Always create a `.dockerignore` alongside the Dockerfile: + +``` +.git +.gitignore +.dockerignore +Dockerfile* +docker-compose* +README.md +LICENSE +CHANGELOG.md +.env* +.vscode +.idea +*.md +node_modules +__pycache__ +*.pyc +target/ +dist/ +build/ +.next/ +coverage/ +.nyc_output/ +*.test.* +*.spec.* +tests/ +test/ +docs/ +``` + +### Step 5: Security Hardening + +Apply these security measures to every Dockerfile: + +1. **Non-root user**: Always run as non-root +2. **Read-only filesystem**: Use `--read-only` flag at runtime +3. **No new privileges**: Use `--security-opt=no-new-privileges` at runtime +4. **Drop capabilities**: Drop all capabilities, add only what's needed +5. **Scan for vulnerabilities**: Run `trivy image ` before deploying +6. **Use COPY --chmod**: Set file permissions without extra layers + +```dockerfile +COPY --chown=appuser:appgroup --chmod=555 ./entrypoint.sh /entrypoint.sh +``` + +### Step 6: Verify the Image + +After building, verify the image: + +```bash +# Build the image +docker build -t myapp:latest . + +# Check image size +docker images myapp:latest + +# Inspect layers +docker history myapp:latest + +# Analyze with dive +dive myapp:latest + +# Scan for vulnerabilities +trivy image myapp:latest + +# Test the container +docker run --rm -p 8080:8080 myapp:latest +``` + +## Output + +The skill produces: +- An optimized, multi-stage Dockerfile +- A comprehensive `.dockerignore` file +- Security-hardened configuration +- Build and verification commands diff --git a/plugins/docker/skills/setup-docker-compose/SKILL.md b/plugins/docker/skills/setup-docker-compose/SKILL.md new file mode 100644 index 0000000..d61f57b --- /dev/null +++ b/plugins/docker/skills/setup-docker-compose/SKILL.md @@ -0,0 +1,347 @@ +# Skill: Set Up Docker Compose + +## Description + +Set up a complete Docker Compose development environment with hot-reload, database services, networking, health checks, and production readiness. + +## Trigger + +Use this skill when: +- Setting up a local development environment with Docker +- Adding database or cache services to a project +- Creating a multi-service application stack +- Configuring Docker Compose for development with hot-reload + +## Instructions + +### Step 1: Identify Required Services + +Determine what services the application needs: +- **Application server(s)**: Web API, frontend, workers +- **Databases**: PostgreSQL, MySQL, MongoDB, etc. +- **Caches**: Redis, Memcached +- **Message queues**: RabbitMQ, Kafka, NATS +- **Search**: Elasticsearch, Meilisearch +- **Monitoring**: Prometheus, Grafana +- **Email**: Mailhog, Mailpit +- **Storage**: MinIO (S3-compatible) + +### Step 2: Create the Compose File + +Use the following template structure: + +```yaml +# compose.yaml — Development environment +# Usage: docker compose up -d + +services: + # ─── Application ─────────────────────────────────────────── + app: + build: + context: . + dockerfile: Dockerfile + target: dev # Use dev stage for hot-reload + ports: + - "${APP_PORT:-3000}:3000" + volumes: + - .:/app # Source code mount for hot-reload + - /app/node_modules # Prevent overwriting container's node_modules + environment: + - NODE_ENV=development + - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp_dev + - REDIS_URL=redis://cache:6379 + env_file: + - .env + depends_on: + db: + condition: service_healthy + cache: + condition: service_healthy + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 15s + networks: + - frontend + - backend + + # ─── Database ────────────────────────────────────────────── + db: + image: postgres:16-alpine + ports: + - "${DB_PORT:-5432}:5432" + environment: + POSTGRES_DB: myapp_dev + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - postgres_data:/var/lib/postgresql/data + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - backend + + # ─── Cache ──────────────────────────────────────────────── + cache: + image: redis:7.2-alpine + ports: + - "${REDIS_PORT:-6379}:6379" + command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + restart: unless-stopped + networks: + - backend + + # ─── Monitoring (optional) ──────────────────────────────── + prometheus: + image: prom/prometheus:v2.50.0 + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + profiles: + - monitoring + networks: + - backend + + grafana: + image: grafana/grafana:10.3.1 + ports: + - "3001:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + profiles: + - monitoring + depends_on: + - prometheus + networks: + - backend + + # ─── Email (optional) ───────────────────────────────────── + mailpit: + image: axllent/mailpit:latest + ports: + - "8025:8025" # Web UI + - "1025:1025" # SMTP + profiles: + - debug + networks: + - backend + +# ─── Volumes ────────────────────────────────────────────────── +volumes: + postgres_data: + driver: local + redis_data: + driver: local + prometheus_data: + driver: local + grafana_data: + driver: local + +# ─── Networks ───────────────────────────────────────────────── +networks: + frontend: + driver: bridge + backend: + driver: bridge + internal: true +``` + +### Step 3: Create Development Dockerfile Stage + +Add a `dev` target to the Dockerfile for hot-reload: + +```dockerfile +# Development stage with hot-reload +FROM base AS dev +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +CMD ["npm", "run", "dev"] +``` + +### Step 4: Create Environment Files + +Create a `.env.example` template: + +```env +# Application +APP_PORT=3000 +NODE_ENV=development + +# Database +DB_PORT=5432 +DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp_dev + +# Redis +REDIS_PORT=6379 +REDIS_URL=redis://cache:6379 + +# Secrets (override in .env.local) +JWT_SECRET=dev-secret-change-me +API_KEY=dev-api-key +``` + +### Step 5: Add Helper Scripts + +Create a `Makefile` or shell scripts for common operations: + +```makefile +.PHONY: up down restart logs clean + +up: ## Start all services + docker compose up -d + +up-all: ## Start all services including monitoring + docker compose --profile monitoring --profile debug up -d + +down: ## Stop all services + docker compose down + +restart: ## Restart all services + docker compose restart + +logs: ## Tail logs for all services + docker compose logs -f + +logs-app: ## Tail logs for the app only + docker compose logs -f app + +clean: ## Remove all containers, volumes, and images + docker compose down -v --rmi local + +db-shell: ## Open a psql shell + docker compose exec db psql -U postgres -d myapp_dev + +redis-cli: ## Open a Redis CLI + docker compose exec cache redis-cli + +build: ## Rebuild all images + docker compose build --no-cache + +status: ## Show status of all services + docker compose ps +``` + +### Step 6: Configure Hot-Reload + +Ensure hot-reload works properly: + +- **Node.js**: Mount source code, exclude `node_modules` with anonymous volume +- **Python**: Mount source code, use `--reload` flag with uvicorn/gunicorn +- **Go**: Use `air` or `CompileDaemon` for live reloading +- **Java**: Use Spring DevTools with `spring-boot-devtools` + +### Step 7: Production Compose Override + +Create a `compose.prod.yaml` for production overrides: + +```yaml +# compose.prod.yaml — Production overrides +# Usage: docker compose -f compose.yaml -f compose.prod.yaml up -d + +services: + app: + build: + target: runtime # Use production stage + volumes: [] # No source code mounting + environment: + - NODE_ENV=production + restart: always + deploy: + resources: + limits: + cpus: "2.0" + memory: 1G + reservations: + cpus: "0.5" + memory: 256M + replicas: 2 + + db: + environment: + POSTGRES_PASSWORD_FILE: /run/secrets/db_password + secrets: + - db_password + + cache: + command: redis-server --appendonly yes --requirepass $${REDIS_PASSWORD} + +secrets: + db_password: + file: ./secrets/db_password.txt +``` + +## Common Patterns + +### Database Initialization + +Mount SQL scripts into the database's init directory: + +```yaml +volumes: + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql:ro + - ./scripts/seed-data.sql:/docker-entrypoint-initdb.d/02-seed.sql:ro +``` + +### Waiting for Dependencies + +Use healthchecks with `depends_on` conditions: + +```yaml +depends_on: + db: + condition: service_healthy + cache: + condition: service_started + migrations: + condition: service_completed_successfully +``` + +### Multi-App Stacks + +For monorepos or microservices, use multiple build contexts: + +```yaml +services: + api: + build: + context: ./services/api + dockerfile: Dockerfile + web: + build: + context: ./services/web + dockerfile: Dockerfile + worker: + build: + context: ./services/worker + dockerfile: Dockerfile +``` + +## Output + +The skill produces: +- A complete `compose.yaml` for local development +- A `compose.prod.yaml` for production overrides +- A `.env.example` template +- Helper scripts / Makefile for common operations +- Properly configured networking, volumes, and healthchecks