Harbor GC 流程分析

Harbor GC 流程分析
Photo by Timelab / Unsplash

最近我们构建环境的 Harbor GC 经常出故障,借排查问题的机会,梳理了 Harbor GC 的流程,这个文档是基于官方的 2.2.3 源码分析的,GC 大概分为这两部分:

  • API 创建任务,并通知 job service,这部分是在 core 组件中完成的。
  • job service 处理任务,并给 core 组件同步任务状态,这部分是在 job service 组件中完成的。

两个组件通过 HTTP 请求的方式交互。

Core 处理 API 请求

API 请求及参数
API 路由注册
最主要的业务逻辑都在 kick 中
Start 中有两步重要的操作:1. 创建 execution 记录,最终会体现到 gc 页面的任务状态 2. 创建 task 记录,task 会跟 job id 关联起来
在创建 task 之后,会通知 job service,由 job service 生成一个 ID 跟 Task 关联起来
在 submit 阶段,有一个很重要的字段 StatusHook,job service 在执行任务时,也会调用这个 hook 同步任务状态给 core 组件
最终会调用 job service 提供的 API 把任务发送过去

至此,创建任务的部分已结束。

Job Service 处理任务

Job Service 处理任务的逻辑又可以再细分为几个步骤:

  • 初始化 Worker 来处理任务
  • API 接收 core 的请求,把任务保存用于后续处理
  • 任务分发,从 Redis 中获取任务所需要的参数,并交给 Worker 来处理
  • 状态变更,任务的每个阶段都会有状态的变更
    这部分代码量比较大,截图时有可能会只截取重要的部分。

Worker 初始化

创建 worker 以及 pool,注册 job 类型,job 在创建时,会有一个 name 的参数,指的就是这个类型,比如 gc 的类型是 GARBAGE_COLLECTION,后面在处理任务的时候,会用这个类型做反射。
worker 和 pool 都是在这里创建的
woker 在创建时,还会初始化一个 script,这是一段 luna 脚本,简单来说是把对 redis 的一些操作做了封装,变成了原子操作
单个 job 的注册
每种 job 都会生成一个 job type,并且注册了一个 DynamicHandler
DynamicHandler 也就是下面这段代码传进来的匿名 function,其中调用了 redisJob.Run,这个才是真正的 job 处理逻辑。
最后启动所有的 worker
worker 启动之后,会轮询 redis 获取 job 数据用来消费

处理 API 请求

API 路由注册
从 core 组件发送的参数可知,launchJob 走的是 default 分支
里面的 enqueue 最终把 job 的数据放到了 redis list 里, redis 的 key 是 {namespace}:jobs:GARBAGE_COLLECTION,其中 namespace 是可配置的

任务分发

当 job 通过 api 保存之后,woker 会不断地从 redis 获取任务来消费
前面提到的 luna 脚本,这里会用来配合一些参数从 redis 获取 job
luna 脚本里操作了 job queue,让一条 job 出栈,放到了 in progress 的队列里,表示正在处理中

job 出栈之前还会判断当前正在处理的 job 数量,只有小于最大并发数,才能取出数据,其中用到的四个 redis key 分别是

  • {namespace}:jobs:GARBAGE_COLLECTION job 队列,前面提到的 api 请求就是把 job 保存到这里了
  • {namespace}:jobs:GARBAGE_COLLECTION:{woker pool id}:inprogress worker 正在处理的 job,这个 woker id 是 worker 启动生成的随机字符串,在另一个 redis key 里保存的。
  • {namespace}:jobs:GARBAGE_COLLECTION:lock 当前正在处理的 job 数量
  • {namespace}:jobs:GARBAGE_COLLECTION:max_concurrency 最大并发数
fetch job 取出数据交给 processJob,接着调用了 runJob,最终迂回到了 DynamicHandler
也就是上面提到的 redisJob.Run

状态更新

redisJob.Run 里面用 tracker 对 job 数据做了一次封装,主要是用来更新 job 状态,这部分就不展开讲了。

Todo

  • Garbage Collector 逻辑
  • job 重试逻辑
  • 通知 core 组件