数据专栏

智能大数据搬运工,你想要的我们都有

数据资讯

数据学院

数据百科

MInitab应该算是比较知名的统计软件了,我现在正在学习当中,然后查找了一些小技巧帮助自己学习,对Minitab感兴趣可以跟着我一起学习啊!想要下载试用的话,可以在在这里下载: Minitab试用 Minitab的LinkedIn组是一个很好的工具,可以提出问题并获得经验丰富的人的意见,这些人具有分析数据和从事各种专业领域的统计工作的经验。例如,一个成员问出: “我正在尝试创建一个图表,可以按月监视变化。我有[去年]数据,并想将其与[今年]数据进行比较...我应该使用哪种图表,并且可以自动更新?谢谢。 ”当问出这一个问题时,Minitab用户社区将提供一些很棒的信息和有用的建议。参与者经常超越问题本身,不仅回答提出的问题,而且提出该问题所隐含的问题。 在本篇及后续文章中,将会仔细解析小组成员提出的各种建议,因为每个建议都有其优点。首先:一个简单的个人差异图,其中包含一些很酷的技巧,可以在新数据可用时立即进行更新。 个人差异表 监视每月变化的一种简单方法是使用个人图表。这是在Minitab统计软件中执行此操作的方法,如果您想一起玩,请使用我正在使用的数据集。 在数据表中需要四列:月份名称,今年的数据,去年的数据,以及一列表示今年和去年之间的差异的列。 将右键单击“差异”列,然后选择“公式”>“将公式分配给列...”,这将显示以下对话框。用一个简单的减法公式来完成它,但是根据您的情况,可能需要一个不同的公式: 分配此公式后,当输入今年和去年的数据时,它们之间的差额将即时计算出来。 现在,可以创建差异的“个人图表”或“ I图表”。 选择“统计”>“控制图”>“个人的变量图”>“个人...”,只需选择“差异”列作为我的变量。 Minitab将创建以下图表,显示去年数据与今年数据之间的差异: 自动更新个人图表 现在,您会注意到,当开始工作时,只获得了今年9月份的数据。如果需要全年更新时,会发生什么? 这很容易做到-可以返回一月份的数据表以添加上一季度的数据。Diff列使用其分配的公式(由列标题中的绿色小十字表示)来计算差异: 现在,如果看一下之前创建的I-图,会在左上角看到一个大黄点。 如上图所示,当右键单击该黄点并选择“自动更新”时,Minitab会使用一年中最后三个月的信息自动更新个人图表: 从上图可以看出我们可能会在一年的最后一个月发生某些特殊原因的变化,现在可以采取措施提前预防了!
来源:OSCHINA
发布时间:2020-07-15 16:47:00
前言 工作经常接触到海量文件储存,曾经把数百万个文件存储在 NTFS 格式的磁盘中,结果导致重启后无法识别磁盘,拆下硬盘插到外接 USB 硬盘盒上居然可以识别读取,吓得我赶紧删掉数据保住硬盘。 分布式存储方案 tfs (Taobao File System) 接触 tfs 是因为接受一个旧项目使用到了 tfs。tfs 这个项目 taobao 开源的一个大坑,项目缺少维护和文档,依赖老旧给编译部署带来极大的困难。 minio 文档全面,门槛低,上手容易,支持分布式,纠错码,s3。 leofs 测试了一下,底层好像性能不高。 ceph 文档复杂,学习曲线陡峭,对硬件网络要求高。大规模私有云的利器 2020年疫情期间在家磨了一个月的 ceph,部署 ceph 必须操作系统必须得 ubuntu,docker 不支持最新版本的要求。存储需要物理磁盘,网络要稳定。 seaweedfs 支持对象存储,resultful,s3,分布式灵活。个人开发的项目,有一些 bug,更新较快。 用来做图片服务器,目前很稳定。
来源:OSCHINA
发布时间:2020-07-15 13:09:00
Visdom: https://github.com/facebookresearch/visdom 1. 安装 Visdom: pip install visdom 2. 启动visdom服务: visdom 3. 编写visdom脚本 pytest.py: from visdom import Visdom import numpy as np import math vis = Visdom() # 单个条形图 vis.bar( X =np.random.rand( 20 )) # 堆叠条形图 vis.bar( X =np.abs(np.random.rand( 5 , 3 )), opts = dict ( stacked = True , legend =[ 'Sina' , '163' , 'AliBaBa' ], rownames =[ '2013' , '2014' , '2015' , '2016' , '2017' ] ) ) # 分组条形图 vis.bar( X =np.random.rand( 20 , 3 ), opts = dict ( stacked = False , legend =[ 'A' , 'B' , 'C' ] ) ) 4. 运行脚本pytest.py: python pytest.py 5. web端访问: http://localhost:8097/
来源:OSCHINA
发布时间:2020-07-14 20:03:00
在很多情况,我们由于疏忽会将一些敏感信息误传到 Git 仓库上面去。 尽管我们可以使用 git rm 将包含敏感信息文件删除掉,然后重新提交上传,文件就不会在仓库文件列表显示。 但是这并不能完全将敏感信息文件从仓库中完全删除, commit history 仍然会有敏感信息的文件的残留,我们仍然可以从仓库中的 commit history 中访问到文件。 如果想要将敏感信息文件完全删除。不仅需要将文件从 github 中的文件列表中删除,同时还需要将文件从 github 的 commit history 中的 文件相关信息删除。删除 commit history 文件相关信息,主要有两种方法: filter-branch BFG 一、filter-branch 1.1 移除数据 filter-branch 是 git 自带的命令: git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all 请将上面命令中的 PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA 替换为你要删除的文件名路径(相对路径、绝对路径均可)。 如果你想删除文件夹的话,只需要添加一个 -r 参数即可,形如: git filter-branch --force --index-filter \ 'git rm -r --cached --ignore-unmatch PATH-TO-YOUR-DIR-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all 1.2 避免再次提交 为了防止敏感文件再次被提交,可以将其加入到 .gitignore 文件中。 1.3 提交仓库 执行以下命令将其强制推送到仓库中: git push origin --force --all -all 参数将修改作用于远程的所有分支。 1.4 提交 tags 以上命令不会对 tag 生效,如需修改,执行命令: git push origin --force --tags 二、BFG 除了使用 git 自带的 filter-barch 命令,还有一个更加方便的命令工具, 可以帮助我们删除 commit history 中的敏感信息。 这就是 BFG 。 首先下载 BFG 工具: wget http://repo1.maven.org/maven2/com/madgag/bfg/1.12.16/bfg-1.12.16.jar 执行命令: javar -jar bfg-1.12.16.jar --delete-files YOUR-FILE-WITH-SENSITIVE-DATA 和使用 filter-branch 一样,将 YOUR-FILE-WITH-SENSITIVE-DATA 替换为你要删除的文件路径,然后执行命令提交到仓库中: git push origin --force --all 三、参考文章 Git移除敏感数据 从Github中的Commit历史移除敏感文件
来源:OSCHINA
发布时间:2020-07-14 18:41:00
在产品精细化运营时代,经常会遇到产品增长问题:比如指标涨跌原因分析、版本迭代效果分析、运营活动效果分析等。这一类分析问题高频且具有较高时效性要求,然而在人力资源紧张情况,传统的数据分析模式难以满足。本文尝试从0到1实现一款轻量级大数据分析系统——MVP,以解决上述痛点问题。 文章作者:数据熊,腾讯云大数据分析工程师。 一、背景及问题 在产品矩阵业务中,通过仪表盘可以快速发现增长中遇到的问题。然而,如何快速洞悉问题背后的原因,是一个高频且复杂的数据分析诉求。 如果数据分析师通过人工计算分析,往往会占用0.5-1天时间才能找到原因。因此,人工计算分析方式,占用人力大,且数据分析效率低。 另外,产品版本迭代与业务运营活动,也需要对新版本、新功能、新活动进行快速数据分析,已验证效果。 因此,在产品矩阵业务精细化运营中,存在大量的数据分析诉求,且需要快速完成。 在传统的数据分析模式下,对于每个需求,一般需要经历3-5天才能解决问题。除此之外,该模式还需要大量数据分析师对接需求。因此,在数据分析师人力紧缺情况下,该模式无法满足产品增长的数据分析诉求。 二、解决办法 在传统数据分析模式失效情况下,急需开拓新的数据分析模式,以快速满足产品增长的数据分析诉求。 为此,笔者和项目小团队从0到1实现一款轻量级大数据分析系统——MVP,希望通过MVP数据分析,驱动产品从"Minimum Viable Product" to "Most Valuable Product"。 除此之外,通过MVP数据分析系统,一方面希望提升数据分析效率;另一方面希望节省数据分析人力。 MVP数据分析系统分为四个模块,在产品业务-经营指标模块,基于AARRR模型对产品增长指标分析,分析产品增长北极星指标;在指标异常-根因预警模块,对增长指标异动进行监控,并提供根因线索;在分析工具-增长分析模块,对用户行为进行深入分析,洞悉用户行为;在AB-Test实验评估模块,对业务决策方案进行实验,评估业务决策的合理性。通过四个模块,实现数据分析驱动产品精细化运营。 三、技术实现 一款轻量级大数据分析系统,至少需要从数据建模、技术选型、页面交互三方面实现。数据建模如水流,贯穿整个数据分析系统;技术选型是基础设施,支撑整个系统高效运转;页面交互是面向用户,用数据说话,对业务增长进行数据赋能。 1. 数据建模 在开发MVP之前,由于历史原因,现有的产品矩阵中产品与产品之间,存在数据建设分散、数据开发重复、数据隔离等问题,一个用户会存在多条信息记录。 这种数据格局,不仅会导致计算、存储、人力资源的浪费,更严重的是会很大程度影响上层数据应用的效率。因此,旧的数据模式行不通,需要开拓新的数据模式。 MVP数据分析系统底层数据建设,一方面基于“用户(User)+事件ID(Event)+配置(Config)”思路,对产品数据信息进行高度抽象整合,收敛产品矩阵业务数据;另一方面,基于Key-Value模型,生成用户大宽表,一个User_Id仅有一条记录信息。 2. 技术选型 在日常产品数据可视化中,通常会想到使用MySQL进行页面交互式数据分析,但是MySQL数据库承载数据能力在百万级,适合对结果型数据进行分析,对于上亿级数据是无能为力。 在复杂的数据分析场景中,通常需要基于用户画像与用户行为,对用户进行OLAP多维自由交叉组合分析。因此,对于百万级以上的产品业务,使用MySQL是无法满足OLAP实时分析,需要尝试新的技术选型。 为了实现实时OLAP分析,对业界的大数据分析平台的技术方案我们进行了调研比较。业界存储引擎主要是HDFS与HBASE,计算引擎使用比较多的是Impala,Druid,ClickHouse,Spark。Druid系统维护成本高,无Join能力,且语法应用相对复杂。 从计算速度角度,ClickHouse比Presto快2倍+,比Impala快3倍+,比SparkSql快约4倍,计算性能比较如下。 实测数据,对2.2亿+条1.79GB记录数据,进行单表聚合0.095s,分析速度18.95GB/s。 和Impala相比,ClickHouse可以通过JDBC直接导入,数据导入成本低,ClickHouse系统维护成本相对低。另外,ClickHouse语法简单,易用性很强,对页面开发友好,可以快速开发出可视化页面。 基于上面这些因素,我们采用HDFS+ClickHouse+Spark技术方案。在这里,使用Spark补齐ClickHouse无法进行大规模Join操作短板,比如处理大规模复杂的关联分析任务。 另外,Spark可以无缝访问HDFS中Hive表数据,无需重新导数据,应用效率高。使用HDFS存储历史全量标签与行为数据(占比约80%),使用ClickHouse存储近期标签与行为数据(占比20%)。 3. 页面交互 MVP页面交互形式,80%数据分析诉求是可以直接通过页面实时分析完成,剩下约20%复杂分析任务,是通过提交任务式分析完成。 页面实时分析秒级返回分析结果,提交任务式分析需要5-15分钟返回结果。经营指标体系、事件模型分析、漏斗模型分析、留存模型分析等,是通过页面实时分析完成,用户人群画像洞察、用户兴趣偏好洞察是通过提交任务式分析完成。 4. 应用效果 按照传统数据分析模式,根据“提出需求->需求评审->写需求单->数据分析->输出结果”的规范流程,数据诉求需要经历3-5天才能解决问题,通过MVP系统可以快速完成数据分析诉求,大大缩短工期,对分析效率提升明显。目前MVP数据分析系统已经在内部使用,近期,使用MVP进行数据分析任务数达到1500+,高峰突破两千次。 从“人工数据分析 -> 工具化数据分析”的转变,对数据分析效率提升明显,更有利于数据驱动产品精细化运营。 5. 总结 本文尝试介绍从0到1实现一款轻量级大数据分析系统——MVP。目前MVP数据分析系统已经在内部使用,对于提升数据分析效率明显,为数据驱动产品业务增长赋能。同时,节省了数据分析师的人力投入。后期,基于产品矩阵业务,在完善现有模块情况下,还将对各个增长工具进行进一步打磨,提升MVP使用体验。 MVP乘风出海,结合先悉数据平台服务产业端 MVP作为内部系统,目前为部门在移动数据分析中节约了大量的时间成本,并沉淀了丰富的互联网分析模板与工具。在部门服务行业客户过程中,我们发现MVP所代表的移动数据分析解决方案,是目前传统产业数字化转型同样需要的必备工具。 为此,后续我们利用轻量级数据平台—— 先悉 作为数据底座,解决了MVP对外部署的底层平台问题,开发了可单独私有化交付给行业客户使用的MVP toB版本,帮助行业客户通过实时用户行为分析、画像洞察为驱动,优化运营策略。 先悉数据平台是一款轻量级的大数据平台产品, 有部署性价比高、运维便利、可私有化等特点,能够以“小而美”的方式满足中小规模项目的大数据应用落地 。在具体项目实践中,先悉数据平台+MVP形成了一套优势互补的组合,目前已经开始为行业客户提供“开箱即用”的移动分析服务。 先悉功能简介: 先悉具备高性能、批流一体的大数据组件,无需自行部署各类繁杂的开源组件,快速实现私有化数据平台的部署; 先悉提供可视化任务流,作为数据开发平台,结合Spark SQL及我们提供的SPL,在图形化界面快速开发一款数据应用; 先悉自带强大可视化图表能力,可快速建立一个可视化站点,向同事、客户及领导展示您的数据指标。 先悉数据平台咨询/商务合作: Xdata_Suite@tencent.com 参考文章: [1] https://zhuanlan.zhihu.com/p/54907288 [2] https://clickhouse.tech/docs/en/sql-reference/ statements/create/ 看腾讯技术,学云计算知识,关注云加社区
来源:OSCHINA
发布时间:2020-07-14 16:00:00
HBase结构的读写流程 (1). HBase0.96版本之前: (2). HBase0.96开始: a. 当客户端获取到.meta文件的位置之后,会缓存.meta.文件的位置 b. 客户端还会缓存HRegion的位置 -ROOT-存在的意义: 》 HBase是为了存储大量数据 》数据量大的时候,会产生大量的元数据 》元数据过多,一个Block可能不够,那么就需要分布式存储 》设置-ROOT-记录元数据的位置 》-ROOT- 总账 -> .meta. 分账 -> 数据 舍弃-ROOT-的原因: 》一条HBase的元数据在100字节左右 》Block的大小默认是128M=134217728B 》一个Block大概能存储134W条元数据 》一个表一般能产生3-5条元数据 》一个Block大概能记录26W个表的信息 》一个项目再复杂,表的个数一般也不会过百 》此时-ROOT-没有存在的意义
来源:OSCHINA
发布时间:2020-07-13 19:06:00
数据仓库管理着整个银行或公司的数据,数据结构复杂,数据量庞大,任何一个数据字段的变化或错误都会引起数据错误,影响数据应用,同时业务的发展也带来系统不断升级,数据需求的不断增加,数据仓库需要不断的升级和维护,才能保证为全行提供持续完整准确的数据服务。 所以数据仓库基本上是全行或全公司版本最多的系统,如何保证在频繁的变化中保证数据的准确和系统的稳定,需要数据仓库的开发管理必须做到高效、有条不紊。 ​ 1、数据仓库开发流程 1.1、规范先行 ​ 数据仓库从开发上看,数据加载和导入的程序相对固定,开发工作主要是数据转换的SQL脚本的分析和开发。那SQL的分析和开发最主要的还是基于业务逻辑进行编写,所以对数据字段的理解以及对业务规则的熟悉是数据仓库模型人员和开发人员都需要具备的知识,同时数据和规则又会不断变化,那如何确保快速开发,开发的代码具有可读性、模型设计具有一致性,最重要的是在数据仓库建立时就制定相应的规范,使整个团队能按规范同步进行开发、设计。那在数据仓库中主要有以下规范: (1)命名规范 :包括ETL作业、数据库或大数据平台的对象 (表、字段、存储过程、schema名或库名) 、脚本名、文件名等都需要按一定的规则进行命名,以便快速定位。 (2)ETL开发规范 :包括抽取、加载作业的开发规范、调度工具的使用规范、SQL脚本或作业的开发规范、开发流程规范等: (3)数据模型设计和维护规范 :主要对主模型区、汇总指标层、集市层的模型设计原则、方法、重要规则(如客户ID)进行统一。 通过规范先行,能在数据仓库建设及后续维护中能快速统计数据仓库的运行情况,如系统作业的关键路径、表数量以及空间使用情况,源系统变化的影响情况等,避免产生混乱,比如许多数据仓库或系统随着不断变化和增加,连哪些表在使用,哪些数据已经不更新了、目标表使用了哪些源系统数据字段都不能马上分析出来,需要花费人力来梳理,一段时间后又回归混乱。这种情况不仅无法有效分析数据仓库的实际运行情况,更会带来生产问题的安全隐患。 1.2、开发流程 之前已经提到数据仓库从头建设的流程,那现在以某个数据应用对数据仓库提出需求来看整个系统维护的开发流程,主要步骤如下: ​ (1)需求分析 :确定数据集市和数据仓库的接口字段和内容,明确数据需求; (2)模型开发和维护 :分析现有模型是否满足所有接口字段需求,如果不满足则需要从源系统增加入仓的表数据,并分析更新主数据区、汇总指标区和数据集市的逻辑模型、物理模型,并确定数据接口字段的映射关系,如果满足则只需确认映射规则; (3)ETL开发 :开发数据库或大数据平台的数据脚本以及作业脚本,并根据测试和生产验证的情况修正逻辑模型; 1.3、分工及职责 数据仓库团队主要分为模型人员、ETL开发人员和测试人员,其中模型人员主要是进行需求分析和模型维护,ETL开发人员负责代码实现和系统维护,开发流程中各角色工作如下: ​ 那在许多银行实际开发中,根据公司团队规模不同模型人员的职责也会有所差别,模型人员有的属于数据仓库开发团队,只负责数据模型维护,有的属于科技规划团队即又称SA,模型人员除了模型维护可能还兼顾项目经理、系统分析的角色。那模型人员也可能分别负责主模型区、汇总指标区和数据集市。所以模型团队内部也需要定期同步数据模型的变化和更新,统一设计规则和数据分布边界; 2、数据仓库开发管理系统 ​ 通过规范、标准流程和分工协作可以保证数据仓库开发工作有条不紊,但如何高效执行整个开发流程,提高代码开发效率。则需要有数据开发管理工具的支持。 之前在ETL开发中也介绍了一些开发实践,如标准的数据采集和加载作业、按ETL算法和数据映射自动生成数据转换脚本,那这些都可以通过工具整合并管理。通过开发管理工具对整个开发流程的模型数据、ETL数据和代码进行管理和维护,通过系统化来协助模型设计和开发,那对于一个数据仓库开发管理系统,主要有以下几方面功能: 2.1、数据模型维护功能 模型维护的功能许多是有文档来进行,通过系统的整合可以提高效率,增加信息的可统计性。 (1)对于源系统调研信息进行管理,可对源系统的每个表和字段调研备注信息进行存储修改,同时针对每个需求新增的表和字段都进行维护,以便沉淀经验。 (2)逻辑模型管理,这个功能如果已经是通过ERWIN或POWERDESIGN等工具进行管理,可以只将结果和历史版本进行维护。如果自己开发,可以集成一些开源工具的逻辑模型功能,统一在开发管理系统中维护。 (3)物理模型管理:物理模型主要是根据逻辑模型可以自动生成物理模型,模型人员和ETL开发人员在这个基础上进行物理化,增加索引、压缩、分区等信息。开发管理系统需要对物理模型进行存储和记录版本变更记录,那各个数据区的物理模型都可以在开发管理系统中维护,同时针对每次版本的变更,自动生成数据库或者大数据平台的数据库脚本。 2.2、ETL作业信息配置及代码生成 (1)数据映射:管理第5节介绍的数据转换作业映射文档,在配置算法等信息后,自动生成数据转化作业代码; (2)数据采集和加载:管理数据采集作业和加载作业的信息,具体可见第4节,并自动生成采集和加载作业的脚本; (3)调度作业:可以集成调度工具测试环境,根据ETL作业脚本信息,自动生成调度作业的脚本并同步作业信息到调度系统,并在调度工具中配置依赖关系后并测试后形成上线的调度作业配置版本。 2.3、打通测试环境和版本管理工具 数据仓库的代码主要是ETL脚本,无需编译,只需放在规范的目录下即可,由于生成代码后还需要提交到版本管理工具以及测试环境进行测试,因此可以直接调用版本管理工具的命令进行生成的代码更新,再通过版本发布工具发布到测试环境。如果没有版本发布工具,可以直接在开发管理工具中集成脚本传输的功能,在测试环境验证后再更新版本管理工具上的代码分支。 通过打通测试环境和版本管理工具,可以提高自动化,确保从系统自动产生代码和脚本,使维护的信息和生产脚本确保一致。 实际开发中,数据仓库可能会有多个团队进行维护,许多厂商也会有些工具,但要从数据仓库全开发流程以及结合各银行或公司的版本管理、测试管理流程来设计工具,提高开发效率这个层面,厂商一般不会考虑那么全面,需要银行数据仓库管理人员进行规划。通过统一规范及基础上通过开发管理工具可以更好的统一全行的数据开发规范,提高开发效率和代码质量,让更多的人力投入到数据应用开发和分析中。 通过开发管理工具对整个开发流程的模型数据、ETL数据和代码进行管理和维护,通过系统化来协助模型设计和开发,那对于一个数据仓库开发管理系统,主要有以下几方面功能:实际开发中,数据仓库可能会有多个团队进行维护,许…
来源:OSCHINA
发布时间:2020-07-13 13:58:00
2、热点参数限流 注意: 若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)),否则可能会有统计错误。 3、通过 ParamFlowRuleManager 的 loadRules 方法更新热点参数规则 了解理多,可以搜索一下快递云100平台。
来源:OSCHINA
发布时间:2020-07-13 01:03:00
简介 时序数据库 用来做监控很强大 比如: 安装部署 https://github.com/OpenTSDB/opentsdb/releases/tag/v2.4.0 下载解压 tar.gz 初始化表 env COMPRESSION=NONE HBASE_HOME=/hadoop/app/hbase-1.2.0-cdh5.15.1 TSDB_TTL=259200 ./src/create_table.sh 在 build 目录下 vi opentsdb.conf 启动 ./tsdb tsd opentsdb 架构 查询 opentsdb 存储模型 opentsdb 的设计 TCollector 下载安装 https://github.com/OpenTSDB/tcollector/releases/tag/v1.3.2 tar zxvf tcollector-1.3.2.tar.gz 启动 ./tcollector start -H localhost -p 4242 自带的采集器在 /tcollector-1.3.2/collectors 里面 0 文件夹里面都是 持续的自带的采集脚本, 300 代表 每隔 300秒执行一次的采集脚本 自定义脚本开发 比如在 tcollector-1.3.2/collectors/0 里面 写一个 脚本 sin.py 然后重启即可 ,在界面可以看到 grafana 图表展示 https://grafana.com/ wget https://dl.grafana.com/oss/release/grafana-7.0.6-1.x86_64.rpm sudo yum install grafana-7.0.6-1.x86_64.rpm 启动 service grafana-server start 默认端口 3000 http://192.168.0.205:3000/login 默认密码和用户名都是 admin 可以使用 grafana 展示 opentsdb 采集的数据
来源:OSCHINA
发布时间:2020-07-12 10:52:00
摘要: 弹性裸金属服务器服务于市场的技术概要分析 混合云和第三方虚拟化软件部署 伴随着公有云的高速发展,混合云打通客户线下专有云和线上公有云资源的需求日趋强烈。Open stack和VMware等IaaS stack在公有云部署,同时管理客户线上和线下IaaS资源。 可以看到,VMware cloud on AWS就属于此种混合云业务应用场景 而Open stack和VMware cloud等IaaS stack在公有云部署,最为关键就是要求公有云暴露CPU虚拟化能力,否则在普通虚拟机中部署嵌套虚拟化技术,性能完全无法接受。 具体到intel X86 CPU体系,则要求公有云平台把计算资源的完整虚拟化特性(intel VT-x和VT-d等硬件虚拟化技术)对外提供,使得VMware ESXi, KVM, XEN, Hyper-V等虚拟化平台和技术能够平滑上云 高隔离容器部署 容器技术具备轻量敏捷等技术优势,正在成为Devops主流技术。相对于公有云VM部署容器,使用弹性裸金属服务器部署容器具备零虚拟化开销等性能优势。 同时我们注意到clear container, RunV,以及Kata container等具备高隔离安全特性的新型容器技术,依赖CPU完整虚拟化特性(比如intel VT-x)。此种高隔离高安全容器技术只可能部署在弹性裸金属服务器 高质量计算服务 所谓高质量计算服务,是指零资源争抢、零虚拟化开销,和高隔离安全 1) 零资源争抢 虚拟化技术在提高数据中心资源利用率同时,也引入资源争抢等业务难题。考虑多个VM运行在一台物理服务器之上:CPU core、L1/L2/LLC cache、内存带宽等CPU和内存子系统资源通过虚拟化技术抽象和切分,同时提供给多个VM使用。上述CPU和内存带宽在VM间的资源争抢很难根本解决。 2) 零虚拟化开销 虚拟化技术必然带来性能开销,而弹性裸金属服务器不存在CPU和内存虚拟化开销 3) 高隔离安全 弹性裸金属服务器是真正意义上用户独占,诸如hypervisor逃逸、CPU微架构侧信道攻击等问题天然免疫 高速低时延RDMA网络支持 RDMA网络在超低时延和减轻CPU负载等方面优势明显,但是在网络虚拟化支持方面的短板明显;而公有云网络部署的关键是通过网络虚拟化达到网络资源的租户隔离。而弹性裸金属服务器在支持原生ROCE和IB RDMA网络方面,具有天然优势。 因此可以看到各家云厂商均以裸金属服务器支持RDMA网络,以此部署HPC和异构计算的高速低时延互联需求 RISC ISA CPU支持 传统来讲,intel x86体系结构对CPU虚拟化技术等软硬件支持最为完善,加之intel XEON x86处理器在服务器市场的垄断市场地位,主流公有云IaaS虚拟化技术均基于intel XEON x86。 但是必须看到对于特定细分市场,RSIC CPU仍然具备相当优势。比如Power ISA CPU在金融保险等市场的优势地位,以及ARMv8 ISA在新兴服务器市场崭露头角。如何支持Power和ARMv8等RISC服务器,是公有云厂商必须回答的问题 弹性裸金属服务器无需CPU和内存虚拟化技术的特别适配,能够使得Power和ARMv8等处理器快速公有云上线部署 GPU性能无损支持 诸如Nvidia 1080Ti等GPU,其对虚拟化技术支持有限,通过虚拟机方式输出GPU计算性能,会有严重性能下降;而弹性裸金属服务器可做到GPU性能无损输出 阿里云弹性裸金属产品详情页>> 原文链接 本文为云栖社区原创内容,未经允许不得转载
来源:OSCHINA
发布时间:2018-05-18 20:57:00
摘要: 阿里云宣布全新一代FPGA云服务器F3正式上线,并且开通邀测! 近期,阿里云宣布全新一代FPGA云服务器F3正式上线,并且开通邀测。实现云上 FPGA 加速业务的快速研发、安全分发、一键部署和弹性伸缩能力。为人工智能产业、图片视频转码、基因计算提供强有力的加速服务。 在FPGA Shell架构上,F3不仅沿用了前代的技术,并且充分支持OpenCL,HLS以及RTL的开发流程,能够让多种应用程序开发的工程师,在不需要关注底层硬件细节的情况下,很好地完成异构计算的定制开发工作。 在硬件上,采用了创新的单卡双芯片设计,卡内双芯片互联带宽高达600Gbps,卡间互联通过硬核实现,支持100G Mac协议。单片FPGA逻辑量250万,外挂4个DDR4通道,提供64GB存储能力。 原文链接
来源:OSCHINA
发布时间:2018-05-18 20:26:00
摘要: 520不认怂! 男生学了这招,再也不用担心找不到女朋友了!传说中成功率99%的表白语都在这儿。 马上就要到520了,如果还有男生觉得现在开始准备过节有点早的话,那我只想说:朋友,注孤生了解一下!520、521这天大家都可以放心大胆地告白,虐狗程度堪比情人节(单身狗瑟瑟发抖)。 云栖小编总结了以下表白语,给开发哥哥们做参考,男生学(戏)了(精)这(上)些(身),再也不用担心找不到女朋友了! 颜值不够?情话来走一走!纸短情长啊,道不尽太多涟漪。。。 白羊座: 金牛座: 双子座: 巨蟹座: 狮子座: 处女座: 天秤座: 天蝎座 射手座 摩羯座: 水瓶座: 双鱼座: 最后一个:祝福各位猿们都表白成功!(ps:小编已经成功了,哈哈哈哈)! 本文为云栖社区原创内容,未经允许不得转载。 原文链接
来源:OSCHINA
发布时间:2018-05-18 19:57:00
什么是Kubernetes? Kubernetes是一个便携式的、可扩展的开源平台,用于管理容器化的工作负载和服务,这有助于声明式配置和自动化。它有一个巨大的,快速增长的生态系统。Kubernetes的服务、支持和工具被广泛使用。 2014年,谷歌开源了Kubernetes项目。Kubernetes建立在 谷歌大规模运行生产工作负载的十年半的经验之上 ,并结合社区的最佳思想和实践。 为什么我需要Kubernetes,它能做什么? Kubernetes有许多特性。它可以被认为是:*一个容器平台*一个微服务平台*一个移动的云平台,还有更多。 Kubernetes提供了一个以容器为中心的管理环境。它代表用户工作负载协调计算、网络和存储基础设施。这提供了许多简单的平台作为服务(PaaS),以基础设施的灵活性作为服务(IaaS),并支持跨基础设施提供商的可移植性。 Kubernetes是怎样一个平台的? 尽管Kubernetes提供了很多功能,但是仍然有一些新的场景会从新特性中受益。应用程序特定的工作流可以简化,以加速开发人员的速度。最初可以接受的特别编排通常需要在规模上实现健壮的自动化。这就是为什么Kubernetes也被设计为构建组件和工具的生态系统的平台,使其更易于部署、扩展和管理应用程序。 标签 授权用户组织他们的资源。 注释 使用户可以用定制的信息来装饰资源,以方便他们的工作流程,并为管理工具提供一种方便的检查点状态的方法。 此外, Kubernetes控制plane 是建立在开发人员和用户可用的相同 api 之上的。用户可以编写自己的控制器,比如 调度程序 ,使用 自己的api ,这些api可以被通用 命令行工具 作为目标. 这个 设计 使许多其他系统能够在Kubernetes之上构建。 Kubernetes不做的事 Kubernetes并不是一个传统的、包罗万象的PaaS(平台作为服务)系统。由于Kubernetes在容器级别而不是在硬件级别上运行,所以它提供了一些通常适用的特性,比如部署、扩展、负载平衡、日志记录和监视。然而,Kubernetes并不是单一的,这些默认的解决方案是可选的和可插入的。Kubernetes为构建开发者平台提供了构建模块,但保留了用户的选择和灵活性。 Kubernetes: 不限制所支持的应用程序的类型。Kubernetes的目标是支持极其多样化的工作负载,包括无状态、有状态和数据处理工作负载。如果一个应用程序可以在容器中运行,它应该在Kubernetes上运行得很好。 不部署源代码,也不构建应用程序。持续集成、交付和部署工作流是由组织文化和偏好以及技术需求决定的。 不提供应用程序级的服务,例如中间件(例如消息总线)、数据处理框架(例如Spark)、数据库(如mysql)、缓存和集群存储系统(例如Ceph)作为内置服务。这样的组件可以在Kubernetes上运行,也可以通过在Kubernetes上运行的应用程序访问,比如开放服务代理。 不指定日志、监视或警报解决方案。它提供了一些集成作为概念的证明,以及收集和导出度量的机制。 不提供或授权配置语言/系统(例如, jsonnet )。它提供了一个声明性的API,它可以被任意形式的声明性规范作为目标。 不提供或采用任何综合的机器配置、维护、管理或自修复系统。 此外,Kubernetes不只是一个编排系统。事实上,它消除了编排的需要。编排的技术定义是执行定义的工作流:首先是a,然后是B,然后是C。相反,Kubernetes由一组独立的、可组合的控制过程组成,这些过程不断地将当前状态驱动到所提供的期望状态。怎么从A到C都不重要。也不需要集中控制。用它得到一个更容易使用、更强大、更健壮、更有弹性和可扩展的系统。 为什么使用容器? 比对以下图片可找到你应该使用容器的理由 部署应用程序的旧方法是使用操作系统包管理器在主机上安装应用程序。这样做的缺点是将应用程序、配置、库和生命周期与主机操作系统纠缠在一起。 新的方法是基于操作系统级虚拟化(而不是硬件虚拟化)部署容器。这些容器彼此隔离,从主机上分离:它们有自己的文件系统,它们不能看到彼此的进程,而且它们的计算资源使用也可以被限制。它们比vm更容易构建,而且由于它们与底层基础结构和主机文件系统之间的解耦,它们在云和OS分布中是可移植的。 因为容器小而快速,所以一个应用程序可以在每个容器映像中打包。这种一对一的应用程序对图像的关系释放了容器的全部好处。使用容器,可以在构建或发布时创建不可变容器映像,而不是部署时间,因为每个应用程序不需要与应用程序堆栈的其余部分组成,也不需要与生产基础设施环境结合。在构建或发布时生成容器映像,可以使一个一致的环境从开发进入生产。类似地,容器比VMs更加透明,这有助于监视和管理。当容器程序的生命周期由基础结构管理而不是由容器内的程序管理器隐藏时,这一点尤其正确。最后,对于每个容器使用单个应用程序,管理容器就等于管理应用程序的部署。 容器的好处摘要: 敏捷的创建与部署: 与VM镜像使用相比,增加了容器镜像创建的方便性和效率。 持续开发、集成和部署: 提供可靠和频繁的容器镜像构建和部署,快速和简单的回滚(由于镜像的不变性)。 Dev和Ops分离的关注: 在构建或发布时间而不是部署时间创建应用程序容器镜像,从而将应用程序与基础结构解耦。 可观察性: 不仅仅是表面的信息和度量,还包括应用程序的健康和其他信号。 跨开发、测试和生产的环境一致性 :在笔记本电脑上运行和在云中运行一样。 可在 OS distribution 与云之间移植: 在Ubuntu、RHEL、CoreOS、on-prem、谷歌Kubernetes引擎和其他任何地方运行。 以应用程序为中心的管理: 提高抽象级别在虚拟硬件上运行OS以使用逻辑资源在OS上运行应用程序。 松散耦合,分布式,弹性,自由的 微服务: 应用程序被分割成更小的独立的块,并且可以动态地部署和管理,而不是在一个大型的单用途机器上运行一个庞大的整体堆栈。 资源隔离: 可预测的应用程序的性能。 资源利用: 高效率和密度。 Kubernetes是什么意思?K8s? Kubernetes这个名字源于希腊语,意思是舵手或领航员,是总督和 控制论 的根源。K8s是用8来替换“ubernete” 这8个字母而得到的缩写。
来源:OSCHINA
发布时间:2018-05-17 23:56:00
摘要: 阿里FPGA云服务器平台FaaS(FPGA as a Service)在云端提供统一硬件平台与中间件,可大大降低加速器的开发与部署成本。普惠开发者 FPGA (现场可编程门阵列)由于其硬件并行加速能力和可编程特性,在传统通信领域和IC设计领域大放异彩。一路走来,FPGA的技术并不是一个新兴的硬件器件,由于其开发门槛过高,硬件加速算法的发布和部署保护要求非常高,FPGA的使用一直是高冷的美人,没有走入平常百姓家。也就导致FPGA的计算潜力还没有得到深入的挖掘。 阿里FPGA云服务器平台FaaS(FPGA as a Service)在云端提供统一硬件平台与中间件,可大大降低加速器的开发与部署成本。用户可以直接使用部署加速器提供商提供的加速服务;也可以在无需了解底层硬件的情况下,快速开发和部署自己的定制加速器。 FaaS平台提供给所有的加速器开发者统一的FPGA硬件接口Shell,提前帮用户解决了FPGA开发难度最大的高速接口开发及调试,例如PCIe,Fiber接口, DDR控制器等等,大大简化了开发的时间;用户能够直接得到硬件平台和FPGA接口的最大性能,不会因为团队开发能力和经验的欠缺,造成硬件平台性能浪费。 在提供统一接口提供安全性和便捷性的前提下,阿里云FaaS也尽最大努力保证用户设计的灵活性和快捷性,Role的概念应运而生。Role在动态区域,不同于Shell,用户可以根据需要,随时更换Role部分;这种Shell + Role的组合方式,保证了Shell的最轻量化,极大的提升了开发的便捷性,大大缩短了开发所需时间。 与Shell和 Role对应,在服务器端,FaaS也提供相应的驱动和软件库,为用户提供统一及灵活的软件支持,比如DMA驱动,寄存器访问驱动等等。 传统的FPGA开发如果从硬件设计开始,需要经历原理设计、PCB设计、PCB生产、装配测试等 漫长的硬件周期; 在逻辑设计阶段,也需要从板卡启动调试、接口调试、驱动开发等最底层的工作开始;这些工作完成之后,开能开始正常的逻辑开发工作。 阿里FaaS平台大大简化了整个FPGA的设计流程。使用FaaS实例,无需硬件周期;逻辑设计阶段,也可直接跳过板卡启动调试以及接口调试,可以直接开始用户逻辑设计;而且,Role的提供,也可简化或者省略一部分用户逻辑的设计。 原文链接 本文为云栖社区原创内容,未经允许不得转载。
来源:OSCHINA
发布时间:2018-05-17 21:35:00
本节我们讨论 Kubernetes 网络这个重要主题。 Kubernetes 作为编排引擎管理着分布在不同节点上的容器和 Pod。Pod、Service、外部组件之间需要一种可靠的方式找到彼此并进行通信,Kubernetes 网络则负责提供这个保障。本章包括如下内容: Kubernetes 网络模型 各种网络方案 Network Policy Kubernetes 网络模型 Kubernetes 采用的是基于扁平地址空间的网络模型,集群中的每个 Pod 都有自己的 IP 地址,Pod 之间不需要配置 NAT 就能直接通信。另外,同一个 Pod 中的容器共享 Pod 的 IP,能够通过 localhost 通信。 这种网络模型对应用开发者和管理员相当友好,应用可以非常方便地从传统网络迁移到 Kubernetes。每个 Pod 可被看作是一个个独立的系统,而 Pod 中的容器则可被看做同一系统中的不同进程。 下面讨论在这个网络模型下集群中的各种实体如何通信。知识点前面都已经涉及,这里可当做复习和总结。 Pod 内容器之间的通信 当 Pod 被调度到某个节点,Pod 中的所有容器都在这个节点上运行,这些容器共享相同的本地文件系统、IPC 和网络命名空间。 不同 Pod 之间不存在端口冲突的问题,因为每个 Pod 都有自己的 IP 地址。当某个容器使用 localhost 时,意味着使用的是容器所属 Pod 的地址空间。 比如 Pod A 有两个容器 container-A1 和 container-A2,container-A1 在端口 1234 上监听,当 container-A2 连接到 localhost:1234,实际上就是在访问 container-A1。这不会与同一个节点上的 Pod B 冲突,即使 Pod B 中的容器 container-B1 也在监听 1234 端口。 Pod 之间的通信 Pod 的 IP 是集群可见的,即集群中的任何其他 Pod 和节点都可以通过 IP 直接与 Pod 通信,这种通信不需要借助任何的网络地址转换、隧道或代理技术。Pod 内部和外部使用的是同一个 IP,这也意味着标准的命名服务和发现机制,比如 DNS 可以直接使用。 Pod 与 Service 的通信 Pod 间可以直接通过 IP 地址通信,但前提是 Pod 得知道对方的 IP。在 Kubernetes 集群中, Pod 可能会频繁的销毁和创建,也就是说 Pod 的 IP 不是固定的。为了解决这个问题,Service 提供了访问 Pod 的抽象层。无论后端的 Pod 如何变化,Service 都作为稳定的前端对外提供服务。同时,Service 还提供了高可用和负载均衡功能,Service 负责将请求转发给正确的 Pod。 外部访问 无论是 Pod 的 IP 还是 Service 的 Cluster IP,它们只能在 Kubernetes 集群中可见,对集群之外的世界,这些 IP 都是私有的。 Kubernetes 提供了两种方式让外界能够与 Pod 通信: NodePort Service 通过 Cluster 节点的静态端口对外提供服务。外部可以通过 : 访问 Service。 LoadBalancer Service 利用 cloud provider 提供的 load balancer 对外提供服务,cloud provider 负责将 load balancer 的流量导向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。 以上就是 Kubernetes 网络模型的相关讨论。 下一节我们来看看 Kubernetes 支持的网络方案。 书籍: 1.《每天5分钟玩转Kubernetes》 https://item.jd.com/26225745440.html 2.《每天5分钟玩转Docker容器技术》 https://item.jd.com/16936307278.html 3.《每天5分钟玩转OpenStack》 https://item.jd.com/12086376.html
来源:OSCHINA
发布时间:2018-05-17 20:34:00
OSD服务端消息的接收起始于OSD::init()中的messenger::add_dispatcher_head(Dispatcher *d)函数 |- 358 void add_dispatcher_head(Dispatcher *d) { || 359 bool first = dispatchers.empty(); || 360 dispatchers.push_front(d); || 361 if (d->ms_can_fast_dispatch_any()) || 362 fast_dispatchers.push_front(d); || 363 if (first) || 364 ready(); //如果dispatcher list空,启动SimpleMessenger::ready,不为空证明SimpleMessenger已经启动了 || 365 } 在SimpleMessenger::ready()中,启动DispatchQueue等待mqueue,如果绑定了端口就启动 accepter接收线程 76 void SimpleMessenger::ready() - 77 { | 78 ldout(cct,10) << "ready " << get_myaddr() << dendl; | 79 dispatch_queue.start(); //启动DispatchQueue,等待mqueue | 80 | 81 lock.Lock(); | 82 if (did_bind) | 83 accepter.start(); | 84 lock.Unlock(); | 85 } Accepter是Thread的继承类,Accepter::start()最终调用Accepter::entry(),在entry中 accept并把接收到的sd加入到Pipe类中 void *Accepter::entry() { ... struct pollfd pfd; pfd.fd = listen_sd; pfd.events = POLLIN | POLLERR | POLLNVAL | POLLHUP; while (!done) { int r = poll(&pfd, 1, -1); if (pfd.revents & (POLLERR | POLLNVAL | POLLHUP)) break; // accept entity_addr_t addr; socklen_t slen = sizeof(addr.ss_addr()); int sd = ::accept(listen_sd, (sockaddr*)&addr.ss_addr(), &slen); if (sd >= 0) { errors = 0; ldout(msgr->cct,10) << "accepted incoming on sd " << sd << dendl; msgr->add_accept_pipe(sd); //注册一个pipe,启动读线程,从该sd中读取数据 } else { ldout(msgr->cct,0) << "accepter no incoming connection? sd = " << sd << " errno " << errno << " " << cpp_strerror(errno) << dendl; if (++errors > 4) break; } } ... return 0; 在SimpleMessenger::add_accept_pipe(int sd)中,申请一个Pipe类并把sd加入到Pipe中,开始Pipe::start_reader() 340 Pipe *SimpleMessenger::add_accept_pipe(int sd) - 341 { | 342 lock.Lock(); | 343 Pipe *p = new Pipe(this, Pipe::STATE_ACCEPTING, NULL); | 344 p->sd = sd; | 345 p->pipe_lock.Lock(); | 346 p->start_reader(); | 347 p->pipe_lock.Unlock(); | 348 pipes.insert(p); | 349 accepting_pipes.insert(p); | 350 lock.Unlock(); | 351 return p; | 352 } Pipe类内部有一个Reader和Writer线程类,Pipe::start_reader()启动Pipe::Reader::entry(),最终启动Pipe::reader函数 134 void Pipe::start_reader() - 135 { | 136 assert(pipe_lock.is_locked()); | 137 assert(!reader_running); |- 138 if (reader_needs_join) { || 139 reader_thread.join(); || 140 reader_needs_join = false; || 141 } | 142 reader_running = true; | 143 reader_thread.create("ms_pipe_read", msgr->cct->_conf->ms_rwthread_stack_bytes); | 144 } |- 48 class Reader : public Thread { || 49 Pipe *pipe; || 50 public: || 51 explicit Reader(Pipe *p) : pipe(p) {} || 52 void *entry() { pipe->reader(); return 0; } || 53 } reader_thread; 在Pipe::reader函数中根据tag接收不同类型的消息,如果是CEPH_MSGR_TAG_MSG类型消息调用read_message接收消息,并把消息加入到mqueue中 void Pipe::reader() { pipe_lock.Lock(); if (state == STATE_ACCEPTING) { accept(); //第一次进入此函数处理 assert(pipe_lock.is_locked()); } // loop. while (state != STATE_CLOSED && state != STATE_CONNECTING) { assert(pipe_lock.is_locked()); ...... ...... else if (tag == CEPH_MSGR_TAG_MSG) { ldout(msgr->cct,20) << "reader got MSG" << dendl; Message *m = 0; int r = read_message(&m, auth_handler.get()); pipe_lock.Lock(); if (!m) { if (r < 0) fault(true); continue; } ...... ...... ...... // note last received message. in_seq = m->get_seq(); cond.Signal(); // wake up writer, to ack this ldout(msgr->cct,10) << "reader got message " << m->get_seq() << " " << m << " " << *m << dendl; in_q->fast_preprocess(m); //mds 、mon不会进入此函数,预处理 if (delay_thread) { utime_t release; if (rand() % 10000 < msgr->cct->_conf->ms_inject_delay_probability * 10000.0) { release = m->get_recv_stamp(); release += msgr->cct->_conf->ms_inject_delay_max * (double)(rand() % 10000) / 10000.0; lsubdout(msgr->cct, ms, 1) << "queue_received will delay until " << release << " on " << m << " " << *m << dendl; } delay_thread->queue(release, m); } else { if (in_q->can_fast_dispatch(m)) { reader_dispatching = true; pipe_lock.Unlock(); in_q->fast_dispatch(m); pipe_lock.Lock(); reader_dispatching = false; if (state == STATE_CLOSED || notify_on_dispatch_done) { // there might be somebody waiting notify_on_dispatch_done = false; cond.Signal(); } } else { //mds进入此else in_q->enqueue(m, m->get_priority(), conn_id); //把接收到的messenger加入到mqueue中 } } } ...... ...... } // reap? reader_running = false; reader_needs_join = true; unlock_maybe_reap(); ldout(msgr->cct,10) << "reader done" << dendl; } 在Pipe::DispatchQueue::enqueue函数中加入到mqueue中 void DispatchQueue::enqueue(Message *m, int priority, uint64_t id) { Mutex::Locker l(lock); ldout(cct,20) << "queue " << m << " prio " << priority << dendl; add_arrival(m); if (priority >= CEPH_MSG_PRIO_LOW) { mqueue.enqueue_strict( id, priority, QueueItem(m)); } else { mqueue.enqueue( id, priority, m->get_cost(), QueueItem(m)); } cond.Signal(); //唤醒dispatch_queue.start() 启动的dispatchThread,进入entry进行处理 }
来源:OSCHINA
发布时间:2018-05-17 18:52:00
Dipatcher类是消息分发的接口,OSD、MON、等类都继承该类,并实现了Dipatcher的消息分发接口 1079 class OSD : public Dispatcher, - 1080 public md_config_obs_t { | 1081 /** OSD **/ 128 class Monitor : public Dispatcher, - 129 public md_config_obs_t { | 130 public: | 131 // me | 132 string name; 在OSD::init()函数中把不同类型的Dipatcher加入到SimpleMessenger实例中 | 2146 // i'm ready! | 2147 client_messenger->add_dispatcher_head(this); | 2148 cluster_messenger->add_dispatcher_head(this); | 2149 | 2150 hbclient_messenger->add_dispatcher_head(&heartbeat_dispatcher); | 2151 hb_front_server_messenger->add_dispatcher_head(&heartbeat_dispatcher); | 2152 hb_back_server_messenger->add_dispatcher_head(&heartbeat_dispatcher); | 2153 | 2154 objecter_messenger->add_dispatcher_head(service.objecter); 在Messenger::add_dispatcher_head(Dispatcher *d)中加入Messenger::list dispatchers中,并调用ready(),SimpleMessenger::ready()重写了基类的ready, |- 358 void add_dispatcher_head(Dispatcher *d) { || 359 bool first = dispatchers.empty(); || 360 dispatchers.push_front(d); || 361 if (d->ms_can_fast_dispatch_any()) || 362 fast_dispatchers.push_front(d); || 363 if (first) || 364 ready(); || 365 } 在ready函数中调用DispatchQueue::start, start()函数启动DispatchQueue::DispatchThread和DispatchQueue::LocalDeliveryThread线程类,最终调用DispatchQueue::entry()和DispatchQueue::run_local_delivery。 216 void DispatchQueue::start() - 217 { | 218 assert(!stop); | 219 assert(!dispatch_thread.is_started()); | 220 dispatch_thread.create("ms_dispatch"); //调用Thread::create->Thread::try_create->Thread::_entry_func->Thread::entry_wrapper->DispatchThread::entry | 221 local_delivery_thread.create("ms_local"); | 222 } |- 98 class DispatchThread : public Thread { || 99 DispatchQueue *dq; || 100 public: || 101 explicit DispatchThread(DispatchQueue *dq) : dq(dq) {} ||- 102 void *entry() { ||| 103 dq->entry(); ||| 104 return 0; ||| 105 } || 106 } dispatch_thread; 在DispatchQueue::entry()中调用根据不同的命令码调用不同的Messenger类中的处理函数 void DispatchQueue::entry() { . . switch (qitem.get_code()) { case D_BAD_REMOTE_RESET: msgr->ms_deliver_handle_remote_reset(qitem.get_connection()); break; case D_CONNECT: msgr->ms_deliver_handle_connect(qitem.get_connection()); break; case D_ACCEPT: msgr->ms_deliver_handle_accept(qitem.get_connection()); break; case D_BAD_RESET: msgr->ms_deliver_handle_reset(qitem.get_connection()); break; default: assert(0); } } else { Message *m = qitem.get_message(); if (stop) { ldout(cct,10) << " stop flag set, discarding " << m << " " << *m << dendl; m->put(); } else { uint64_t msize = pre_dispatch(m); msgr->ms_deliver_dispatch(m); post_dispatch(m, msize); } } . . } 在Messenger::ms_deliver_dispatch中最终调用不同的Dipatcher继承类的ms_dispatch进行处理 |- 579 void ms_deliver_dispatch(Message *m) { || 580 m->set_dispatch_stamp(ceph_clock_now(cct)); || 581 for (list::iterator p = dispatchers.begin(); || 582 p != dispatchers.end(); ||- 583 ++p) { ||| 584 if ((*p)->ms_dispatch(m)) //在Dispatcher继承类中进行处理 ||| 585 return; ||| 586 } || 587 lsubdout(cct, ms, 0) << "ms_deliver_dispatch: unhandled message " << m << " " << *m << " from " || 588 << m->get_source_inst() << dendl; || 589 assert(!cct->_conf->ms_die_on_unhandled_msg); || 590 m->put(); || 591 }
来源:OSCHINA
发布时间:2018-05-17 17:39:00
CoreOS下的Docker配置是通过flannel unit来实现的 通过命令可以看出配置文件的默认位置 systemctl cat docker # /run/systemd/system/docker.service [Unit] Requires=torcx.target After=torcx.target Description=Docker Application Container Engine Documentation=http://docs.docker.com After=containerd.service docker.socket network-online.target Wants=network-online.target Requires=containerd.service docker.socket [Service] EnvironmentFile=/run/metadata/torcx Type=notify EnvironmentFile=-/run/flannel/flannel_docker_opts.env Environment=DOCKER_SELINUX=--selinux-enabled=true /run/flannel/flannel_docker_opts.env 这个文件默认是没有的,所以我们创建对应目录和文件即可。 补充:如果docker run的时候报如下错误: Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2/007018df729636dd7c3d22ea683d13b6f5f0657b7c2c9e0014c671id argument. 则需要修改/run/systemd/system/docker.service文件中的这一项 Environment=DOCKER_SELINUX=--selinux-enabled=false 然后 systemctl daemon-reload systemctl restart docker 然后编辑文件内容如下: vi flannel_docker_opts.env DOCKER_OPTS="--registry-mirror=https://xxxxx.mirror.aliyuncs.com" 保存后,启动docker服务即可 systemctl daemon-reload systemctl restart docker 最后验证一下 ps aux |grep docker root 831 1.3 0.8 272276 32908 ? Ssl 16:55 0:00 /run/torcx/bin/dockerd --host=fd:// --containerd=/var/run/docker/libcontainerd/docker- containerd.sock --selinux-enabled=true --registry-mirror=https://xxxxx.mirror.aliyuncs.com 这样就成功了,很简单吧。 补充: 大家有没有发现docker.service配置文件的路径在/run/下,我一直觉得很奇怪/run/应该都是运行时,果然发现系统或服务重启后,docker.service中的配置经常会还原了。一直想解决的办法,经过尝试,可以通过如下方法处理: # 复制/run/里面的配置文件到/etc/下 cp /run/systemd/system/docker.service /etc/systemd/system/docker.service # 重新加载服务 systemctl daemon-reload # 重启服务 systemctl restart docker.service # 验证 systemctl cat docker.service 可以看到文件已经变更过来了。
来源:OSCHINA
发布时间:2018-05-17 17:05:00
有时候,我们无法使用像GKE或AKE这样的托管服务,甚至处于离线或与外网隔离的状态,而无法直接访问互联网。然而,即使在这种情况下,仍然是有方法使用Rancher管理集群的。 本文中,我们将向你介绍如何在离线或内网环境中运行Rancher 2.0。 私有镜像库 因为所有与Rancher相关的服务都在容器中运行,因此首先你需要的是在环境中存储容器。在本文的示例中,我们将使用Docker Registry(Docker镜像仓库)。如果你已经有了镜像仓库,可以跳过这些步骤。 注意:在Rancher 2.0中,只有没有身份认证的镜像仓库才能获取启动和运行Rancher 2.0所需的所有镜像。这并不会影响在工作负载中使用的可配置镜像仓库。 要运行Docker Registry,你需要运行一个registry:2镜像的实例。我们将公开默认端口(5000),挂载一个主机目录确保我们有足够的空间(至少需要8GB)并且获得适当的I/O性能。 docker run -d -p 5000:5000 --restart=always --name registry -v /opt/docker-registry:/var/lib/registry registry:2 让Rancher镜像运行起来 在镜像仓库设置完成后,就开始同步所需的镜像来运行Rancher 2.0。这一步骤我们将讨论两个场景: 场景1:你有一台可访问DockerHub的主机来提取和保存镜像,另有一台可以访问你的私有镜像仓库的单独的主机,以用于push镜像。 场景2:你有一台可以访问DockerHub以及私有镜像仓库的主机。 场景1:一台主机访问DockerHub,另一台访问私有镜像仓库 Rancher每一次的版本更新发布( https://github.com/rancher/rancher/releases/tag/v2.0.0),都会随之提供针对这一场景的插件。你需要如下脚本: **rancher-save-images.sh:**该脚本将从DockerHub中拉取所有需要的镜像,并且将所有镜像保存成一个rancher-images.tar.gz压缩文件。可以将该文件传输到能够访问你私人镜像仓库的内部部署主机上。 **rancher-load-images.sh:**该脚本将从rancher-images.tar.gz中加载镜像,将它们push到你的私有镜像仓库。在脚本的第一个参数需要提供私有镜像仓库的主机名rancher-load-images.sh registry.yourdomain.com:5000 场景一的流程 场景2:一台可以访问DockerHub以及私有镜像仓库的主机 针对这一场景,Rancher每一次的版本更新时 ( https://github.com/rancher/rancher/releases/tag/v2.0.0 ),都会提供一个名为rancher-images.txt的文件。该文件包含了运行Rancher 2.0所需要的全部镜像。可以将它绑定到任何现有的自动化服务中同步你可能拥有的镜像,也可以使用我在下面展示的脚本/Docker镜像。 场景二的流程 配置Rancher,使用私有镜像仓库 流程的最后一步是配置Rancher,将私有镜像仓库作为获取镜像的源。这可以通过在Setting视图中使用system-default-registry进行配置。 设置栏 若想要使用私有镜像仓库,在配置设置时不要使用https:// 或 http://作为前缀 这样做可以确保那些被用于向集群添加节点的rancher/rancher-agent容器,会使用该值作为前缀。其他需要使用的镜像也需要这样配置。 如果你想在启动rancher/rancher容器时进行配置,可以使用环境变量CATTLE_SYSTEM_DEFAULT_REGISTRY 例如: docker run -d -p 80:80 -p 443:443 -e CATTLE_SYSTEM_DEFAULT_REGISTRY=registry.yourdomain.com:5000 registry.yourdomain.com:5000/rancher/rancher:v2.0.0 创建一个集群 你可以通过运行rancher/rancher容器所在主机的IP来访问Rancher 2.0 UI。最开始启动大约需要1分钟,第一次访问时系统会提示你设置密码。 设置密码 接下来,你需要配置URL,节点将通过该地址和Rancher 2.0安装建立联系。在默认情况下,它会显示你访问UI的IP地址,不过当你想使用DNS名称或者负载均衡器的话,你可以在这里进行更改。 在Global视图中,点击Add Cluster _ 添加集群_ 本文会带领你创建一个没有高级选项的custom集群。想要在你的集群中配置高级选项,请参考相关的文档。 添加名叫testcluster的自定义集群 点击Next,创建testcluster集群。 在下一个界面,你会获得一个生成的命令,用于在你想要添加到集群的节点上启动。在该命令中使用的镜像应该会自动地用你配置好的私有镜像仓库作为前缀。 为集群添加节点 现在,对你想要添加的节点,你可以选择要为它们使用哪些角色,并且可以选择配置该节点的IP。如果没有指定,则会自动检测IP。有关Node Roles的含义,也请参考文档。 在项目中配置对镜像仓库的访问 如前所述,目前Rancher 2.0还不支持使用带有身份认证的私有镜像仓库来运行Rancher 2.0需要的镜像。不过在这种情况下,Rancher 2.0支持project中的工作负载。 想要使用身份认证配置镜像仓库,你可以在集群中打开你的项目(Default项目是自动为你创建的)。在Default项目中,可以导航到Resources->Registries来配置用于工作负载的镜像仓库。 在project默认设置里配置镜像仓库 点击Add Registry 添加镜像仓库 填写访问镜像仓库所需的信息: 总 结 希望这篇指南能够帮助大家进一步了解如何在离线或内网环境中设置Rancher 2.0。我们知道很多环境中还会有代理,我们后续还会发布代理设置的相关文章,敬请期待! 最后附上我在本文中用到的一些命令,希望它们能够为你所用或带给你更多灵感。
来源:OSCHINA
发布时间:2018-05-17 09:38:00
Author: xidianwangtao@gmail.com Equivalence Class概念及其意义 2015年,google发表的关于Borg的论文“ Large-scale cluster management at Google with Borg ”中对Equivalence Class的描述如下: Equivalence classes: Tasks in a Borg job usually have identical requirements and constraints, so rather than determining feasibility for every pending task on every machine, and scoring all the feasible machines, Borg only does feasibility and scoring for one task per equivalence class – a group of tasks with identical requirements. Equivalence Class目前是用来在Kubernetes Scheduler加速Predicate,提升Scheduler的吞吐性能。Kubernetes scheduler及时维护着Equivalence Cache的数据,当某些情况发生时(比如delete node、bind pod等事件),需要立刻invalid相关的Equivalence Cache中的缓存数据。 一个Equivalence Class是用来定义一组具有相同Requirements和Constraints的Pods的相关信息的集合,在Scheduler进行Predicate阶段时可以只需对Equivalence Class中一个Pod进行Predicate,并把Predicate的结果放到Equivalence Cache中以供该Equivalence Class中其他Pods(成为Equivalent Pods)重用该结果。只有当Equivalence Cache中没有可以重用的Predicate Result才会进行正常的Predicate流程。 什么样的Pods会被归类到同一Equivalence Class呢?按照其定义,其实只要Pods有某些相同的field,比如resources requirement、label、affinity等,它们都被认为是Equivalent Pods,属于同一Equivalence Class。但是考虑到用户可能随时修改Pods的fields,会导致Scheduler需要及时更新该Pod所属的Equivalence Class变动,从而导致可能正在进行的Predicate需要感知这一变化并作出变更,这使得问题变得异常复杂。因此,目前Scheduler只把那些属于同一OwnerReference(包括RC,RS,Job, StatefulSet)的Pods归类到同一Equivalence Class,比如某个RS定义了N个副本,那么这N个副本Pods就对应一个Equivalence Class。Scheduler会为每个Equivalence Class中的Equivalent Pods计算出一个uint64 EquivalenceHash值。 注意,截止Kubernetes 1.10,即使有两个一样Pod Template的RS,也会对应两个Equivalence Class。 Equivalence Class工作原理 要想使用Equivalence Class需要启用 EnableEquivalenceClassCache Feature Gate,截止Kubernetes 1.10,该Feature还是Alpha阶段。 前期我的几遍关于scheduler的博客中对Predicate的分析中提到,所有注册成功的Predicate Policy都会在 scheduler.findNodesThatFit(pod, nodes, predicateFuncs ...) 过程中按照一定的并行数对每个node调用 scheduler.podFitsOnNode(pod, node, predicateFuncs ...) 进行注册的Predicate Policys检查。 podFitsOnNode的输入是一个pod,一个node和一系列注册成功的predicateFuncs,用来检查node是否满足该pod的预选条件。加入了Equivalence Class之后,预选阶段会发生了如下变化: 预选之前,先检查该pod是否有对应的Equivalence Class。 如果有对应的Equivalence Class,那么接下来检查Equivalence Cache中是否有可用的Predicate Result,否则触发完整的正常预选。 如果有可用的Predicate Result,那么直接使用该Cached Predicate Result完成预选,否则触发完整的正常预选。 Equivalence Cache会存储每个node的Predicates Results,是一个3层Map对象: 第一层key是node name,表示节点名称; 第二层key是predicateKey,表示预选策略,因此该node对应的algorithmCache Entries数量最多不超过Scheduler注册的Predicate Policies数量,这用来保证Cache大小,防止查找Equivalence Cache时性能太差。 第三层key是Equivalence Hash,前面已经提到过。 比如, algorithmCache[$nodeName].predicatesCache.Get($predicateKey)[$equivalenceHash] 表示 $equivalenceHash 对应的Pods在 $nodeName 节点上进行 $predicateKey 进行预选是否成功。 截止Kubernetes 1.10,predicateKey支持列表如下(20个): MatchInterPodAffinity CheckVolumeBinding CheckNodeCondition GeneralPredicates HostName PodFitsHostPorts MatchNodeSelector PodFitsResources NoDiskConflict PodToleratesNodeTaints CheckNodeUnschedulable PodToleratesNodeNoExecuteTaints CheckNodeLabelPresence CheckServiceAffinity MaxEBSVolumeCount MaxGCEPDVolumeCount MaxAzureDiskVolumeCount NoVolumeZoneConflict CheckNodeMemoryPressure CheckNodeDiskPressure 注意,即使该Pod找到对应的Equivalence Class,Equivalence Cache中也有可能没有可用的Predicate Result,或者对应的Predicate Result已经失效。这时就会触发正常的Predicate,并把Result写到Equivalence Cache中。 如何维护和更新Equivalence Cache呢?如果频繁的更新整个node对应的Equivalence Cache,这违背了Equivalence Cache设计的初衷,并不能提升Predicate的效率。 前面提到过Equivalence Cache的三层Map结构设计,第二层Key是predicateKey,因此Scheduler能做到只invalid单个Predicate Result,而不是盲目的invalid整个node的algorithmCache。 Scheduler会Watch相关API Objects Add/Update/Delete Event,并根据相关策略invalid对应的Equivalence Cache数据,具体的逻辑请看下面的源码分析部分。 Equivalence Class源码分析 Equivalence Cache数据结构 Equivalence Cache结构定义如下: // EquivalenceCache holds: // 1. a map of AlgorithmCache with node name as key // 2. function to get equivalence pod type EquivalenceCache struct { sync.RWMutex getEquivalencePod algorithm.GetEquivalencePodFunc algorithmCache map[string]AlgorithmCache } // The AlgorithmCache stores PredicateMap with predicate name as key type AlgorithmCache struct { // Only consider predicates for now predicatesCache *lru.Cache } Equivalence Cache真正的缓存数据是通过algorithmCache Map存储,其key为nodeName。 每个node上的Predicate Result Cache通过AlgorithmCache.predicateCache存储,predicateCache是LRU(Least Recently Used,最少最近使用算法)Cache,只能存储一定数量的Entries,Kubernetes中指定最大值为100(Kubernetes 1.10默认实现的Predicate Funcs一共有20个)。 LRU Cache是一个Cache置换算法,含义是“最近最少使用”,当Cache满(没有空闲的cache块)时,把满足“最近最少使用”的数据从Cache中置换出去,并且保证Cache中第一个数据是最近刚刚访问的。由“局部性原理”,这样的数据更有可能被接下来的程序访问,提升性能。 predicateCache也是k-v存储,key为predicateKey,value为PredicateMap。 predicateMap的key为uint64的Equivalence Hash,value为HostPredicate。 HostPredicate用来表示Pod使用Predicate Policy与某个node的匹配结果,结构如下: // HostPredicate is the cached predicate result type HostPredicate struct { Fit bool FailReasons []algorithm.PredicateFailureReason } Equivalence Cache的核心操作 InvalidateCachedPredicateItem :用来从Equivalence Cache中删除某个node上某个predicate policy的所有EquivalenceHash(对应Equivalent Pods)的Predicate Result缓存数据。 func (ec *EquivalenceCache) InvalidateCachedPredicateItem(nodeName string, predicateKeys sets.String) { ... if algorithmCache, exist := ec.algorithmCache[nodeName]; exist { for predicateKey := range predicateKeys { algorithmCache.predicatesCache.Remove(predicateKey) } } ... } InvalidateCachedPredicateItemOfAllNodes :用来删除所有node上指定predicate policy集合对应的所有EquivalenceHash(对应Equivalent Pods)的Predicate Result缓存数据。 func (ec *EquivalenceCache) InvalidateCachedPredicateItemOfAllNodes(predicateKeys sets.String) { ... // algorithmCache uses nodeName as key, so we just iterate it and invalid given predicates for _, algorithmCache := range ec.algorithmCache { for predicateKey := range predicateKeys { // just use keys is enough algorithmCache.predicatesCache.Remove(predicateKey) } } ... } PredicateWithECache :检查Equivalence Cache中的Predicate Result缓存数据是否有可用的数据,如果命中缓存,则直接根据缓存中的Predicate Result作为该pod在该node上该Predicate policy的预选结果返回。如果没命中,则返回false和失败原因。 // PredicateWithECache returns: // 1. if fit // 2. reasons if not fit // 3. if this cache is invalid // based on cached predicate results func (ec *EquivalenceCache) PredicateWithECache( podName, nodeName, predicateKey string, equivalenceHash uint64, needLock bool, ) (bool, []algorithm.PredicateFailureReason, bool) { ... if algorithmCache, exist := ec.algorithmCache[nodeName]; exist { if cachePredicate, exist := algorithmCache.predicatesCache.Get(predicateKey); exist { predicateMap := cachePredicate.(PredicateMap) // TODO(resouer) Is it possible a race that cache failed to update immediately? if hostPredicate, ok := predicateMap[equivalenceHash]; ok { if hostPredicate.Fit { return true, []algorithm.PredicateFailureReason{}, false } return false, hostPredicate.FailReasons, false } // is invalid return false, []algorithm.PredicateFailureReason{}, true } } return false, []algorithm.PredicateFailureReason{}, true } UpdateCachedPredicateItem :当PredicateWithECache使用Predicate Result Cache数据命中失败时,scheduler会调用对应的Predicate Funcs触发真正的预选逻辑,完成之后,就通过UpdateCachedPredicateItem将刚预选的结果更新到Equivalence Cache缓存中。每个node的predicateCache的初始化也是在这里完成的。 // UpdateCachedPredicateItem updates pod predicate for equivalence class func (ec *EquivalenceCache) UpdateCachedPredicateItem( podName, nodeName, predicateKey string, fit bool, reasons []algorithm.PredicateFailureReason, equivalenceHash uint64, needLock bool, ) { ... if _, exist := ec.algorithmCache[nodeName]; !exist { ec.algorithmCache[nodeName] = newAlgorithmCache() } predicateItem := HostPredicate{ Fit: fit, FailReasons: reasons, } // if cached predicate map already exists, just update the predicate by key if v, ok := ec.algorithmCache[nodeName].predicatesCache.Get(predicateKey); ok { predicateMap := v.(PredicateMap) // maps in golang are references, no need to add them back predicateMap[equivalenceHash] = predicateItem } else { ec.algorithmCache[nodeName].predicatesCache.Add(predicateKey, PredicateMap{ equivalenceHash: predicateItem, }) } } Equivalence Cache的初始化 Kubernetes在注册predicates、priorities、scheduler extenders时,同时也会进行Equivalence Cache的初始化,并将其传入scheduler config中。 // Creates a scheduler from a set of registered fit predicate keys and priority keys. func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, extenders []algorithm.SchedulerExtender) (*scheduler.Config, error) { ... // Init equivalence class cache if c.enableEquivalenceClassCache && getEquivalencePodFuncFactory != nil { pluginArgs, err := c.getPluginArgs() if err != nil { return nil, err } c.equivalencePodCache = core.NewEquivalenceCache( getEquivalencePodFuncFactory(*pluginArgs), ) glog.Info("Created equivalence class cache") } ... } // NewEquivalenceCache creates a EquivalenceCache object. func NewEquivalenceCache(getEquivalencePodFunc algorithm.GetEquivalencePodFunc) *EquivalenceCache { return &EquivalenceCache{ getEquivalencePod: getEquivalencePodFunc, algorithmCache: make(map[string]AlgorithmCache), } } NewEquivalenceCache负责Equivalence Cache的初始化工作,那么getEquivalencePod又是在哪完成注册的呢?defualt algorithm provider初始化时完成注册GetEquivalencePodFunc(只能使用defualt provider?通过configfile就不行吗?),注意这里 factory.PluginFactoryArgs 只传入了PVCInfo。 GetEquivalencePodFunc is a function that gets a EquivalencePod from a pod. pkg/scheduler/algorithmprovider/defaults/defaults.go:38 func init() { ... // Use equivalence class to speed up heavy predicates phase. factory.RegisterGetEquivalencePodFunction( func(args factory.PluginFactoryArgs) algorithm.GetEquivalencePodFunc { return predicates.NewEquivalencePodGenerator(args.PVCInfo) }, ) ... } 为什么只传入PVCInfo呢?或者为什么需要PVCInfo呢?要回答这个问题,我们先来看看EquivalencePod和getEquivalencePod的定义。 // EquivalencePod is a group of pod attributes which can be reused as equivalence to schedule other pods. type EquivalencePod struct { ControllerRef metav1.OwnerReference PVCSet sets.String } EquivalencePod定义了具备哪些相同属性的Pods属于Equivalent Pods,Equivalence Hash就是根据Pod的EquivalencePod中指定的两个属性来计算的,这两个属性分别是: ControllerRef:对应Pod的meta.OwnerReference,对应Pod所属的Controller Object,可以是RS,RC,Job,StatefulSet类型之一。 PVCSet:是Pod所引用的所有PVCs IDs集合。 因此,只有两个Pod属于同一个Controller并且引用可同样的PVCs对象才被认为是EquivalentPod,对应同一个Equivalence Hash。 getEquivalencePod根据Pod Object中的OwnerReference和PVC信息获取它所属的EquivalencePod对象。 func (e *EquivalencePodGenerator) getEquivalencePod(pod *v1.Pod) interface{} { for _, ref := range pod.OwnerReferences { if ref.Controller != nil && *ref.Controller { pvcSet, err := e.getPVCSet(pod) if err == nil { // A pod can only belongs to one controller, so let's return. return &EquivalencePod{ ControllerRef: ref, PVCSet: pvcSet, } } return nil } } return nil } 何时生成Pod对应的Equivalence Hash 预选的入口是findNodesThatFit,也就是在findNodesThatFit中调用了getEquivalenceClassInfo计算Pod的EquivalenceHash,然后把该hash值传入podFitsOnNode中进行后续的Equivalence Class功能。 func findNodesThatFit( pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*v1.Node, predicateFuncs map[string]algorithm.FitPredicate, extenders []algorithm.SchedulerExtender, metadataProducer algorithm.PredicateMetadataProducer, ecache *EquivalenceCache, schedulingQueue SchedulingQueue, alwaysCheckAllPredicates bool, ) ([]*v1.Node, FailedPredicateMap, error) { ... var equivCacheInfo *equivalenceClassInfo if ecache != nil { // getEquivalenceClassInfo will return immediately if no equivalence pod found equivCacheInfo = ecache.getEquivalenceClassInfo(pod) } checkNode := func(i int) { nodeName := nodes[i].Name fits, failedPredicates, err := podFitsOnNode( pod, meta, nodeNameToInfo[nodeName], predicateFuncs, ecache, schedulingQueue, alwaysCheckAllPredicates, equivCacheInfo, ) ... } ... } getEquivalenceClassInfo计算pod的EquivalenceHash的原理如下: // getEquivalenceClassInfo returns the equivalence class of given pod. func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceClassInfo { equivalencePod := ec.getEquivalencePod(pod) if equivalencePod != nil { hash := fnv.New32a() hashutil.DeepHashObject(hash, equivalencePod) return &equivalenceClassInfo{ hash: uint64(hash.Sum32()), } } return nil } 可见,EquivalenceHash就是对getEquivalencePod利用FNV算法进行哈希的。 Equivalent Pod的Predicate Result何时加到PredicateCache中 我们先看看podFitsOnNode的相关实现: func podFitsOnNode( pod *v1.Pod, meta algorithm.PredicateMetadata, info *schedulercache.NodeInfo, predicateFuncs map[string]algorithm.FitPredicate, ecache *EquivalenceCache, queue SchedulingQueue, alwaysCheckAllPredicates bool, equivCacheInfo *equivalenceClassInfo, ) (bool, []algorithm.PredicateFailureReason, error) { ... if predicate, exist := predicateFuncs[predicateKey]; exist { // Use an in-line function to guarantee invocation of ecache.Unlock() // when the in-line function returns. func() { var invalid bool if eCacheAvailable { // Lock ecache here to avoid a race condition against cache invalidation invoked // in event handlers. This race has existed despite locks in equivClassCacheimplementation. ecache.Lock() defer ecache.Unlock() // PredicateWithECache will return its cached predicate results. fit, reasons, invalid = ecache.PredicateWithECache( pod.GetName(), info.Node().GetName(), predicateKey, equivCacheInfo.hash, false) } if !eCacheAvailable || invalid { // we need to execute predicate functions since equivalence cache does not work fit, reasons, err = predicate(pod, metaToUse, nodeInfoToUse) if err != nil { return } if eCacheAvailable { // Store data to update equivClassCacheafter this loop. if res, exists := predicateResults[predicateKey]; exists { res.Fit = res.Fit && fit res.FailReasons = append(res.FailReasons, reasons...) predicateResults[predicateKey] = res } else { predicateResults[predicateKey] = HostPredicate{Fit: fit, FailReasons: reasons} } result := predicateResults[predicateKey] ecache.UpdateCachedPredicateItem( pod.GetName(), info.Node().GetName(), predicateKey, result.Fit, result.FailReasons, equivCacheInfo.hash, false) } } }() ... } podFitsOnNode时会先通过PredicateWithECache检查是否Equivalence Cache中有该缓存命中: 如果有命中数据可用,则对应的Predicate Policy就算处理完成。 如果没有命中数据才会触发调用predicate,然后将predicate的结果通过UpdateCachedPredicateItem添加/更新到缓存中。 维护Equivalence Cache 我们回到Scheduler Config Factory,看看Scheduler中podInformer、nodeInformer、serviceInformer、pvcInformer等注册的EventHandler中对Equivalence Cache的操作。 Assume Pod 当完成pod的调度后,在Bind Node之前,会先进行Pod Assume,在Assume过程中,会对Equivalence Cache有操作。 // assume signals to the cache that a pod is already in the cache, so that binding can be asynchronous. // assume modifies `assumed`. func (sched *Scheduler) assume(assumed *v1.Pod, host string) error { ... // Optimistically assume that the binding will succeed, so we need to invalidate affected // predicates in equivalence cache. // If the binding fails, these invalidated item will not break anything. if sched.config.Ecache != nil { sched.config.Ecache.InvalidateCachedPredicateItemForPodAdd(assumed, host) } return nil } Assume Pod时调用InvalidateCachedPredicateItemForPodAdd对Equivalence Cache进行操作。 func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, nodeName string) { // GeneralPredicates: will always be affected by adding a new pod invalidPredicates := sets.NewString("GeneralPredicates") // MaxPDVolumeCountPredicate: we check the volumes of pod to make decision. for _, vol := range pod.Spec.Volumes { if vol.PersistentVolumeClaim != nil { invalidPredicates.Insert("MaxEBSVolumeCount", "MaxGCEPDVolumeCount", "MaxAzureDiskVolumeCount") } else { if vol.AWSElasticBlockStore != nil { invalidPredicates.Insert("MaxEBSVolumeCount") } if vol.GCEPersistentDisk != nil { invalidPredicates.Insert("MaxGCEPDVolumeCount") } if vol.AzureDisk != nil { invalidPredicates.Insert("MaxAzureDiskVolumeCount") } } } ec.InvalidateCachedPredicateItem(nodeName, invalidPredicates) } InvalidateCachedPredicateItemForPodAdd中可以看出,Assume Pod会删除该node上以下predicateKey对应的predicateCache: GeneralPredicates; 如果该pod中引用了PVCs,则会删除"MaxEBSVolumeCount", "MaxGCEPDVolumeCount", "MaxAzureDiskVolumeCount"这些PredicateCaches; 如果pod volume中使用了AWSElasticBlockStore,则会删除MaxEBSVolumeCount PredicateCache; 如果pod volume中使用了GCEPersistentDisk,则会删除MaxGCEPDVolumeCount PredicateCache; 如果pod volume中使用了AzureDisk,则会删除MaxAzureDiskVolumeCount PredicateCache; Update Pod in Scheduled Pod Cache 在scheduler进行NewConfigFactory时,注册Update assignedNonTerminatedPod Event Handler为updatePodInCache。 func (c *configFactory) updatePodInCache(oldObj, newObj interface{}) { ... c.invalidateCachedPredicatesOnUpdatePod(newPod, oldPod) c.podQueue.AssignedPodUpdated(newPod) } func (c *configFactory) invalidateCachedPredicatesOnUpdatePod(newPod *v1.Pod, oldPod *v1.Pod) { if c.enableEquivalenceClassCache { // if the pod does not have bound node, updating equivalence cache is meaningless; // if pod's bound node has been changed, that case should be handled by pod add & delete. if len(newPod.Spec.NodeName) != 0 && newPod.Spec.NodeName == oldPod.Spec.NodeName { if !reflect.DeepEqual(oldPod.GetLabels(), newPod.GetLabels()) { // MatchInterPodAffinity need to be reconsidered for this node, // as well as all nodes in its same failure domain. c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes( matchInterPodAffinitySet) } // if requested container resource changed, invalidate GeneralPredicates of this node if !reflect.DeepEqual(predicates.GetResourceRequest(newPod), predicates.GetResourceRequest(oldPod)) { c.equivalencePodCache.InvalidateCachedPredicateItem( newPod.Spec.NodeName, generalPredicatesSets) } } } } updatePodInCache调用invalidateCachedPredicatesOnUpdatePod对Equivalence Cache做了如下处理: 如果pod Labels做了更新,那么会删除所有nodes上Equivalence Cache中的MatchInterPodAffinity PredicateCache; 如果pod的resource request做了更新,那么会删除该node上Equivalence Cache中的GeneralPredicates PredicateCache; Delete Pod in Scheduled Pod Cache 同样的,当发生删除assignedNonTerminatedPod时,对应会调用invalidateCachedPredicatesOnDeletePod更新Equivalence Cache。 func (c *configFactory) invalidateCachedPredicatesOnDeletePod(pod *v1.Pod) { if c.enableEquivalenceClassCache { // part of this case is the same as pod add. c.equivalencePodCache.InvalidateCachedPredicateItemForPodAdd(pod, pod.Spec.NodeName) // MatchInterPodAffinity need to be reconsidered for this node, // as well as all nodes in its same failure domain. // TODO(resouer) can we just do this for nodes in the same failure domain c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes( matchInterPodAffinitySet) // if this pod have these PV, cached result of disk conflict will become invalid. for _, volume := range pod.Spec.Volumes { if volume.GCEPersistentDisk != nil || volume.AWSElasticBlockStore != nil || volume.RBD != nil || volume.ISCSI != nil { c.equivalencePodCache.InvalidateCachedPredicateItem( pod.Spec.NodeName, noDiskConflictSet) } } } } invalidateCachedPredicatesOnDeletePod更新Equivalence Cache的处理总结为: 删除该node上Equivalence Cache中的GeneralPredicates PredicateCache; 如果该pod中引用了PVCs,则会删除该node上Equivalence Cache中的"MaxEBSVolumeCount", "MaxGCEPDVolumeCount", "MaxAzureDiskVolumeCount"这些PredicateCaches; 如果pod volume中使用了AWSElasticBlockStore,则会删除该node上Equivalence Cache中的MaxEBSVolumeCount PredicateCache; 如果pod volume中使用了GCEPersistentDisk,则会删除该node上Equivalence Cache中的MaxGCEPDVolumeCount PredicateCache; 如果pod volume中使用了AzureDisk,则会删除该node上Equivalence Cache中的MaxAzureDiskVolumeCount PredicateCache; 删除所有nodes上Equivalence Cache中的MatchInterPodAffinity PredicateCache; 如果pod的resource request做了更新,那么会删除该node上Equivalence Cache中的GeneralPredicates PredicateCache; 如果pod volume中引用了GCEPersistentDisk、AWSElasticBlockStore、RBD、ISCSI之一,则删除该node上Equivalence Cache中的NoDiskConflict PredicateCache。 Update Node 当发生node update event时,对应会调用invalidateCachedPredicatesOnNodeUpdate更新Equivalence Cache。 func (c *configFactory) invalidateCachedPredicatesOnNodeUpdate(newNode *v1.Node, oldNode *v1.Node) { if c.enableEquivalenceClassCache { // Begin to update equivalence cache based on node update // TODO(resouer): think about lazily initialize this set invalidPredicates := sets.NewString() if !reflect.DeepEqual(oldNode.Status.Allocatable, newNode.Status.Allocatable) { invalidPredicates.Insert(predicates.GeneralPred) // "PodFitsResources" } if !reflect.DeepEqual(oldNode.GetLabels(), newNode.GetLabels()) { invalidPredicates.Insert(predicates.GeneralPred, predicates.CheckServiceAffinityPred) // "PodSelectorMatches" for k, v := range oldNode.GetLabels() { // any label can be topology key of pod, we have to invalidate in all cases if v != newNode.GetLabels()[k] { invalidPredicates.Insert(predicates.MatchInterPodAffinityPred) } // NoVolumeZoneConflict will only be affected by zone related label change if isZoneRegionLabel(k) { if v != newNode.GetLabels()[k] { invalidPredicates.Insert(predicates.NoVolumeZoneConflictPred) } } } } oldTaints, oldErr := helper.GetTaintsFromNodeAnnotations(oldNode.GetAnnotations()) if oldErr != nil { glog.Errorf("Failed to get taints from old node annotation for equivalence cache") } newTaints, newErr := helper.GetTaintsFromNodeAnnotations(newNode.GetAnnotations()) if newErr != nil { glog.Errorf("Failed to get taints from new node annotation for equivalence cache") } if !reflect.DeepEqual(oldTaints, newTaints) || !reflect.DeepEqual(oldNode.Spec.Taints, newNode.Spec.Taints) { invalidPredicates.Insert(predicates.PodToleratesNodeTaintsPred) } if !reflect.DeepEqual(oldNode.Status.Conditions, newNode.Status.Conditions) { oldConditions := make(map[v1.NodeConditionType]v1.ConditionStatus) newConditions := make(map[v1.NodeConditionType]v1.ConditionStatus) for _, cond := range oldNode.Status.Conditions { oldConditions[cond.Type] = cond.Status } for _, cond := range newNode.Status.Conditions { newConditions[cond.Type] = cond.Status } if oldConditions[v1.NodeMemoryPressure] != newConditions[v1.NodeMemoryPressure] { invalidPredicates.Insert(predicates.CheckNodeMemoryPressurePred) } if oldConditions[v1.NodeDiskPressure] != newConditions[v1.NodeDiskPressure] { invalidPredicates.Insert(predicates.CheckNodeDiskPressurePred) } if oldConditions[v1.NodeReady] != newConditions[v1.NodeReady] || oldConditions[v1.NodeOutOfDisk] != newConditions[v1.NodeOutOfDisk] || oldConditions[v1.NodeNetworkUnavailable] != newConditions[v1.NodeNetworkUnavailable] { invalidPredicates.Insert(predicates.CheckNodeConditionPred) } } if newNode.Spec.Unschedulable != oldNode.Spec.Unschedulable { invalidPredicates.Insert(predicates.CheckNodeConditionPred) } c.equivalencePodCache.InvalidateCachedPredicateItem(newNode.GetName(), invalidPredicates) } } 因此,node update时,会删除该node对应的Equivalence Cache中如下PredicateKey的PredicateCache: GeneralPredicates, 前提:node.Status.Allocatable或node labels发生变更. ServiceAffinity, 前提:node labels发生变更。 MatchInterPodAffinity, 前提:node labels发生变更。 NoVolumeZoneConflict, 前提:failure-domain.beta.kubernetes.io/zone或failure-domain.beta.kubernetes.io/region Annotation发生变更; PodToleratesNodeTaints, 前提: Node的Taints(对应scheduler.alpha.kubernetes.io/taints Annotation)发生变更. CheckNodeMemoryPressure, CheckNodeDiskPressure, CheckNodeCondition, 前提:如果对应的Node Condition发生变更。 Delete Node 当发生node delete event时,对应会调用InvalidateAllCachedPredicateItemOfNode更新Equivalence Cache。 // InvalidateAllCachedPredicateItemOfNode marks all cached items on given node as invalid func (ec *EquivalenceCache) InvalidateAllCachedPredicateItemOfNode(nodeName string) { ec.Lock() defer ec.Unlock() delete(ec.algorithmCache, nodeName) glog.V(5).Infof("Done invalidating all cached predicates on node: %s", nodeName) } 因此,node delete时,则会从Equivalence Cache中删除整个node对应的algorthmCache。 Add or Delete PV 当发生pv add或者delete event时,对应会调用invalidatePredicatesForPv更新Equivalence Cache。 func (c *configFactory) invalidatePredicatesForPv(pv *v1.PersistentVolume) { // You could have a PVC that points to a PV, but the PV object doesn't exist. // So when the PV object gets added, we can recount. invalidPredicates := sets.NewString() // PV types which impact MaxPDVolumeCountPredicate if pv.Spec.AWSElasticBlockStore != nil { invalidPredicates.Insert(predicates.MaxEBSVolumeCountPred) } if pv.Spec.GCEPersistentDisk != nil { invalidPredicates.Insert(predicates.MaxGCEPDVolumeCountPred) } if pv.Spec.AzureDisk != nil { invalidPredicates.Insert(predicates.MaxAzureDiskVolumeCountPred) } // If PV contains zone related label, it may impact cached NoVolumeZoneConflict for k := range pv.Labels { if isZoneRegionLabel(k) { invalidPredicates.Insert(predicates.NoVolumeZoneConflictPred) break } } if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // Add/delete impacts the available PVs to choose from invalidPredicates.Insert(predicates.CheckVolumeBindingPred) } c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) } 因此,当add或者delete PV时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: MaxEBSVolumeCount, MaxGCEPDVolumeCount, MaxAzureDiskVolumeCount,前提:PV类型是这三者的范围内; Update PV 当发生pv update event时,对应会调用invalidatePredicatesForPvUpdate更新Equivalence Cache。 func (c *configFactory) invalidatePredicatesForPvUpdate(oldPV, newPV *v1.PersistentVolume) { invalidPredicates := sets.NewString() for k, v := range newPV.Labels { // If PV update modifies the zone/region labels. if isZoneRegionLabel(k) && !reflect.DeepEqual(v, oldPV.Labels[k]) { invalidPredicates.Insert(predicates.NoVolumeZoneConflictPred) break } } c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) } 因此,当update PV时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: NoVolumeZoneConflict, 前提:PV的failure-domain.beta.kubernetes.io/zone或failure-domain.beta.kubernetes.io/region Annotation发生变更; Add or Delete PVC func (c *configFactory) invalidatePredicatesForPvc(pvc *v1.PersistentVolumeClaim) { // We need to do this here because the ecache uses PVC uid as part of equivalence hash of pod // The bound volume type may change invalidPredicates := sets.NewString(maxPDVolumeCountPredicateKeys...) // The bound volume's label may change invalidPredicates.Insert(predicates.NoVolumeZoneConflictPred) if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // Add/delete impacts the available PVs to choose from invalidPredicates.Insert(predicates.CheckVolumeBindingPred) } c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) } 当发生pvc add或者delete event时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: "MaxEBSVolumeCount", "MaxGCEPDVolumeCount", "MaxAzureDiskVolumeCount" PredicateCaches; NoVolumeZoneConflict PredicateCaches; CheckVolumeBinding,前提,VolumeScheduling这个Feature Gate是启用状态; Update PVC func (c *configFactory) invalidatePredicatesForPvcUpdate(old, new *v1.PersistentVolumeClaim) { invalidPredicates := sets.NewString() if old.Spec.VolumeName != new.Spec.VolumeName { if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // PVC volume binding has changed invalidPredicates.Insert(predicates.CheckVolumeBindingPred) } // The bound volume type may change invalidPredicates.Insert(maxPDVolumeCountPredicateKeys...) } c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) } 当发生pvc update event时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: CheckVolumeBinding,前提:VolumeScheduling这个Feature Gate是启用状态,并且PVC对应的PV发生变更; "MaxEBSVolumeCount", "MaxGCEPDVolumeCount", "MaxAzureDiskVolumeCount" PredicateCaches,前提:PVC对应的PV发生变更; Add or Delete Service func (c *configFactory) onServiceAdd(obj interface{}) { if c.enableEquivalenceClassCache { c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) } c.podQueue.MoveAllToActiveQueue() } func (c *configFactory) onServiceDelete(obj interface{}) { if c.enableEquivalenceClassCache { c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) } c.podQueue.MoveAllToActiveQueue() } 当发生Service Add或Delete event时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: CheckServiceAffinity; Update Service func (c *configFactory) onServiceUpdate(oldObj interface{}, newObj interface{}) { if c.enableEquivalenceClassCache { // TODO(resouer) We may need to invalidate this for specified group of pods only oldService := oldObj.(*v1.Service) newService := newObj.(*v1.Service) if !reflect.DeepEqual(oldService.Spec.Selector, newService.Spec.Selector) { c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) } } c.podQueue.MoveAllToActiveQueue() } 当发生Service Update event时,会从Equivalence Cache中删除所有nodes的以下predicateKey对应的PredicateCache: CheckServiceAffinity,前提:Service的Selector发生变更。 Equivalence Class的不足 Equivalence Class Feature最困难的就是如何最优的维护和更新Equivalence Cache,做到每次更新都是最小粒度的、准确无误的,目前这方面还需优化。 Equivalence Cache只缓存Predicate Result,并不支持Priority Result数据的缓存和维护(社区正在实现基于Map-Reduce方式优化),通常情况下,Priority Funcs的处理逻辑要比Predicate Funcs复杂,支持的意义就更大。 Equivalence Class目前只能根据Pod对应的OwnerReference和PVC信息进行Equivalence Hash,如果能摒弃OwnerReference的考虑,充分考虑Pod spec中那些核心的field,比如resource request, Labels,Affinity等,缓存命中的几率可能会大的多,Predicate的性能就能得到更显著的提升。 总结 Equivalence Class是用来给Kubernetes Scheduler加速Predicate,从而提升Scheduler的吞吐性能。当然,普通用户其实无需关注Equivalence Class Feature,因为目前的scheduler性能对大部分用户来说已经足够了,但对于有大规模AI训练场景的用户,可以多关注它。
来源:OSCHINA
发布时间:2018-05-17 00:48:00
摘要: 表格存储的增量数据流功能能够使用户使用API获取Table Store表中增量数据,并可以进行增量数据流的实时增量分析、数据增量同步等。通过创建Table Store触发器,能够实现Table Store Stream和函数计算的自动对接,让计算函数中自定义的程序逻辑自动处理Table Store表中发生的数据修改,充分的利用了函数计算全托管、弹性伸缩的特点。 函数计算(Function Compute) 是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。 Table Store Stream是用于获取Table Store表中增量数据的一个数据通道,通过创建Table Store触发器,能够实现Table Store Stream和函数计算的自动对接,让计算函数中自定义的程序逻辑自动处理Table Store表中发生的数据修改。 表格存储高并发的写入性能以及低廉的存储成本非常适合物联网、日志、监控数据的存储,我们可以将数据写入到表格存储中,同时在函数计算中对新增的数据做简单的清洗、转换、聚合计算等操作,并将清洗之后的数据写回到表格存储的结果表中,并对原始明细数据及结果数据提供实时访问。 下面,我们使用函数计算对表格存储中的数据做简单的清洗,并写入到结果表中。 数据定义 我们假设写入的为日志数据,包括三个基础字段: 字段名称 类型 含义 id 整型 日志id level message 整型 字符串 日志的等级,越大表明等级越高 日志的内容 我们需要将 level>1 的日志写入到另外一张数据表中,用作专门的查询。 实现过程: 创建实例及数据表 在 表格存储的控制台 创建表格存储实例(__本次以 华东2 distribute-test 为例__),并创建源表(__source_data__)及结果表(__result__),主键为均 __id (整型)__,由于表格存储是 schemafree 结构,无需预先定义其他属性列字段。 开启数据源表的Stream功能 触发器功能需要先开启数据表的 Stream功能 ,才能在函数计算中处理写入表格存储中的增量数据。 Stream记录过期时长 为通过 StreamAPI 能够读取到的增量数据的最长时间。 由于触发器只能绑定现有的函数,故先到函数计算的控制台上在同region创建服务及函数。 创建函数计算服务 在 函数计算的控制台 上创建服务及处理函数,我们继续使用华东2节点。 1.在华东2节点创建服务。 2.创建函数依次选择:空白函数——不创建触发器。 函数名称为:etl_test,选择 python2.7 环境,在线编辑代码 函数入口为:etl_test.handler 代码稍后编辑,点击下一步。 3.进行服务授权 由于函数计算需要将运行中的日志写入到日志服务中,同时,需要对表格存储的表进行读写,故需要对函数计算进行授权,为方便起见,我们先添加 AliyunOTSFullAccess 与 __AliyunLogFullAccess __权限,实际生产中,建议根据权限最小原则来添加权限。 4.点击授权完成,并创建函数。 5.修改函数代码。 创建好函数之后,点击对应的 函数 — 代码执行 ,编辑代码并保存,其中,INSTANCE_NAME(表格存储的实例名称)、REGION(使用的区域)需要根据情况进行修改: 使用示例代码如下: #!/usr/bin/env python # -*- coding: utf-8 -*- import cbor import json import tablestore as ots INSTANCE_NAME = 'distribute-test' REGION = 'cn-shanghai' ENDPOINT = 'http://%s.%s.ots-internal.aliyuncs.com'%(INSTANCE_NAME, REGION) RESULT_TABLENAME = 'result' def _utf8(input): return str(bytearray(input, "utf-8")) def get_attrbute_value(record, column): attrs = record[u'Columns'] for x in attrs: if x[u'ColumnName'] == column: return x['Value'] def get_pk_value(record, column): attrs = record[u'PrimaryKey'] for x in attrs: if x['ColumnName'] == column: return x['Value'] #由于已经授权了AliyunOTSFullAccess权限,此处获取的credentials具有访问表格存储的权限 def get_ots_client(context): creds = context.credentials client = ots.OTSClient(ENDPOINT, creds.accessKeyId, creds.accessKeySecret, INSTANCE_NAME, sts_token = creds.securityToken) return client def save_to_ots(client, record): id = int(get_pk_value(record, 'id')) level = int(get_attrbute_value(record, 'level')) msg = get_attrbute_value(record, 'message') pk = [(_utf8('id'), id),] attr = [(_utf8('level'), level), (_utf8('message'), _utf8(msg)),] row = ots.Row(pk, attr) client.put_row(RESULT_TABLENAME, row) def handler(event, context): records = cbor.loads(event) #records = json.loads(event) client = get_ots_client(context) for record in records['Records']: level = int(get_attrbute_value(record, 'level')) if level > 1: save_to_ots(client, record) else: print "Level <= 1, ignore." 对表格存储 Stream 数据的格式详情请参考 Stream 数据处理 绑定触发器 1.回到表格存储的实例管理页面,点击表 source_data 后的 使用触发器 按钮,进入触发器绑定界面,点击 使用已有函数计算 , 选择刚创建的服务及函数,勾选 表格存储发送事件通知的权限 , 进行确定。 2.绑定成功之后,能够看到如下的信息: 运行验证 1.向 source_data 表中写入数据。 2.在 result 表中查询清洗后的数据 点击 result 表的数据管理页面,会查询到刚写入到 source_data 中的数据。 当然,向 soure_data 写入level <=1的数据将不会同步到 result 表中 原文链接 本文为云栖社区原创内容,未经允许不得转载
来源:OSCHINA
发布时间:2018-05-16 22:07:00
摘要: 智能媒体管理提供了 Cloud Native 架构的文档转换/预览服务,本文介绍快速搭建的示例,让您用 DIY 的体验方式实现文档预览功能。 一、导语 智能媒体管理 提供了 Cloud Native 架构的文档转换/预览服务,本文介绍快速搭建的示例,让您用 DIY 的体验方式实现文档预览功能。 二、环境准备 2.1 准备 RAM 子账号 为了实现安全的文档转换/预览功能,本文使用 RAM 子账号来管理资源并进行代码开发。 2.1.1 创建 test 子账号 登陆 访问控制 页面,点击“用户管理”,选择“新建用户”,创建子账号 test ,创建成功如下图所示: 2.1.2 子账号授权 在创建子账号成功后的界面,选择 test 子账号,点击“授权”,确保给该子账号授予如下策略: AliyunOSSFullAccess 。具有完整访问 OSS 的权限,让文档转换能够在 OSS 存储转换数据。本文为了快速搭建而选择此权限,如果需要更精细的权限控制,可以参考 OSS 子账号设置常见问题 。 AliyunSTSAssumeRoleAccess 。具有申请 STS Token的权限,预览时为客户端提供 STS Token,从而保证前端访问 OSS 的安全性。 AliyunIMMFullAccess 。具有执行 IMM 服务的权限,具有了该权限就可以调用文档转换接口。 2.1.3 生成子账号的AK(Access Key) 代码开发时,需要使用AK(Access Key)。在 2.1.1章节 创建子账号成功后的界面,选择 test 子账号,点击“管理”,进入 test 子账号后再点击“创建 AccessKey”,保存好 AccessKey ID 和 Secret,它们将用于代码开发。 注意:目前每个子账号最多使用2个 AK,可以通过删除旧的 AK 来解决。 如上图中,得到 AK ID 类似 LTAIxxxxxxxxxxx ,对应的 AK Secret 类似 W1yyyyyyyyyyyyyyyyyyyy 。 注意:这里提供的值只是示例,不能直接使用,具体的值请您在控制台获取并保存。 2.2 准备 OSS 服务 文档转换/预览是基于 OSS 实现,需要 OSS 的存储空间支持,本文通过 创建新桶 来实现,如下图所示。 在 华东2(上海区域)创建了 yourid-dev-imm 桶。 注意:该桶名只是示例,不能直接使用,需要替换为您的桶名。 2.3 准备 IMM 服务 转换/预览使用 IMM 服务,先 开通产品 产品,然后 创建项目 ,如下图所示,在 华东2(上海区域)创建了 imm 项目。 注意:一定需保证创建 IMM 项目 时指定的 Region 要和您创建 OSS 桶 指定的区域相同,才能够正常工作,目的是避免转换时带来跨 OSS Region 访问的流量费用。 2.4 准备开发环境 本文基于 “IntelliJ IDEA + Maven” 准备 Java 开发环境,参考 IMM 服务的 Java SDK 文档。 注意:在 pom.xml 文件中添加 aliyun-java-sdk-core 和 aliyun-java-sdk-imm 的版本依赖。 com.aliyun aliyun-java-sdk-core true 3.5.1 com.aliyun aliyun-java-sdk-imm true 1.2.1 三、开发部署 3.1 转换代码 环境准备好后,可以参考如下 Java 代码实现文档转换,技术细节请参考 转换原理 。 import com.aliyuncs.imm.main.IMMClient; import com.aliyuncs.imm.model.v20170906.*; import com.aliyuncs.exceptions.*; public class TestImmOffice { public static void main(String[] args) { String accessKeyId = "LTAIxxxxxxxxxxx"; //RAM 中 test 子账号的 AK ID String accessKeySecret = "W1yyyyyyyyyyyyyyyyyyyy"; //RAM 中 test 子账号的 AK Secret IMMClient client = new IMMClient("cn-shanghai", accessKeyId, accessKeySecret); ConvertOfficeFormatResponse resp = new ConvertOfficeFormatResponse(); ConvertOfficeFormatRequest req = new ConvertOfficeFormatRequest(); req.setProject("IMM"); //在 IMM 中创建的项目 req.setSrcUri("oss://yourid-dev-imm/test-data/office/paxos.pptx"); //OSS 源文件路径 req.setTgtUri("oss://yourid-dev-imm/test-data/office/paxos.pptx/output"); //OSS 转换文件路径 req.setTgtType("vector"); try { resp = client.getResponse(req); System.out.printf("requestId=%s, taskId=%s, tgtloc=%s", resp .getRequestId(), resp.getTaskId(),resp.getTgtLoc()); }catch (ClientException e){ System.out.println("error"); } } } 转换成功后,您将在 OSS 的 yourid-dev-imm 桶( 华东2---上海区域 ),对应 test-data/office/paxos.pptx/output/doc 目录下(注意:转换引擎增加了 doc 后缀),得到 meta.json , fp*.json , 以及 I 目录下的文件。 3.2 预览部署 转换成功后,可以基于部署的前端预览引擎实现预览功能,技术介绍请参考 预览原理 。如下图所示,在 yourid-dev-imm 桶( 华东2---上海区域 )中添加了 preview目录 ,它包含了预览引擎的文件。 3.3 前端访问 部署预览引擎后,前端就可以通过访问预览引擎所在 OSS 桶的域名,实现预览功能。基于上述章节介绍,可以通过 https://yourid-dev-imm.oss-cn-shanghai.aliyuncs.com/preview/index.html 路径访问渲染引擎。 注意:实际使用时请把 yourid-dev-imm 更换为您的桶。 3.3.1 OSS 公共读权限预览 为了快速体验预览引擎的效果,我们简化 OSS 的权限配置为公共读,如下图所示。 此时,可以通过如下地址格式完成文档预览。 https://yourid-dev-imm.oss-cn-shanghai.aliyuncs.com/preview/index.html ?url=https://yourid-dev-imm.oss-cn-shanghai.aliyuncs.com/test-data/office/paxos.pptx/output ®ion=oss-cn-shanghai &bucket=yourid-dev-imm 其中 ? 前面部分为访问渲染引擎的路径,而 url=xxx 为转换文档的目标地址( 注意:在 output 后无需加上 / ,渲染引擎会自动处理 ), region=oss-cn-shanghai 表示 OSS 桶所在区域, bucket=yourid-dev-imm 为桶名。 注意:实际使用时,需要把 yourid-dev-imm 更换为自己的桶。 3.3.2 OSS 私有权限预览(高安全) 设置桶为私有,但让渲染引擎 preview 目录下的文件为公共读,然后为转换文件申请STS Token policy = { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:" + accountId + ":" + bucket + "/" + prefix + "/*" ] }, { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:" + accountId + ":" + bucket ], "Condition": { "StringLike": { "oss:Prefix": prefix + "/*" } } } ] } 此时把 accountId 设置为 * ,表示任意用户;bucket 设置为 yourid-dev-imm ,表示您自己创建的桶;prefix 设置为本文中的路径 test-data/office/paxos.pptx/output 。然后,调用 STS 的 AssumeRole 得到 AccessKeyId,AccessKeySecret,SecurityToken三元组,然后用如下方式组合,即可实现安全的预览访问。 https://yourid-dev-imm.oss-cn-shanghai.aliyuncs.com/preview/index.html ?url=https://yourid-dev-imm.oss-cn-shanghai.aliyuncs.com/test-data/office/paxos.pptx/output &accessKeyId=STS.AAAAAAAAAA &accessKeySecret=BBBBBBBBBBB &stsToken=CCCCCCCCCCCCC ®ion=oss-cn-shanghai &bucket=yourid-dev-imm &... 四、参考文档 参考如下的文章,帮助您掌握相关背景: 智能媒体管理产品文档转换/预览功能介绍(1)---Cloud Native架构 智能媒体管理产品文档转换/预览功能介绍(2)---转换原理 智能媒体管理产品文档转换/预览功能介绍(3)---前端预览 原文链接 本文为云栖社区原创内容,未经允许不得转载
来源:OSCHINA
发布时间:2018-05-16 21:36:00
摘要: 本文将为您介绍,在采集多类日志数据的情况下,阿里云业务实时监控服务(ARMS)之前端监控如何优化日志上报 前端监控 (又叫UEM,User Experience Management, 用户体验管理) 一般帮助用户定位页面性能瓶颈、复现用户端的偶发问题。其监控的主要功能包括但不限于: 日志采集 日志上报 数据分析 平台展示 异常报警 使用前端监控平台时,用户最关心的问题往往首先是: 平台可以监控哪些数据? 会不会影响业务性能? 这就涉及到前端监控的监控指标和日志上报。带着这两个问题,本文就为您介绍一下,在采集多类日志数据的情况下, 阿里云业务实时监控服务(ARMS)之前端监控 (以下简称为阿里云前端监控)如何优化日志上报,让上报速度快、更快、无比快! 文章主要分为两个部分:第一部分"监控指标介绍"解释前端监控一般需要上报哪些数据;第二部分"日志上报优化秘笈"解释前端监控如何针对这些数据上报进行优化。 监控指标介绍 阿里云前端监控 专注于 Web 端真实用户体验数据监控,从访问速度、页面运行稳定性和服务调用成功率三个方面监控前端的健康度。另外,阿里云前端监控还提供针对轻量级交互行为的实时统计功能,可帮助您及时发现业务问题。 阿里云前端监控的指标如下: 1. 页面是否正常响应 页面性能日志 — 实时统计与页面相关的 12 个关键性能指标,帮助您迅速准确地定位性能瓶颈: DNS 解析耗时 TCP 连接耗时 SSL 安全连接耗时 网络请求耗时 DOM 解析耗时 资源加载耗时 首包时间 白屏时间 首次可交互时间 DOM Ready 时间 页面完全加载时间 …… 访问统计日志 — 统计 PV、UV 数据。短时间内断崖式的变化很容易反映业务问题。 2. 页面运行是否稳定 页面稳定性日志 — 阿里云前端监控以 JS 错误率衡量页面运行稳定性,会采集因页面加载和页面交互产生的 JS Error 报错文件、错误堆栈的详细信息,快速定位用户访问时发生的错误问题。 3. API 请求是否正常响应 API 调用日志 — 提供 API 调用结果、耗时及相关信息,快速发现并定位 API 问题。 4. 业务相关日志 自定义上报日志 — 某些业务逻辑的结果、速度、统计值等自定义内容可帮助您发现业务问题。 如果前端业务复杂、访问量级较大,那么相应地,前端监控上报的日志类型及日志量也会快速增长。前端监控的最基本原则是日志获取和日志上报不能影响业务性能,所以在这种情况下,日志上报性能尤为重要。 接下来,我们就介绍一下阿里云前端监控平台的日志上报优化秘笈。只要学会了这几种新姿势,即便日志量不断增长,您也能游刃有余! 日志上报优化秘笈 第一招:HTTP No Content 日志上报只关心日志有没有上报,并不关心上报请求的返回内容,甚至完全可以不需要返回内容。所以使用 HTTP HEAD 的方式上报,并且返回的响应体为空,可避免响应体传输造成的资源损耗。只需要设置一个 nginx 服务器来记录日志内容并返回 200 状态码即可。 fetch(`${url}?t=perf&page=lazada-home&load=1168`, {mode:'no-cors',method:'HEAD'}) 第二招:HTTP 2.0 HTTP/2 头部压缩 每次 HTTP 请求都会传输一系列的请求头来描述请求的资源及其特性,然而实际上每次请求都有很多相同的值,如 Host: 、 user-agent: 、 Accept 等。这些数据会占用 300-800 byte 的传输量,如果携带大的 cookie,请求头甚至会占据 1 kb 的空间,而实际真正需要上报的日志数据仅有 10~50 byte。在 HTTP 1.x 中,每次日志上报请求头都携带了大量的重复数据导致性能浪费。 HTTP/2 头部压缩 采用 Huffman Code 压缩请求头,并用动态表更新每次请求不同的数据,从而将每次请求的头部压缩到很小。 HTTP/1.1 效果 HTTP/2.0 效果 压缩头部后,每条日志请求都大幅缩小,响应的速度也相应提升。 HTTP/2 多路复用 用户浏览器和日志服务器之间产生多次 HTTP 请求,而在 HTTP/1.1 Keep-Alive 下,日志上报会以串行的方式传输,并让后面的日志上报延时。通过 HTTP/2 的多路复用来合并上报,可为您节省网络连接的开销。 第三招:HTTP POST 合并 POST 请求因为 request body 可以有更大施展空间,相比一条日志一次 HTTP HEAD 请求的方式,在 HTTP POST 中一次包含多条日志的内容更加经济。 在 HTTP POST 的基础上,可以顺便解决用户关掉或者切换页面造成的漏报问题。 以前常见的解决方式是监听页面的 unload 或者 beforeunload 事件,并以通过同步的 XMLHttpRequest 请求或者构造一个特定 src 的 标签来延迟上报。 window.addEventListener("unload", uploadLog, false); function uploadLog() { var xhr = new XMLHttpRequest(); xhr.open("POST", "/r.png", false); // false表示同步 xhr.send(logData); } 这种上报方式的弊端是会影响下一个页面的性能。更优雅的方式是使用 navigator.sendBeacon() ,它能够异步发送日志数据。 window.addEventListener("unload", uploadLog, false); function uploadLog() { navigator.sendBeacon("/r.png", logData); } 合并前 合并后(navigator.sendBeacon) 理想情况下,合并 N 个日志上报耗费的总时间可缩减至原来的 1/N。 总结 阿里云前端监控 的日志上报整体优化流程如下: 经过这几步优化后,日志上报性能明显提升: 日志上报的传输量可大幅降低至原来的 1/10 左右 日志上报响应时间可提速 80% 左右 实际大促业务场景在线上的验证结果表明,业务性能不会受到影响。所以,即便您的业务访问量级较大或者性能要求较高,也无需担心接入后的性能问题,可放心接入使用。 附:阿里云业务实时监控服务(ARMS)前端监控系介绍 业务实时监控服务(ARMS) 应用监控 前端监控 自定义监控 原文链接 本文为云栖社区原创内容,未经允许不得转载
来源:OSCHINA
发布时间:2018-05-16 21:05:00
摘要: 一、 MaxCompute是什么 你的OSS数据是否作堆积在一旁沉睡已久存储成本变为企业负担你是否想唤醒沉睡的数据驱动你的业务前行MaxCompute可以帮助你高效且低成本的解决这些问题通过对海量数据进行分析和计算来实现勾勒用户画像、提升营销转化、挖掘产品优化方向、预测业务发展等丰富的业务场景。 一、 MaxCompute 是什么? 你的OSS数据是否作堆积在一旁沉睡已久,存储成本变为企业负担?你是否想唤醒沉睡的数据,驱动你的业务前行?MaxCompute可以帮助你高效且低成本的解决这些问题,通过对海量数据进行分析和计算来实现勾勒用户画像、提升营销转化、挖掘产品优化方向、预测业务发展等丰富的业务场景。 MaxCompute是一项提供快速、完全托管的EB级数据仓库解决方案的大数据计算服务,可以高效并经济的分析处理海量数据。作为阿里云大数据旗舰产品,MaxCompute的EB级别性能处理达到了全球领先性,被Forrester评为全球云端数据仓库领导者。同时,MaxCompute也是阿里巴巴内部大数据旗舰平台,阿里巴巴近99%的数据存储以及95%的计算能力都在这个平台上产生。 最近MaxCompute重磅推出了一项重要特性: OSS外表查询功能 。该功能可以帮助您直接对OSS中的海量文件进行查询,而不必将数据加载到MaxCompute 表中,既节约了数据搬迁的时间和人力,也节省了多地存储的成本。除此之外,MaxCompute外表查询功能还拥有如下的优势: 1、MaxCompute是一个无服务器的分布式计算架构,无需用户再额外维护和管理服务器基础设施,能方便及时的为OSS用户提供临时按需的查询服务,从而大大帮助企业节省成本。目前该功能处于公测阶段,免费使用; 2、支持处理OSS上开源格式的结构化文件,包括:Avro、CSV、ORC、Parquet、RCFile、RegexSerDe、SequenceFile和TextFile,同时支持gzip压缩格式; 3、提供灵活的用户自定义代码的处理框架,用来支持处理OSS上非结构化文件,用户可以自行编写代码直接对OSS上的数据进行处理和计算。比如对OSS上的视频,图像,音频,基因,气象等数据进行特征提取和分析,可以支持丰富的第三方音视频处理库; 二、 客户案例 1、 华大基因 基因技术从实验室逐渐进入生活场景,数据体量爆发式增长,远超出传统计算能力所能支持的范围。基于这样的背景,华大选择了MaxCompute。在百万人基因组项目中,对人群结构的分析,oss存放了大量的fastq文件,传统计算方式需3-5天,且需要将数据同步到数据仓库,现在通过外表功能,MaxCompute可使整个分析在1小时内完成,极大加速了数据吞吐和交付生产效率。 2、天弘基金 天弘基金旗下的余额宝,是中国规模最大的货币基金。除理财功能外,余额宝还是移动互联网时代的现金管理工具。余额宝每天有大量的金融数据交换文件存放在oss上,需要进行超大文本文件的结构化分析,之前是把oss文件先下载到本地,然后再上传到MaxCompute,链路长且效率不高。现在oss上的大文件可以直接用外部表的方式加载到MaxCompute做分析,整个链路的效率得到了大幅提升。 三、 如何使用MaxCompute? 下面我们通过两个简单的示例,介绍如何通过MaxCompute外表功能实现对OSS数据的分析和处理。 场景一:物联网采集数据分析 Step1 :准备工作 1、开通OSS 、MaxCompute服务 您可以通过官网分别开通OSS、 MaxCompute服务 ,并创建OSS bucket、 MaxCompute Project 。 2、采集数据到OSS 您可以使用任何数据集来执行测试,以验证我们在这篇文章中概述的最佳实践。 本文准备一批 CSV 数据存在 OSS 上,endpoint 为 oss-cn-beijing-internal.aliyuncs.com ,bucket 为 oss-odps-test ,数据文件的存放路径为 /demo/vehicle.csv 。 3、授权MaxCompute访问OSS MaxCompute需要直接访问OSS的数据,前提需要将OSS的数据相关权限赋给MaxCompute的访问账号,可以直接登录阿里云账号后, 点击此处完成一键授权 。 Step2 :通过MaxCompute创建外部表 创建外部表,语句如下: CREATE EXTERNAL TABLE IF NOT EXISTS ambulance_data_csv_external ( vehicleId int, recordId int, patientId int, calls int, locationLatitute double, locationLongtitue double, recordTime string, direction string ) STORED BY 'com.aliyun.odps.CsvStorageHandler' LOCATION 'oss://oss-cn-beijing-internal.aliyuncs.com/oss-odps-test/Demo/'; Step3 :通过MaxCompute查询外部表 外部表创建成功后,便可如同普通表一样使用这个外部表。假设/demo/vehicle.csv数据如下: 1. 1,1,51,1,46.81006,-92.08174,9/14/2014 0:00,S 2. 1,2,13,1,46.81006,-92.08174,9/14/2014 0:00,NE 3. 1,3,48,1,46.81006,-92.08174,9/14/2014 0:00,NE 4. 1,4,30,1,46.81006,-92.08174,9/14/2014 0:00,W 5. 1,5,47,1,46.81006,-92.08174,9/14/2014 0:00,S 6. 1,6,9,1,46.81006,-92.08174,9/14/2014 0:00,S 7. 1,7,53,1,46.81006,-92.08174,9/14/2014 0:00,N 8. 1,8,63,1,46.81006,-92.08174,9/14/2014 0:00,SW 9. 1,9,4,1,46.81006,-92.08174,9/14/2014 0:00,NE 10. 1,10,31,1,46.81006,-92.08174,9/14/2014 0:00,N 执行如下 SQL 语句: 1. select recordId, patientId, direction from ambulance_data_csv_external where patientId > 25; 输出结果如下: 1. +------------+------------+-----------+ 2. | recordId | patientId | direction | 3. +------------+------------+-----------+ 4. | 1 | 51 | S | 5. | 3 | 48 | NE | 6. | 4 | 30 | W | 7. | 5 | 47 | S | 8. | 7 | 53 | N | 9. | 8 | 63 | SW | 10. | 10 | 31 | N | 11. +------------+------------+-----------+ 关于更多详细的OSS外表使用方法,请参考官方文档, 点这里 。 场景二:阿里云产品消费账单分析 Step1 :准备工作 完成案例1中准备工作1、3步骤。 Step2 :通过费用中心同步账单数据到OSS 打开费用中心->消费记录-> 存储到OSS ,输入oss bucket,此示例为oms-yl , 服务开通后,每天会将增量的实例消费明细数据生成文件同步存储到您的OSS指定的bucket中。 Step3 :通过MaxCompute注册账单处理类 1、点击 这里下载, odps-udf-example-0.30.0-SNAPSHOT-jar-with-dependencies.jar 2、将自定义代码编译打包,并上传到 MaxCompute。 add jar odps-udf-example-0.30.0-SNAPSHOT-jar-with-dependencies.jar Step4 :通过MaxCompute创建外部表 示例:创建5月4日的账单消费表 CREATE EXTERNAL TABLE IF NOT EXISTS oms_oss_0504 ( 月份 string, 资源拥有者 string, 消费时间 string, 消费类型 string, 账单编号 string, 商品 string, 计费方式 string, 服务开始时间 string, 服务结束时间 string, 服务时长 string, 财务核算单元 string, 资源id string, 资源昵称 string, TAG string, 地域 string, 可用区 string, 公网ip string, 内网ip string, 资源配置 string, 原价 string, 优惠金额 string, 应付金额 string, 计费项1 string, 使用量1 string, 资源包扣除1 string, 原价1 string , 应付金额1 string, 计费项2 string, 使用量2 string, 资源包扣除2 string, 原价2 string, 应付金额2 string, 计费项3 string, 使用量3 string, 资源包扣除3 string, 原价3 string, 应付金额3 string, 计费项4 string, 使用量4 string, 资源包扣除4 string, 原价4 string, 应付金额4 string, 计费项5 string, 使用量5 string, 资源包扣除5 string, 原价5 string, 应付金额5 string, 计费项6 string, 使用量6 string, 资源包扣除6 string, 原价6 string, 应付金额6 string, 计费项7 string, 使用量7 string, 资源包扣除7 string, 原价7 string, 应付金额7 string, 计费项8 string, 使用量8 string, 资源包扣除8 string, 原价8 string, 应付金额8 string, 计费项9 string, 使用量9 string, 资源包扣除9 string, 原价9 string, 应付金额9 string ) STORED BY 'com.aliyun.odps.udf.example.text.TextStorageHandler' --STORED BY 指定自定义 StorageHandler 的类名。 with SERDEPROPERTIES ( 'odps.text.option.complex.text.enabled'='true', 'odps.text.option.strict.mode'='false' --遇到列数不一致的情况不会抛异常,如果实际列数少于schema列数,将所有列按顺序匹配,剩下的不足的列补NULL ) LOCATION 'oss://oss-cn-beijing-internal.aliyuncs.com/oms-yl/2018-05-04/' USING 'text_oss.jar'; --同时需要指定账单中的文本处理类定义所在的 jar 包 Step5 :通过MaxCompute查询外部表 查询示例:查询MaxCompute按量存储消费账单 select 月份,使用量3,原价3,应付金额3 from oms_oss where 计费项3='Storage' and 商品=大数据计算服务MaxCompute(按量付费); 输出结果如下: 四、 总结 通过上述示例,将沉睡在OSS中的非结构化数据激活,通过MaxCompute把海量数据分析工作效率提升至分钟级,帮助客户更高效、更低成本的挖掘海量数据价值。 原文链接 本文为云栖社区原创内容,未经允许不得转载
来源:OSCHINA
发布时间:2018-05-16 20:24:00
盘点云计算的优势,较低的托管成本、较低的基础架构复杂性、较高的可扩展性……这些都是实实在在的好处。不过对于企业来说,选择云计算最关键的驱动在于产品速度,换句话说,利用适当的云计算产品和技术,我们可以在最短时间内把理念变成用户需要的实际产品。 过去三年里,有大量的企业投入到了容器化架构的怀抱之中,从踩坑到克服未知的难题,再到得心应手,一部分公司逐渐尝到了“产品速度”带来的甜头。 但也有另一部分企业,容器运行测试、扩大测试范围、得到管理层批准、全面迁移上云,并且相信容器编排工具Kubernetes会处理好所有的事情,然后在一切似乎顺利进展的过程中,莫名其妙陷入了与指数级增长的复杂度抗争的漩涡之中——监控、存储、处理不同组件、定义通信、网络安全……进退两难。 问题到底出在哪呢?以下六点,抛砖引玉。 第一:基础设施 企业一般会在IaaS厂商那里买几台机器,希望通过将基础设施迁移到云端来达到马上省钱的目的,却发现成本非但没有降低,反而可能有些上升,于是开始反思是不是哪里出了问题。 出现这种情况其实是正常的,首先通过不断的调优,资源方面的节省才能显现出来;其次云计算的核心更多在于低风险和敏捷性,而不完全是“可见的成本“。 想象我们用过去普遍的方式为数据中心购买硬件,很可能会买到错误的大小或者错误的机器,此时我们应该拿这些机器怎么办呢?另一方面,把应用调整到一个不适合的环境中本身是一个耗时费力的过程,这方面的成本怎么算? 选择云计算的话,以上问题迎刃而解,原本购买基础设施的风险要小得多,配置环境的时间和人力成本同样大大降低,也就是说我们产品速度上的成本降低了。对于开发者,是花两年时间来做对业务有直接价值的工作,还是与错误的环境斗争到底,答案是很明显的。 所以第一个经验是,首先思考如何降低风险,而不是降低硬件成本。 第二:自动化 过去的经验告诉我们,私有云也可以实现非常高的自动化水平,一切取决于企业是否选择了正确的服务和工具,像自动化部署、CI/CD、自动化配置、伸缩等等。 在这一方面需要注意的问题主要集中在落地模式和建设规划两方面。落地模式上,企业需要按照自身情况来做出选择,是使用定制化产品,还是在开源框架的基础上自己进行开发;建设规划上,企业的自动化过程往往不是一蹴而就的,需要一个循序渐进的过程,前期的IT架构梳理极为关键。 第三:文化 在技术向云计算转变的同时,企业文化也应该随之而变。传统的模式下,企业规避风险即不惜一切代价将不确定性最小化,这导致很多企业IT拒绝进行改变,也就逐渐在竞争中落了下风,而这也是部分企业在云计算技术方面实施后,发现并没有解决问题,反而产生了新问题的原因所在。 正确的做法是应用一种“实验“的文化,通过分析、规划、评估和测试来尝试不同的方法,根据结果来进行调整,随时准备好迭代、准备好回滚、准备好再一次尝试。 这也就是为什么DevOps被公认为是一种文化,也是IT转型的重要途径。当然文化本身的转变也不是一朝一夕的事情,需要从早期开始落实,更需要持续不断的影响和强调。 第四:微服务 关于微服务架构的定义、优势等等不必多言,从spring cloud、dubbo到最新的service mesh,这些年大红大紫的微服务架构,真正能够拿出来作为可实践案例的不多。 除了对于技术栈的选择困难,很多企业的误区在于“一步到位”的错觉,导致企业本身将最初的门槛设立极高,而正常的过程是应当以最小代价发布第一个微服务,在跨过这个门槛之后,剩下的工作可以被复制而且速度会越来越快。 另一个教训,其实不止是微服务架构方面,就是尽量不要重复造轮子,参考或者选用成熟的解决方案永远会是最便捷的落地途径。 第五:容器化 Docker是容器化最突出的技术代表。利用Docker构建一个容器,包含一个微服务所需的所有依赖,而后部署在任何想要部署的地方。 而在最近,docker受到了一些批评和质疑,我们认为原因在于很多企业对docker的期望有些不切实际,而忽略了容器化同时需要考虑的调度编排、存储网络、服务注册发现以及宿主机管理等等紧密联系的技术问题。 另一方面,我们要知道Docker其实并不是一项新技术,它的创新更多是在思想层面,在它对于封装、隔离、发布的改进。 第六:编排 真正用于生产的应用往往会跨越多个容器,往往会部署在多台机器上,会涉及到如服务发现、负载均衡、高可用、存储、网络等等。目前容器编排事实上的标准是Kubernetes。 关于Kubernetes,难点在于这一容器编排工具本身包含很多高难度技术概念,学习门槛很高、学习曲线很陡峭,同时缺少管理流程。所以这里容易出现的是“用不起来“的问题,解决办法除了活学活用,还可以考虑使用第三方包装好的Kubernetes面板和解决方案。 好雨,让云落地,提供以应用为中心的云计算产品和服务。访问 https://www.goodrain.com 了解更多
来源:OSCHINA
发布时间:2018-05-16 13:07:00
Rancher 2.0正式版已全面发布。Rancher 2.0是一个开源的Kubernetes管理平台,为企业用户提供Kubernetes-as-a-Service (Kubernetes即服务),并且能够实现多Kubernetes集群的统一纳管。这一创造性的统一纳管功能将解决生产环境中企业用户可能面临的基础设施不同的困境。Rancher 2.0是业界第一个能统一纳管来自Google(GKE)、Amazon(EKS)和Azure(AKS)等公有云上托管的Kubernetes服务的平台。 在Rancher 2.0中,我们重点关注的一个领域就是身份认证和授权。在Kubernetes强大的基础功能之外,Rancher 2.0格外专注于简易性和易用性,它是一个既强大又易于使用的系统。Rancher 2.0让管理员能够管理多集群环境,同时还能够帮助用户快速启动并运行环境。本文将从身份认证和授权的角度,介绍Rancher能够给组织、管理员和用户带来哪些好处。 在深入讨论Rancher能带来什么之前,我们将先在本文前半部分简要回顾一下Kubernetes身份认证与授权相关的概念。如果想深入了解这些概念的更多细节,可参考Kubernetes官方的文档: https://kubernetes.io/docs/admin/authentication/ https://kubernetes.io/docs/admin/authorization/rbac/ 身份认证 想要理解Kubernetes的身份认证以及Rancher如何对这一功能进行拓展加强,那么就必须要先理解下面这几个关键的概念:身份认证策略、用户和组,以及用户模拟。 身份认证策略(Authentication Strategies) Kubernetes提供了多种身份认证策略,包括:客户端证书、OpenID Connect令牌、Webhook令牌认证、身份认证代理、服务账户令牌等等。每一种策略都有它的优缺点,但最终它们都要负责判断申请API调用的用户的身份,这样Kubernetes RBAC框架才可以决定是否要授权给申请调用者,让其执行其请求的操作。 尽管已经有大量可用的策略能解决大多数情况,但需要注意的是,配置它们需要精确控制Kubernetes控制平台的配置和部署。像Google这样的云服务提供商通常会将其锁定,防止用户按照自己的喜好配置它。而Rancher 2.0解决了这个问题,我们会在后面讨论。 用户和组(Users and Groups) Kubernetes有两种类型的用户:服务账户和普通用户。其中服务账户完全由Kubernetes管理,而“普通”用户则完全不受Kubernetes的管理。事实上,Kubernetes没有用户或组的API资源。因此最终,普通用户和组在用户绑定中表现为晦涩的对象,用以做权限的检查。 用户模拟(User Impersonation) Kubernetes中的用户模拟是一个用户(或服务账户)扮演另一个用户的能力。一个subject必须明确地具有“模拟”特权,才能够执行对其他用户的模拟。虽然这可能看起来是一个相当模糊和细微的功能,但它对于Rancher如何实现身份验证至关重要。 授 权 要理解Kubernetes中的授权以及Rancher如何构建它,还必须理解这些概念:roles(角色)、clusterRoles(集群角色)、roleBindings(角色绑定)和clusterRoleBindings(集群角色绑定)。从命名就能看出,这些概念之间非常相似,但适用于不同的范围。 roles是命名空间的一个作用域,这意味着它是在命名空间中创建的,并且只能由该命名空间内的roleBinding引用。roleBinding在用户、组或者服务账户(在Kubernetes中称为subject)和命名空间中的role之间创建关联。它有效地说明了用户 X在命名空间Z中具有Y角色,或者我们给一个具体的例子:Sarah能够在“dev”这个命名空间中进行部署的创建、更新和删除。 clusterRole的样子和作用方面与role非常相似。唯一的区别是它没有命名空间。clusterRole是在集群层面定义的。同样的,clusterRoleBinding是roleBinding的无命名空间版本。当你创建clusterRoleBinding时,意味着你为特定的subject赋予了可用于整个集群、每个命名空间的权限。 需要注意的是:roleBinding可以引用role或者clusterRole。无论它引用的role类型是什么,权限只适用于rolebinding所在的命名空间。 有了对Kubernetes基础概念的理解,我们接下来可以开始讨论Rancher是如何使用和增强它们,创建出强大且易于使用的身份认证和授权系统的。 Rancher的身份认证和授权 Rancher 2.0的主要目标之一,是帮助系统管理员运行多个异构的Kubernetes集群。这些集群可以是来自于云提供商或本地解决方案的任何组合,这就产生了许多有趣的身份认证和授权挑战。其中我们确定并解决的关键问题是: 如何在不同类型的集群中拥有统一的身份验证体验? 如何管理跨集群的用户和权限? 如何启用“自动服务”方式使用集群,同时保持适当的控制水平? 如何防止用户在低信任环境中获得对底层基础设施资源的过多访问? 每一种挑战我们接下来都会讨论。 统一认证 为了实现跨集群的统一身份认证体验,我们将Rancher服务器设计成所有身份验证的中心点。管理员只需在Rancher中配置一次身份认证工具,它就可以应用到任何地方。之后,在所有访问Kubernetes集群的请求面前,Rancher都相当于一个身份验证代理。 由于大多数云提供商不公开必要的hooks用来插入Kubernetes的各种认证策略,因此Rancher的身份验证代理位于外部,独立于集群而存在。它执行必要的身份认证工作,收集用户的身份和任何的组,然后通过用户模拟将请求转发到适当的集群,以充当该用户。正因为认证方法是标准的Kubernetes无记号令牌,因此Rancher的代理可以无缝地插入kubectl等现有的Kubernetes工具中。 用户管理 正如之前所说,Kubernetes没有一等用户的理念,而Rancher有。用户可以由管理员手动创建,也可以在GitHub等身份认证工具那里按需创建(Github在头一次打开时需要用户登录)。我们从Rancher 1.x中吸取了经验教训,在默认情况下,本地身份认证是开启且始终开启的。这样以来,Rancher在默认情况下是安全的,并且在身份认证工具出现故障时提供了访问Rancher的备份机制。 创建一个驻留在中央Rancher服务器上的一等用户资源可以带来很多好处。例如,管理员现在可以查看和操作任何特定用户对所有集群的访问权限。它还使Rancher能够管理特定于每个用户的资源,如系统首选项、API令牌和节点模板。最后,它使得管理用户权限变得更简单,我们将在下文讨论。 RBAC 授权 在深入讨论授权之前,我们必须先介绍和讨论一个关键的Rancher概念:项目。项目是可以应用于各种策略的命名空间的集合。这些策略(并非所有的策略都进入了我们的初始GA版本)包括RBAC、网络访问、pod安全性和配额管理。项目“拥有”命名空间,以及为项目所做的任何RBAC绑定都适用于项目中的所有命名空间。这个关键概念允许将集群有效地分割和组织成更小、更易于管理的块(chunks)。 Rancher有效地为用户提供了三层roles或权限:全局、集群和项目层级。全局定义了你可以在单个集群之外执行的操作。对于大多数人来说,这可以认为是将用户或组的子集标记为“管理员”,其余部分标记为“普通”用户。除了可以完全访问所有集群外,管理员还可以执行配置身份验证提供者和管理用户等操作。而普通用户只能访问他们拥有或已被邀请的集群或项目。 Rancher RBAC直接建立在Kubernetes RBAC之上(前面讨论的role和binding概念)。如果你了解Kubernetes的概念,Rancher RBAC就很容易理解。实际上,我们在Rancher服务器中创建roles和bindings模板,并将它们传播到适当的集群。因此,我们在Rancher API中有以下自定义资源:roleTemplates,clusterRoleTemplateBindings以及projectRoleTemplateBindings。管理员可以管理roleTemplates和集群,而项目所有者可以使用它们授予对其集群或项目不同程度的访问权限。 自助服务访问 Rancher默认支持自助服务访问模式,帮助组织授权用户从Kubernetes获得更多信息。普通用户可以创建自己的集群并成为其所有者。他们是该集群的管理员,可以将其他用户和组设成集群成员,授予他们访问权限。一旦用户成为了集群成员,它就可以在集群中创建项目并成为这些项目的所有者。作为项目所有者,可以邀请其他人称为项目成员或所有者。项目成员能够在他们所属的项目中创建命名空间并部署工作负载。你可以看到,这个自助服务系统是如何创建的,并让用户能够快速且轻松地启动和运行。 而这种方式下,也有常见的问题:“如果我不想让用户创建集群或项目,该怎么办?” 这一问题有几个答案。首先,如果他们不能访问基础设施资源(意味着他们无法创建虚拟机或者没有组织云提供商的密钥),那么他们无法创建功能集群。其次,我们的RBAC系统是可配置的,这样管理员可以在默认情况下明确地选择用户可以做什么。最后,用户可以直接被添加到项目中,而不需要创建明确的集群成员。这意味着他们将不能创建新的项目,而只能使用那些他们被明确添加进去的项目。通过这种方式,Rancher使组织能够授权它们的用户,同时给予管理员他们所需要的控制。 控制基础设施层级的访问 许多用例会要求用户限制他们可以部署的容器类型以及这些容器允许执行的内容。为了解决这个问题,Kubernetes搬出了podSecurityPolicies。这是一个非常重要的功能,但它的原始形式却很难正确使用。关于它是如何工作的,以及他能做什么,这些讨论操出了本文的范围,但我们可以这么总结它:podSecurityPolicies允许管理员限制可以部署在集群中的pod类型。用一个简单和容易理解的例子来说就是,它可以防止用户部署特权容器,这为许多用例解决了大的安全漏洞。 Rancher不仅支持podSecurityPolicies,而且增强了该功能,大大提高了可用性。使用Rancher,管理员可以在全局定义一组用于所有集群的podSecurityPolicy模板。然后,集群所有者可以将默认策略分配给集群,并在每个项目基础上管理例外情况。换句话说,集群所有者可以说:“除了少数特殊项目外,所有项目都有一个限制策略,阻止他们部署特权容器。”此功能可用于安全的多租户集群。 总 结 通过本文,希望你能看到我们在Rancher 2.0中对身份验证和授权的关注。所有这一切都建立在Kubernetes基本概念的基础之上。秉承Rancher一贯关注可用性及简单性的原则,Rancher 2.0对Kubernetes身份认证和授权进行了更多增强和扩展,以创建出更加强大的组合,帮助企业用户更简单快捷落地Kubernetes。
来源:OSCHINA
发布时间:2018-05-16 10:26:00
安装 chart 当我们觉得准备就绪,就可以安装 chart,Helm 支持四种安装方法: 安装仓库中的 chart,例如: helm install stable/nginx 通过 tar 包安装,例如: helm install ./nginx-1.2.3.tgz 通过 chart 本地目录安装,例如: helm install ./nginx 通过 URL 安装,例如: helm install https://example.com/charts/nginx-1.2.3.tgz 这里我们使用本地目录安装: 当 chart 部署到 Kubernetes 集群,便可以对其进行更为全面的测试。 将 chart 添加到仓库 chart 通过测试后可以将其添加到仓库,团队其他成员就能够使用。任何 HTTP Server 都可以用作 chart 仓库,下面演示在 k8s-node1 192.168.56.106 上搭建仓库。 在 k8s-node1 上启动一个 httpd 容器。 通过 helm package 将 mychart 打包。 执行 helm repo index 生成仓库的 index 文件。 Helm 会扫描 myrepo 目录中的所有 tgz 包并生成 index.yaml 。 --url 指定的是新仓库的访问路径。新生成的 index.yaml 记录了当前仓库中所有 chart 的信息: 当前只有 mychart 这一个 chart。 将 mychart-0.1.0.tgz 和 index.yaml 上传到 k8s-node1 的 /var/www/charts 目录。 通过 helm repo add 将新仓库添加到 Helm。 仓库命名为 newrepo ,Helm 会从仓库下载 index.yaml。 现在已经可以 repo search 到 mychart 了。 除了 newrepo/mychart ,这里还有一个 local/mychart 。这是因为在执行第 2 步打包操作的同时, mychart 也被同步到了 local 的仓库。 已经可以直接从新仓库安装 mychart 了。 如果以后仓库添加了新的 chart,需要用 helm repo update 更新本地的 index。 这个操作相当于 Ubutun 的 apt-get update 。 小结 本章我们学习了 Kubernetes 包管理器 Helm。 Helm 让我们能够像 apt 管理 deb 包那样安装、部署、升级和删除容器化应用。 Helm 由客户端和 Tiller 服务器组成。客户端负责管理 chart,服务器负责管理 release。 chart 是 Helm 的应用打包格式,它由一组文件和目录构成。其中最重要的是模板,模板中定义了 Kubernetes 各类资源的配置信息,Helm 在部署时通过 values.yaml 实例化模板。 Helm 允许用户开发自己的 chart,并为用户提供了调试工具。用户可以搭建自己的 chart 仓库,在团队中共享 chart。 Helm 帮助用户在 Kubernetes 上高效地运行和管理微服务架构应用,Helm 非常重要。 下节我们开始学习 Kubernetes 网络。 书籍: 1.《每天5分钟玩转Kubernetes》 https://item.jd.com/26225745440.html 2.《每天5分钟玩转Docker容器技术》 https://item.jd.com/16936307278.html 3.《每天5分钟玩转OpenStack》 https://item.jd.com/12086376.html
来源:OSCHINA
发布时间:2018-05-16 07:06:00
本文作者:AIOps智能运维 作者简介 凌薇 百度云智能运维业务研发负责人 负责百度云Noah自动化运维平台和智能运维解决方案的探索,在服务管理、资源管理、变更管理和故障管理的业务分析和设计方面经验丰富,致力于推进AIOps在百度业务、公有云以及私有云客户的运维场景落地。 为什么要写这篇文章 做了这么多年项目,参加过无数次团队内外的项目复盘,发现不少 项目延期 和客户 交付质量 的问题。这些问题给产品和技术负责人带来了不少应急“救火”的困扰。分析这些Case后,发现问题集中在以下几个方面: 需求界定不清晰、系统设计有缺陷、研发质量无保障、无效沟通耗时长,导致项目反复并且严重延期; 跨团队协作推动成本高,多团队交付进度出现Delay,项目整体目标不可控; 概要设计文档、API手册、产品使用手册和运维手册质量差,客户学习成本高; 我们团队通常会使用 项目复盘 (Case Study)的方法来应对这些情况。复盘主要为了解决以下两个问题:其一, 为项目延期和客户交付风险找到可行的解决方法 ;其二, 给团队成员一些指导,避免同一个问题重复出现 。当然,这些复盘工作一般在某个项目组内部开展,需要一种方式能够在多个项目组之间共享,这便是我写此文章的原因。 项目管理和研发质量控制是一个比较复杂的系统工程,本文不会系统的讲解一些理论和原则,而是以实际案例为索引分享一下项目管理中常见的问题以及必须要采取的方法。前车之覆,后车之鉴,希望能对新晋的项目管理同学有些帮助。 案例一:多角色人员协作的业务项目 场景描述 某监控产品需要对多个Region的多个云服务(例如虚机、数据库组件、缓存组件、消息队列组件)提供多个指标趋势图对比查看功能。产品研发需要PM设计产品形态和逻辑,UE设计产品视觉交互,若干FE研发前端页面,若干RD研发后端业务逻辑,QA测试业务功能,测试通过后FE和RD联合上线。项目研发从预期的1个半月拖延至实际3个月。研发中经历至少5轮测试,发现2个产品缺陷,5个技术方案缺陷,低级Bug预估20+,Bug收敛速度极慢。这是一个极其典型的项目管理和研发质量失控案例,参与项目的多数是新同学, 研发流程规范 上认知度严重欠缺。 为了便于大家对项目过程的理解,附注一下相关的产品设计、接口设计和低级Bug例子: 产品设计缺陷 :PM产品设计时遗漏在趋势图上标注不同Region的云服务名字,导致用户无法定位指标所归属的云服务实例。 接口设计缺陷 :产品要求一个趋势图最多支持30个云服务实例的指标对比,前端FE接口参数检验限定为20个,后端RD接口参数校验限定为10个;趋势图指标数据查询无论用户选择的时间段多长,指标查询的粒度都是60s,导致在时间跨度很大的情况下,返回数据点过多页面渲染性能差。 低级Bug :选择实例和监控项之后可以查看趋势图,但是取消监控项选择之后趋势图未消失;时间选择框对趋势图展示的指标数据的时间段控制作用失效。 关键问题 产品设计和接口设计缺陷应该是在什么阶段发现? 低级Bug为什么那么多?Bug收敛速度为什么那么慢? 项目出现延期风险时,项目负责人应该在什么阶段管控风险? 解决方案 这个项目的关键是没有严格遵循常规的软件研发流程规范。 PRD没有经过评审 ,PM简单画了几个原型图开始安排前端FE和业务RD开发,导致产品缺陷没有在PRD评审阶段发现; 前端FE和后端RD的接口设计 没有完备的文档 ,没有经过充分的沟通;RD提测代码时没有经过整体场景的联调和Demo Review,导致低级Bug在测试阶段才暴露出来; QA的 测试准入 没有严格执行,低级Bug多的情况下,QA未实施测试打回; 技术负责人没有在项目即将延期时进行 问题复盘 ,而是在延期两周后才跟进问题,错过了关键的 项目修复时间 ,增加了很多不必要的多人协作成本。 这个案例在很多 新项目新团队成员 中比较常见。对于多角色协作的项目需要执行严格的研发流程规范,需求相关的MRD,产品设计PRD,UE设计稿、总体设计和详细设计文档都需按照规范撰写并且经过 团队评审 ,只有前一个环节通过才能进入下一个环节。尽早交流和持续沟通使开发人员获得对产品和业务的信息,始终遵守“ 及早发现,及早解决 ”的准则,如此我们便能在软件研发过程中价值最低的阶段修正问题。RD在交付QA之前需要进行 系统联调 和 Demo Review ,确保研发和产品设计保持一致。研发质量需要符合 编码规范 ,并且有 单测指标 ,单测的行覆盖率和分支覆盖率不达标QA可以拒绝测试。QA需要严格执行测试准入,对于低级Bug多的同学不仅拒绝测试,并且团队内公示项目中每个同学的 代码质量 。 项目管理者需要执行严格的研发流程规范,需要合理的安排项目的进度。通常的经验是为 1/3计划、1/6编码、1/4构件测试以及1/4系统测试 ,所以一定不要在前期的设计评审阶段和后期的联调单测阶段节省时间,不然会使得项目风险频发,脱离控制。任何创造性活动都伴随着枯燥艰苦的劳动,编程也不例外。大家通常期望项目在接近结束时,软件项目能收敛的快一些,然而,情况却是越接近完成,收敛的越慢。一个风险问题的暴露,一个里程碑的进度偏离,最终会累积使得整体进度偏离很远。慢性进度偏离是士气杀手,及早的问题复盘,持续的信息同步有助于将脱轨的项目拉回到正常的轨道。 案例二:多团队联合解决方案实施 场景描述 部门成立一个近20个团队的 联合项目 ,实施核心业务线的SCAR(项目代号)。该项目的主要目标是结合多个平台系统提供的能力,组合成一个复杂解决方案,帮助业务提升能力。项目的周期是一年,多个平台研发团队和十几个业务团队需要完成该解决方案的研发和业务落地。项目实施中的初期发现平台研发符合预期,若干个业务团队没有承诺明确的落地时间,或者承诺的时间因为团队的其他项目影响落后于项目预期。联合团队采取了 紧急沟通 ,实施了一些 双重汇报机制和指标管控方法 ,保障了项目如期交付。这个项目成功落地业务线取得了非常好的效果,作为成功案例在多个团队做项目管理分享。 关键问题 如何确保多团队目标的一致性? 如何跟进项目及时发现进度风险? 解决方案 多团队协作的一个重要问题是每个团队都有各自的 重点项目指标 ,SCAR项目只是其中的一个,也可能不是其重点项目,各个团队投入的关注度和资源不一定充分。在这种情况下,需要成立横向联合的 虚拟团队 ,在多个团队的经理层面明确项目目标,使得该目标变成经理自身考核KPI中的一项,并且每个团队还需要抽取出一名成员作为接口人参与联合项目。虚拟团队实施双重汇报机制,团队成员除了向各自的经理汇报以外,还需要向横向联合团队的负责人汇报,团队成员的年底绩效考核时,横向联合团队的负责人也会给出一份评价。这种机制可以确保各个团队的项目经理对项目的 支持度 ,以及跨团队的成员在项目中有足够的投入和友好的协作。 因为涉及的团队比较多,多个业务团队的落地进展对横向团队负责人来说是一个黑箱。横向联合团队负责人需要设定相应的指标监督项目进度和识别项目风险。关键的指标包括以下三类: 先行指标(Leading Indicator) :项目启动之初建立该项指标,明确到项目结束时SCAR系统具备的能力以及在业务线实施的覆盖度,通过这些指标可以引导项目负责人关注黑箱中该注意的事情。 线性指标(Linearity Indicator) :为了达到目标设定的理想进度指标,可以理解为项目分季度分月的KPI指标,比如系统研发的进度,比如业务线实施覆盖度。以业务线实施覆盖度为例,可能14个业务线,第一个季度实施4个业务线,后面的两个季度每个季度实施5个业务线。设置线性目标是为了指导 项目分阶段 的进度,避免因为项目时间跨度过长项目风险整体不可控。 趋势指标(Trend Indicator) :以时间标准为基础,根据对过去的观察,从趋势中预测未来。例如,项目的初期系统易用性较差,业务落地的成本高,前期的业务实施覆盖度指标有可能落后于一开始设置的线性指标。经过一段时间的系统优化,业务落地的成本变低了,后期的业务实施覆盖度指标有可能快于一开始设置的线性指标。项目负责人需要周期性Review项目的趋势指标,及时 协调资源 ,调整项目的进度和 把控风险 。 通过虚拟团队的双重汇报机制,通过设定项目的先行指标和线性指标,周期性Review趋势指标,可以帮助项目负责人在多团队协作项目中能够比较好识别项目风险和调度资源解决问题。 案例三:ToB客户验收项目 场景描述 团队承接了某个客户的平台研发,需要交付时提供完备的系统概要设计文档、用户手册和标准运维流程手册给客户。项目交付前期团队内部抽查了部分文档,发现一些文档内容的 完备性、可读性和可操作性欠缺 ,急需规范和优化。为了保障对客户高质量的输出,团队陷入紧急的文档优化过程,很多RD和PM进入了加班“救火”状态。在ToB项目中,每一份文档都代表着对客户的承诺和服务意识,代表着一个团队的技术素养,需要认真对待。 关键问题 什么阶段该写文档?一个好的文档应该具备什么样的特征? 团队经理和项目负责人如何保障文档质量? 解决方案 概要设计文档需要在项目设计阶段产出并且评审通过,而不是在项目交付阶段进行补充;接口设计需要在研发之前完备并且经过评审;用户手册需要在实施客户培训前完备,具备良好的易读性,客户学习成本低;运维巡检和故障处理SOP手册需要在交付客户运维之前完备,并且经过演练执行。 一个团队应该有确定的 可执行文档规范 ,而不是每个项目每个团队成员想当然的自行发挥才华,交付质量参差不齐。对每个文档的维护也提供了项目的状态监督和预警机制,文档使各项计划和决策在整个团队范围内得到交流,也便于及时发现项目的问题。 概要设计文档 需要明确项目的背景、名字解释、功能概述、性能指标、依赖的软硬件环境、系统的总体架构、系统核心业务模型、系统各模块交互的数据关系、各模块的功能概述、各模块依赖第三方服务的接口说明。 HTTP Restful风格的 接口设计文档 需要明确面向资源的HTTP URL设计方法、HTTP Method使用说明、HTTP Status Code使用说明、接口鉴权方法,接口输入和返回的各种参数说明、接口返回系统错误码的明确含义等。 用户使用手册 需要场景化,有截图,需要明确给用户标识出错误使用的风险。 运维巡检和故障处理手册 需要步骤清晰可执行。 文档规范的执行效果需要有质量控制方法,不然文档规范就成了摆设。项目负责人常用的方法可以称之为“ 海关与监视器 ”,团队经理常用的质量控制方法是随机检验。 “ 海关 ”是指我们先设下 重重关卡 ,文档唯有通过检验之后才能进入下一步的研发流程,如果文档无法通过评审,便被打回重做或者是废弃。概要设计文档和接口设计文档应该使用此方法。 “ 监视器 ”是指我们可以将不满足质量检测的文档内容标识上 记号 ,这个文档将不会在流程中的各个关卡被截住,整个流程将会变得顺畅,在这种检测中,有问题的地方超过一定的量,则需要立即修订。对于用户手册和运维巡检手册中场景的覆盖度问题可以视情况采用“监视器”的方法进行多轮迭代。 随机检验 就是随机抽查。因为项目很多,不同项目负责人对文档把控的严格程度也会有偏差,所以对于团队经理来说,逐个文档检查的成本非常高,改变检验的频率也就理所当然。如果一个经理对他的下属事必躬亲,就可能造成干预,而且将会浪费更多的时间在不会出错的下属上。更糟糕的是,他的下属可能因此会形成 依赖性 ——反正什么事情老板最后都会检查。随机检验应用在管理上,既可以增加项目负责人的责任感,又可以节省经理时间。 不管使用上述哪种文档质量检查方法都是在培养团队的文档质量意识,因为交付给客户每一项质量差的文档都可能会导致客户的流失,也会影响团队口碑和产品品牌。 寄 语 以上是对几个典型项目场景下遇到问题的复盘思考,这些案例给我们的启示如下: 多角色人员协作的业务项目: 严格遵守软件研发流程&及早发现问题及早解决 ; 多团队联合解决方案实施: 建立双重汇报机制&设定并且盯好业务指标 ; ToB客户验收项目: 珍惜客户&重视团队文档交付质量 ; 希望这些分享可以给新晋的项目管理负责人一些参考,避免一些不必要的弯路。后续依然会持续关注团队的项目实施和分析,欢迎更多有兴趣的同学一起讨论和分享。 原文链接地址: https://developer.baidu.com/topic/show/290334
来源:OSCHINA
发布时间:2019-09-12 17:32:00
本文作者:AIOps智能运维 作者简介 芃熙 百度云高级研发工程师 负责百度云Noah监控产品报警系统的设计和研发,在大规模分布式系统、监控、运维on-call方面具有广泛的实践经验。 干货概览 百度云的Noah监控系统( Argus )是保障百度内外服务高可用的基石。它具有诸如机器监控、实例监控、HTTP监控、域名监控、日志监控、自定义监控等多种监控手段,具备“海陆空” 全方位的监控能力 ,让服务异常无处遁形。如果你看过本公众号之前的系列文章,相信你会觉得我所言非虚。然而如此强大的监控系统所产生的“辣么多”报警如果不能及时精准地送达给运维人员,那么一切都还只是个传说。今天我们就聊聊报警如何送达的问题,注意,我们今天不谈报警,我们只谈报警的搬运工—— 百度云Noah通告平台 ! 一个都不能少 报警不同于普通的通知,它反映的是线上服务即将或正在遭受损失,如果我们把核心报警搞丢了,造成线上故障得不到及时解决,这个责任是巨大的。由于报警系统天然就这样要求高可靠性,因此我们奉行“ at-least-once ”的投递原则,确保报警至少有一次能成功抵达用户,做到“ 该报的 报警一个都不能少 ”。而为了实现这个目标我们经历过不少坑: 机房网络连通性问题 我们发送报警要依赖四个底层发送网关( 电话网关、短信网关、IM网关、邮件网关 )来向用户发送消息,如下图所示。由于公司网络环境的原因,这些网关部署在某些特定机房,和上游的监控系统部署在不同机房中,这样机房间的网络拥塞或抖动将直接影响报警发送。解决这种问题,可以将底层发送网关主备部署到不同的机房,由上游系统重试解决。也可以考虑建设额外的网络路由通路,例如机房A到B不通时,绕经机房C “曲线救国”。具体选择何种策略,要依据不同的网络现状而定。 限流 资源永远是有限的。对我们来说底层网关的发送能力是瓶颈所在,尤其是电话网关,线路资源非常宝贵,有明确的 路数限制 (例如100路)。这样当某业务的报警量很大时,它的报警将用光有限的发送资源,将导致其他业务的重要报警发送延迟甚至失败。因此,需要对不同业务的报警量进行限流,避免单业务报警量过大影响其他业务。 报警追查 监控系统中关于如何报警有多种配置,例如报警最大次数、报警允许等待时长、报警屏蔽等,这些配置都会影响报警发送行为。经常有用户反馈报警 不符合预期 ,例如“该收的报警没有收到,不该收的报警却收到了”等这种咨询问题,其实往往都是由于报警配置导致的,并非系统功能不正常,因此我们面临很多报警追查的需求。为此,我们将报警从产生到最终发送整个生命周期中的处理历史都记录下来,让用户像查询快递物流信息一样去追查报警处理历史。我的工作是不是真的很像快递搬运工(偷笑.jpg)! 若报警只如初见 随着业务的发展,我们发现报警量越来越大。通告平台每天都面临百万级的报警量,而这些报警中却有大量相似、重复的 冗余报警 。导致核心报警淹没在大量冗余报警中,极易造成 报警遗漏 。如果遇到骨干网拥塞或数据中心故障,那报警量就需要再加几个数量级,这就是传说中的报警风暴,风暴期间运维人员的手机、邮箱迅速会“爆”掉,据说以前有人根据报警短信的响铃频率来判断故障是否恢复,不管这是真事还是笑谈,他的囧境可见一斑。 因此, 报警收敛 成为了监控报警领域面临的一个共同命题。目前,业界一般的策略是分析报警内容,按照相同关键字进行报警合并。这种策略往往效果很差,因为事实上很多关联报警的内容本身并不包含相同关键字。针对这一问题我们逐渐演化出两类策略: 分维度报警合并策略 ,即按照报警维度属性(机房、机器、实例、服务等)合并。 基于关联挖掘的合并策略 ,即采用离线数据挖掘或机器学习的方式,从历史报警中挖掘出具有关联关系的监控策略,然后将相关联监控策略下的报警进行合并。 经过以上的合并策略后,我们的报警量减少了 九成以上 ,有效地减少了冗余报警对运维人员的干扰。对报警 合并算法 感兴趣的朋友可以参考我们的之前的专题文章《我在百度对抗报警风暴》详细了解呦! 优雅的外表 经过合并后的多个报警,如何友好展示是另一个随之而来的问题。如果把原始报警内容堆叠到一起展示的话,用户将很难理解。毫无疑问,需要对这些原始报警的内容进行抽象概括以友好显示。我们将这一过程称之为“ 报警渲 染 ”。举个例子,在一个服务集群下配置监控策略Rule_A,该策略下在一个短期时间窗口内共有110条报警, 如图所示: 经过报警合并,最终渲染为一条报警,展示如下: 从最终的报警内容中,运维人员可以快速了解报警的严重程度、触发报警的监控策略、影响范围、报警时间等信息。我们针对不同的合并策略分别有不同的渲染模版,目的就是让运维人员能快速准确地获取到报警信息。另外,对于 电话报警 ,渲染逻辑要更加复杂些,因为报警是以语音的形式触达用户的,受TTS技术所限,很难把形如“pr-nginx_xxx_pv_rule”的策略名,通过电话语音播报给用户,即使播放出英文读音,也会让人莫名其妙,因此我们定义了若干简化的 语音模板 ,只播报核心概要内容,同时提醒用户关注短信、邮件等其他渠道获取报警详情。 7*24值班也能睡个安稳觉 刚刚我们聊了 报警风暴 问题,它往往是由机房级故障或者网络故障触发大量的冗余报警导致的。而实际上我们观察到还有一个问题同样能带来报警冗余——那就是报警接收人配置过多。很多业务线将多个运维成员都配置到报警接收人里,而实际上运维人员是轮流值班的,非值班人员完全没有必要接收这些报警,这也造成了资源浪费。因此,我们集成了 值班功能 ,支持设置值班周期、交接班时间、值班提醒,多种值班角色,每天动态地将报警发送给值班人,这减少了一大批冗余报警。 另外,为了确保核心报警能得到及时响应并有效解决,我们引入了“ 报警升级 ”,即一个报警如果没有在限定时间内得到处理的话,那么该报警将自动升级到更高一级的接收人那里。看个示例,如下图: 报警发送给值班人后,如果该值班人在2分钟内没有认领或者在10分钟之内没有处理完成,则自动把该报警发送下一级接收人,如图中的yunxiaolin, yunxiaobo,并直接发送电话报警;如果他们在10分钟之内没有认领或者20分钟之内没有处理完成该报警,则继续升级到下一级接收人,如图中的yunxiaoyu。值班人收到报警后要回复正确的指令来“认领”或“完成”该报警,我们借此来判断该报警是否继续升级。假如你认为报警足够重要的话,你可以设置 多级升级 ,甚至到“厂长”!(偷笑.jpg) 总结 今天我们一起聊了 百度云Noah 通告平台 遇到过的沟沟坎坎,有如何发报警的基础问题,有报警风暴时报警压缩、报警渲染的难点问题,也有我们在on-call轮值和报警升级场景下的思考。作为报警的“搬运工”,我们始终相信“简单”的事也可以做得不同!希望我们的努力能让运维兄弟们过得更轻松一点,幸福一点。 目前平台还在持续进化中,欢迎大家积极留言共同交流! 原文链接地址: https://developer.baidu.com/topic/show/290333
来源:OSCHINA
发布时间:2019-09-12 17:31:00
本文作者:AIOps智能运维 作者简介 周伟 百度云高级研发工程师 负责百度云智能运维(Noah)告警通告系统的设计和研发,在大规模分布式系统、运维监控、精准报警等方面具有广泛的实践经验。 干货概览 本文提到的 异常检测(Anomaly Detection) 特指在运维领域中对 时序数据 的异常检测,目的是为了发现时序数据中状态的 变化 。 在我们看来,异常检测一般可分为两类:简单异常检测和复杂异常检测 简单异常检测 :一般指 恒定阈值检测 ,比如判断可用性指标是否小于99.99%,如果小于则检测结果为异常,否则检测结果为正常。简单恒定阈值检测一般适用于可用性、资源利用率等监控指标。 复杂异常检测 :对于收入、错误率、流量等业务监控指标,需要检测天/周/月/年等 环比变化 或者 突增突降 等状态变化,这类业务监控指标的 维度特征较多 (比如地域、运营商等), 特征变化也比较快 (例如电商定期搞活动,流量瞬间涨起来,干扰正常的异常检测)。恒定阈值检测往往无法检测到这类业务监控指标的状态变更,所以还需要复杂异常检测算法来检测业务监控指标的异常。为了发现监控指标的状态变化,复杂异常检测往往会采用 机器学习 、 深度学习 等比较复杂的技术。 和简单异常检测相比,复杂异常检测对落地流程和异常检测的运行平台提出了更高的要求。 落地复杂异常检测的阻塞点 为了支持简单异常检测,异常检测的运行平台只需要能够支持用户添加自定义报警判断规则即可。比如,对于上面提到的可用性指标,用户可以在系统中将报警判断规则配置为表达式“SLI < 99.99%”,然后等着接收报警就可以了。 然而对高级异常检测算法,算法从研发到上线,一般需要包括以下三步: 线下编写算法脚本 (一般是Matlab或Python脚本),并根据历史数据,离线训练算法所依赖的参数,通过用历史Case进行回溯验证效果。在这个过程中,算法工程师需要 反复调整算法参数 ,直到准确率和召回率都达到目标为止。 将算法脚本改写成线上代码 (一般是C++、JAVA语言等编译型语言)。这是因为线下编写的算法代码一般和线上运行代码使用不同语言开发,线下实验算法代码更侧重研发效率,需要快速迭代和测试,而线上运行代码更侧重运行效率,高效稳定运行。 最后 将改写后的算法代码生效到线上 ,并观察其运行稳定性和资源消耗情况,及时调整线上资源分配情况。 以上流程在实践的过程中存在若干阻塞点,导致复杂算法落地时间长,算法迭代效率低: 线下实验的过程中,历史数据、历史Case、不同实验所使用的算法、参数和效果全靠人工管理,常常出现 指标数据不全 ,历史Case不全,实验过程兜圈子的情况。 线下代码和线上代码 开发语言不一样 ,代码移植需要消耗比较多的时间,常常不能保证线上代码和线下代码的效果完全一致。 一些复杂算法对 资源的消耗很大 ,贸然上线会影响监控系统部分或整体的稳定性。 落地复杂异常检测的需求 上述阻塞点很容易导致算法迭代速度跟不上指标变化速度的情况,最终限制了检测效果的提升。所以,为了保证算法的顺利落地,我们需要提供一个新的异常检测支持方案,满足以下的需求: 需要一个方便算法 快速迭代 和 算法验证 的环境,用于在线下调试算法脚本,并对历史Case回溯,保证算法的 普适性 。 需要能 弥合 线下脚本和线上 代码 鸿沟 的机制,尽快将算法生效到线上,能快速验证算法代码的真实效果,否则线下实验脚本和线上生效代码迁移成本太高,费事费力费程序员。 需要评估线上代码的 资源消耗和稳定性 情况。因为复杂异常判断算法,其资源需求差异性特别大,比如有的会采用深度学习等非常耗CPU/GPU的算法,而有的仅仅是简单公式计算。另外,这类算法开发迭代特别快,算法代码很容易引入Bug,所以需要在不影响其他线上算法运行的同时,采用线上真实流量验证算法稳定性。 需要 分离 算法代码和算法参数,并支持 算法参数的独立更新 。因为算法经过快速迭代后,会逐渐稳定下来,但是算法的参数是对历史数据的特征表达,所以算法所依赖的参数需要周期性更新,保障算法达到最优效果。 落地方案 为了解决上述问题和需求,我们推出了 异常检测运行平台 。基于运行平台,算法工程师可以用脚本语言(当前支持Python脚本语言)线下编写异常检测算法,并在线下回溯历史Case,当策略调试完毕后,可以直接将Python算法脚本生效到到线上。同时,还支持算法参数的独立更新,大大加快算法的生效速度。 异常检测运行平台包括三个环境: 离线环境、在线环境、近线环境 ,下面详细介绍。 1 离线环境 离线环境会提供如图1所示的 策略开发框架 ,异常开发框架提供了Python运行环境和常用的Python库,基于开发框架,算法工程师采用 一致的抽象接口 编写算法代码,这样可以保证在线下开发的算法代码可以直接放到线上环境运行,从而弥合线下脚本和线上代码鸿沟。为了回溯Case,开发框架还提供了历史时序数据组件、异常判断评价组件(支持准确率、召回率、F1 Score等评价指标)。由于复杂异常检测算法不像恒定阈值算法可以根据数据直观看出判断结果,复杂异常检测算法往往需要运行并输出异常判断结果(能图形化展示更好),才能评估算法效果,所以开发框架提供了 图形组件 ,可以图形化展示异常检测结果。 图1 策略开发框架 图2展示了算法开发接口,其中包括四个标准接口: load_model函数 负责加载算法参数; get_data函数 负责加载指定时间段的历史时序数据; initialize函数 负责在算法正式运行前进行初始化,比如加载算法参数、申请内存等等; detect函数 负责对时序数据点进行异常检测,并返回异常检测结果(正常或异常)。 图2 用于高级异常检测的抽象接口 2 在线环境 算法工程师在线下环境基于开发框架开发完策略算法后,可以将算法发布到在线环境。在线环境提供了跟策略开发框架一致接口的 策略运行时环境 。策略运行时环境会接收上游发送的时序数据,并驱动Python算法脚本周期性运行,同时将产生的异常判断结果发送到下游,用于报警通告。 图3 在线环境架构 在线环境的架构图如图3所示,在线环境主要包括以下三个模块: 任务分发模块 :负责管理运维人员提交的高级异常检测算法配置,并将每个算法配置组装成任务,分配给任务运行模块。 数据调度模块 :数据调度模块 周期性 从任务管理模块同步任务的分配信息,根据任务分配信息,将每个任务所需的数据调度给相应的任务运行模块,同时将数据也Copy一份,写入到时序数据缓存中。 任务运行模块 :任务运行模块周期性从任务分发模块拉取分配到的任务,并为每个任务启动一个策略运行时环境。策略运行时环境支持跟开发框架相同的接口,所以可以直接驱动基于开发框架开发的算法代码的运行。策略运行环境刚启动的时候,会调用intialize函数,进行初始化操作,然后策略运行环境不断接收数据调度模块调度过来的数据,并驱动调用detect函数,detect函数会对接收到的数据进行判断,并返回判断结果(正常或异常),运行时环境收到判断结果后,将其发送到下游,用于报警发送。有时算法在刚启动或运行的时候,需要拉取近期的时序数据,这时通过get_data函数从时序数据缓存中获取即可。另外,任务运行时环境还会周期性检测算法依赖的参数是否有变更,如果算法参数被更新了,则会调用load_model函数重新加载配置。 3 近线环境 在线下环境编写算法代码,如果直接贸然上线到在线环境,往往会存在很多问题,所以策略算法在离线环境验证可用后,首先会放到近线环境运行起来,近线环境和线上环境的架构和功能其实没有本质差别,只是用途不同而已。近线环境的目的,一方面是为了用线上真实流量数据来 验证算法的资源消耗 ,以发现算法运行的真实资源需求,只是不真正发送告警而已;另一方面,是为了 验证算法的稳定性 ,比如是否有内存泄漏、是否有崩掉、是否有错误日志等等。如果发现有问题,算法工程师可以快速调整并重新部署到近线环境进行验证。 总 结 本文主要介绍了异常检测的相关背景、应用场景和需求分析,然后我们给出了百度云的高级异常检测算法快速落地方案——异常检测运行平台。目前运行在这套异常检测运行平台上的高级算法超过20个,每天处理近千万监控指标的异常判断。算法的线上迭代周期从周级别减少到天或小时级别,很好地支撑了业务方对不同高级异常检测的需求。另外,我们还将平台开放给业务运维人员使用,由业务运维人员研发的异常检测算法可以有效引入业务线人员的专家经验。 关于高级异常检测算法的落地,有任何想法和疑问,欢迎留言一起交流。 原文链接地址: https://developer.baidu.com/topic/show/290332
来源:OSCHINA
发布时间:2019-09-12 17:30:00
本文作者:AIOps智能运维 作者简介 运小伟 百度高级研发工程师 负责百度监控平台报警子系统的设计和研发,在大规模分布式系统、运维监控、精准报警等方面具有广泛的实践经验。 干货概览 Argus(Noah 监控3.0)是百度内部最大的监控平台,提供了 机器监控、进程监控、日志监控、远程监控、自定义监控 等多种监控方式。它还支持集群级别的监控配置和管理,并支持复杂的 异常判断 ,提供多种途径的 报警手段 。 图1 Argus监控系统示意图 从系统架构层面,Argus主要包括 采集、汇聚计算、数据存储、报警通路 和 可视化 五个主要部分。报警通路除负责异常判断、报警发送外,还支持 报警回调 和联动 故障自愈机器人 等功能。报警通路目前承载了千万级实例异常判断和报警,每天会自动执行数百次故障自愈任务。本篇文章会重点分异常判断和报警发送两部分来介绍报警通路的功能。 异常判断 判断规则 异常判断是报警通路的核心部分,其支持的判断规则决定了监控报警能力的强大与否,Argus报警通路支持以下两类判断规则: 内置的判断规则 : 该部分支持 四则运算 、 逻辑运算 以及各种 内置函数 。例如:metric_a < 99.99% && metric_b < 99.99% 、 abs(metric_c) > 100 等 。 自定义的判断规则 : 运维人员可先用脚本编写异常判断规则,然后将脚本提交到报警通路,报警通路负责 执行脚本 并产生报警。该部分适用于比较复杂的异常判断场景,比如复杂的同环比报警、多维度数据分析等场景。 判断流程 图2 异常判断流程 报警通路在接收到上游发送过来的数据后,首先找到跟数据相关联的报警配置,然后根据配置的判断规则进行异常判断,如满足判断规则,则产生报警;为了防止频繁的抖动报警,报警通路还支持 防抖动过滤策略 ,例如,M个周期中有N个周期的数据被检测为异常才会产生一个报警。另外,报警通路还会将产生的报警事件存储到运维知识库,供业务平台或用户来查询和使用。 异常判断例子 图3 异常判断例子 图3是一个异常判断的例子,为判断某业务在某机房的可用性指标是否达标,报警通路会对该指标的每个数据点逐一进行异常判断,如果连续3次都小于99.99%,则产生异常警报;反过来,只有连续3次都大于或等于99.99%,该次故障才会被判定为结束。 异常的自动化处理方案 异常判断产生的异常除了用于报警发送,还有下面三种自动化处理方案: 脚本回调功能: 报警通路支持在异常实例或机器上执行某个脚本的能力。比如当检测到某个实例异常时,可以执行此实例上的脚本(如:重启实例),就可以在无需人工参与的情况下自动修复异常的实例。 HTTP回调功能: 为了支持集群级别的故障处理能力,报警通路还支持HTTP回调功能。报警通路会将报警POST给指定的URL,接收端即可处理集群下的所有报警,以此来决定是否触发某个复杂的故障处理动作。 联动故障自愈机器人: 产生的报警还可以联动故障自愈机器人,执行相关自愈动作。故障自愈机器人是一款面向感知、决策、执行的故障自愈机器人,相关介绍,详见之前的公众号文章《AIOps时代,你准备好了吗?》。 报警发送 图4 报警发送流程图 异常判断产生的警报,如果想到达运维工程师,还需要经过 报警过滤、报警合并、报警渲染 三个环节。报警过滤会将运维工程师不希望收到的冗余警报过滤掉。报警合并会将相关联的警报合并到一块发送。报警渲染就是将结构化的警报数据渲染成文本信息,供运维工程师查看。 报警过滤 用户希望在以下三个场景过滤掉报警,针对这三种需求,我们提供了相应的功能: 报警屏蔽过滤: 运维工程师不希望接收预期内(比如模块上线期间)的报警,希望可以临时过滤掉相关报警。 报警依赖过滤: 运维工程师希望只收到产生故障的根因报警(即精准报警),这样利于快速定位线上问题。比如某台机器出现故障(因)时,那么这台机器上所有实例的报警(果)应该过滤掉,只需给运维工程师发送机器故障的报警。 最大报警次数过滤: 如果一个服务持续异常,运维工程师不希望持续收到同一故障的报警,可以限制最大报警次数。 报警合并 即使有上述报警过滤措施,但在较大规模故障发生时,仍然还可能产生大量的报警,造成 报警风暴 。因此,我们需要对同源的报警进行合并发送,一条信息中一般会包含多个相关联的警报。报警合并的细节详见本公众号之前的文章《我在百度对抗报警风暴(一)》。 报警渲染 合并后的报警,会按照默认格式渲染成短信、邮件、语音等,发送给运维工程师。此外,运维工程师也可以通过配置 报警 渲染模板 ,来自定义报警样式。 总结 本篇文章主要介绍了百度监控平台报警通路子系统的核心功能,在报警规则、异常判断、警报自动化处理、报警过滤等方面做了详细介绍。关于报警通路的实现细节和系统架构,会在后续的文章中介绍,敬请期待。 原文链接地址: https://developer.baidu.com/topic/show/290331
来源:OSCHINA
发布时间:2019-09-12 17:30:00
本文作者:AIOps智能运维 作者简介 运小博 百度高级研发工程师 从事有关运维数据分析相关的工作,负责异常检测系统和报警收敛等工作,重点关注时序数据分析、故障诊断等相关领域技术。 干货概览 自动异常检测旨在发现复杂业务指标(请求量、收入等)的异常波动,是智能监控系统中的重要环节。百度的业务种类繁多,各业务的监控需求迥异,参数配置成本繁重,给异常检测带来了巨大的挑战。本文整理了运小博在 2017CNUTCon全球运维技术大会 上分享的《百度大规模时序指标自动异常检测实战》和在 CCF TF“人工智能时代的互联网运维” 主题研讨会中分享的《百度智能运维实践之异常检测》的内容。主要介绍百度运维部IOP团队开发的自动异常检测系统及其核心技术能力,并重点讨论了大规模时序异常检测参数配置成本高的问题。演讲展示了三种常用异常检测算法及其适用场景,并基于此讨论了算法的自主选择策略,以及每种算法的参数自动配置方法。 背景介绍 异常检测需要监控的业务繁多,覆盖了搜索、广告、地图、糯米等百度大部分的产品业务。及时发现这些业务请求数、拒绝数、响应时间、流水和订单等数据的异常波动,是业务稳定性的重要保证。这些数据不但数量众多,而且不同业务的曲线也有截然不同的特征。从上图的三幅曲线图可以看出: ☞第一幅曲线图中有蓝、绿两根曲线,分别代表当前时刻数据和上周同一时刻的数据。蓝色曲线几乎完全覆盖了绿色曲线,说明数据有规整的周期特性。 ☞第二幅曲线图中,紫色曲线是当前时刻数据,蓝色曲线是上一周的数据。可以看出:数据有一定的周期性,但又不如第一幅图那么规整。 ☞第三幅曲线图中的数据大致平稳,在某些时段出现了异常上涨。 所以,我们的异常检测系统面临两个挑战:一是数据规模大---总共有百万量级的指标;二是曲线的特征差异明显,监控难度大。 通用场景的异常检测算法 对曲线特征进行梳理后,我们发现大多数曲线都可以分数到下面三个场景中: 场景一:数据无规律波动,但正常基本在一个较小的波动范围内,典型的场景就是拒绝数监控,通常我们会按照拒绝数的常态波动范围设定一个或多个恒定阈值,超过阈值即报警。 场景二:数据的长期波动幅度较大,但正常情况下短期的波动幅度较小,体现在图像上是一根比较光滑的曲线,不应该有突然性的上涨或者下跌。典型的场景包括糯米的订单、流水。这类场景监控的主要思想就是环比附近的数据,检查是否存在突然的大幅上涨或下跌。 场景三:数据有规律地周期性波动,比如广告收入或搜索流量等。检测这类数据的方法是与历史数据作同比,从而发现异常。 恒定阈值类算法 场景一的问题可以使用恒定阈值解决,超过设定阈值就报警。比如拒绝数监控,我们可以设定在一个单位时间内超过100个拒绝就报警。但是,实际使用中会出现单点毛刺的问题,也就是一个单点超过阈值的报警。当数据来回抖动时,就会产生大量无效报警。常见方法就是通过filter来解决,比如设置为连续5个时刻都超过阈值才报警,但这种方法太过僵硬,中间只要有一个点回到阈值范围内就不报。 我们采用的是更加柔性的累积法:一段时间窗口内数据的均值超过阈值触发才报警。这样不但能够滤除毛刺,还考虑了原始数据的累计效应。 突升突降类算法 场景二要解决的是突升突降的问题,我们求取数据最近两个窗口的均值变化比例(见上图公式),将原始数据转换到了变化比例空间(r空间),如右下的小图所示。在r空间上设置阈值就可以检测出数据的突升或突降。 同比类算法 场景三中的数据有显著的周期性,我们计算历史上相同时间窗口内数据的均值和标准差,然后计算当前点的z-score值,即当前点的值减去均值之后再除以标准差。逐点计算z值可以把原始数据转换到另外一个空间(z空间),在z空间设置阈值就可以发现这类异常了。比如左下的小图里蓝色曲线是当前的数据,红色和绿色的曲线是历史同时刻数据。如果要检测图中红色圆圈的部分是否异常,我们以历史数据(红色方块内的数据)为基准计算均值和标准差。右下的小图展示了蓝色曲线在z空间的形态,如果取值位于红色阈值线的下方,即可报警。 算法选择决策树&参数自动配置算法 不同曲线需要选取不同的算法,大量曲线的算法选择成本很高。例如,右上的小图是某产品在不同省份的流量数据,我们看到流量大的省份(如北京、广东)的曲线周期性很明显,更适合同比算法,流量小的省份比如西藏的曲线基本区域平稳,更适合配置恒定阈值。 另外,算法在不同时段的参数不同,工作日和休假日的参数、白天和晚上的参数都不同,参数配置成本非常高。 除此之外,曲线特征会随着业务系统的架构调整发生相应的变化,算法和参数需要定期维护。例如右下的小图是某个子系统的流量数据,箭头时刻这个子系统下线了,此事算法和参数都需要做出相应调整。 因此,我们希望帮助用户自动选择算法和配置参数。接下来我们将分别介绍算法选择决策树和参数自动配置算法。 算法选择决策树 曲线配置算法本质上在建立数据特点与算法本身的映射。周期性数据选择配置同比算法,非周期数据会通过波动范围来界定。当数据的全局波动(长期波动)远大于局部波动(短时波动)的时候,我们倾向于选择突升突降;当全局波动近似等于局部波动的时候,恒定阈值算法就会更合适。 接下来需要解决的问题就是:如何判断数据是否有周期性?如何界定数据的全局与局部波动范围? 我们提出了一种基于差分的数据周期特征判断方法。先将临近的两天数据做差分,如果是周期数据,差分后就可以消除掉原有数据的全局波动,然后结合方差的阈值判断就可以确定数据是否有周期性。实验发现,不同天的数据有一定的上下浮动,因此差分之前可以先对数据做归一化。 前面的方法能够分离出周期性数据,接下来要度量数据的全局波动和局部波动的相对大小。数据方差可以直接表达全局波动范围。对数据施加小尺度的小波变换可以得到局部波动,局部波动的方差反应了局部波动的大小。 结合周期性数据的判断方法和数据的全局、局部波动的表示,就可以得到图中的算法选择决策树了。 参数自动配置算法 算法选择以后,我们需要给每种算法自动配置参数。首先,介绍恒定阈值的自动参数配置。如左下小图中的一段数据,直观来说红色区域的数值因为很罕见所以一般会被认为是有异常。通过估算这些罕见数据出现的概率,即可确定曲线的阈值。把数据看作是一组独立同分布的随机变量的值,我们可以使用ECDF(经验累积概率分布曲线)来估计随机变量的概率分布(右下角的小图所示)。ECDF曲线的横轴是数据值,纵轴是概率,表达的是小于等于某数值的样本比例。用户给定经验故障概率(ECDF的纵轴),即可查找到数值的阈值(ECDF的横轴)。我们通过ECDF把配置阈值转换成了配置经验故障概率。尽管不同曲线的阈值不一样,但曲线的经验故障概率常常是一致的。 实际使用中,因为历史数据样本有限,ECDF与真实CDF有一定差距,直接使用容易有较多误报,我们使用了补偿系数解决这个问题。 刚才介绍了恒定阈值算法的自动配置参数过程,突升突降算法自动配置参数也是类似的,我们可以利用前文提到的空间转换公式把原始数据转换到r空间,然后在r空间上配置恒定阈值。除了r空间上的阈值之外,还有窗口大小w需要设置,不同曲线一般不会有太大区别,我们就不自动设置了。 同比算法也一样,使用z-score的方法把原始数据转换到z空间,就转换成了在z空间上自动配置恒定阈值参数的问题。同比天数k和窗口大小w一般也可以使用全局设置。 总结 本文从百度内部的实际异常检测场景出发,介绍了三种通用的异常检测方法,并介绍了算法自主选择策略,以及三种算法的参数自动配置策略,极大的降低了用户算法选择和参数配置的成本,有效地解决了百度内部大规模时序指标的自动异常检测的实际问题。 若您有其他疑问,或者想进一步了解百度在异常检测方面的实战经验,欢迎在 AIOps智能运维 后台留言或通过评论反馈! 若本次分享的内容对您有所帮助,不妨通过赞赏功能鼓励运小博! AIOps智能运维 将坚持分享运维领域的原创干货! 欲睹运小博真容,速速点击文末“ 阅读原文 ”回顾2017CNUTCon全球运维技术大会精彩瞬间! 原文链接地址: https://developer.baidu.com/topic/show/290330
来源:OSCHINA
发布时间:2019-09-12 17:29:00
本文作者:AIOps智能运维 作者简介 运小博 百度高级研发工程师 从事有关运维数据分析相关的工作,负责异常检测系统和报警收敛等工作,重点关注时序数据分析、故障诊断等相关领域技术。 干货概览 在本系列上一篇文章《我在百度对抗报警风暴(一)》中,百度高级研发工程师运小博介绍了报警风暴的成因及简单的报警合并策略。本篇文章中,运小博将介绍关联策略的报警合并策略、基于报警数据挖掘的机房故障分析、报警关注度分析、值班与逐级通告机制和报警回调等技术。 报警合并策略 关联策略的报警合并 某个模块的出现问题的时候,往往会引发上游或者下游模块也一并报警。假设模块A调用了模块B,当模块B出现问题的时候,很显然模块A和模块B都会产生报警。为了解决这个问题,我们尝试从 历史 报警数据 中 挖掘 关 联的报警策略 列表,然后就可以使用《我在百度对抗报警风暴(一)》提到的 报警合并 机制 对跨模块的相关报警进行合并。 历史上每次B模块出现同样的问题的时候都会导致A模块有类似的报警,换言之,若历史上A模块的策略rule1和B模块的rule2经常同时报警,那么A模块的策略rule1和B模块的策略rule2就可能存在关联。因此我们可以挖掘历史报警数据中的关联关系,即 关联的报警策略列表 。 如上图横轴为时间轴,每一个位置都产生报警的时间,比如最开始的报警是A模块的rule1,然后是B模块的rule2等等。然后使用 挖掘窗口 (上图中的大括号为一个挖掘窗口)进行滑动,把位于同一个窗口内的报警归结为一个事务。 接下来我们就可以使用常见关联分析算法挖掘 频繁项集 (历史上经常在一起出现的报警策略)和 关联规则 (报警策略之间存在很强的关系)了。我们先要回答一个问题,怎么样定义报警策略的频繁出现?或者说,两个报警策略是否存在关联? 一个项集的 支持度计数 被定义为该项集出现的次数,这里没有用传统的支持度是因为历史报警数据产生的数据往往较多,而实际项集数据出现的比较稀疏,意味着支持度的分母巨大,分子却很小。 置信度 是针对一条关联规则X:rulem->Y:rulen而言定义的,代表了X:rulem导致Y:rulen发生的可能的概率。 支持度计数S_count(X:rulem)=以 X:rulem开头的transaction的数量 支持度计数S_count(X:rulem->Y:rulen)=以X:rulem开头,并且包含Y:rulen的transaction的数量 置信度c(X→Y)计算公式如下: 支持度和置信度超过一定数值即为所需的关联规则。按照这样的规则,在等待发送队列中当某个报警发送时在报警策略关联表中查找等待队列中是否包含关联策略,如果包含就合并成一条报警信息发送。 机房故障期间的报警合并 机房故障 (网络中断、机房断电等)会导致部署在该机房的模块异常,引发大规模的报警风暴。报警风暴不但会对报警的处理造成打扰,而且可能会增大报警系统压力,严重时可能导致后续报警 延迟送达 。因此,如果能快速准确地感知机房故障,并将相关报警进行合并,将会有利于运维人员 快速捕捉故障根因 ,还能 减少报警系统压力 。 一个机房往往部署有多个业务系统,而一个业务系统也会把自己的模块部署在多个机房中以提高可用性。当某个业务的运维工程师发现单个机房的多个模块出现故障,就会怀疑是机房故障,但很难直接确认。所以,他们一般都会找其它业务的运维工程师寻求确认。如果多个业务都在这个机房出现问题,就基本能够确定是机房的基础设施出了故障。 通常,我们会想使用每个机房的报警数量来表征这个机房的状态,但百度部分产品的体量庞大,当这些业务故障的时候,会导致报警量突升,从而影响对机房的判断,因此最终我们选择了异常策略比例 RuleRatio 和异常业务比例 ProductRatio 两个特征。 为了寻找如何用上面的两个特征确定是否有机房故障,我们抽取了历史一段时间各个机房的特征,并利用散点图来训练。如下图所示,横坐标是 RuleRatio ,纵坐标是 ProductRatio ,每个点代表某个时段的某个机房。图中红色为机房有故障的样本,蓝色为无故障的样本。 从上图看,线性分类器就能区分故障和非故障的样本。 对历史数据进行回溯,上图横轴是时间,纵轴是按上述指标计算的异常评分,每一条曲线代表了一个机房,几个异常点分别是某一次机房的故障。 报警关注度 报警关注度 是指报警发送后有实际处理的比例,但系统运维一段时间后都会发现部分报警的关注度并非100%,大多数情况下并非是值班工程师不尽责,而是部分报警策略随着系统的演化已经失效而又没有及时删除,因此需要我们有一种方法识别无效报警。 夜间关注度分析 我们观察了一条报警的处理过程。收到一条有效的短信报警后,值班工程师会登录运维系统(包括监控系统、预案系统等)对报警进行定位、处理,这些行为会体现在各种运维系统的访问日志中。通过收集这些日志,就可以对每条报警的处理情况进行分析:如果在收到报警后的一段时间内访问过运维系统,可以认为该报警得到了关注,反之就认为该报警没有得到关注。汇总一段时间后,就能够筛选出 关注度较低 的报警策略,即为 无效报警策略 。 报警值班与逐级通告机制 百度监控系统( Argus )报警通路帮助运维团队建立值班机制,支持配置值班表,并设定交接班时间、值班周期等,值班系统会按周期自动轮转,并以短信、邮件等形式在交接班前通知接班值班人。为了保证核心报警得到及时有效的处理, Argus 中还建立有 逐级通告机制 ,支持配置多个通报升级时间和通报方式(电话、短信等)。报警发出后,值班人必需及时认领报警,超时未认领会按照配置升级并通告。下图就是一个值班和逐级通告配置的例子。 报警自愈机制 很多报警都有明确的处理预案,报警发生后,值班工程师登录机器或者中控机执行预案(脚本)就可以完成这类故障的处理。比如,清理磁盘这类操作,可能就是一个简单的删除日志和tmp目录的脚本。如果这类报警可以完全自动处理,无需人工干预,就能够大量节省人工成本,同时减少报警量。因此, Argus 报警系统提供了 报警回调 机制,在报警发生时可以回调预案处理脚本。 上面介绍的这类自愈场景比较简单,这类预案往往对于程序没有任何干扰,可以“无脑”执行预案脚本即可。对于更复杂的场景,值班工程师往往需要根据服务的整体情况来调整预案。例如当某个实例异常需要重启的时候,需要综合判断其他实例的状态才能确定是否以及何时可以重启该实例,如果无脑重启可能会给服务造成损失。这种场景的自愈操作可能是有损的,需要对服务整体情况有一个判断才可以执行预案, Argus 报警系统为此提供了另外一种回调机制。在发生报警时,报警系统会把相关的报警策略、实例的状态都统一发送给一个 中 枢决策服务 ,由中枢决策服务统一做出判断。 总结 《我在百度对抗报警风暴》系列文章初步介绍了智能报警合并策略、机房故障挖掘、报警关注度分析、值班与逐级通告平台和报警回调等技术。 时至今日,还有更多的复杂策略在 Argus 上运行,在这些策略的帮助下,百度运维工程师收到的短信报警量减少了 80% 以上,尤其是网络、数据中心等大规模故障期间,有效地减少了报警风暴对运维人员的干扰,增强了报警的精准程度和实际价值。 若您有其他疑问或想进一步了解 Argus ,欢迎留言反馈! 原文链接地址: https://developer.baidu.com/topic/show/290329
来源:OSCHINA
发布时间:2019-09-12 17:28:00
本文作者:AIOps智能运维 作者简介 运小博 百度高级研发工程师 从事有关运维数据分析相关的工作,负责异常检测系统和报警收敛等工作,重点关注时序数据分析、故障诊断等相关领域技术。 干货概览 百度监控系统 Argus 保障了百度内外产品服务的高可用,《我在百度对抗报警风暴》系列文章将会介绍百度高级研发工程师运小博在实践中如何运用 报警合并 、 机房故障分析 、 报警关注度分析 、 值班与逐级通告平台 和 报警回调 技术等对抗报警风暴。本文将主要介绍报警风暴形成的原因和报警合并策略中简单的报警合并策略。 Argus 名字含义:希腊语“Argus”的意思是“明亮的”、“明察秋毫的”,在古代希腊神话里面的巨人Argus长有一百只眼睛,因此可以观察到所有方向的事物与动静;后世以此来比喻机警、机灵的护卫,我们希望百度监控系统能够如巨人Argus全面洞察异常并报警,故命名为 Argus 。 报警风暴 百度监控系统( Argus )是保障百度内外产品服务高可用的利器。小到机器的磁盘是否打满、虚拟机实例上的进程或端口是否存活,大到产品的流量是否稳定、机房网络是否联通,尽在 Argus 的掌握之中。 两年前,百度每一个运维工程师都被报警风暴所困扰。白天,百度的运维工程师们平均11分钟就会接收一次短信报警,在夜间则是平均14分钟一次,而实际数据统计发现,有效短信报警占比不到15%。因此短信报警的冗余度是非常高的,已经造成了报警风暴。 经过分析,报警风暴的形成主要有这么几个原因: 报警重复度>58% 分析其原因,首先报警策略执行周期计算,因此会持续产生 重复报警 ,部分策略甚至会导致持续报警达1小时以上。更严重的情形是,一次故障可能引发多个相关策略报警。比如一台机器死机,首先机器层面报警,然后实例层面报警,接着服务上游也可能会报警。 报警关注度不足 我们把报警发送后有实际处理的比例作为 报警关注度 的度量指标,发现实际关注度并不高,而在夜间短信报警关注率则低至25%。但事实上夜间短信报警的级别一般都是比较高的。这就意味着很多报警策略的发送方式和实际的报警等级已经相违背了。这是因为报警的关注度随着业务发展发生变化,但是这些关注度的变化没有及时的在报警系统中修改,导致已经变得不那么重要紧急的报警,却还在以短信的形式给值班工程师发送报警。 报警接收人冗余 每个报警策略平均有3个接收人,部分报警甚至超过了7个。报警策略的接收人往往会填写了运维团队中的所有人,但实际值班人只有一个人,大家按周期轮转。因此,对于一个特定的报警,大部分同学是不需要即时关注的。 报警有效性不足 超过88%以上的报警都是单实例报警,40%以上只需要简单的处理即可恢复,比如磁盘打满或者内存泄露等。因此我们在 Argus 中增加了 自愈机制 ,自愈成功后,报警也就无需继续发送了。 针对上面的这些问题,我们在设计 Argus 时使用了智能报警合并策略、基于报警数据挖掘的机房故障分析、报警关注度分析、值班与逐级通告平台和报警回调技术等。 Argus 的功能逐渐完善,并把周级报警短信总量削减了 85% 。 报警合并策略 报警合并对很多做监控的同学来说并不陌生,大部分介绍报警收敛的文章都提到了这个过程,但大多数提及的报警合并都是将某个时间窗口内的报警简单的合并成为一条,此举对削减报警数量固然有效,但不利于值班工程师进行故障诊断。我们希望把若干描述同一故障的报警合并在一起,让值班工程师可以快速捕捉到故障本质,甚至故障根因,而并非一味的削减报警量。 在本文中,我们先介绍一个合并来源于 相同报警策略 或者相同模块的重复报警的策略,下一篇文章中将讨论如何合并 跨模块 、 跨策略 的报警和一个消除机房网络故障期间报警风暴的方法,这一方法也可以用来检测机房的网络故障。 简单的报警合并策略 最简单的报警合并方法可以基于报警策略的自然属性,包含策略名或者部署维度等。百度的生产系统使用了 虚拟化 技术混布各项服务,因此部署的逻辑维度包含了实例、模块、集群等层次,物理维度包含机器和机房层次。一个层次同时刻的报警,大多数都存在着一定联系,因而可以将这些报警合并。举一个实际的例子,A模块在bj机房部署有100个实例,统一为每个实例配置了端口存活报警策略rule1,某个时刻这个策略在0.A.bj(A模块在bj机房的0号实例)和1.A.bj(A模块在bj机房的1号实例)上都报警了。这两个报警属于同一个模块的同一个策略,因而可以合并在一起,便于值班工程师了解整体情况。最终发送的报警样式如下: {A:instance:rule1}{总体异常实例比例:2%}{异常(2):0.A.bj,1.A.bj}{05-02 16:49:36 - 16:54:09} {http://dwz.cn/… } 简单介绍一下这条报警的意思: ◤A:instance:rule1代表的是报警策略rule1属于A模块,并且是实例级别的报警; ◤总体异常实例比例是使用异常实例数量除以实例总数量计算出来的; ◤0.A.bj和1.A.bj属于A服务下的两个异常实例,异常时间段是05-02 16:49:36 到16:54:09; ◤后面有个短链,用户可以打开短链查看更详细的报警内容。 当合并的内容过多时,我们将最主要的报警或者报警的总结汇总到短信内容里面,具体的每一条细节报警、报警起始结束时间、报警持续时间、报警配置内容等细节信息都会在短链的页面中展示。 了解了报警合并的策略,接下来介绍一下实际合并的机制。一个报警产生以后,我们先把这个报警插入一个发送等待队列而非立即发送。每一个报警策略都有一个在等待队列里的最长存留时间,换言之,是这条报警可以容忍的 最长延迟发送时间 。报警产生后先插入等待队列里面去,在队列里等进行延迟计时,当达到了能够容忍的延迟时间以后,我们在等待队列中找到可以和该报警一起合并发送的报警,根据实际的合并维度渲染成不同的报警短信内容,然后合并成一条报警短信发送。在下面的例子里,A,B,C代表了3个不同的模块,A模块上配置了两条报警策略分别为rule1,rule2,B模块上配置了rule3,C模块上配置了rule4,红色的报警A:rule1即将到达最长存留时间,按照合并策略,黄色的报警可以一起合并为一条发送,这样实际的报警信息中包含了三条报警。 总结 在今天的故事里,我们从多个角度分析了报警风暴的问题,并重点介绍了报警合并技术中简单的报警合并策略。之后的故事里我们会继续介绍关联规则的报警合并策略、基于报警数据挖掘的机房故障分析、报警关注度分析、值班与逐级通告平台和报警回调技术等,请持续关注AIOps智能运维! 若您有其他疑问或者想进一步了解百度监控系统 Argus ,欢迎留言或评论! 原文链接地址: https://developer.baidu.com/topic/show/290328
来源:OSCHINA
发布时间:2019-09-12 17:28:00
本文作者:AIOps智能运维 作者简介 Mason 百度资深研发工程师 负责百度智能运维(Noah)云监控平台的架构设计与研发工作,致力于推进监控能力在公有云及私有云场景落地。 0 1 写在前面 云发展速度快、成长空间大, 监控场景复杂 从2006年AWS正式拉开云计算商用大幕至今,云已经从概念普及进入广泛应用阶段, 云服务 成为水电一样的基础服务已成为行业共识。最新Gartner的报告预测到2019年 公有云市场 将达到2062亿美元,较2018年将会增长17.3%,然而这个规模依然只占全球范围内IT支出的5.4%(据Gartner预测,2019年全球IT支出将达到3.8万亿)。从这些数据可以看出,未来相当长一段时间,云计算业务还将继续处于快速发展阶段,并且有着巨大的增长空间。 随着云服务的快速发展,部署在云上的业务系统越来越多,规模也越来越大,与此同时针对云上业务 系统的监控 也就变得越来越重要。Gartner报告显示,尽管已经有39%的上云企业为其系统定制了监控解决方案,但整体上来说,监控系统的覆盖范围还有很多需要完善的地方,尤其是针对 混合云 业务场景的监控。 0 2 监控愿景 为客户提供完整的 云上系统监控解决方案 调研典型上云客户业务系统发现,中大型客户更倾向于将系统构建在混合云环境之上,并依赖公有云提供的计算、网络、存储等服务,来实现自身业务的 弹性 ,而小型客户则更多的直接将业务系统部署到云上,并且出于运维和研发成本的考虑依赖的云服务种类和数量越来越多。对于一个典型的云系统的监控来说,除了要关注云系统的模块架构组成外,还要关注其依赖的云服务,同时从业务价值的角度出发,还要关注服务的连通性和性能,当故障的时候,需要能够提供对应的手段去定位和分析产生问题的原因。 通过分析,可以得出云上客户对监控的需求如下: 支持云上服务监控 ,如云磁盘、对象存储、数据库、大数据等云服务监控; 支持跨云主机或与用户自建环境组成的混合云场景监控; 支持客户业务系统以及客户业务价值监控,支撑客户日常运维与运营行为; 预留 扩展能力 ,提供相应机制或开放API接口,供其它故障处理系统、变更管理系统感知监控目标的状态变化,并依此构建完整的运维体系。 0 3 实现思路 服务模型屏蔽差异 标准组件提升系统能力 01 构建服务模型屏蔽服务间模型差异 云由服务提供商提供的一系列计算、存储、AI应用类服务组成,每种服务的业务模型都不尽相同。如虚机、块存储的实例模型,数据库、缓存、容器服务的集群模型;语音识别、语音合成、人脸识别服务提供的API或API集合模型。构建在这些云服务资源之上的系统,由于业务场景不同,也会呈献出或繁或简的架构形态。为了应对 结构复杂 、 模型多变 的云上需求,提供可扩展、适应性强的监控能力,就需要定制出一套 标准的模型 出来,对上屏蔽不同云服务资源模型与客户业务系统资源模型的差异,对下支撑标准监控能力建设,这就是 服务管理模型 。服务模型要解决如下两个问题: 抽象实体模型特征 ,针对云服务资源或客户业务系统按功能、结构划分出来的具有一致性功能的实体; 刻画模型间关系 ,用于描述或定义不同类型的实体间层级或关联关系,支撑不同实体间指标数据计算。 02 围绕服务模型构建可伸缩监控能力 由于服务模型屏蔽掉了业务模型的差异,在监控能力建设方面,就可以围绕服务模型构建标准化的采集、计算、存储、异常检测、告警、可视化等能力。通过形式多样的采集手段实现监控对象指标的收集,再通过计算和模型间关系实现业务指标的转换,并将对应结果存储起来,供后续的异常检测分析与可视化使用。 0 4 产品目标 打造从云资源到客户业务系统到终端 用户价值的全栈监控产品 在标准化的监控能力建设完成之后,要做的是细分客户监控场景,并针对性的打造细分场景监控子产品。 用户在使用云系统的整个过程可以简化为上图所示模型。用户通过公共网络连接到服务,对应的用户请求通过入口服务完成转发,由具体的部署在容器、虚机或物理机上应用服务进程完成处理并返回给用户,当然在处理的过程中通常会涉及到不同应用服务进程间调用和对云服务资源的调用。根据监控的场景,将监控的场景细分为以下几个场景: 站点监控 ,监测客户服务的连通性与可用性,监测分布在不同地理位置或网络的用户的访问服务的状态和性能; 应用(系统)监控 ,监控应用或系统的资源使用情况及健康状态,通过进程、日志、脚本、Http、端口、语义等多种手段; 主机监控 ,监测应用进程运行的主机/容器等宿主环境的资源使用情况与健康状态; 云服务监控 ,监测云上业务系统依赖的云服务资源状态和性能; 业务监控 ,从业务价值的角度去分析对应变化以及追踪导致这些变化的可能诱因。 0 5 写在最后 扩展监控生态,护航云上业务 通过云监控提供的实时异常检测机制和可视化效果,不仅可以让客户对自身业务现状、以及支撑业务的系统状态了然于胸,还可以在问题发生时帮助客户快速定位故障,保障业务价值的连续稳定。同时,通过监控系统通过预留的接口可以方便的实现与外部自系统对接,与其它自动化系统共同构建监控运维生态,为云上客户业务系统的稳定保驾护航。 本文介绍了我们云上监控产品的愿景与设计思路,接下来,我们还会深入的介绍如何使用百度云上的监控、运维管理产品来定制构建自己的解决方案,敬请期待! 原文链接地址: https://developer.baidu.com/topic/show/290327
来源:OSCHINA
发布时间:2019-09-12 17:27:00
本文作者:AIOps智能运维 作者简介 运小军 百度云资深研发工程师 负责百度智能运维方向大规模日志处理、海量事件数据存储相关设计研发工作,在分布式系统架构、大数据存储计算、高性能网络服务和即时通讯服务有广泛实践经验。 干货概览 前文《面对海量事件数据,我来告诉你怎么办!》中我们介绍了百度线上业务运维场景下海量事件数据存储与计算平台EventDB的系统架构、集群规划及未来发展方向,本文将介绍我们在EventDB高可用方向 面临的问题、建设经验及后续计划 ,希望与业界同行一起交流学习。 问题 作为百度智能运维大数据核心存储平台,其可用性高低直接决定了上游业务系统可用性高低,我们建设可用性之初主要面临如下几个问题: 关键监控指标缺失: 导致无法准确掌握系统状况,排查问题困难; 流量缺乏管控: 一是终端用户直接使用ES API很容易造成接口误用;二是无法防御恶意请求和非预期流量洪峰; 数据规模持续增长: 导致原有存储模型无法满足系统性能和扩展性需要; 参数配置不合理: 默认配置参数没有按业务场景进行优化调整。 可用性建设 为了提升平台可用性,针对上述问题我们做了如下几方面工作: 1 完善监控体系 我们建立了 多层次 监控指标,从机器、容器(Container)、JVM、ElasticSearch内部指标再到业务监控指标,这些监控指标对及时了解系统运行状况、分析定位问题至关重要。 机器、容器: CPU/MEMORY/DISK/NET/FD/PROCESS JVM: Eden/Survivor/Old/Full GC/Young GC/Thread ElasticSearch: Queue/Cache/Search Context/Marvel 业务监控: PV/PVLOST/Response Time/主备数据一致性 其中ElasticSearch内部指标是通过API 实时提供 ,为了图形化展示这些指标并记录历史数据我们使用Marvel插件,Marvel插件通过定期调用ElasticSearch监控API提供更细粒度监控指标,能让我们看到基于每个索引(Index)的监控数据,这个功能在我们定位IO突增问题时发挥了重要作用。 2 统一调用接口 ElasticSearch自身提供了非常丰富的API,从数据操作到参数配置再到集群管理。如果把所有ES API都开放给终端用户会给平台带来非常大风险,一是我们无法预料用户行为,二是每个用户对ElasticSearch掌握程度不同,很容易造成误用。为了加强流量管理能力我们做了两方面工作: 一是由平台提供 统一数据操作API ,使用BigQuery API将所有存储、查询需求通过SQL来表达,用户通过SQL来操作数据,如果SQL有问题或恶意请求会被直接阻止掉; 二是对所有接口进行 配额限流管理 ,超出单位时间配额的请求会被拒绝访问。 3 优化存储模型 ElasticSearch数据存储模型由索引(Index)、类型(Type)、文档(Document)组成,分别对应关系型数据库中库(Database)、表(Table)、行(Row)。设计合理的存储模型不光能满足业务需求,还能极大提升系统 扩展性 和 读写性能 。 分库设计 数据规模小的情况下我们为了简便可以将数据都存放在一个库中,当数据规模越来越大,这种存储方式会带来两方面问题: 一是数据 难以管理维护 ,例如我们想把某类业务数据清理掉,无法通过直接删除索引的方式来清理数据; 二是 影响性能 ,任何读写请求都会影响索引中其他数据读写。 所以平台设计之初就需要我们 合理规划索引 ,一般的做法是按 业务和时间 两个维度来进行分库,不同的业务使用不同的索引,然后依据数据规模按天/月/年来创建索引。 合理设置分片 单个索引该设置几个分片?每个分片大小多少合适?这两个问题是我们在规划设计索引时必须要考虑的问题。 索引分片过小一方面导致ElasticSearch在内存中维护大量索引分片元信息,集群管理负荷增加进而引发 集群不稳定 ;另一方面ElasticSearch在查询时会扫描所有索引分片,分片过多会 影响查询性能 ; 索引分片过大将导致数据过于集中,读写操作在同一分片上的概率增加进而 影响操作性能 。 合理的做法是先 评估索引数据规模 ,按照单个分片不小于1G的原则来设置分片数,这样能避免产生大量小分片;另一个原则是要让分片在集群中 尽量均匀分布 ,实践经验就是分片数最好是数据节点数的1.5~3倍,这样能避免单个分片过大。 过期数据处理 数据价值会随着时间越来越低,任何一个存储系统都不可能永久无限制地保存所有历史数据,因为无论从成本投入、维护难度上都是得不偿失。所以针对不同业务场景我们需要制定清晰的历史数据清理策略,对于过期低价值数据进行 定期清理 ,这对保持集群稳定,提高资源利用率至关重要。 4 优化配置参数 下面这些参数都是我们认为比较重要的参数,在这里只说明其对系统的影响不作具体值建议,大家可以根据各自业务场景自行进行调整。 JVM参数 -Xms -Xmx: 设置Heap大小,建议不超过32G(JVM使用压缩指针用32位地址寻址32G空间); -XX:+ExitOnOutOfMemoryError: 发生内存溢出时保证JVM进程及时退出,避免节点假死(JVM进程还在但无法正常提供服务) ; backlog: 已建立TCP连接处理队列长度,该队列满时会丢弃TCP连接并抛出Connection Reset异常。JVM默认50,建议适当增大应对流量洪峰。 Elasticsearch参数 index.number_of_shards: 索引分片数,需要依据数据规模来设置,在索引创建时设置,后期无法更改; index.number_of_replicas: 索引分片副本数,需要依据数据重要程度来设置,既能在索引创建时设置,也能后期通过API更改; index.refresh_interval: 内存中数据写入到磁盘间隔,该参数越小数据可查询延迟越小,可靠性越高但性能低;该参数越大数据可查询延迟越大,可靠性越低但性能高;默认1s,建议增大。 ElasticSearch作为高可用集群,单个节点挂掉并不会影响整个集群功能。当故障节点恢复时,为了避免恢复工作对集群造成太多影响(主要是避免过多的I/O消耗),可以设置如下两个参数: cluster_concurrent_rebalance: 集群中允许多少个分片同时迁移重分配; node_concurrent_recoveries: 一个node上允许多少个分片同时恢复。 成果及计划 经过不懈努力, 事件数据存储平台已扩展到百量级的数据节点,日处理事件大小数百GB,可用性达99.999% 。用户涵盖业务报警、异常分析、根因定位、关联分析、日志追踪,已经成为百度智能运维大数据核心存储平台。 为应对数据规模、流量持续增长的压力,持续保持系统高可用性,我们计划做如下两方面的建设: 冷热数据分离 : 建设冷热数据分离存储架构,一方面可以有效避免冷热数据互相影响,有效提升热数据读写性能;另一方面可以针对冷热数据进行存储介质优化,例如:使用SSD硬盘来保存热数据,使用SATA硬盘保存冷数据,既能提升读写效率又能降低存储成本; ES版本升级: 新版本ES在稳定性、易用性、安全性及可维护性上都有很大提升,定期升级版本能避免很多不必要的维护工作。 原文链接地址: https://developer.baidu.com/topic/show/290326
来源:OSCHINA
发布时间:2019-09-12 17:23:00
本文作者:AIOps智能运维 作者简介 运小涵 百度云资深研发工程师 负责百度服务管理系统和监控平台架构研发工作。在分布式系统和大规模数据处理、可用性工程方向有广泛的实践经验。 干货概览 近日,百度云资深研发工程师董涵受邀出席由InfoQ主办的QCon北京2018全球软件开发大会,发表了“ 大数据实时计算和存储系统的高可用建设 ”的主题演讲。分享了百度监控后端服务在日均处理万亿级监控数据场景下, 保障服务可用性 方面的运维及开发经验,重点介绍了故障自愈、容量建设等内容,获得了参会人员的广泛关注。 Noah是 百度运维自动化平台 的统称,它涵盖了服务管理、监控、部署、任务调度等一系列产品和能力。而Noah的“眼睛”是 Argus监控系统 。经过多年发展,它已经成为一个功能覆盖全、性能强大的监控体系,它不仅支撑了百度内部所有核心业务的监控需求,还支撑了很多外部用户的运维基础设施建设,是服务高可用的基石。正因为此,监控系统本身的高可用架构建设,一直是运维技术的重中之重。 挑战:如何确保高可用性 如上图所示,百度监控系统具备 业务规模大 、 系统吞吐高 等特点,其自身可用性建设是一个持续性的工程,在服务发展的每个阶段,可用性工作的侧重点也各有不同。 在前期的可用性建设中,百度已经实现了服务的 弹性扩缩容 能力,并且系统已经具备 N+1冗余 ,实现了故障处理流程的自动化。 随着业务量的不断增大和接入用户量的增多,我们遇到了 突发流量过载 和 故障恢复效率低 的问题。一方面,我们支撑了百度数百个业务,每个业务每天都在做着大量的迭代更新,任何非预期的监控配置变化都可能造成监控系统的流量突增甚至过载。另一方面,随着各业务产品自身逐渐实现了故障自愈能力,监控系统已经成为了每个业务产品故障自愈效率的关键一环,这也就迫切需要监控系统自身,提高可用性和故障恢复效率。因此,我们将 容量管理 、 故障自愈 列为重点建设方向,以完善和加强百度监控系统的高可用能力。 容量数据建设:两个阶段 我们的容量数据经历了两个阶段: 最初我们依赖离线压测结果和人工修正,估算系统的负载情况。但这种方式的缺陷是显而易见的。 首先, 线上服务的部署依赖关系复杂、流量大、变动也很频繁 。离线压测一般投入资源很有限,是线上服务的缩水版。其测试结果很难准确反映线上服务的容量,也很难暴露线上大流量场景才能出现的一些问题。例如,某些临界区非法占用导致程序低概率崩溃的问题,属于低概率复现的bug。在线上大流量环境中才能出现。但对存储类应用的影响较大。可能造成反复重启的慢节点,进而影响系统容量。这种问题的提前暴露,对可用性保障有重要意义。 其次, 人工估算修正重度依赖工程师的经验 ,对于每个系统来说,一般只有某些高级开发和运维工程师才能估算出有效的数据。难以归纳为统一的方法并作为工程经验加以传承,是不可持续的。 经过前期的可用性建设,我们的服务都实现了N+1冗余能力,具备线上压测的条件。因此,我们引入了 线上压测 ,使用线上压测的系统服务数据,来作为容量数据。 我们的压测思路:在线上冗余集群, 接入实际线上流量和部分模拟的业务流量 。通过不断增加模拟业务流量,直到服务的核心指标,类似pv、pvlost、平响等指标开始出现异常,说明系统已达到服务能力上限。 这样做的好处是大部分流量是线上流量,更贴近实际线上场景。压测平台需要提供的流量也比较小,降低数据库类型下游处理数据污染的工作量。 我们已经基于这一组容量数据,实现了全局限流能力,可有效拦截全局流量突增造成的系统过载。 过载保护:两种限流方式 我们的服务包含了采集、计算、存储和查询等多个环节,每个环节都或多或少地会受到流量过载的影响。 最初给我们造成比较大影响的是 “查询”操作 的流量,因为监控系统本身就是一次写入、反复查询的业务场景。我们的服务端提供了thrift、http等多种查询接口,这些接口的流量直接受用户业务量和操作方式的影响。例如,用户会查询一个相当长时间范围的监控数据,通过脚本进行批量查询,或请求失败时的反复重试。这些都增加了请求量,给线上服务的稳定性造成了影响。 对于此类场景,我们一方面 优化系统性能 ,通过业务隔离、冷热数据分离、VM参数调优等方式,提升系统吞吐量。另外,我们也提供了 标准化SDK ,统一查询重试策略,增加退避重试、自动重选下游等逻辑。在保障成功率的同时,避免了不规范的用户操作对容量的影响。 当然,上述情况更多是能够规避预期内流量,但随着新业务的不断接入,经常由于配置错误、日志格式变化等造成数据采集端、汇聚计算侧数据量突增的情况。对于这部分流量,我们通过 限流 来解决,核心思想是基于统计信息,在数据流经过的各个环节,参考配额进行限流。 经过权衡实现成本和限流效果,我们选择了两种限流方式, 本地源端限流和全局通路限流 。 本地源端限流主要体现高时效性和低开销,覆盖单实例维度超限/流量超限场景。 全局通路限流可以覆盖全局超限场景,例如某个产品线整体的多维度组合超限。另外,它所有流量都要通过proxy转发,这种方式几乎可以在系统中的任意层次接入,不需要对系统中的模块进行改造。 故障自愈:如何缩短时间 在以前的故障中,由于监控系统本身的复杂度,我们的故障止损时间多在5min以上,最长的甚至达到十数分钟。长时间的故障,轻则造成业务失败率高、趋势图展示失败等,带来用户困扰;重则触发漏报警或误报警,无法让业务感知自身的线上故障,造成业务损失。 因此提升监控系统自身故障的 止损时效性 , 缩短故障时间 ,也是关键一环。 在故障感知层面,为了提升故障感知 召回率和时效性 。我们在系统中使用了多种指标组合,既包含系统监控、业务监控、网络监控,也包含SLI、服务、集群、实例多种维度的指标,同时使用智能异常检测技术,做到高准确和高时效。 另外,就是自愈逻辑,需要针对不同系统的业务特点进行适配。例如,对于存储系统,自动止损需要参考数据完备性指标,只有在数据是完整的情况下,才能将流量切入目标集群。 还有一些场景,受限于成本和业务特性,我们仅能给出降级止损方案。例如我们的事件数据库(EventDB),由于某些原因,其备集群只保留了部分数据,无法承担全量查询。这种场景下,自愈系统就需要将流量尽可能倾向主集群,在主集群恢复可用状态时,第一时间将流量切回,避免长时间降级。我们通过给集群设置不同的权值实现了这个能力。 此外,我们的自愈操作会定期进行 演练和盲测 ,确保止损预案和自愈效果符合当前系统的设计,并能够满足业务需求。 当前我们的自愈能力已经覆盖了流式计算、时序数据库、事件数据库等核心子系统。上线以来,一共执行过数百次故障自愈操作, 故障召回率达到99%以上,MTTR小于2min ,有效满足了故障恢复效率优化的需求,并降低了运维工程师的人力投入。 总结 分布式系统的高可用设计是一个复杂的课题,我们会持续在架构容错升级、提升故障自愈能力和恢复效率等层面继续探索,将系统的可靠性做到最优。 原文链接地址: https://developer.baidu.com/topic/show/290324
来源:OSCHINA
发布时间:2019-09-12 17:21:00
本文作者:AIOps智能运维 作者简介 运小军 百度云资深研发工程师 负责百度智能运维方向大规模日志处理、海量事件数据存储相关设计研发工作,在分布式系统架构、大数据存储计算、高性能网络服务和即时通讯服务有广泛实践经验。 干货概览 百度线上业务运维场景下会产生海量数据,这些数据大体可分成两类:一类是 时序数据 ,例如:CPU、内存、磁盘、网络状态等数据,主要用于反映系统当前及历史运行状态;另一类是 事件数据 ,例如:报警、异常、上线、变更事件,主要用于记录发生事件的详细信息。 如何存储这些海量数据,并提供灵活高效的查询分析能力,一直是我们面临的主要挑战。基于这两种不同类型数据,我们提供了两种不同存储方案: TSDB 作为时序数据存储平台提供了多维度时序数据存储及按维度聚合计算查询能力; EventDB 作为事件存储平台提供了事件/日志数据(半结构、无结构)存储、查询、统计分析功能,是百度智能运维大数据平台核心组成部分,以其海量存储能力及灵活分析能力在故障定位、故障诊断、根因分析、关联分析中发挥着不可替代的作用。 本文主要介绍EventDB系统架构、集群规划以及未来发展方向,希望跟业界同行一起交流学习。 系统架构 EventDB是百度智能运维团队基于ElasticSearch构建的一套海量事件数据存储计算平台,整个平台由两部分组成: 流量接入层 、 存储计算层 ;通过分层将平台核心功能与辅助功能进行隔离,架构上更加便于功能扩展和维护。 流量接入层 一个高可用数据存储平台,需要 流量管控能力 , 单机房容灾能力 以及 流量统计分析能力 ,因此我们引入了统一流量接入层,统一流量接入层基于OpenResty开发,负责接收、转发所有请求和响应,主要包括如下几个部分: 流量管控 平台通过HTTP接口对外提供服务,为了保证平台稳定性和安全性,需要对非法请求进行拒绝,对流量异常突增进行限流,以及对访问接口进行规范统一,具体提供以下几个功能: 流量鉴权 :每个接入业务方需要申请 访问Token ,只有带合法Token的请求才会被允许访问,能有效防止平台被乱用; 配额限流 :基于Token分配流量配额,当业务访问流量超过最大配额,该业务后续请求将被拒绝,主要是防止平台被超额使用以及异常流量突增造成平台不稳定; 统一API :对ElasticSearch提供原生API进行屏蔽封装,提供更加易用的API,降低使用成本和不规范操作带来的稳定性风险。 服务容灾 为了提升平台容灾能力,我们在存储层构建了主备集群,通过流量接入层提供的主备双写和流量快速切换能力,能达到单机房故障时对上层业务无损。 主备双写 :对于写入请求支持自动主备双写; 流量切换 :对于查询请求,支持自动主备切换,能根据关键指标异常快速切换查询流量到备集群,大幅降低故障恢复时间。 以上功能实现不需要业务接入方做任何特殊处理,所有处理对业务方完全透明。 流量分析 作为存储平台我们需要各种统计指标来衡量当前集群状态、流量分成、平响以及请求成功/失败率,而存储层无法提供这些指标,需要在流量接入层来实现。 流量统计分析 :提供基于Token的 流量统计 、 平均响应时间 、 成功/失败请求 等指标统计。 存储计算层 作为最终事件数据存储计算引擎,我们选择使用ElasticSearch来满足海量数据存储及复杂统计分析需求,主要使用ElasticSearch如下功能: 海量存储: 支持TB级海量数据存储; 查询统计 :支持基于属性、关键字模糊查询等多种查询方式; 分布式/水平扩展 :支持将数据进行分片后存储到多个节点上,并支持水平扩展; 数据高可用 :支持数据分片副本,同一个分片可以有多个副本保存到不同节点上。 平台架构图 集群规划 作为百度智能运维大数据核心存储平台,其 可用性 高低直接决定了上游业务系统可用性高低,为了保证高可用性,平台需要具备有效应对不同故障场景的能力:单机故障场景下,平台依赖ElasticSearch自身容错能力,自动移除故障节点并将故障节点上的数据迁移到其他节点;单机房故障场景下,我们通过 主备集群切换 来最大程度保证平台可用性。 双集群 我们建立了两套ElasticSearch集群作为 互备集群 ,由接入层负责主备双写并保证数据最终一致性。当一个集群发生故障,也是由接入层来负责查询流量切换,对终端用户而言就像使用一个集群。 主备双写 :为了不影响写性能,我们采取同步写主集群异步写备集群的方式,主集群写入成功即代表写入成功,请求立即返回,无需等待备集群也写入成功; 主备一致性 :上述主备双写策略会导致主备数据不一致,为了降低这种数据不一致对业务造成的影响,前期我们主要通过主备数据不一致监控来发现问题并通过工具来修复,后期我们通过程序进行备集群失败重试,重试多次依然失败的情况下会记录日志,然后程序会定期加载错误日志重试,保证主备数据最终一致性; 流量切换 :为了降低平均故障恢复时间到达快速止损目的,平台会依据失败请求、平均响应时间等指标是否异常来判断集群是否发生故障,当连续几个决策周期都发现指标异常,会将查询流量自动切到备集群。 部署模式 单个ElasticSearch集群由如下三种不同功能节点构成: Coordinator Node :协调节点,请求解析/转发,不存储数据; Master Node :集群管理节点,维护集群节点信息,索引元信息; Data Node :数据存储节点,处理读写请求。 该部署模式各节点职责清晰,非常便于有 针对性 地优化扩容:存储容量不足扩容Data Node,读写流量增长扩容Coordinator Node,Master Node只负责管理集群;这种部署模式也是ElasticSearch官方推荐的模式。 平台建设初期因为数据量和流量规模不大,我们ElasticSearch集群没有Coordinator Node,由Master Node充当Coordinator Node角色既负责整个集群的管理又负责解析转发用户的读写请求。随着数据量和访问量规模越来越大,Master Node压力越来越大,尤其是当集群中有节点重启时,因为涉及到分片再分配,整个过程耗时一个小时以上,后来调整部署模式加入Coordinator Node之后,Master Node压力骤减整个节点重启恢复耗时不到十分钟。 部署架构图 未来展望 当前我们主要使用ElasticSearch海量存储和简单查询功能,大部分计算分析工作是由各个业务端实现,其实ElasticSearch除了海量存储能力,其在 数值聚合计算 、 关联查询 、 模糊 匹配 方面都有非常好的支持,未来我们期望能更进一步挖掘ElasticSearch在计算分析上的潜力,将大数据存储和分析功能进行整合形成一套统一的大数据存储分析平台,对终端用户而言只需要通过接口表达需求就能直接获得分析结果。 原文链接地址: https://developer.baidu.com/topic/show/290325
来源:OSCHINA
发布时间:2019-09-12 17:22:00
本文作者:AIOps智能运维 作者简介 四金 百度高级研发工程师 负责百度智能运维(Noah)监控平台的设计和研发工作,在监控系统的配置管理方向有广泛实践经验。 干货概览 随着软件系统的发展,监控目标场景越来越广泛,对监控系统的能力要求也越来越高。对于监控系统来说,从能力上看基本可以划分为 数据采集、数据计算、数据存储、异常检测、报警处理以及监控可视化 六块。为了更好应对大规模、复杂化的监控业务场景,我们不仅仅需要在具体监控能力上做深、做强,还需要建立对应机制来统筹这些能力一起良好协作。今天的这篇文章就为大家介绍监控系统的神经中枢—— 配置管理与分发系统 ,让我们一起揭开它神秘的面纱吧! 需求 在业务系统发展的初期,由于场景简单,对监控的需求也比较简单,比如仅采集默认的机器监控数据,不需要进行进程、日志等监控能力。同时监控的规模也相对较小,用户的配置数量一般在百或千级别,这时只需定期读取数据库中的配置就可以很好的工作。 随着业务系统的快速发展,业务体量越来越大,业务复杂度越来越高,对监控的需求也越来越高。传统的简单读取数据库配置在 业务扩展性 和 性能 上遇到了挑战。 1 支持不同类型的配置管理 监控指标采集、计算、报警等方面的配置种类越来越多。如物理机的机器资源类指标、应用程序的进程和日志指标的采集配置、站点的连通性采集配置、服务器的宕机检测任务配置、多个实例间指标的计算任务配置、指标数据的异常检测配置、告警信息发送配置等。 2 支持不同场景的配置分发 高并发配置下载场景 :监控系统中每个物理机部署一个Agent用于对部署在该机器上的应用程序相关指标进行采集。Agent需要下载与主机关联的采集配置,在大规模的监控系统中,Agent的数量达到百万级别,采集配置的更新周期为10s,配置分发系统需要应对高并发查询的压力。 一致性配置下载场景 :为了应对高可用、大规模的数据计算及报警场景,各个子系统往往使用集群化部署。以报警集群为例,同一机器的数据可能会由报警集群的不同实例进行判断,若配置在集群内不一致,那么报警系统的行为就会变得不可预期。 3 支持故障快速恢复 配置分发系统作为监控的枢纽,关联的模块比较多,系统故障会影响采集、计算、报警子系统工作的正确性以及用户新加监控配置不生效,所以需要相应的方案来保障监控系统的快速恢复或重建能力,来保障监控系统的可用。 图1 配置管理&分发系统交互流程图 方案 梳理完问题、需求,接下来就要针对这些问题进行相应的改造升级。下面我们从技术的角度介绍下如何解决这些问题吧! 一、 支持配置可扩展性 复杂的监控能力意味着监控系统的 配置种类灵活多样 。如果直接将配置分发模型与业务模型对接,意味着业务上的每次改动都需要配置下发系统进行对应的变更。那么如何统一配置的多样性,做到配置下发对上层业务透明呢?答案是: 归类+抽象 。 将不同的配置按照“ 目录 ”进行分类管理,实现统一的配置管理需求。 以 文件 作为载体,所有配置都以文件的形式进行管理。不同的文件内容格式代表着不同类型的配置,原有格式的升级以及新类型的添加统一抽象为文件处理,增强了系统的扩展能力。 通过 代码管理 系统管理文件的方式,实现变更历史追踪。通过对文件系统的定时备份与构建快速故障恢复机制提升系统的可用性与可靠性。 图2 配置归类&抽象 在归类方面,由于不同的能力需要的配置形式不同,我们以此为依据进行分类。对应到实现中则通过目录表达分类的含义,通过子目录来表达配置的层级与归属等关系。这里我们将配置目录的层级分为监控功能层级->用户层级->应用层级三层,在应用目录下将具体配置(如进程监控配置、日志监控配置)写入文件中。 二、 确保一致下发 通过对配置管理方式的抽象与归类整理,配置的一致性下发可以通过构建配置文件内容的一致性机制解决。我们使用“ 版本 ”作为文件内容一致性机制的核心。当用户变更配置时,配置管理系统会生成一个全局唯一的版本描述此次配置变更操作,版本中包含此次变更操作对应的配置文件变更详情。 配置下发时,在各个子系统会定期检测配置版本差异并更新本地配置至最新版本,从而保证配置在每个更新周期内保持一致。 三、 应对高并发压力 大规模的压力则主要体现在采集Agent的配置下发部分。由于Agent只需拿到部署主机所需的监控配置,因此将配置文件按照监控的最小单元进行拆分,并按照规范进行打包。 图3 Agent配置拆分&下载流程 当前的业务部署往往采用 混布方式 ,同一主机中会部署多个不同类型的应用程序。为了支持这种监控场景,主机上部署的采集Agent会查找主机上部署的应用并下载对应的多个应用配置。当业务规模增大时,物理机增多,配置下载请求也会成倍增长,因配置存储的Server端很容易达到 性能瓶颈 。通过可水平扩展的静态文件下载服务来应对高并发下载压力,通过ETag方式检测文件是否变更,只针对变更的配置文件才进行传输以减少下载流量,最终满足了百万级主机、千万级实例的配置下发需求。 四、 故障快速恢复 考虑到配置在监控系统中的重要程度,为了保障业务的可用性,就需要构建监控系统的快速故障恢复机制。 同时,由于监控系统配置集中管理,随着系统的发展,配置的体积也在不断增长,配置文件体积达到数十GB级别,并且全部由小文件组成,文件个数也达到了数百万级别。为了减轻系统压力,在系统中加入“ 快照 ”机制,每隔一段时间便生成当前全量配置的快照,当出现大量更新时,先通过“快照”减少更新量,再通过对应同步机制进行少量的文件更新。 总 结 经过不断的努力和多次改造,目前的配置管理及分发可以满足监控系统的需求。由于能够灵活管理多种配置,并且快速、一致地送至各个系统。在面对故障场景时候,也可以及时撤回配置,避免更大的损失。然而随着监控业务的发展,系统架构的变迁,起着中枢作用的配置管理与分发系统将会面临新的挑战。路漫漫其修远兮,吾将上下而求索! 原文链接地址: https://developer.baidu.com/topic/show/290323
来源:OSCHINA
发布时间:2019-09-12 17:20:00
本文作者:AIOps智能运维 作者简介 张洋洋 百度高级研发工程师 负责百度智能运维产品(Noah)的分布式时序数据库和通用配额管理平台的设计研发工作,在分布式存储和配额管理方向有广泛的实践经验。 干货概览 通过百度大规模时序数据存储系列文章的介绍,想必读者对百度智能监控系统Noah的TSDB不再陌生,它主要用来存储Noah监控系统的 时序指标数据 ,包括但不限于硬件和软件的可用性指标、资源使用率指标和性能指标等。如《百度大规模时序数据存储(二)|存储选型及数据模型设计》文章所述, Noah-TSDB是基于HBase为底层存储的基础上自主研发的,其优秀的性能离不开HBase的贡献。今天主要聊聊在百度智能监控场景下的HBase相关实践经验,先简单介绍一下HBase。 HBase架构简介 HBase是一个基于Java、开源的、非关系型的、面向列存储的分布式可扩展的大数据存储数据库。HBase的集群主要由HMater和RegionServer两种角色组成,底层以HDFS作为存储设施,集群由Zookeeper协助管理。其架构如下图所示: 简单介绍一下HBase中相关组件的作用: HMaster HMaster 是整个集群的 大脑 ,负责数据表的操作、集群的负载均衡和故障恢复等集群管理工作。 RegionServer HBase 将表以行为单位划分成许多 片段 ,每个片段称为一个 Region。这些Region被分配到RegionServer进行管理。在读写流程中,定位到数据所在RegionServer后,Client与RegionServer直接交互进行数据的读写。 Zookeeper HBase作为一个大规模的分布式系统,Zookeeper的作用是至关重要的。首先Zookeeper作为HMaster HA解决方案,保证了至少有一个HMaster处于工作状态。其次Zookeeper通过心跳机制探活RegionServer,当RegionServer故障时及时通知HMaster进行故障处理工作。最后Zookeeper保存了维护全局元信息的META表的路径,Client第一次与HBase集群交互时,需要通过META表来获取目标数据所在的RegionServer。 上面简单介绍了HBase的架构和各组件的基本信息,下面和大家分享一下在百度最大规模时序数据库的场景下使用HBase时遇到的几个典型问题和优化方案。 热点问题 大家都知道木桶效应,对于TSDB系统来说,热点Region所在的RegionServer就是影响整个”水桶”容量最短的那块木板。理想情况下HBase 中所有的请求应该均匀的分布在所有RgionServer的所有Region 上,当个别Region收到的读写请求数量大幅超过其它的Region,它所在的Region就有可能成为热点。 Noah-TSDB初期曾遇到监控元数据表设计不合理导致热点的问题。当时研发同学收到Noah-TSDB 写入模块队列堵塞 的业务报警,从Noah监控系统上看到同时间段访问HBase异常明显增长。HBase中的个别RegionServer频繁进行GC,网络 I/O 和磁盘 I/O 密集,操作队列中待执行的请求堆积严重,负载明显高于其它的RegionServer。查看异常RegionServer的日志发现大量请求访问的是同一个Region:”tsdb-meta,*** 1.”。初步定位是由于该Region 负载过高 ,导致它所在的RegionServer成为热点,进而导致系统的吞吐量下降,上游写入模块请求堆积。 tsdb-meta是用来存储监控指标的名称、周期等元信息的表,表中红色填充的行代表其拥有数据量超过正常水平的,表结构如下: 分析上面的存储结构,我们可以知道: 同一个监控对象(namespace)的监控指标元信息将会存储在 HBase 表的同一行。 不同监控对象的指标数量不同,将导致行的大小不均匀。 HBase中数据分片是以行为单位,每行的数据存储在同一个Region中,当某一行存储的监控指标数量远大于正常水平时,该行就有可能成为热点。 综上所述,当个别监控对象拥有的 监控指标个数过多 时,tsdb-meta可能会出现热点问题。同时经我们验证发现,成为热点的监控对象拥有的监控指标的数量大约是正常水平的20倍左右,进一步确认了故障原因。 定位到根因后,我们决定从两个方面来着手解决这个问题。一方面, 定期统计监控对象拥有的指标个数 ,及时发现由于监控配置异常和不合理使用导致的个别监控对象拥有的监控指标过多的问题。第二方面,对tsdb-meta表结构改造,将原来按列分布的数据修改为 按行展开平铺 ,充分打平数据,利用HBase按行自动分片的机制来达到负载均衡的状态。第一方面主要是从业务层面对不合理使用的情况进行人工干预。今天主要着重介绍第二方面。 tsdb-meta表Schema改造 前文大体介绍了表结构改造的思路,避免单行数据过大导致热点问题。我们将监控对象和监控指标的名称信息一起作为行键,只保留一列用于存储指标的其余信息,避免了因单行数据过大导致的热点问题。 预分区 tsdb-meta表优化后,我们发现生产环境存储数据的 tsdb-data表也存在热点问题。tsdb-data是用来存储监控指标数值的表,生产环境是按时间跨度进行分表,每两天的数据存储在一张表中。数据的行键由数据hash后的特征变量ts_uid和时间基准timestamp_base组成,利用HBase存储时按行键的字典顺序排序的特点,将不同的监控指标的数据散列到不同的Region,相同监控对象的指标数据顺序排列,达到优化查询的效果。由于tsdb-data表的日常访问量基数较大,当某个监控对象拥有的指标数量高于平均水平,那么该监控对象的监控指标很大概率会被分配到相同的Region,导致该Region过大,进成为热点,集群会分裂过大的Region来维持负载均衡的状态。频繁的分裂操作会占用大量资源,影响RegionServer的吞吐量。为解决因Region过大导致的热点,我们采用了 对数据表进行预分区 的方法。 在对tsdb-data表进行预分区时,我们发现只通过指定Region数量来实现预分区的效果并不理想,因为会出现实际写入量与槽位分配不均的问题。HBase数据表是按照行键的字节空间均匀划分而不是按照实际存储的数据量进行划分。如下图所示,图中红色方块代表实际存储的数据,白色的方块代表无实际数据的行。 如上图,虽然数据表已经按照行键的字节空间划分成3个Region了,但是明显Region 3中实际存储的数据量远大于Region 1和Region 2。这种情况下Region 3有成为热点的可能性。为了改善这种情况,Noah-TSDB结合了生产环境中的tsdb-data表按等间隔时间跨度分表的特点,决定参照历史表的使用情况对新表进行预分区。根据生产环境实际产生的行键和预期的分区大小计算出Region分界值,然后根据分界值将表划分成实际水位相近的Region,这样虽然每个Region的槽位大小不一样,但是每个Region实际存储的数量是相当的,进一步降低产生热点的风险。 如何合理的设置Region数量 在前文介绍的预分区策略中,除了需要参考生产环境的实际使用情况外还需要根据机器资源和分裂阈值等系统参数来预估合适的Region大小,Region大小确定后,我们可以预估出整体的Region数量。那么如何判断当前集群是否能够承载调整后的Region数量呢?如果Region的 数量不合理 有哪些危害呢?在讨论Region数量对集群的影响之前,我们先了解一些基础知识: 在HBase的数据写入流程中,数据是先写到Memstore(写缓存)排序,然后异步Flush追加到HFile中。一个Region中的多个列族对应多个Memstore,Memstore Flush的最小单位是Region。 当一个RegionServer中所有Memstore的大小总和达到阈值hbase.regionserver.global.memstore.upperLimit * hbase_heapsize会触发Memstore Flush。根据 Memstore从大到小依次Flush,直至MemStore内存使用量低于阈值 hbase_heapsize * hbase.regionserver.global.memstore.lowerLimit。 HBase 会定期Flush Memstore来保障Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行Flush导致的问题,定期的Flush操作有随机延时。 综上可知,一方面由于同一个RegionServer共享Memstore,Region数量过多会导致Memstore Flush的频率变快,产生的HFile变多,HBase持续的进行 Compaction,引发 合并风暴 。另一方面由于HBase定期Flush Memstore,每次执行 Flush需要将每个Region的每个列族对应的Memstore写入文件并存到HDFS, Region数量越多,每次需要一起处理的文件数量就越大,即使存在随机时延机制,短时间内文件的创建和数据的迁移较多也会加大集群负载,可能引起快照超时、客户端超时和批量加载超时,降低TSDB系统性能。因此Region数量过多会降低系统吞吐量。 Region数量过少也会 降低系统性能 。当数据量不变的情况下,由于Region数量过少导致单个Region过大,每个Region处理的写入请求数偏高,当Flush的速度慢慢被写入速度追上并赶超时,就会堵塞写入,影响RPC,进而影响HBase的整体写入和查询,降低系统的吞吐量。 Region数量设置不合理,会降低TSDB系统整体性能与可靠性,一般推荐的单个RegionServer管理的Region数量计算方法如下: #{Region} = (RS memory)*(total memstore fraction)/((memstore size)*(# {column families})) 举个例子,如果RegionServer的参数如下: Java Heap Size of HBase RegionServer in Bytes设置的是20G hbase.regionserver.global.memstore.upperLimit是0.4 hbase.hregion.memstore.flush.size是128M 多个表的列族个数共2个 那么 #{Region} = 20 * 1024 * 0.4/ (128 * 2) = 32。这个公式是在假设所有的Region都在以相同的速率写的前提下,如果实际只有部分Region在写入数据,结果可以根据比例、结合业务进行调整。例如Noah-TSDB的场景下,数据是按照时间分表,一般两天的数据存在一张数据表中,数据的写入都集中在最近的一张表,因此实际写入活跃的Region数量远小于Region的总数量,所以实际每个RegionServer管理的Region的数量大约是通过上述公式直接计算结果的3倍左右。 预估出整体的Region数量和单个RegionServer管理的Region数量后,就可以合理的进行 容量规划 ,在集群调整的时候预估需要的机器资源。 总 结 上面就是今天介绍的全部内容了,给大家简单分享了一些使用HBase的实践经验。其实在实际使用时我们也发现了HBase过重,运维成本较高等问题,也在持续的进行调研和架构升级,大家有什么好的建议欢迎不吝赐教。另外文中如果有理解不到位或者偏差的地方,欢迎大家指正。 阅读推荐 原文链接地址: https://developer.baidu.com/topic/show/290322
来源:OSCHINA
发布时间:2019-09-12 17:19:00