Part0: 日志/报错/可观测性
这块的缺失, 给问题排查带来了比较大的阻碍;
已经优化了几轮, 补齐了大部分日志打印/上下文, 增加调用来源app_code/request_id等等, 并且把后台报错也向上传递到了SaaS, 近期在接入otel
仍然存在的问题:
- 日志全部输出到标准输出, 混在一起, 导致非常难以捞日志
- 同步相关的过程日志过多
Part1: 整体调用链路及架构
前端vue -> SaaS -> SDK -> API
存在一个由 API 反向生成的 SDK 嵌入到了 SaaS;
并且, SaaS 调用 API 除了 SDK, 还存在call_through
透传的方式;
另外, 通过 SDK 调用 API 拼装了一批 headers, 用于控制返回值等 bkuser_shell/apis/viewset.py
本身功能并不多, 是否可以考虑:
- 简化甚至去掉SDK, 使用独立文件封装
- 简化SaaS到API之间的协议, 去掉大部分header封装 (可能需要引入/api/v3)
- 优化报错提示, 后台报错到SaaS再到前端, 直接在页面可见
Part2: API auth的复杂度 (运维切换耗时最多)
最早没有api auth, 后来也没有统一接入esb走jwt, 而是实现了自定义token进行的api auth
后来优化实现 bkuser_core/enhanced_account/authentication.py
三种认证, 四种场景:
- app_code / app_secret (用于容器化前后台调用的接口认证, 此时saas 和api的app_code/app_secret一致)
- TOKEN 认证 (原先的token认证, 目前在逐步去除, 外部版已去除, 内部上云版规划去除中)
- jwt (内外部版均已支持, 从esb过来的流量)
- 无认证 (原先二进制版, 未暴露服务, 无认证)
目前, 需要逐步推动去掉 2/4; 只保留 1/3; 主推 3;(只有本项目自己模块内调用用 1)
Part3: API 层的复杂度 (bug重灾区)
当前由于历史原因, 存在
/api/v1
(login在用)
/api/v2
(当前主要暴露的接口)
/api/v3
(已有实现, saas有两个接口调用)
存在问题:
/api/v2
由于接口非常灵活, 同一个viewset有多重继承, 并且由参数控制了input slz/output slz以及一些过滤的逻辑, 导致整体耦合在一起;
- 不好排查问题
- 目前已经有性能问题, 但是难以优化, 因为一些参数已经放出, 使用方误用/滥用无法控制
/api/v3
当前的实现方案还是过于灵活, 需要考虑去掉重做方案
目标: 对外提供简单 API, 所有参数可控; 降低参数与逻辑的耦合; 强单一职责, 一个viewset只做一件事情;
- 给 SaaS 的API, 尽量灵活(可控的, 减少开发量)
- 给外部的 API, 尽量简单(不可控, 降低风险)
Part4: 目录同步的复杂度 (排查问题耗时最多)
当前的plugin及同步:
重点是local+ldap/mad;
当前存在的问题:
- 过于抽象, 层层嵌套;
- 使用了十分动态的语法糖实现, 导致很难跟踪代码调用链路, 很难拿到明确的报错及堆栈;
需要考虑的处理:
- 简化模型, 减少抽象(足够当前 5 种plugin即可), 统一的数据模型+适当的adapter;
- 去动态化, 去掉dataclass(post_init)和get_attr等语法糖, 明确的注入及调用链, 明确的报错堆栈
- 减少
约定
;
可以考虑设计一个新的数据模型+plugin模型, 逐步迁移;
Part6: 权限模块
权限模型比较特殊, 资源是基于目录的递归(都是同一种类型, 但是带层次)
所以实现时, 做了大量定制;
目前这块逻辑需要考虑如何优化, 必要时考虑重新设计权限模型(尽量简化)
iam_filter_actions = ("list", "list_tops")
=> self.filter_queryset
=>
https://github.com/wklken/bk-user/blob/891cb75458ccf514e0e87415784beee51617f4a7/src/api/bkuser_core/apis/v2/viewset.py#L231
问题: 权限落在api上, api需要处理来自saas的请求(有iam), 也要处理来自esb的请求, 此时 SaaS 的场景会更多, 导致 api 很多地方没法配合理的权限(多个场景调用统一个 API) => 应该把权限控制移动到saas层
Part6: 分层及缓存
- 分层: 目前缺少一层中间抽象层, 例如service; (由于是django models, 这层本身就比较模糊), 但是需要考虑切一层出来
- 缓存: 目前用的 cache_page(有bug), django localmem(jwt获取公钥缓存)
考虑的处理方式:
- api层到viewset
- service层 (缓存控制/权限控制)
- model层
Type: proposal