标签 cron 下的文章

Running a Cronjob Under Docker Container

当您想要安排计划任务,可以使用内置在 macOS 和 Linux 中的常见工具,比如 cron,或者像 AWS Lambda 这样的特殊工具。Cron 不如 AWS Lambda 强大,但它在 Unix 系统的后台任务中工作得很好,特别是在使用容器的情况下。然而,对于 Docker 来说这有点复杂,因为不能简单地从终端开始新的 cron 作业,并期望它工作。

How to Dockerize a Cron Job

要在 Docker 容器中运行 cron 作业,您需要使用 cron 并在 Docker 容器的前台运行它。

下面是一个如何设置的例子:

Create Cron File

创建一个文件,其中包含要在 Docker 容器下运行的所有 cron 作业。

cat cron

我们的示例文件如下:

* * * * * echo "Current date is `date`" > /var/log/cron

Create Dockerfile

接下来,创建一个安装 cron 服务的 Dockerfile,并将脚本复制到容器。

在这里,我们提供了 3 个 Dockerfile 示例,它们使用不同的操作系统。

Dockerfile with Alpine Linux

FROM alpine:3

# Copy cron file to the container
COPY cron /etc/cron.d/cron

# Give the permission
RUN chmod 0644 /etc/cron.d/cron

# Add the cron job
RUN crontab /etc/cron.d/cron

# Link cron log file to stdout
RUN ln -s /dev/stdout /var/log/cron

# Run the cron service in the foreground
CMD [ "crond", "-l", "2", "-f" ]

Dockerfile with Apache and PHP

FROM php:8.0-apache

# Install cron
RUN apt update && \
    apt -y install cron

# Copy cron file to the container
COPY cron /etc/cron.d/cron

# Give the permission
RUN chmod 0644 /etc/cron.d/cron

# Add the cron job
RUN crontab /etc/cron.d/cron

# Link cron log file to stdout
RUN ln -s /dev/stdout /var/log/cron

# Start cron service
RUN sed -i 's/^exec /service cron start\n\nexec /' /usr/local/bin/apache2-foreground

Dockerfile with Ubuntu Linux

FROM ubuntu:latest

# Install cron deamon
RUN apt update && apt install -y cron

# Copy cron file to the container
COPY cron /etc/cron.d/cron

# Give the permission 
RUN chmod 0644 /etc/cron.d/cron

# Add the cron job
RUN crontab /etc/cron.d/cron

# Link cron log file to stdout
RUN ln -s /dev/stdout /var/log/cron

# Run the cron service in the foreground
CMD ["cron", "-f"]

Build and Run Container

当前目录中有两个文件,一个是 cron, 它包含了 cronjob。 一个是 Dockerfile, 它有 Docker 的构建指令。运行以下命令使用 Dockerfile 构建 Docker 镜像。

docker build -t my_cron .

镜像构建成功后,启动容器:

docker run -d my_cron

这将启动容器下的 cron 守护进程,它将执行 cron 文件中定义的所有计划作业。

Test Setup

我们已经链接了 cron 日志文件 /var/log/cron/dev/stdout ,Cron 服务生成的所有日志
可以使用 docker logs 命令查看。

首先,使用 docker ps 命令查找容器 id 或名称。

docker ps

然后检查 Docker 容器的日志文件。

docker logs container_id

在 cronjobs 中,我打印了当前日期并把它们写入日志中。

Running Cronjobs in Docker

输出如上所示,这意味着 cron 作业在 Docker 容器下正常运行。

CLI Proxy API 的核动力驴一天 3 个小版本,每天打开电脑都是打开 render 更新部署。
于是搓了这样一个用 Deno Deploy 的 cron 触发 Render deploy hook 的小工具
每天凌晨 4 点自动更新 render 部署的 cpa(你也可以用来更新其他 render 项目)


去这里复制一下 Deploy Hook 就好了,改一下代码里面占位的 RENDER_HOOK_URL
非常简单的小工具

