返回 Skill 列表
extension
分类: 开发与工程无需 API Key

ofelia

在Docker环境中使用Ofelia调度任务时 —— 通过INI文件或Docker标签配置job-exec、job-run、job-local、job-service-run,cron时间调度,防止任务重叠,日志记录到Slack/电子邮件/磁盘,与Docker Compose集成,Swarm服务,以及排查计划容器作业的故障

person作者: jakexiaohubgithub

Ofelia — Docker Job Scheduler

Modern, lightweight cron replacement for Docker. Executes commands inside containers via Docker API. Written in Go, configured via INI files or Docker container labels.

  • Repo: https://github.com/mcuadros/ofelia
  • Image: mcuadros/ofelia:latest
  • License: MIT

When to Use

  • Scheduling recurring tasks inside Docker containers (backups, cleanup, reports)
  • Replacing host-level cron with container-aware scheduling
  • Configuring job-exec, job-run, job-local, or job-service-run via INI or Docker labels
  • Setting up job logging, overlap prevention, or Slack/email notifications
  • Running scheduled tasks in Docker Swarm services

When NOT to Use

  • Non-Docker environments — use standard cron or systemd timers instead
  • Complex workflow orchestration (Airflow, Prefect) — Ofelia is single-job scheduling

Quick Reference

| Concept | Summary | |---------|---------| | job-exec | Run command in existing container (docker exec) | | job-run | Run command in new ephemeral container (docker run) | | job-local | Run command on Ofelia host (or inside Ofelia container) | | job-service-run | Run command as one-shot Swarm service | | Config methods | INI file (--config) or Docker labels (--docker) | | Label format | ofelia.<JOB_TYPE>.<JOB_NAME>.<PARAM>=<VALUE> | | Schedule syntax | Go cron — @every 10s, @hourly, 0 1 * * * (seconds optional) | | Overlap prevention | no-overlap = true per job |

Configuration Methods

INI File

[global]
slack-webhook = https://hooks.slack.com/services/XXX
slack-only-on-error = true

[job-exec "flush-logs"]
schedule = @hourly
container = nginx-proxy
command = /bin/bash /flush-logs.sh
user = www-data
no-overlap = true

Launch: ofelia daemon --config=/etc/ofelia.conf

Docker Labels

Labels go on the target container for job-exec (requires ofelia.enabled=true), or on the Ofelia container for job-run, job-local, job-service-run.

labels:
  ofelia.enabled: "true"
  ofelia.job-exec.flush-logs.schedule: "@hourly"
  ofelia.job-exec.flush-logs.command: "/bin/bash /flush-logs.sh"
  ofelia.job-exec.flush-logs.user: "www-data"
  ofelia.job-exec.flush-logs.no-overlap: "true"

Both methods can be combined — INI for globals/local jobs, labels for exec jobs.

Docker Compose — Typical Setup

services:
  ofelia:
    image: mcuadros/ofelia:latest
    command: daemon --docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - app
    labels:
      # job-run and job-local labels go HERE (on ofelia)
      ofelia.job-run.backup.schedule: "0 2 * * *"
      ofelia.job-run.backup.image: "alpine:latest"
      ofelia.job-run.backup.command: "sh -c 'tar czf /backup/data.tar.gz /data'"
      ofelia.job-run.backup.volume: "/backup:/backup:rw"

  app:
    image: myapp:latest
    labels:
      # job-exec labels go HERE (on target container)
      ofelia.enabled: "true"
      ofelia.job-exec.migrate.schedule: "@daily"
      ofelia.job-exec.migrate.command: "python manage.py migrate --noinput"

Docker Compose with INI File

services:
  ofelia:
    image: mcuadros/ofelia:latest
    command: daemon --config=/etc/ofelia.conf --docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./ofelia.conf:/etc/ofelia.conf:ro

Container Filtering

Limit label discovery to specific containers:

command: daemon --docker -f label=com.docker.compose.project=${COMPOSE_PROJECT_NAME}

Schedule Syntax

Uses Go's robfig/cron. Seconds field is optional (supported for backward compatibility).

| Expression | Meaning | |-----------|---------| | @every 30s | Every 30 seconds | | @every 5m | Every 5 minutes | | @hourly | Top of every hour | | @daily / @midnight | Midnight daily | | @weekly | Midnight Sunday | | 0 1 * * * | 1:00 AM daily (5-field, no seconds) | | 0 0 1 * * * | 1:00 AM daily (6-field, with seconds) | | */5 * * * * | Every 5 minutes |

Gotcha: Older Ofelia versions required 6-field (seconds first). Current versions accept both 5-field and 6-field. If migrating old configs, 0 0 1 * * * (6-field) = 0 1 * * * (5-field).

Job Types

See references/job-parameters.md for complete parameter tables.

job-exec

Runs inside an existing running container. Equivalent to docker exec.

Required: schedule, command, container

[job-exec "clear-cache"]
schedule = @hourly
container = redis-server
command = redis-cli FLUSHDB
user = redis

