首页 文章 精选 留言 我的

精选列表

搜索[创意玩法],共3191篇文章
优秀的个人博客,低调大师

DeepSeek Harness 正式开源:一切皆插件,Agent 框架的新玩法

DeepSeek 正式开源了 DeepSeek Harness(DSH)的 v0.1 开发者预览版——一个"一切皆插件"的 Agent 运行时框架,MIT 协议,源码已托管到 GitHub。 和市面上大多数 AI Agent 框架不同,DSH 的核心卖点不是模型能力,而是架构的开放性。它没有选择重头造一个插件系统,而是基于一个叫 Cordis 的元框架构建——Cordis 只负责插件的加载、卸载和依赖关系,Agent 框架的所有具体组件(模型适配器、工具注册表、会话日志、沙箱、UI)都是不同的 Cordis 插件。 这意味着开发者不需要改一行 DSH 源码,就能替换其中任何一个能力模块。想换掉默认的沙箱实现,或者加一个自定义的调度策略,写个插件就行。 核心:Cordis 元框架 DSH 的底层是一个叫 Cordis 的元框架。Cordis 的定位是"时空可组合性元框架"(A Meta-Framework of Spatiotemporal Composability),目前 v4.0.0-rc.8,MIT 协议,由 DeepSeek AI 团队工程师 shigma 维护。 "时空可组合性"这个概念来自 Cordis 团队的学术论文(arXiv 预印本)。论文提出了两个维度的形式化: 时间可组合性:组件被移除时,其副作用能被完全撤销 空间可组合性:组件能声明并对其他组件的状态变化做出响应 翻译成人话就是:Cordis 不关心你的插件具体做什么,它只关心插件的加载、卸载和依赖关系。每个插件通过 ctx 上下文提供服务,通过 inject 声明依赖,通过类型化事件通信——emit 观察、waterfall 包装、parallel 和 serial 控制执行顺序。注册是可逆的副作用,卸载时自动清理。 这意味着 DSH 的根基是一个学术级别的插件框架,而不是随便拼凑的"插件系统"。 架构分层 DSH 的架构文档读起来像操作系统的设计文档。系统分为几个层次: 最底层是 dsh-base——模型适配器、工具注册表、持久化、沙箱和审批策略、设置、凭据、遥测。这是每个 profile 的第一层。 上面是 dsh-web-app(浏览器界面)或 dsh-headless(无服务器的单次运行器)。 profile 是命名的插件组合。一个 profile 列出它依赖的 bundle,bundle 是 Cordis 配置行和代码的分发格式。配置通过 patch 机制叠加——每个 bundle 的配置、profile 的 patch、home 目录的 patch、命令行 --patch 参数,按顺序应用到空白的 entry 列表上。 用户可以随时运行 dsh --profile web --dump-config 查看当前完整的插件树,然后用 patch 替换其中任何一行。 核心 package DSH 的 monorepo 包含 68 个 package,覆盖了 Agent 系统的每个环节: Package 职责 core/session 仅追加的会话事件日志 core/system-prompt 提示词和工具 schema 组装 core/tools 作用域工具注册表与执行管线 core/agent Agent 接口与注册表 core/agent-loop 默认的 Agent 驱动实现 llm/llm 消息与流词汇,适配器接口 Seam:能力替换的设计精髓 DSH 最优雅的设计是 seam(能力缝合点) 的概念。一个 seam 有三个角色:Service Definition(声明接口)、Service Provider(实现它)、Consumer(使用它)。 文件系统和子进程的 provider 共享同一个执行世界,所以把它们的 provider 指向远程沙箱,Bash、PTY 和 LSP 会一起跟着过去,不需要为每个工具写单独的 provider 适配。这就是"替换一个 provider 就能改变整个产品"的含义。 四种运行模式 DSH 提供了四种预设模式,每种加载不同的插件组合: 标准模式:完整的工具组合,日常使用 PTC 模式:程序化工具调用(Programmatic Tool Calling),由模型生成一段代码来组合多轮工具调用 极简模式:仅保留一个 shell 工具和一个文件编辑工具,用于最小环境下的模型基准测试 创造模式:可以检查当前运行时、在内存中试验 Cordis 插件,并据此组合和创作新的模式 会话日志与事件溯源 DSH 的会话日志是仅追加(append-only)设计的。模型看到的一切——系统提示词、思维链、工具调用与结果、子 Agent 调度、每一次上下文注入——都会被记录。 架构文档里写得很明确:"Model-visible means logged." 任何到达模型请求的信息都必须能从日志中重建,运行时有一个断言来保证这一点。 恢复、分叉、检索与回放共享同一份事件流。fork 一个会话只需要 ctx.sessions.fork(source, boundary, childSessionId)——从日志中的某个时间点分支,创建一个新的子会话。 事件系统 事件就是扩展点,DSH 把事件分为三个域: 会话事件:追加到日志的持久事实,重新加载后仍然存在 Agent 事件(agent/*):携带活跃 Agent 引用,用于观察或拦截进行中的工作 能力事件:无需导入循环即可向某个 seam 附加策略和适配器 开始使用 安装方式很简单,有 Node.js 就能跑: npx @deepseek-ai/dsh web 默认启动在 http://127.0.0.1:3080。源码安装则走 git clone → pnpm install → pnpm build → pnpm dsh web 的路径。 参考来源 DeepSeek Harness 官方公告 DeepSeek Harness GitHub DSH 架构文档(中文) Cordis 元框架 Cordis 学术论文

优秀的个人博客,低调大师

布隆过滤器新玩法了~ | 博学谷狂野架构师

布隆过滤器 作者: 博学谷狂野架构师 GitHub:GitHub地址 (有我精心准备的130本电子书PDF) 只分享干货、不吹水,让我们一起加油!😄 什么是布隆过滤器 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。 布隆过滤器可以理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置的合理,它的精确度可以控制的相对足够精确,只会有小小的误判概率。 当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。打个比方,当它说不认识你时,肯定就不认识;当它说见过你时,可能根本就没见过面,不过因为你的脸跟它认识的人中某脸比较相似 (某些熟脸的系数组合),所以误判以前见过你。 套在上面的使用场景中,布隆过滤器能准确过滤掉那些已经看过的内容,那些没有看过的新内容,它也会过滤掉极小一部分 (误判),但是绝大多数新内容它都能准确识别。这样就可以完全保证推荐给用户的内容都是无重复的。 布隆过滤器的原理 其本质就是一个只包含0和1的数组。具体操作当一个元素被加入到集合里面后,该元素通过K个Hash函数运算得到K个hash后的值,然后将K个值映射到这个位数组对应的位置,把对应位置的值设置为1。查询是否存在时,我们就看对应的映射点位置如果全是1,他就很可能存在(跟hash函数的个数和hash函数的设计有关),如果有一个位置是0,那这个元素就一定不存在。 首先需要初始化一个二进制的数组,长度设为 L,同时初始值全为 0 。 当写入一个 A1=1000 的数据时,需要进行 H 次 hash 函数的运算**(这里为 2 次);与 HashMap 有点类似,通过**算出的 HashCode 与 L 取模后定位到 0、2 处,将该处的值设为 1。 A2=2000 也是同理计算后将 4、7 位置设为 1。 当有一个 B1=1000 需要判断是否存在时,也是做两次 Hash 运算,定位到 0、2 处,此时他们的值都为 1 ,所以认为 B1=1000 存在于集合中。 当有一个 B2=3000 时,也是同理。第一次 Hash 定位到 index=4 时,数组中的值为 1,所以再进行第二次 Hash 运算,结果定位到 index=5 的值为 0,所以认为 B2=3000 不存在于集合中。 整个的写入、查询的流程就是这样,汇总起来就是: 对写入的数据做 H 次 hash 运算定位到数组中的位置,同时将数据改为 1 。当有数据查询时也是同样的方式定位到数组中。一旦其中的有一位为 0 则认为数据肯定不存在于集合,否则数据可能存在于集合中。 布隆过滤器的特点 只要返回数据不存在,则肯定不存在。 返回数据存在,但只能是大概率存在。 同时不能清除其中的数据。 在有限的数组长度中存放大量的数据,即便是再完美的 Hash 算法也会有冲突,所以有可能两个完全不同的 A、B 两个数据最后定位到的位置是一模一样的。 删除数据也是同理,当我把 B 的数据删除时,其实也相当于是把 A 的数据删掉了,这样也会造成后续的误报。 基于以上的 Hash 冲突的前提,所以 Bloom Filter 有一定的误报率,这个误报率和 Hash 算法的次数 H,以及数组长度 L 都是有关的。 应用场景 缓存穿透 我们经常会把一部分数据放在Redis等缓存,比如产品详情。这样有查询请求进来,我们可以根据产品Id直接去缓存中取数据,而不用读取数据库,这是提升性能最简单,最普遍,也是最有效的做法。一般的查询请求流程是这样的:先查缓存,有缓存的话直接返回,如果缓存中没有,再去数据库查询,然后再把数据库取出来的数据放入缓存,一切看起来很美好。但是如果现在有大量请求进来,而且都在请求一个不存在的产品Id,会发生什么?既然产品Id都不存在,那么肯定没有缓存,没有缓存,那么大量的请求都怼到数据库,数据库的压力一下子就上来了,还有可能把数据库打死。 使用布隆过滤器的特点,只要返回数据不存在,则肯定不存在,返回数据存在,但只能是大概率存在,这种特点可以大批量的无效请求过滤掉,能够穿透缓存的知识漏网之鱼,无关紧要。 检查单词拼写 检查一个单词拼写是否正确,因为有海量的单词数量,每天可能有新的单词,使用布隆过滤器,可以将单词映射到很小的内存中,可以经过简单的几次hash运行就可以进行校验,只要返回数据不存在,则肯定不存在,返回数据存在,但只能是大概率存在,虽然可能有误报,但是对系统的提升是革命性的。 Guava的布隆过滤器 这就又要提起我们的Guava了,它是Google开源的Java包,提供了很多常用的功能。 Guava中,布隆过滤器的实现主要涉及到2个类,BloomFilter和BloomFilterStrategies,首先来看一下BloomFilter的成员变量。需要注意的是不同Guava版本的BloomFilter实现不同。 布隆过滤器解析 成员变量分析 COPY/** guava实现的以CAS方式设置每个bit位的bit数组 */ private final LockFreeBitArray bits; /** hash函数的个数 */ private final int numHashFunctions; /** guava中将对象转换为byte的通道 */ private final Funnel<? super T> funnel; /** * 将byte转换为n个bit的策略,也是bloomfilter hash映射的具体实现 */ private final Strategy strategy; 这是它的4个成员变量: LockFreeBitArray是定义在BloomFilterStrategies中的内部类,封装了布隆过滤器底层bit数组的操作。 numHashFunctions表示哈希函数的个数。 Funnel,它和PrimitiveSink配套使用,能将任意类型的对象转化成Java基本数据类型,默认用java.nio.ByteBuffer实现,最终均转化为byte数组。 Strategy是定义在BloomFilter类内部的接口,代码如下,主要有2个方法,put和mightContain。COPYinterface Strategy extends java.io.Serializable { /** 设置元素 */ <T> boolean put(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits); /** 判断元素是否存在*/ <T> boolean mightContain( T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits); ..... } 创建布隆过滤器,BloomFilter并没有公有的构造函数,只有一个私有构造函数,而对外它提供了5个重载的create方法,在缺省情况下误判率设定为3%,采用BloomFilterStrategies.MURMUR128_MITZ_64的实现。 BloomFilterStrategies.MURMUR128_MITZ_64是Strategy的两个实现之一,Guava以枚举的方式提供这两个实现,这也是《Effective Java》书中推荐的提供对象的方法之一。 COPYenum BloomFilterStrategies implements BloomFilter.Strategy { MURMUR128_MITZ_32() {//....} MURMUR128_MITZ_64() {//....} } 二者对应了32位哈希映射函数,和64位哈希映射函数,后者使用了murmur3 hash生成的所有128位,具有更大的空间,不过原理是相通的,我们选择相对简单的MURMUR128_MITZ_32来分析。 先来看一下它的put方法,它用两个hash函数来模拟多个hash函数的情况,这是布隆过滤器的一种优化。 put方法 COPYpublic <T> boolean put( T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) { long bitSize = bits.bitSize(); // 先利用murmur3 hash对输入的funnel计算得到128位的哈希值,funnel现将object转换为byte数组, // 然后在使用哈希函数转换为long long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); // 根据hash值的高低位算出hash1和hash2 int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); boolean bitsChanged = false; // 循环体内采用了2个函数模拟其他函数的思想,相当于每次累加hash2 for (int i = 1; i <= numHashFunctions; i++) { int combinedHash = hash1 + (i * hash2); // 如果是负数就变为正数 if (combinedHash < 0) { combinedHash = ~combinedHash; } // 通过基于bitSize取模的方式获取bit数组中的索引,然后调用set函数设置。 bitsChanged |= bits.set(combinedHash % bitSize); } return bitsChanged; } 在put方法中,先是将索引位置上的二进制置为1,然后用bitsChanged记录插入结果,如果返回true表明没有重复插入成功,而mightContain方法则是将索引位置上的数值取出,并判断是否为0,只要其中出现一个0,那么立即判断为不存在。 mightContain方法 COPYpublic <T> boolean mightContain( T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) { long bitSize = bits.bitSize(); long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); for (int i = 1; i <= numHashFunctions; i++) { int combinedHash = hash1 + (i * hash2); // Flip all the bits if it's negative (guaranteed positive number) if (combinedHash < 0) { combinedHash = ~combinedHash; } // 和put的区别就在这里,从set转换为get,来判断是否存在 if (!bits.get(combinedHash % bitSize)) { return false; } } return true; } Guava为了提供效率,自己实现了LockFreeBitArray来提供bit数组的无锁设置和读取。我们只来看一下它的put函数。 COPYboolean set(long bitIndex) { if (get(bitIndex)) { return false; } int longIndex = (int) (bitIndex >>> LONG_ADDRESSABLE_BITS); long mask = 1L << bitIndex; // only cares about low 6 bits of bitIndex long oldValue; long newValue; // 经典的CAS自旋重试机制 do { oldValue = data.get(longIndex); newValue = oldValue | mask; if (oldValue == newValue) { return false; } } while (!data.compareAndSet(longIndex, oldValue, newValue)); bitCount.increment(); return true; } Guava布隆过滤器使用 引入坐标 COPY<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.0-jre</version> </dependency> 代码实现 COPY public class GuavaBloomFilter { /** * 设置布隆过滤器大小 */ private static final int size = 100000; /** * 构建一个BloomFilter * 第一个参数Funnel类型的参数 * 第二个参数 期望处理的数据量 * 第三个参数 误判率 可不加,默认 0.03D */ private static final BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(), size); public static void main(String[] args) { //成功计数 float success = 0; //失败计数 float fial = 0; Set<String> stringSet = new HashSet<String>(); for (int i = 0; i < size; i++) { //生成随机字符串 String randomStr = RandomStringUtils.randomNumeric(10); //加入到set中 stringSet.add(randomStr); //加入到布隆过滤器 bloomFilter.put(randomStr); } for (int i = 0; i < size; i++) { //生成随机字符串 String randomStr = RandomStringUtils.randomNumeric(10); //布隆过滤器校验存在 if (bloomFilter.mightContain(randomStr)) { //set中存在 if (stringSet.contains(randomStr)) { //成功计数 success++; } else { //失败计数 fial++; } //布隆过滤器校验不存在 } else { // set中存在 if (stringSet.contains(randomStr)) { //失败计数 fial++; } else { //成功计数 success++; } } } System.out.println("判断成功数:"+success + ",判断失败数:" + fial + ",误判率:" + fial / 100000); } 输出 COPY判断成功数:97084.0,判断失败数:2916.0,误判率:0.02916 可以通过增加误判率的参数来调整误判率 COPY/** * 构建一个BloomFilter * 第一个参数Funnel类型的参数 * 第二个参数 期望处理的数据量 * 第三个参数 误判率 可不加,默认 0.03D */ private static final BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(), size,0.00001); 输出 COPY判断成功数:100000.0,判断失败数:0.0,误判率:0.0 本文由传智教育博学谷狂野架构师教研团队发布。 如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。 转载请注明出处!

优秀的个人博客,低调大师

还在用ES查日志吗,快看看石墨文档 Clickhouse 日志架构玩法

1 背景 石墨文档全部应用部署在Kubernetes上,每时每刻都会有大量的日志输出,我们之前主要使用SLS和ES作为日志存储。但是我们在使用这些组件的时候,发现了一些问题。 成本问题: SLS个人觉得是一个非常优秀的产品,速度快,交互方便,但是SLS索引成本比较贵 我们想减少SLS索引成本的时候,发现云厂商并不支持分析单个索引的成本,导致我们无法知道是哪些索引构建的不够合理 ES使用的存储非常多,并且耗费大量的内存 通用问题: 如果业务是混合云架构,或者业务形态有SAAS和私有化两种方式,那么SLS并不能通用 日志和链路,需要用两套云产品,不是很方便 精确度问题:SLS存储的精度只能到秒,但我们实际日志精度到毫秒,如果日志里面有traceid,SLS中无法通过根据traceid信息,将日志根据毫秒时间做排序,不利于排查错误 我们经过一番调研后,发现使用Clickhouse能够很好的解决以上问题,并且Clickhouse省存储空间,非常省钱,所以我们选择了Clickhouse方案存储日志。但当我们深入研究后,Clickhouse作为日志存储有许多落地的细节,但业界并没有很好阐述相关Clickhouse采集日志的整套流程,以及没有一款优秀的Clickhouse日志查询工具帮助分析日志,为此我们写了一套Clickhouse日志系统贡献给开源社区,并将Clickhouse的日志采集架构的经验做了总结。先上个Clickhouse日志查询界面,让大家感受下石墨最懂前端的后端程序员。 2 架构原理图 我们将日志系统分为四个部分:日志采集、日志传输、日志存储、日志管理。 日志采集:LogCollector采用Daemonset方式部署,将宿主机日志目录挂载到LogCollector的容器内,LogCollector通过挂载的目录能够采集到应用日志、系统日志、K8S审计日志等 日志传输:通过不同Logstore映射到Kafka中不同的Topic,将不同数据结构的日志做了分离 日志存储:使用Clickhouse中的两种引擎数据表和物化视图 日志管理:开源的Mogo系统,能够查询日志,设置日志索引,设置LogCollector配置,设置Clickhouse表,设置报警等 以下我们按照这四大部分,阐述其中的架构原理 3 日志采集 3.1 采集方式 Kubernetes容器内日志收集的方式通常有以下三种方案 DaemonSet方式采集:在每个 node 节点上部署LogCollector,并将宿主机的目录挂载为容器的日志目录,LogCollector读取日志内容,采集到日志中心。 网络方式采集:通过应用的日志 SDK,直接将日志内容采集到日志中心 。 SideCar方式采集:在每个 pod 内部署LogCollector,LogCollector只读取这个 pod 内的日志内容,采集到日志中心。 以下是三种采集方式的优缺点: DaemonSet方式 网络方式 SideCar方式 采集日志类型 标准输出+文件 应用日志 部署运维 一般,维护DaemonSet 低,维护配置文件 日志分类存储 可通过容器/路径等映射 业务独立配置 支持集群规模 取决于配置数 无限制 适用场景 日志分类明确、功能较单一 性能要求极高的场景 资源消耗 中 低 我们主要采用DaemonSet方式和网络方式采集日志。DaemonSet方式用于ingress、应用日志的采集,网络方式用于大数据日志的采集。以下我们主要介绍下DeamonSet方式的采集方式。 ​ 3.2 日志输出 从上面的介绍中可以看到,我们的DaemonSet会有两种方式采集日志类型,一种是标准输出,一种是文件。 引用元乙的描述:虽然使用 Stdout 打印日志是 Docker 官方推荐的方式,但大家需要注意:这个推荐是基于容器只作为简单应用的场景,实际的业务场景中我们还是建议大家尽可能使用文件的方式,主要的原因有以下几点: Stdout 性能问题,从应用输出 stdout 到服务端,中间会经过好几个流程(例如普遍使用的JSON LogDriver):应用 stdout -> DockerEngine -> LogDriver -> 序列化成 JSON -> 保存到文件 -> Agent 采集文件 -> 解析 JSON -> 上传服务端。整个流程相比文件的额外开销要多很多,在压测时,每秒 10 万行日志输出就会额外占用 DockerEngine 1 个 CPU 核; Stdout 不支持分类,即所有的输出都混在一个流中,无法像文件一样分类输出,通常一个应用中有 AccessLog、ErrorLog、InterfaceLog(调用外部接口的日志)、TraceLog 等,而这些日志的格式、用途不一,如果混在同一个流中将很难采集和分析; Stdout 只支持容器的主程序输出,如果是 daemon/fork 方式运行的程序将无法使用 stdout; 文件的 Dump 方式支持各种策略,例如同步/异步写入、缓存大小、文件轮转策略、压缩策略、清除策略等,相对更加灵活。 从这个描述中,我们可以看出在docker中输出文件在采集到日志中心是一个更好的实践。所有日志采集工具都支持采集文件日志方式,但是我们在配置日志采集规则的时候,发现开源的一些日志采集工具,例如fluentbit、filebeat在DaemonSet部署下采集文件日志是不支持追加例如pod、namespace、container_name、container_id等label信息,并且也无法通过这些label做些定制化的日志采集。 agent类型 采集方式 daemonset部署 sidecar部署 ilogtail 文件日志 能够追加label信息,能够根据label过滤采集 能够追加label信息,能够根据label过滤采集 fluentbit 文件日志 无法追加label信息,无法根据label过滤采集 能够追加abel信息,能够根据label过滤采集 filebeat 文件日志 无法追加label信息,无法根据label过滤采集 能够追加label信息,能够根据label过滤采集 ilogtail 标准输出 能够追加label信息,能够根据label过滤采集 能够追加label信息,能够根据label过滤采集 fluentbit 标准输出 能够追加label信息,能够根据label过滤采集 能够追加abel信息,能够根据label过滤采集 filebeat 标准输出 能够追加label信息,能够根据label过滤采集 能够追加label信息,能够根据label过滤采集 基于无法追加label信息的原因,我们暂时放弃了DeamonSet部署下文件日志采集方式,采用的是基于DeamonSet部署下标准输出的采集方式。 ​ 3.3 日志目录 以下列举了日志目录的基本情况 目录 描述 类型 /var/log/containers 存放的是软链接,软链到/var/log/pods里的标准输出日志 ​标准输出 /var/log/pods 存放标准输出日志 ​标准输出 /var/log/kubernetes/ master存放Kubernetes 审计输出日志 标准输出 /var/lib/docker/overlay2 存放应用日志文件信息 文件日志 /var/run 获取docker.sock,用于docker通信 文件日志 /var/lib/docker/containers 用于存储容器信息 两种都需要 因为我们采集日志是使用的标准输出模式,所以根据上表我们的LogCollector只需要挂载/var/log,/var/lib/docker/containers两个目录。 3.3.1 标准输出日志目录 应用的标准输出日志存储在/var/log/containers目录下,​文件名是按照K8S日志规范生成的。这里以nginx-ingress的日志作为一个示例。我们通过ls /var/log/containers/ | grep nginx-ingress指令,可以看到nginx-ingress的文件名。 nginx-ingress-controller-mt2wx_kube-system_nginx-ingress-controller-be3741043eca1621ec4415fd87546b1beb29480ac74ab1cdd9f52003cf4abf0a.log ​ 我们参照K8S日志的规范:/var/log/containers/%{DATA:pod_name}_%{DATA:namespace}_%{GREEDYDATA:container_name}-%{DATA:container_id}.log。可以将nginx-ingress日志解析为: pod_name:nginx-ingress-controller-mt2w namespace:kube-system container_name:nginx-ingress-controller container_id:be3741043eca1621ec4415fd87546b1beb29480ac74ab1cdd9f52003cf4abf0a 通过以上的日志解析信息,我们的LogCollector 就可以很方便的追加pod、namespace、container_name、container_id的信息。 ​ 3.3.2 容器信息目录 应用的容器信息存储在/var/lib/docker/containers目录下,目录下的每一个文件夹为容器ID,我们可以通过cat config.v2.json获取应用的docker基本信息。 3.4 LogCollector采集日志 3.4.1 配置 我们LogCollector采用的是fluent-bit,该工具是cncf旗下的,能够更好的与云原生相结合。通过Mogo系统可以选择Kubernetes集群,很方便的设置fluent-bit configmap的配置规则。 3.4.2 数据结构 ​fluent-bit的默认采集数据结构 @timestamp字段:string or float,用于记录采集日志的时间 log字段:string,用于记录日志的完整内容 Clickhouse如果使用@timestamp的时候,因为里面有@特殊字符,会处理的有问题。所以我们在处理fluent-bit的采集数据结构,会做一些映射关系,并且规定双下划线为Mogo系统日志索引,避免和业务日志的索引冲突。 _time_字段:string or float,用于记录采集日志的时间 _log_字段:string,用于记录日志的完整内容 例如你的日志记录的是{"id":1},那么实际fluent-bit采集的日志会是{"_time_":"2022-01-15...","_log_":"{\"id\":1}" 该日志结构会直接写入到kafka中,Mogo系统会根据这两个字段_time_、_log_设置clickhouse中的数据表。 3.4.3 采集 如果我们要采集ingress日志,我们需要在input配置里,设置ingress的日志目录,fluent-bit会把ingress日志采集到内存里。 然后我们在filter配置里,将log改写为_log_ 然后我们在ouput配置里,将追加的日志采集时间设置为_time_,设置好日志写入的kafka borkers和kafka topics,那么fluent-bit里内存的日志就会写入到kafka中 日志写入到Kafka中_log_需要为json,如果你的应用写入的日志不是json,那么你就需要根据fluent-bit的parser文档,调整你的日志写入的数据结构:https://docs.fluentbit.io/manual/pipeline/filters/parser 4 日志传输 Kafka主要用于日志传输。上文说到我们使用fluent-bit采集日志的默认数据结构,在下图kafka工具中我们可以看到日志采集的内容。 在日志采集过程中,会由于不用业务日志字段不一致,解析方式是不一样的。所以我们在日志传输阶段,需要将不同数据结构的日志,创建不同的Clickhouse表,映射到Kafka不同的Topic。这里以ingress为例,那么我们在Clickhouse中需要创建一个ingress_stdout_stream的Kafka引擎表,然后映射到Kafka的ingress-stdout Topic里。 5 日志存储 我们会使用三种表,用于存储一种业务类型的日志。 Kafka引擎表:将数据从Kafka采集到Clickhouse的ingress_stdout_stream数据表中 createtablelogger.ingress_stdout_stream ( _source_String, _pod_name_String, _namespace_String, _node_name_String, _container_name_String, _cluster_String, _log_agent_String, _node_ip_String, _time_Float64, _log_String ) engine=KafkaSETTINGSkafka_broker_list='kafka:9092',kafka_topic_list='ingress-stdout',kafka_group_name='logger_ingress_stdout',kafka_format='JSONEachRow',kafka_num_consumers=1; 物化视图:将数据从ingress_stdout_stream数据表读取出来,_log_根据Mogo配置的索引,提取字段在写入到ingress_stdout结果表里 CREATEMATERIALIZEDVIEWlogger.ingress_stdout_viewTOlogger.ingress_stdoutAS SELECT toDateTime(toInt64(_time_))AS_time_second_, fromUnixTimestamp64Nano(toInt64(_time_*1000000000),'Asia/Shanghai')AS_time_nanosecond_, _pod_name_, _namespace_, _node_name_, _container_name_, _cluster_, _log_agent_, _node_ip_, _source_, _log_AS_raw_log_,JSONExtractInt(_log_,'status')ASstatus,JSONExtractString(_log_,'url')ASurl FROMlogger.ingress_stdout_streamwhere1=1; 结果表:存储最终的数据 createtablelogger.ingress_stdout ( _time_second_DateTime, _time_nanosecond_DateTime64(9,'Asia/Shanghai'), _source_String, _cluster_String, _log_agent_String, _namespace_String, _node_name_String, _node_ip_String, _container_name_String, _pod_name_String, _raw_log_String, statusNullable(Int64), urlNullable(String), ) engine=MergeTreePARTITIONBYtoYYYYMMDD(_time_second_) ORDERBY_time_second_ TTLtoDateTime(_time_second_)+INTERVAL7DAY SETTINGSindex_granularity=8192; 6 总结流程 日志会通过fluent-bit的规则采集到kafka,在这里我们会将日志采集到两个字段里 _time_字段用于存储fluent-bit采集的时间 _log_字段用于存放原始日志 通过mogo,在clickhouse里设置了三个表 app_stdout_stream: 将数据从Kafka采集到Clickhouse的Kafka引擎表 app_stdout_view: 视图表用于存放mogo设置的索引规则 app_stdout:根据app_stdout_view索引解析规则,消费app_stdout_stream里的数据,存放于app_stdout结果表中 最后mogo的UI界面,根据app_stdout的数据,查询日志信息 7 Mogo界面展示 查询日志界面 设置日志采集配置界面 以上文档描述是针对石墨Kubernetes的日志采集,想了解物理机采集日志方案的,可以在下文中找到《Mogo使用文档》的链接,运行docker-compose体验Mogo 全部流程,查询Clickhouse日志。限于篇幅有限,Mogo的日志报警功能,下次在讲解。 ​ 8 资料 github地址: https://github.com/shimohq/mogo Mogo文档:https://mogo.shimo.im Mogo使用文档:https://mogo.shimo.im/doc/AV62KU4AABMRQ fluent-bit文档:https://docs.fluentbit.io/ K8S日志 6 个 K8S 日志系统建设中的典型问题,你遇到过几个:https://developer.aliyun.com/article/718735 一文看懂 K8S 日志系统设计和实践:https://developer.aliyun.com/article/727594 9 个技巧,解决 K8S 中的日志输出问题:https://developer.aliyun.com/article/747821 直击痛点,详解 K8S 日志采集最佳实践:https://developer.aliyun.com/article/749468?spm=a2c6h.14164896.0.0.24031164UoPfIX Clickhouse Clickhouse官方文档:https://clickhouse.com/ Clickhouse作为Kubernetes日志管理解决方案中的存储:http://dockone.io/article/9356 Uber 如何使用 ClickHouse 建立快速可靠且与模式无关的日志分析平台?:https://www.infoq.cn/article/l4thjgnr7hxpkgpmw6dz 干货 | 携程ClickHouse日志分析实践:https://mp.weixin.qq.com/s/IjOWAPOJXANRQqRAMWXmaw 为什么我们要从ES迁移到ClickHouse:https://mp.weixin.qq.com/s/l4RgNQPxvdNIqx52LEgBnQ ClickHouse 在日志存储与分析方面作为 ElasticSearch 和 MySQL 的替代方案:https://mp.weixin.qq.com/s/nJXorcgi0QfXPCKr_HdUZg 快手、携程等公司转战到 ClickHouse,ES 难道不行了?:https://mp.weixin.qq.com/s/hP0ocT-cBCeIl9n1wL_HBg 日志分析下ES/ClickHouse/Loki比较与思考:https://mp.weixin.qq.com/s/n2I94X6tz2jOABzl1djxYg

优秀的个人博客,低调大师

新闻背后:企业级市场“另类”创业,云库的玩法值得关注

【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 2018年5月,Pure Storage 亚太及日本地区副总裁Michael Alp接受媒体采访,正式确认进入中国市场,一同现身的还有云库新技术公司总裁温智流。Pure Storage将在云库公司的帮助下落地中国。 云库新技术有限公司,这个名字,即便对那些已经在企业级IT市场摸爬滚打多年的老兵来说,可能也是头一回听说。 “顾名思义,云是新一代企业级技术的核心,库则代表里面有很多宝贝和好东西。”云库新技术有限公司总裁温智流表示,“成立云库这家公司,我们想做的事情就是去帮助中国的企业用户,让他们在业务转型过程中能够使用到***进的技术。” 云库还有个非常酷的英文名“InnoMITS”,取意Innovation Makes IT Simple,创新让IT简单。看来,在云计算大行其道的新IT时代,云库希望用简单来打动客户。 对的人做对的事,撬动企业级市场 威科姆创业模型认为,创业活动包括创业者、机会、组织和资源四大要素,彼此互相关联。与Timmons模型不同,威科姆认为创业者是创业活动的中心,其职能体现在与其他三个要素的关系上,即识别和确认创业机会、管理创业资源、领导创业组织。同时,创业者有效处理机会、资源和组织的关系,实现要素间的动态协调和匹配。 简单说,先“选对人”。 据了解,云库团队成员均来自IBM、EMC、HP、GE、Oracle等国际知名IT企业,其服务团队拥有20余年丰富的行业经验,在IT、医疗、汽车、金融、教育、政府等领域拥有深厚的客户基础,并具备深层次对接国际领先技术的能力。据了解,云库现已搭建起完备的销售、技术服务和运营支持体系,几十名资深销售、售前、售后工程师已经在北上广深和众多一线城市就位。 管理团队中,熟悉IT圈的朋友都知道,总裁温智流,江湖人称“水哥”(源自其英文名Waters Wan),是前EMC大中华区最重要的幕后功臣之一。温智流拥有35年以上的IT行业经验,职业生涯从服务支持工程师起步,横跨运营、销售与销售管理、渠道、市场等各个维度。在推动领先技术方案的本土化落地,实现客户增长方面,温智流应该说轻车熟路,让人对他领导的云库团队充满期待。 再“做对事”。 温智流这样描述云库团队的定位和使命:“云库就是个桥梁,把***进的技术带到国内,同时,非常了解和关照本土客户需求,帮助他们用好这些技术。”迈出弥合数字化转型鸿沟的“***步”,云库首先将目光瞄准了全闪存领域的技术***——Pure Storage。 大道至简,用对的人做靠谱的事,云库的创业观目标清晰、方向明确。 手执Pure Storage这把“金刚钻” 在各大分析机构眼中,Pure Storage是全闪存领域当仁不让的“超级明星”。在客户眼中,Pure Storage也备受青睐。 IDC MarketScape全球全闪存阵列2017供应商评估 Gartner 2017全闪存阵列魔力象限 如今,Pure Storage的全闪存业务已经是年收入超10亿美金的“现金牛”,全球客户达4500多家。达到10亿美元营收这一里程碑,Pure Storage用了8年,是企业级IT市场发展最快的上市公司,排在它之后的包括博科、思科、F5、NetApp等知名的企业IT公司。 与各大厂或孵化收购全闪存技术,或研发改造传统存储产品线的做法不同,Pure Storage从创始之初就是“All in”、专注全闪存,完全是“一张白纸”上的正向研发。 Pure Storage亚太及日本地区副总裁Michael Alp表示,“在全闪存领域,Pure Storage拥有700多项技术专利。我们的思路并不是在原来传统存储的基础上进行改造,研发全闪存存储平台。现在,无论块、文件还是对象、容器等,通过Pure Storage的技术,用户可以在统一平台上高效运行任意不同类型应用,基本是***制的。Pure Storage的理念就是创新、简单、好用。” 说到简单、好用时,Michael Alp从衬衣口袋里掏一张名片大小的折叠卡片,这是Pure Storage的产品手册! “只有一张纸片儿”大小的Pure Storage产品方案手册 “以前在老东家的时候,就知道在企业级技术领域,尤其是全闪存市场,Pure Storage会是一个非常强劲的竞争对手。”温智流坦言,“在很多传统行业,现在国内的企业都是‘互联网+’,业务转型步伐快、要求高。大家都意识到,迫切需要一个更优秀的系统级平台,帮助企业更快速、简单、高效地投入新兴应用场景,如容器、AI等。这样,企业才能充分享受新技术红利,加速转型发展。” 再次指了指手中那张“只有一张名片”大小的产品安装手册,适用于任何企业简单到复杂的架构“不管你信不信,我之前是从来没见过这样的。”温智流笑言。 “把Pure Storage领先的技术带到中国市场,我们对这个过程是非常审慎、有选择性的。这次和云库合作,最主要的就是看重云库有能力实施本土化方案,让Pure Storage的产品技术更好地对接本地市场需求,贴近行业客户实际。”Michael Alp表示,“Pure Storage和云库互相都认为大家是优先的、最重要的合作伙伴。包括本地客户服务和售后支持、语言支持等,云库会是对接Pure Storage产品技术和客户的主力军。” 温智流透露,云库目前已有多台Pure Storage测试机器准备就绪,并且已经有很多用户正在进行或要求进行POC中。 据了解,云库未来还会进行资本运作和自主研发,向更多产品线拓展。 最近有一篇题为《为什么说中国to B时代终于来了?》的微信文章大火,阅读量10万+。 其实,To B(面向企业的业务)永远都不会像To C(面向消费者的业务)那样,资本一吹猪上天。To B时代应该是属于云库这样有资源积累的公司,选对人,做对事,持续耕耘,必有所成。让我们对云库的创业拭目以待。 【责任编辑: 赵立京 TEL:(010)68476606】

优秀的个人博客,低调大师

人脸识别向保险业的渗透,未来保险会有哪些新玩法?

人工智能时代,对保险行业充满着机遇。2016年,我国保险行业保费收入达3万亿元,快速跃居世界第二。伴随行业的快速发展和保险受众群体的日益扩大,社会各方对保险产品和服务质量、效率等都有新的期待。 根据调查显示,国内保险客户反馈对险企的改进期待之中,排名前三位的是:更个性化的服务、更安全的隐私保护以及更简单便捷的操作流程。 这一结果并不令人感到意外,要知道,保险行业在投保、理赔、运营三个核心环节仍存在很多痛点,如今的APP几乎能完成大部分流程操作,即便在一定程度上减少了人工审核工作, 但是智能科技的发挥空间依然很大。 智能时代的变化,让我们再次思考科技将给未来保险带来怎样的变化,以及现在该做什么,所幸,我们已经看到了【人工智能+未来保险】的方向和未来趋势。 | 保险业引入“人脸识别”,成果已初见端倪 基于近几年大数据和算法的快速发展,国内保险公

优秀的个人博客,低调大师

【云计算的1024种玩法】为喜欢的人建一个网站

曾几何时,你是不是也为不知道怎么向心爱的人表白而苦恼,书信略显古老,微信稍微有点随意,那么,从今天起,一种高大上的表白方式正式上线阿里云——“为喜欢的人建一个网站”。即使您是能够“攻城掠地”的工程师,也会苦于没有后台服务器的支撑,没关系,阿里云服务器ECS为您解忧;也许您是一个非IT行业人士,对网页一无所知,没关系,我们给您提供一整套的服务,您只需要把爱表达出来就行了。 在阿里云服务器的支撑下,搭建一个属于自己的网站。以阿里云服务器ECS为基础,搭建一个网页平台,用HTML5编出自己喜欢的动态网页内容。 一、 搭建一个可以运行的平台1、开通云服务器阿里巴巴财大气粗,对在校学生也是相当慷慨。阿里云账户完成实名认证和学生身份认证,购买云产品即可享受优惠。 链接如下:https://promotion.aliyun.com/ntms/c

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册