/**
 * Project: render-auto-deploy (sanitized, hardcoded URL version)
 * Purpose: 用 Deno Deploy Cron 定时触发 Render Deploy Hook(自动 redeploy)
 *
 * ✅ 你需要改的地方(部署时只改这两处):
 * 1) 把 RENDER_HOOK_URL 改成你自己的 Render Deploy Hook URL(包含 service id + key)
 * 2) 如果想换时间,把 CRON_SCHEDULE 改成你要的 cron(注意:Deno.cron 用 UTC)
 *
 * ✅ 验证方法:
 * - 打开 "/":看到 running + 当前 UTC 时间 + schedule
 * - 打开 "/trigger":立刻触发一次 Render 部署(返回 deploy.id)
 * - 去 Render Events:会出现 “Triggered via Deploy Hook”
 *
 * 🔥 时区提醒:
 * - Deno.cron 的 cron 表达式按 UTC 解释
 * - 例:UTC 20:00 = 北京时间次日 04:00
 */

// ======================【部署时改这里 1】======================
// Render Deploy Hook(占位符,换成你自己的)
// 形如:https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY>
const RENDER_HOOK_URL =
  "https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY>";

// ======================【部署时改这里 2】======================
// Cron 表达式按 UTC 解释:
// - "0 20 * * *" => 每天 UTC 20:00(北京时间次日 04:00)
// - 测试用: "*/1 * * * *" => 每分钟触发一次(用来验证 cron 是否生效)
const CRON_SCHEDULE = "0 20 * * *";

function nowIso() {
  return new Date().toISOString();
}

async function triggerRenderDeploy(source: "cron" | "http") {
  const runId = crypto.randomUUID();
  console.log(`[${nowIso()}] [${source}] runId=${runId} ⏰ trigger start`);

  // 超时:避免网络卡住导致任务悬挂
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 30_000);

  try {
    const resp = await fetch(RENDER_HOOK_URL, {
      method: "POST", // Render deploy hook 通常 GET/POST 都可,这里用 POST
      signal: controller.signal,
      headers: { "user-agent": "render-auto-deploy/deno" },
    });

    const text = await resp.text().catch(() => "");

    if (!resp.ok) {
      // 抛错 => 让 cron 的 backoffSchedule 生效,自动重试
      throw new Error(
        `Render hook failed: ${resp.status} ${resp.statusText} body=${text.slice(0, 500)}`,
      );
    }

    console.log(
      `[${nowIso()}] [${source}] runId=${runId} ✅ trigger ok body=${text.slice(0, 500)}`,
    );

    return { ok: true, runId, status: resp.status, body: text };
  } finally {
    clearTimeout(timeout);
  }
}

// ====== 定时任务(必须在模块顶层定义,Deno Deploy 才会识别为 Cron)======
Deno.cron(
  "Daily Render Auto Deploy",
  CRON_SCHEDULE,
  // 失败重试节奏(毫秒):1s, 5s, 10s
  { backoffSchedule: [1000, 5000, 10000] },
  async () => {
    try {
      await triggerRenderDeploy("cron");
    } catch (e) {
      console.error(`[${nowIso()}] [cron] ❌`, e);
      throw e; // 继续抛出以触发重试
    }
  },
);

// ====== Web:健康检查 + 手动触发(方便部署后立即验证)======
Deno.serve(async (req) => {
  const url = new URL(req.url);

  // 健康检查:确认服务活着
  if (url.pathname === "/") {
    return new Response(
      `Auto-Deploy is running.\nUTC now: ${nowIso()}\nSchedule(UTC): ${CRON_SCHEDULE}\n`,
      { headers: { "content-type": "text/plain; charset=utf-8" } },
    );
  }

  // 手动触发:GET /trigger
  // 部署完立刻访问一次,看返回里有没有 deploy.id
  if (url.pathname === "/trigger") {
    try {
      const result = await triggerRenderDeploy("http");
      return new Response(JSON.stringify(result, null, 2), {
        headers: { "content-type": "application/json; charset=utf-8" },
      });
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      return new Response(JSON.stringify({ ok: false, error: msg }, null, 2), {
        status: 500,
        headers: { "content-type": "application/json; charset=utf-8" },
      });
    }
  }

  return new Response("Not found", { status: 404 });
});


📌 转载信息
原作者:
sssun
转载时间:
2026/1/20 10:04:34