job-run

Creates a new ephemeral container from an image. Equivalent to docker run --rm. Can also start a stopped container (specify container instead of image).

Required: schedule, plus either image or container

[job-run "db-backup"]
schedule = 0 2 * * *
image = postgres:16-alpine
command = pg_dumpall -h db -U postgres > /backup/dump.sql
volume = /backups:/backup:rw
network = app-network
environment = PGPASSWORD=secret
delete = true
pull = true

job-local

Runs on the host running Ofelia. When Ofelia is containerized, this runs inside the Ofelia container itself.

Required: schedule, command

[job-local "cleanup-tmp"]
schedule = @daily
command = find /tmp -mtime +7 -delete
dir = /tmp

job-service-run

Creates a one-shot Swarm service. For Docker Swarm environments only.

Required: schedule, image

[job-service-run "swarm-task"]
schedule = 0,20,40 * * * *
image = ubuntu:latest
network = swarm_network
command = touch /tmp/example
delete = true

Middleware (Per-Job and Global)

All middleware parameters can be set globally ([global] section / Ofelia container labels) or per-job. Per-job settings override global.

Overlap Prevention

no-overlap = true

Skips execution if previous run is still active. Logs as "skipped".

Slack Notifications

slack-webhook = https://hooks.slack.com/services/T00/B00/XXX
slack-only-on-error = true

Email Notifications

smtp-host = smtp.gmail.com
smtp-port = 587
smtp-user = alerts@example.com
smtp-password = app-password
smtp-tls-skip-verify = false
email-to = ops@example.com
email-from = ofelia@%s
mail-only-on-error = true

email-from supports %s placeholder for hostname. Emails include stdout/stderr log attachments.

Save to Disk

save-folder = /var/log/ofelia
save-only-on-error = false

Saves <timestamp>_<jobname>.stdout.log, .stderr.log, and .json (execution metadata).

Environment Variables

Docker Connection

| Variable | Description | Default | |----------|-------------|---------| | DOCKER_HOST | Docker daemon endpoint | unix:///var/run/docker.sock | | DOCKER_TLS_VERIFY | Enable TLS (1) | unset | | DOCKER_CERT_PATH | TLS certificate directory | unset | | DOCKER_API_VERSION | Override API version | auto-negotiated |

Socket Proxy (recommended for production)

services:
  docker-proxy:
    image: tecnativa/docker-socket-proxy
    environment:
      CONTAINERS: 1
      POST: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  ofelia:
    image: mcuadros/ofelia:latest
    command: daemon --docker
    environment:
      DOCKER_HOST: tcp://docker-proxy:2375
    depends_on:
      - docker-proxy

CLI Commands

| Command | Description | |---------|-------------| | ofelia daemon --config=/path/to/config.ini | Run with INI config | | ofelia daemon --docker | Run with Docker label config | | ofelia daemon --config=/path --docker | Combined (both sources) | | ofelia daemon --docker -f label=KEY=VAL | Filter containers | | ofelia validate --config=/path/to/config.ini | Validate config without running |

Common Mistakes

| Mistake | Fix | |---------|-----| | job-exec labels on Ofelia container | Put on target container with ofelia.enabled=true | | job-run labels on target container | Put on Ofelia container (it creates new containers) | | Ofelia starts before target containers | Use depends_on or start Ofelia last; label reading is at startup | | 6-field cron not working | Current versions use 5-field by default; seconds are optional prefix | | Container name mismatch | Use docker ps name, not service name (Compose prepends project name) | | environment with multiple vars in labels | Use JSON array: ["FOO=bar", "BAZ=qux"] | | volume with multiple mounts in labels | Use JSON array: ["/a:/a:ro", "/b:/b:rw"] | | no-overlap not preventing overlap | Ensure parameter is on the job, not just globally | | job-local runs inside container | Expected when Ofelia is containerized; mount host dirs if needed | | Exit code -1 errors | Usually wrong user — try removing or changing the user parameter | | Max time exceeded | Jobs have 24-hour hard limit; redesign long-running tasks | | Image not found for job-run | Set pull = true (default) or ensure image exists locally |

Gotchas

  • Label reading timing: Ofelia reads labels at startup. If the target container isn't running yet, its job-exec labels won't be discovered. Use depends_on in Compose.
  • Dynamic label updates: Ofelia polls for label changes when using --docker, but adding/removing containers requires the polling to detect changes.
  • Config keys are case-insensitive: Schedule, schedule, and SCHEDULE all work.
  • Stdout/stderr buffer: Limited to 10MB per stream per execution. Larger output is truncated (circular buffer).
  • job-run delete and pull: Both default to true but are stored as strings internally due to a Go defaults library quirk — always quote in labels.
  • Docker auth: For private registries, Ofelia reads ~/.docker/config.json (or $DOCKER_CONFIG). Mount it into the container if using private images with job-run.
  • Swarm mode: job-service-run creates a one-shot service with RestartPolicy: none and MaxAttempts: 1. The service is deleted after completion if delete = true.