Kubernetes 背景任务处理与环境管理实践
1. 动态队列任务调度
在 Kubernetes 中,我们可以通过 kubectl 命令来管理任务和 Pod。例如,查看任务和 Pod 的状态:
$ kubectl get job,pod
NAME COMPLETIONS DURATION AGE
job.batch/addwork 1/1 22s 36s
NAME READY STATUS RESTARTS AGE
pod/addwork-l9fgg 0/1 Completed 0 37s
pod/redis-0 1/1 Running 0 19h
pod/redis-1 1/1 Running 0 19h
pod/redis-2 1/1 Running 0 19h
当 addwork 任务显示为 “Completed” 时,我们可以继续调度 Job 队列:
$ kubectl create -f 10.2.4_JobWorker
job.batch/jobworker created
再次查看任务和 Pod 的状态:
$ kubectl get job,pod
NAME COMPLETIONS DURATION AGE
job.batch/addwork 1/1 22s 3m45s
job.batch/jobworker 0/1 of 2 2m16s 2m16s
NAME READY STATUS RESTARTS AGE
pod/addwork-l9fgg 0/1 Completed 0 3m45s
pod/jobworker-swb6k 1/1 Running 0 2m16s
pod/jobworker-tn6cd 1/1 Running 0 2m16s
pod/redis-0 1/1 Running 0 19h
pod/redis-1 1/1 Running 0 19h
pod/redis-2 1/1 Running 0 19h
当队列清空后,工作节点会再处理一个任务然后退出。我们可以通过以下命令监控 Redis 队列的深度:
$ kubectl exec -it pod/redis-0 -- redis-cli llen queue:task
(integer) 0
当队列长度为 0 时,Pod 会在完成当前任务后进入 “Completed” 状态。
2. 静态队列任务处理
除了使用 Redis 这样的动态队列,Kubernetes 还支持使用静态队列来运行任务。静态队列的长度在任务开始前就已知,并且作为 Job 配置的一部分。以下是几种实现静态队列的方法:
- 使用索引的静态队列 :适用于提前知道任务数量且任务列表易于索引的场景,例如渲染电影帧。Kubernetes 会按照指定的完成次数运行 Job,并为每个任务创建一个 Pod,同时通过环境变量 $JOB_COMPLETION_INDEX 提供索引。示例配置如下:
apiVersion: batch/v1
kind: Job
metadata:
name: echo
spec:
completions: 5 #A
parallelism: 3 #B
completionMode: Indexed #C
template:
spec:
restartPolicy: Never
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command: ["echo", "render frame: $JOB_COMPLETION_INDEX"]
#A the number of times to run the job (upper bound of the index)
#B number of worker pods to run in parallel
#C Run in indexed mode, passing in JOB_COMPLETION_INDEX
- 使用消息队列服务的静态队列 :通过填充消息队列,让每个 Pod 从队列中拉取任务。可以通过 Kubernetes 配置为 Pod 提供所需参数,使容器无需感知队列的存在。Kubernetes 文档使用 RabbitMQ 演示了这种方法: https://kubernetes.io/docs/tasks/job/coarse-parallel-processing-work-queue/
- 通过脚本实现的静态队列 :使用脚本为队列中的每个任务创建一个单独的 Job。例如,如果有 100 个任务,就需要编写脚本创建 100 个 Job。这种方法管理起来比较繁琐,Kubernetes 文档有相关演示: https://kubernetes.io/docs/tasks/job/parallel-processing-expansion/
3. 背景任务的存活探针
对于执行任务的容器,无论是在 Deployment 还是 Job 中,都应该配置存活探针。存活探针可以帮助我们检测由于外部依赖失败导致的进程挂起问题。与 HTTP 服务不同,批处理工作负载通常没有可用于存活检测的端点或端口,因此可以使用命令来进行存活检测。
以下是一个 Ruby 示例,用于将当前 Unix 时间戳写入文件:
def self.log_process_date
fileName = "process.date"
aFile = File.new(::Rails.root.to_s + "/log/" + fileName, "w")
contents = Time.now.to_i.to_s
aFile.write(contents)
aFile.close
end
在正常运行时, log_process_date 方法会被频繁调用。我们可以使用以下 Bash 脚本来检查时间戳文件的时效性:
#!/bin/bash
# Liveness probe for batch process
# The process writes a logfile every time it runs with the current Unix timestamp.
# Usage: process_liveness.sh <path_to_file>
# The file must contain only the latest date as a Unix timestamp and no newlines
if ! rundate=$(<$1); then #A
echo >&2 failed #A
echo "no logfile" #A
exit 1 #A
fi #A
curdate=$(date +'%s') #B
diff=$((curdate-rundate)) #C
if [ $diff -gt 300 ] #D
then #D
echo "too old" #D
exit 100 #D
fi #D
exit 0 #E
#A Read the timestamp file from the input (parameter $1), and exit with an error if it doesn't exist
#B Get the current timestamp from the system
#C Compare the two timestamp
#D Return an error status code if the process timestamp is older than 300 seconds
#E Return a success status code
最后,我们可以在部署文件中配置存活探针来调用这个脚本:
livenessProbe:
initialDelaySeconds: 600 #A
periodSeconds: 30
exec: #B
command: ["./script/process_liveness.sh", "log/process.date"] #B
successThreshold: 1
timeoutSeconds: 1
#A The initial delay is long, as we need time for the app to boot and write the first timestamp
#B The command to run
4. 生产和预发布环境管理
在将应用部署到生产环境之前,通常需要创建一个预发布环境来测试更改。Kubernetes 的命名空间功能可以帮助我们轻松实现这一点。
以下是创建和管理命名空间的步骤:
1. 创建命名空间 :
kubectl create namespace staging
- 切换到命名空间 :
kubectl config set-context --current --namespace=staging
也可以使用 kubectx 和 kubens 工具来简化操作:
# List namespaces
kubens
# Switch to the staging namespace
kubens staging
- 部署应用到命名空间 :
kubectl apply -f .
当在集群中进行了一些命令式的更改后,我们可以使用以下命令导出配置:
# View the deployment as YAML
kubectl get deploy your-deployment -o yaml
# Pipe the deployment YAML config to a file
kubectl get deploy your-deployment -o yaml > your-deployment.yaml
5. 总结
Kubernetes 提供了多种处理背景队列和批处理作业的方法:
- Deployments :可用于构建持续运行的作业队列,使用 Redis 等队列数据结构进行协调。
- Jobs :适用于一次性任务,如手动维护任务。
- CronJobs :可用于定时调度作业,如每日清理任务。
- 静态队列 :可以避免使用队列数据结构,通过配置直接调度任务。
- 存活检查 :对于处理背景任务的 Pod 仍然很重要,可以使用 exec 存活检查来检测挂起的进程。
通过使用命名空间和配置文件,我们可以轻松复制多个环境,并且将 Kubernetes 部署配置视为源代码进行管理,从而提高开发和运维的效率。以下是一个简单的流程图,展示了任务处理的基本流程:
graph LR
A[开始] --> B[调度任务]
B --> C{任务是否完成}
C -- 是 --> D[监控队列深度]
D -- 队列空 --> E[处理最后一个任务]
E --> F[任务完成]
C -- 否 --> B
表格展示不同队列处理方式的优缺点:
| 队列处理方式 | 优点 | 缺点 |
| — | — | — |
| 动态队列(如 Redis) | 灵活,可实时调整任务 | 需要额外的队列管理 |
| 索引静态队列 | 适用于可索引任务,配置简单 | 处于 Beta 阶段,可能有变动 |
| 消息队列服务静态队列 | 容器无需感知队列 | 需要额外的消息队列服务 |
| 脚本静态队列 | 实现简单 | 管理繁琐 |
Kubernetes 背景任务处理与环境管理实践
6. GitOps 理念与配置管理
在 Kubernetes 中,我们大量使用 YAML 配置文件来与对象进行交互。将配置视为代码有诸多好处,不仅能方便地创建多个相同配置的环境,还能在应用部署到生产环境时,像管理代码一样管理配置。
6.1 利用命名空间复制环境
Kubernetes 的命名空间功能可帮助我们轻松创建生产和预发布环境。命名空间提供了名称唯一性,避免了在不同环境中对配置进行大量修改。
创建和使用命名空间的步骤如下:
1. 创建命名空间 :
kubectl create namespace staging
- 切换到命名空间 :
kubectl config set-context --current --namespace=staging
也可以使用 kubectx 和 kubens 工具简化操作:
# 列出命名空间
kubens
# 切换到预发布命名空间
kubens staging
- 部署应用到命名空间 :
kubectl apply -f .
通过上述步骤,我们可以轻松在不同命名空间中部署相同的应用,并且可以通过修改配置文件来调整不同环境的参数,如副本数量、外部服务凭证等。
6.2 以代码方式管理配置的好处
将 Kubernetes 部署配置视为源代码,我们可以利用 Git 的版本控制功能来管理配置的变更。通过 Git 的拉取请求(Merge Request),可以实现对配置变更的审查和自动化部署。
例如,开发人员在分支上修改配置文件,提交拉取请求后,经过团队成员的审查,确认无误后合并到主分支,然后触发自动化部署流程。这样可以确保配置的变更经过严格的审查,减少错误的发生。
7. 处理敏感信息
在配置文件中,我们可能会包含一些敏感信息,如数据库密码、API 密钥等。为了避免将这些信息以明文形式存储在版本控制中,我们可以采用以下方法:
- 使用 Kubernetes Secret :Kubernetes 提供了 Secret 对象,用于存储敏感信息。我们可以将敏感信息加密存储在 Secret 中,然后在配置文件中引用这些 Secret。
# 创建 Secret
kubectl create secret generic my-secret --from-literal=password=my-password
# 在配置文件中引用 Secret
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
env:
- name: PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: password
- 使用外部密钥管理系统 :如 HashiCorp Vault、AWS Secrets Manager 等,这些系统可以安全地存储和管理敏感信息,并提供 API 供 Kubernetes 访问。
8. 总结与展望
Kubernetes 在处理背景队列和批处理作业方面提供了丰富的功能,通过 Deployments、Jobs、CronJobs 等对象,以及动态和静态队列的处理方式,满足了不同场景的需求。同时,命名空间和配置文件的使用,使得环境的复制和管理变得更加简单。
在未来的开发和运维中,我们可以进一步结合 GitOps 理念,将配置管理与 CI/CD 流程集成,实现自动化的部署和更新。同时,合理使用存活探针和敏感信息管理方法,提高应用的稳定性和安全性。以下是一个流程图,展示了 GitOps 流程:
graph LR
A[开发人员修改配置] --> B[提交拉取请求]
B --> C{审查通过?}
C -- 是 --> D[合并到主分支]
D --> E[触发自动化部署]
E --> F[部署到目标环境]
C -- 否 --> A
表格展示不同配置管理方式的特点:
| 配置管理方式 | 特点 |
| — | — |
| 命令式操作 | 简单直接,适合临时操作 |
| 声明式配置 | 可重复使用,便于版本控制 |
| GitOps | 自动化部署,严格审查变更 |
通过合理运用这些技术和方法,我们可以更好地管理 Kubernetes 集群,提高开发和运维效率,确保应用的稳定运行。
超级会员免费看
1144

被折叠的 条评论
为什么被折叠?



