k8s创建定时的 Python 任务(CronJob)

在 Kubernetes (k8s) 中创建一个每日定时的 Python 任务(CronJob),通常使用 CronJob 资源对象。这个 CronJob 会在指定的时间运行一次 Job,执行你的 Python 脚本。

下面是一个完整的流程:


🧩 场景说明

  • 每天凌晨 2:00 执行一个 Python 脚本。
  • Python 脚本打包在 Docker 镜像中。
  • 使用 Kubernetes 的 CronJob 实现定时任务。

✅ 步骤概览

  1. 编写 Python 脚本
  2. 构建 Docker 镜像并推送到镜像仓库
  3. 创建 Kubernetes CronJob YAML 文件
  4. 应用 YAML 并验证

1️⃣ 编写 Python 脚本

比如:script.py

# script.py
import datetime

print("Hello from Python CronJob!")
print(f"Current time: {datetime.datetime.now()}")

2️⃣ 构建 Docker 镜像

Dockerfile 示例:

FROM python:3.10-slim

WORKDIR /app

COPY script.py .

CMD ["python", "script.py"]

构建并推送镜像(以 Docker Hub 为例):

docker build -t your-dockerhub-username/python-cronjob:latest .
docker push your-dockerhub-username/python-cronjob:latest

3️⃣ 创建 Kubernetes CronJob YAML

文件名:python-cronjob.yaml

apiVersion: batch/v1
kind: CronJob
metadata:
  name: python-daily-job
  namespace: your-namespace
spec:
  schedule: "0 2 * * *"   # 每天 02:00 执行
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: python
              image: your-dockerhub-username/python-cronjob:latest
              imagePullPolicy: Always
          restartPolicy: OnFailure

Cron 表达式格式:

┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of the month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
0 2 * *  *

4️⃣ 部署到 Kubernetes

kubectl apply -f python-cronjob.yaml

查看 CronJob 状态:

kubectl get cronjobs
kubectl get jobs --watch
kubectl get pods

查看日志:

kubectl logs <pod-name>

🧪 测试 CronJob 是否生效

你可以手动触发一次 Job 来测试:

kubectl create job --from=cronjob/python-daily-job manual-run

然后查看 Pod 日志确认是否正常运行。


🔐 可选:私有镜像仓库认证

如果你使用的是私有镜像仓库,需要先创建 imagePullSecrets

kubectl create secret docker-registry regcred \
  --docker-server=<your-registry-server> \
  --docker-username=<your-name> \
  --docker-password=<your-pwd> \
  --docker-email=<your-email>

并在 CronJob 中添加:

imagePullSecrets:
  - name: regcred

📌 小贴士

  • 如果脚本需要访问外部资源(如数据库、API),请确保 Kubernetes 集群网络可达。
  • CronJob 默认不会保留历史记录,可以通过 .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 控制保留多少个成功/失败的任务。

❌ 问题

Python 定时任务运行结束后,Pod 没有进入 Completed 状态,而是保持在 Running 状态。查看发现,Pod 中的容器包括:

  • istio-init ✅ 已完成
  • python-daily-job ✅ 脚本已执行完毕(已完成)
  • istio-proxy ❌ 仍在运行

🧠 原因分析

这是 Istio Sidecar 注入导致的常见问题

当你在 Kubernetes 集群中启用了 Istio 的自动 Sidecar 注入(即 Pod 被注入了 istio-proxy 容器),默认情况下:

  • 即使你的主应用容器(比如 python-daily-job)已经退出,只要 istio-proxy 还在运行,整个 Pod 就不会被标记为 Completed
  • 因为 istio-proxy 是一个长期运行的代理服务,默认行为就是持续监听和转发流量,它不会因为主容器退出而退出。

🚫 导致后果

  • CronJob 控制器认为 Job 未完成,因此不会创建下一次定时任务。
  • Pod 长时间处于 Running 状态,浪费资源。
  • .spec.completionTime 不会设置,影响监控和日志清理。

✅ 解决方案

✅ 禁用 Sidecar 注入(推荐用于 Job/CronJob)

对于不需要网络通信或服务治理能力的定时任务来说,不应该注入 Istio Sidecar。

📌 操作方法:

在你的 CronJob YAML 文件中添加注解:

metadata:
  annotations:
    sidecar.istio.io/inject: "false"

