searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

模型开发平台链路事件日志

2024-05-15 06:28:45
8
0

大模型开发主要可以分为: 模型训练, 模型微调, 模型评估, 模型部署几个核心模块. 每个模块从用户操作到最终容器启动运行中间还有多个步骤, 每个步骤都可能导致模块任务失败.我们期望能够记录各个步骤的成功/失败事件并透出给最终用户,方便用户自己去排查定位问题,从而降低日常系统维护答疑的成本.
我们以模型训练任务举例. 用户创建一个模型训练任务, 会涉及到以下步骤:

  1. 数据库保存模型实例
  2. 拉取模型镜像
  3. 获取训练硬件资源, 如gpu, npu等
  4. 生成pod模版
  5. 调度pod资源
  6. 挂载模型代码pvc
  7. 挂载模型训练数据pvc
  8. 启动pod

我们需要在这些步骤前后加上日志埋点, 输出日志, 其次把这些日志搜索出来展示给最终用户.这里会遇到两个问题

  1. pod, pvc这些资源的调度, 创建是通过k8s指令来异步操作的, 如何获取这部分操作的日志
  2. 如何把同一个任务流程的各个步骤串联起来, 避免不相干的日志污染链路日志

针对这两个问题, 我们设计了以下两个方案来解决

k8s事件日志

模型训练任务涉及到很多k8s资源的操作, 如训练数据的pvc挂载, pod的调度, 启动, 关闭等. 这些操作是通过后台应用调用k8s client接口来异步执行的, 后台应用无法同步感知到操作的结果.

k8s client接口执行后,会向k8s集群下发操作指令, 由k8s集群真正执行操作. k8s 提供了informer机制, k8s client可以通过informer来实现对apiserver数据的读取和监听.

informer机制介绍

Kubernetes各个组件都是通过REST API跟API Server交互通信的,而如果每次每一个组件都直接跟API Server交互去读取/写入到后端的etcd的话,会对API Server以及etcd造成非常大的负担。 而Informer机制是为了保证各个组件之间通信的实时性、可靠性,并且减缓对API Server和etcd的负担。

Informer 首先会 list/watch apiserver,Informer 所使用的 Reflector 包负责与 apiserver 建立连接,Reflector 使用 ListAndWatch 的方法,会先从 apiserver 中 list 该资源的所有实例,list 会拿到该对象最新的 resourceVersion,然后使用 watch 方法监听该 resourceVersion 之后的所有变化,若中途出现异常,reflector 则会从断开的 resourceVersion 处重现尝试监听所有变化,一旦该对象的实例有创建、删除、更新动作,Reflector 都会收到"事件通知",这时,该事件及它对应的 API 对象这个组合,被称为增量(Delta),它会被放进 DeltaFIFO 中。

 

Informer 会不断地从这个 DeltaFIFO 中读取增量,每拿出一个对象,Informer 就会判断这个增量的时间类型,然后创建或更新本地的缓存,也就是 store。

如果事件类型是 Added(添加对象),那么 Informer 会通过 Indexer 的库把这个增量里的 API 对象保存到本地的缓存中,并为它创建索引,若为删除操作,则在本地缓存中删除该对象。

DeltaFIFO 再 pop 这个事件到 controller 中,controller 会调用事先注册的 ResourceEventHandler 回调函数进行处理。我们应用层可以通过注册自定义的ResourceEventHandler来监听各个资源对象的变更事件, 获取到资源对象的状态, 变更原因记录到日志中. 

链路traceId

如何把任务中不同步骤的日志串联起来, 也就是如何为不同的日志生成统一的traceId. 我们选择用任务的实例id来作为traceId. 用户创建任务实例后, 把traceId缓存在threadLocal中, 接下来的所有步骤都使用这个traceId. 这些日志通过日志服务采集到日志数据库中, 用户查看事件日志时, 使用实例id来过滤出这些步骤日志, 展示到前端.

k8s资源对象的日志, 在k8s resourceEventHandler实现中, 获取到各个资源相关连的任务实例id来获取到traceId. 这里前提是相关的资源名称, 如pod名称, pvc名称上要填充任务实例id, 然后在resourceEventHandler实现里面通过解析名称来获取到traceId. 

通过以上方案, 初步实现了大模型服务链路事件日志,但是缺点也很明显, 如:

  1. 通过traceId搜索日志, 性能较差
  2. 日志埋点和业务逻辑强耦合
  3. 依赖资源名称来获取traceId, 不够稳定

需要后续继续优化.