完整示例:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: python-daily-job
  namespace: your-namespace
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          annotations:
            sidecar.istio.io/inject: "false"
        spec:
          containers:
            - name: python
              image: your-dockerhub-username/python-cronjob:latest
              imagePullPolicy: Always
          restartPolicy: OnFailure

✅ 这样部署后,只有你的 Python 容器会被启动,不会有 istio-proxyistio-init 容器。


🔍 如何验证是否生效?

部署更新后的 CronJob:

kubectl apply -f python-cronjob.yaml

然后查看新生成的 Pod:

kubectl get pods
kubectl describe pod <pod-name>

你应该只会看到一个容器:你的 Python 容器,并且当它完成后,Pod 会变成 Completed 状态。


🧪 手动测试技巧

你可以手动触发一次 CronJob:

kubectl create job --from=cronjob/python-daily-job manual-run

然后观察 Pod 状态变化:

kubectl get pods -w

确保 Pod 成功运行并进入 Completed 状态。


📌 总结

问题 原因 解决方案
Pod 长时间处于 Running 状态 Istio 注入了 istio-proxy 容器,它不会随主容器退出而退出 在 CronJob 上添加 sidecar.istio.io/inject: "false" 注解

🔍保留已完成的 Pod

✅ 需求总结

  • 使用的是 Kubernetes CronJob 创建的 Job/Pod。
  • Python 脚本运行完成后 Pod 状态变为 Completed
  • 默认情况下,Kubernetes 不会保留已完成的 Job 和 Pod。
  • 目标:让这些已完成的 Pod 保留 24 小时后再被自动删除。

🧠 原因分析

Kubernetes 的 Job 控制器默认不会保留已完成的 Job 和 Pod。你可以通过 .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 来控制保留多少个成功或失败的 Job(以及对应的 Pod)。

但默认值通常为:

successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1

如果你希望每个完成的 Pod 保留 24 小时而不是固定数量的历史任务,就需要额外机制来实现。


✅ 使用 TTL 控制器(推荐)

Kubernetes 提供了一个非常方便的功能叫做 TTL 控制器(TTL Controller for Finished Resources),可以让你指定一个已完成的资源(如 Job、Pod)在多长时间之后被自动删除。

✅ 实现方式

只需要在你的 Job 模板中添加 .spec.ttlSecondsAfterFinished 字段即可。

修改你的 CronJob YAML 如下:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: python-daily-job
  namespace: your-namespace
spec:
  schedule: "0 2 * * *"
  successfulJobsHistoryLimit: 1  # 可选:只保留最近一次成功任务
  failedJobsHistoryLimit: 1      # 可选:只保留最近一次失败任务
  jobTemplate:
    spec:
      template:
        metadata:
          annotations:
            sidecar.istio.io/inject: "false"  # 如果你不希望注入 Istio Sidecar
        spec:
          ttlSecondsAfterFinished: 86400   # 👈 关键配置:保留 24 小时 = 86400 秒, 必须放在这里!
          containers:
            - name: python
              image: your-dockerhub-username/python-cronjob:latest
              imagePullPolicy: Always
          restartPolicy: OnFailure

🔍 说明

  • ttlSecondsAfterFinished: 86400 表示:该 Job 完成后 24 小时内仍然保留在集群中,超过这个时间会被 Kubernetes 自动删除。
  • 这个功能从 Kubernetes v1.21 开始稳定支持。
  • 适用于 JobPodCronJobjobTemplate 中。

✅ 查看保留的 Pod

你可以查看所有已完成的 Pod:

kubectl get pods --field-selector=status.phase==Succeeded

或者根据命名空间过滤:

kubectl get pods -n <namespace> --field-selector=status.phase==Succeeded

这些 Pod 会在 24 小时后自动消失。


⚠️ 注意事项

  • 确保你的 Kubernetes 版本 >= v1.21。
  • 确保 kube-controller-manager 的参数 --controllers 包含 ttl 控制器(默认开启)。
  • 如果你使用的是托管 Kubernetes 服务(如阿里云 ACK、AWS EKS、GKE),一般都已默认启用。

✅ 总结

目标 实现方法
Pod 执行完后保留 24 小时再删除 JobCronJobjobTemplate.spec.template.spec 中设置 ttlSecondsAfterFinished: 86400
Logo

GitCode AI社区是一款由 GitCode 团队打造的智能助手,AI大模型社区、提供国内外头部大模型及数据集服务。

更多推荐