0条评论
0 / 1000
吴****锋
4文章数
0粉丝数
吴****锋
4 文章 | 0 粉丝
吴****锋
4文章数
0粉丝数
吴****锋
4 文章 | 0 粉丝
原创

模型开发平台链路事件日志

2024-05-15 06:28:45
8
0

大模型开发主要可以分为: 模型训练, 模型微调, 模型评估, 模型部署几个核心模块. 每个模块从用户操作到最终容器启动运行中间还有多个步骤, 每个步骤都可能导致模块任务失败.我们期望能够记录各个步骤的成功/失败事件并透出给最终用户,方便用户自己去排查定位问题,从而降低日常系统维护答疑的成本.
我们以模型训练任务举例. 用户创建一个模型训练任务, 会涉及到以下步骤:

  1. 数据库保存模型实例
  2. 拉取模型镜像
  3. 获取训练硬件资源, 如gpu, npu等
  4. 生成pod模版
  5. 调度pod资源
  6. 挂载模型代码pvc
  7. 挂载模型训练数据pvc
  8. 启动pod

我们需要在这些步骤前后加上日志埋点, 输出日志, 其次把这些日志搜索出来展示给最终用户.这里会遇到两个问题

  1. pod, pvc这些资源的调度, 创建是通过k8s指令来异步操作的, 如何获取这部分操作的日志
  2. 如何把同一个任务流程的各个步骤串联起来, 避免不相干的日志污染链路日志

针对这两个问题, 我们设计了以下两个方案来解决

k8s事件日志

模型训练任务涉及到很多k8s资源的操作, 如训练数据的pvc挂载, pod的调度, 启动, 关闭等. 这些操作是通过后台应用调用k8s client接口来异步执行的, 后台应用无法同步感知到操作的结果.

k8s client接口执行后,会向k8s集群下发操作指令, 由k8s集群真正执行操作. k8s 提供了informer机制, k8s client可以通过informer来实现对apiserver数据的读取和监听.

informer机制介绍

Kubernetes各个组件都是通过REST API跟API Server交互通信的,而如果每次每一个组件都直接跟API Server交互去读取/写入到后端的etcd的话,会对API Server以及etcd造成非常大的负担。 而Informer机制是为了保证各个组件之间通信的实时性、可靠性,并且减缓对API Server和etcd的负担。

Informer 首先会 list/watch apiserver,Informer 所使用的 Reflector 包负责与 apiserver 建立连接,Reflector 使用 ListAndWatch 的方法,会先从 apiserver 中 list 该资源的所有实例,list 会拿到该对象最新的 resourceVersion,然后使用 watch 方法监听该 resourceVersion 之后的所有变化,若中途出现异常,reflector 则会从断开的 resourceVersion 处重现尝试监听所有变化,一旦该对象的实例有创建、删除、更新动作,Reflector 都会收到"事件通知",这时,该事件及它对应的 API 对象这个组合,被称为增量(Delta),它会被放进 DeltaFIFO 中。

 

Informer 会不断地从这个 DeltaFIFO 中读取增量,每拿出一个对象,Informer 就会判断这个增量的时间类型,然后创建或更新本地的缓存,也就是 store。

如果事件类型是 Added(添加对象),那么 Informer 会通过 Indexer 的库把这个增量里的 API 对象保存到本地的缓存中,并为它创建索引,若为删除操作,则在本地缓存中删除该对象。

DeltaFIFO 再 pop 这个事件到 controller 中,controller 会调用事先注册的 ResourceEventHandler 回调函数进行处理。我们应用层可以通过注册自定义的ResourceEventHandler来监听各个资源对象的变更事件, 获取到资源对象的状态, 变更原因记录到日志中. 

链路traceId

如何把任务中不同步骤的日志串联起来, 也就是如何为不同的日志生成统一的traceId. 我们选择用任务的实例id来作为traceId. 用户创建任务实例后, 把traceId缓存在threadLocal中, 接下来的所有步骤都使用这个traceId. 这些日志通过日志服务采集到日志数据库中, 用户查看事件日志时, 使用实例id来过滤出这些步骤日志, 展示到前端.

k8s资源对象的日志, 在k8s resourceEventHandler实现中, 获取到各个资源相关连的任务实例id来获取到traceId. 这里前提是相关的资源名称, 如pod名称, pvc名称上要填充任务实例id, 然后在resourceEventHandler实现里面通过解析名称来获取到traceId. 

通过以上方案, 初步实现了大模型服务链路事件日志,但是缺点也很明显, 如:

  1. 通过traceId搜索日志, 性能较差
  2. 日志埋点和业务逻辑强耦合
  3. 依赖资源名称来获取traceId, 不够稳定

需要后续继续优化.

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0