阿里云2020云原生编程挑战赛

一共四道题,在此记录一下。原广告链接:https://tianchi.aliyun.com/specials/promotion/cloudnative

【赛道1】实现一个分布式统计和过滤的链路追踪

用户需要两个程序,一个是数量流(橙色标记)的处理程序,该机器可以获取数据源的http地址,拉取数据后进行处理,并与后端引擎(蓝色标记)通信, 最终数据结果在后端引擎机器上落盘。

enter image description here

【赛道2】实现规模化容器静态布局和动态迁移

赛题背景

阿里每年双11不断的创造奇迹的背后,是巨大的资源成本投入,用以支撑峰值流量。每年各种大促、基础设施的升级都有可能会涉及到单元、中心机房站点变化,而这些站点的迁移、变化,我们可能会短时间借助离线资源、云上资源等,也可能会评估采购物理机,无论是采用哪种方式,我们都期望控制成本用尽可能少的资源成本满足当下站点需求。

日常态,我们使用较少的资源满足业务流量需求,但备战各种大促态尤其是双11,容器平台会承担数十万容器扩容诉求,支撑0点峰值流量,满足用户买买买的需求,同时也使得机器资源成本大幅度提升。

在大促规模化场景下,我们希望通过合理容器静态布局及动态迁移能力,在保证业务稳定性前提下,用尽可能少的机器资源来满足大促资源成本的节省,因而集群规模化容器布局成为了我们的关注点。

静态布局

容器平台规模化建站

  • 每年双11大促,阿里容器平台会提前规划多个机房单元流量、容量分布。此阶段各机房单元站点,可能经历从0到1的新建,也可能经历从1到N规模化的容器布局。使得交易流量从日常态倾于大促态。然后经受第一轮全链路建站压测。

动态迁移

集群变化后容器平台按诉求规模化容器迁移。

  • 大促建站后,通过良好的资源运营方式,集群符合90%的大促态,但业务团队会通过全链路压测、特殊的业务大促活动补容/缩容诉求等,使得各个集群资源状态发生变化。
  • 大促结束后,交易流量会从大促态倾于日常态,在此过程中,集群容器会缩容至日常态,同时集团的机器资源会退水并分时复用于其他团队。

赛题准备

根据下面的单元化集群描述,我们会抽取机房单元中容器、宿主机信息并结合站点集群的变化来准备赛题。

  1. 多单元机房规模化容器布局 enter image description here
  2. 单元机房集群站点变化

enter image description here

  • 初始态: 基础设置环境准备,涉及内核、网络、中间件、存储、集群管理等等验证准备阶段,此时没有任何交易业务容器,涉及到跨bu多团队的协作。
  • 站点火种: 较少的机器、业务容器,用于支撑极少交易流量(如每秒1k笔 / 全局1%%流量),用于验证交易全链路的正确性,并为未来快速规模化扩容埋下火种。
  • 中间态: 较多的机器、业务容器,用于支撑一定规模交易流量(如每秒万笔流量),成为一个长期、平稳日常态单元集群,抑或通过建站使得集群规模化补容到濒临大促态。
  • 大促态: 集团大面积封网、全链路压测稳定,迎接大促。
  • 一个不够形象的串联解释: 当我们想要搬家时,购买了很多储物箱(初始态),但还没到搬家的日期,空闲时先收拾了一部分衣物在部分储物柜中(站点火种),当真正要搬家之前,此阶段可能还要补充储物柜、清理旧物品等,然后陆陆续续的整理好了一个个的储物柜(中间态),,直到真正搬家时候(大促态)。

赛题内容

  1. 规模化容器静态布局场景。我们准备了确定数量多规格机器资源,使得在满足调度约束条件下,确定数量、多种类的应用容器能在这些机器资源上满足扩容诉求,保证建站的确定性(赛题中我们会给出充足的机器,但真正建站场景我们会先通过计算预估机器)。
  2. 规模化容器动态迁移场景。我们会准备一个集群容器静态布局数据,此时集群是一种碎片态。然后通过容器的迁移,按照规则要求尽可能腾空机器资源,过滤空机器,使得碎片态集群现状重新成为饱满态。

赛题挑战

一个单元集群,数万容器、数千主机、核心业务调度约束,综合起来资源碎片问题会更加放大,而这些单元机房可能根本不会有容器的新增,集群会成为我想象中的”饱和态”

静态布局

业界多数调度器均通过不同的策略打分通过queue以one-by-one的方式调度容器,设计上并不感知后续容器信息,串行扩容顺序会左右调度结果,进而极大地可能会导致资源的浪费。下面通过三个容器实例、三台宿主机举例:

enter image description here

动态迁移

enter image description here

赛题数据

应用描述

  1. 集团中的应用信息实际以 “应用名 应用分组” 两个维度的形式呈现,同一个应用名下会有多个应用分组,不会出现一个应用分组对应多个应用名。对应关系是一对多的关系(appName:group=1:N)。
  2. 集团内部应用与应用、分组与分组之间有各种调度规则约束,如应用分时错峰、CPU/内存密集型应用交错布局,但本赛题简化问题描述,不增加这些复杂约束。
  3. 集团应用根据不同画像特性,cpu-model定义了cpu-share、cpu-set 两种类型,内存超卖技术还未规模化铺开,此处我们考量的是保证交易的稳定性的双11大促单元集群中的cpu-set模式、固定内存的核心应用。

【赛道3】服务网格控制面分治体系构建

问题背景

阿里拥有百万级别的服务实例。虽然通过 Nacos 可以解决超大规模服务发现问题,但服务网格的先行者 Istio 却一直是单体全量数据,不能横向扩容。因此我们找个办法,通过让不同的 Pilot 实例负责不同的 Sidecar 即分治方式来支撑超大规模的系统。然而这里的一个挑战是:如何分配 Sidecar 才能让 Pilot 集群整体负载均衡 ?

为了更好地说明问题,我们先将问题简化成以下模型:

img

这里说明如下:

  • 三个部分,从上至下分别是:注册中心、Pilot 集群以及应用;
  • 每个应用中 Sidecar 依赖的服务都是一样的,不同应用依赖的服务可能有重叠;
  • 应用可能随时增减、应用的 Sidecar 也可能随时增减;
  • 每个 Pilot 依据连接上来的 Sidecar 实例需要的全部服务,向注册中心发起服务订阅,将结果存于内存中并推送给他们。

由此可见,Pilot 单个实例承压为:Sidecar 连接量 + 服务数据。服务数据越大,意味着推送前处理的数据越多;连接数越多,意味着需要推送的数据越多。因此,这里的性能优化便成了设法降低这两个值,这体现在 Pilot 集群里便是让 Sidecar 连接更均匀,以及减少 Pilot 服务加载的重复度。但这并不简单,因为我们尝试优化一个时,另一个就会变差。例如,如果我们采取随机连接的方式,那么 Sidecar 连接将会比较平均,单个实例的连接数便较小,但每个 Pilot 加载的服务量增加,因为加载的是 Sidecar 依赖的并集;

img 图1

如果我们采用散列函数按服务依赖映射至 Pilot,单个实例加载的服务会减少,但是多个应用可能会映射到同一 Pilot,连接数又会增加,造成不均匀问题(如图2所示)。

img 图2

所以,我们需要寻找一个最优解。

问题描述

首先我们简化问题,只在应用维度进行 Sidecar 分配,同时为了更好的地理解问题,我们进行以下抽象:

  • 已知:
    • 提供一个应用列表 A,以及每个应用对应的 Sidecar 数量 n;
    • 对于每个应用 a,提供一个服务依赖列表 D;
    • 已知一个 Pilot 集群,并有 m 个实例。
  • 条件:
    • 为了保障 Sidecar 由访问的顺畅,防止惊群,在整个过程中 Pilot 的加载的服务只增不减(即使应用迁移到其它 Pilot 上,加载的数据依然存在),请谨慎调整应用连接的 Pilot;
    • 需要支持应用增减,Sidecar 扩容、缩容,变化后每个 Sidecar 需要的服务需要在对应的 Pilot 上找到,不得缺失。
  • 求:
    • 每个 Pilot 实例需要挂载的应用列表 P(L)(i ∈ [0, m-1])。

【复赛预览】实现一个 Serverless 计算服务调度系统

赛题背景

Serverless 计算服务(如函数计算)根据实际代码执行时间和资源规格收费,让使用者无需为闲置资源付费,背后是服务方采用各种手段优化服务端资源利用率。在优化的过程中,一个关键的问题是平衡请求响应时间和资源利用率,即如何在响应延迟可接受范围内使用尽可能少的资源。Serverless计算服务支撑场景的多样性以及不同应用对于响应延迟的不同要求,也给优化带来了一些额外的空间。本题目是关于如何设计和实现一个 Serverless 计算服务的调度系统。

赛题设置

  • 系统分为APIServer,Scheduler和ResourceManager 3个组件,其中APIServer和ResourceManager由平台提供,Scheduler由选手实现。
  • APIServer提供了Invoke API,该API接受函数名称和event,返回执行结果。
  • ResourceManager提供了ReserveNode API获得Node和ReleaseNode API释放Node,该Node上有容器管理服务,选手可以调用管理服务在Node上为函数创建和销毁容器。其中函数实现已提供,选手无需关注。
  • 当APIServer接收到Invoke请求后会调用Scheduler的AquireContainer API获得执行函数需要的容器,在该容器上执行函数,执行成功后调用ReturnContainer API归还容器。

指标定义

  • 资源使用时间:获得Node成功后开始计时,同一Node释放成功后结束计时,其差值为资源使用时间。
  • 调度延迟:调用AquireContainer所需时间。

赛题描述

评测程序会以不同QPS调用多个函数,计算调度延迟和资源使用时间,按照一定规则对结果评测。


带内管理 带外管理?

存储设备的管理方式分为带外管理和带内管理两种方式,这两种方式通过不同的通道,均可实现对存储设备的管理。

SNMP、RMON、Web、TELNET这些管理方式属于带内管理。

通过CONSOLE、AUX接口管理和通过某些高端设备具有的100BASE-TX的带外管理端口进行管理的方式属于带外管理。

实现方式

具体实现方法如下:

  • 带外管理:存储设备的管理控制数据与用户业务数据信息在不同的链路传送,即管理一条线,业务一条线。典型的连接方式为:维护终端连接存储设备管理网口或串口,对存储设备进行管理;应用服务器通过业务网口连接存储设备,传输业务数据。这两条链路互相独立。《产品文档》中《配置指南》等手册描述的即为这种方式。
  • 带内管理:存储设备的管理控制数据与用户业务数据信息在相同的链路传送,即管理和业务同一条线。典型的连接方式为:应用服务器通过业务网口连接存储设备,通过在应用服务器侧安装Inband Agent软件(该软件属于HostAgent软件包的一部分),来实现在传输业务数据的同时,也能对存储设备进行管理。

区别

  • 可靠性:带外管理由于业务数据和管理数据分别存在于两条独立链路,故在业务链路出现故障时,仍可通过管理链路登录存储设备进行管理。
  • 管理方式:带外管理可以既可以登录ISM管理软件界面,也可以登录CLI命令行界面,提供了多样化的管理方式。带内管理一般情况下只提供ISM管理方式。
  • 组网:带内管理由于管理和业务在同一链路上,故可以减少连接网线的数量。
  • 功能:带内管理可以协调应用服务器和存储设备共同运作,故在某些情况下可以提升存储设备的性能。

带外管理的作用

减少运营成本、提高运营效率、减少宕机时间、提高服务质量。

带外管理使用场景

网管系统必须通过网络来管理设备。如果无法通过网络访问被管理对象,带内网管系统就失效了,这时候带外网管系统就排上用场了。

运维人员和IT设备不在同一个物理地点。这种类型的网络环境包括所有的电信运营商和银行及有分支机构的政府、企业网络。一旦设备故障无法通过网络解决(telnet 、pcanywhere等手段),运维人员只能到现场解决问题。这种类型的网络通过带外网管可以大幅提高网络运维效率,同时有效降低运维成本。

运维人员与IT设备在同一地点,IT设备数目很多统一管理面临很大难度。这种类型IT环境包括所有IDC、企业的数据中心(非托管)、互联网公司、游戏运营商。运维TEAM需要面对几百台甚至上万台服务器,对设备的访问控制授权、操作记录均需要借助带外网管来完成。

参考资料


增强 Kubernetes 网络扩展性和性能 - 阿里

https://mp.weixin.qq.com/s/e4Ph3scexAZKGHA8Yds3rQ

1. Kubernetes Service 性能和扩展性问题

默认的 Kubernetes 的 Service 实现 kube-proxy,它是使用了 iptables 去配置 Service IP 和负载均衡:

image.jpg

如上图所示:

  • 负载均衡过程的 iptables 链路长,导致网络延时显著增加,就算是 ipvs 模式也是绕不开 iptables 的调用;
  • 扩展性差。iptables 规则同步是全量刷的,Service 和 Pod 数量多了,一次规则同步都得接近 1s;Service 和 Pod 数量多了之后,数据链路性能大幅降低。

image.jpg

NetworkPolicy 性能和扩展性问题

NetworkPolicy 是 Kubernetes 控制 Pod 和 Pod 间是否允许通信的规则。目前主流的 NetworkPolicy 实现基于 iptables 实现,同样有 iptables 的扩展性问题:

  • iptables 线性匹配,性能不高, Scale 能力差
  • iptables 线性更新,更新速度慢

使用 eBPF 加速 Service 和 NetworkPolicy 扩展性

关于 eBPF 的介绍如下:

  • Linux 在最近版本提供的可编程接口
  • 通过 tc-ebpf 将 ebpf 程序注入到网卡中
  • 用于大幅降低网络链路的长度和复杂度

image.jpg

如上图所示,使用 tc 工具注入 eBPF 程序到 Pod 的网卡上,可以直接将 Service 和 NetworkPolicy 在网卡中解决,然后直接转发到弹性网卡,大幅降低网络复杂度:

  • 每个节点上运行 eBPF Agent,监听 Service 和 NetworkPolicy,配置容器网卡的 Ingress 和 Egress 规则;
  • 在 Egress 的 eBPF 程序中,判断访问 k8s Service IP 的请求直接负载均衡到后端 Endpoint;
  • 在 Ingress 的 eBPF 程序中,根据 NetworkPolicy 规则计算源 IP 决定是否放行。

image.jpg

PS:我们使用 Cilium 作为节点上的 BPF-agent 去配置容器网卡的 BPF 规则,已贡献 Terway 相关适配:https://github.com/cilium/cilium/pull/10251

性能对比

  • 通过 eBPF 对链路的简化,性能有明显提升,相对 iptables 提升 32%, 相对 ipvs 提升 62%;
  • 通过编程的 eBPF 的形式,让 Service 数量增加到 5000 时也几乎没有性能损耗,而 iptables 在 Service 增加到 5000 时性能损失了 61%。

image.jpg

2. Kubernetes Coredns 性能和扩展性问题

image.jpg

Kubernetes Pod 解析 DNS 域名会 search 很多次,例如上图 Pod 中 DNS 配置,当它请求 aliyun.com,会依次解析:

  • aliyun.com.kube-system.svc.cluster.local -> NXDOMAIN
  • aliyun.com.svc.cluster.local -> NXDOMAIN
  • aliyun.com.cluster.local -> NXDOMAIN
  • aliyun.com -> 1.1.1.1

Coredns 是中心化部署在某一个节点上的,Pod 访问 Coredns 解析经过链路过长,又是 UDP 协议,导致失败率高。

使用 AutoPath 大幅降低 Pod DNS 的查询量

由客户端 Search 变成服务端 Search:

image.jpg

当 Pod 请求 Coredns 时解析域名:

  • Coredns 会通过源 IP 地址查询到 Pod 的信息
  • 然后根据 Pod 的 Namespace 信息,匹配到它真正想访问的是哪个服务直接返回
  • 客户端请求从 4 个直接减少到 1 个,降低了 75% 的 Coredns 请求,从而让失败率降低

在每个节点上使用 node-local-dns

image.jpg

  • 拦截 Pod 的 DNS 查询的请求
  • 将外部域名分流,外部域名请求不再请求中心 Coredns
  • 中间链路使用更稳定的 TCP 解析
  • 节点级别缓存 DNS 解析结果,较少请求中信 Coredns

ExternalDNS 直接使用阿里云的域名解析服务

  • 云原生的 DNS 解析,Pod 直接请求云服务 PrivateZone 中的自定义 DNS 能力;
  • 由 ExternalDNS 监听到服务和 Pod 创建后配置 PrivateZone 中的域名解析;
  • 和原生的 ECS 解析同样的 DNS 解析性能。

image.jpg


【Calico系列】5 kubernetes BGP 更新报文

前言

Calico 是容器网络的一种解决方案,提供了纯 3 层的网络模型,每个容器都通过 IP 直接通信,中间通过路由转发找到对方。容器所在的节点充当传统的路由器,提供了路由查找的功能。

我计划将 Calico/BGP 相关的内容都过一遍,把这个过程记录下来。本篇是对 bgp 报文的抓包记录,便于在学习bgp中更有实感。

由于网络的水深与个人能力有限,本文不免存在错误之处。如有疑问,请查阅文末参考资料或与我线上/线下讨论。

背景

kubernetes 部署在裸金属上,使用 calico BGP 并与交换机交换路由,可以充分发挥硬件的速度优势。具体部署架构可参考 calico 官方的部署模式 Calico over IP fabrics #The AS Per Rack model, leaf-spine 二层架构:

img

其中 Spine 与 Leaf 之间使用三层 ebgp 互联,每个机柜是一个 AS,机柜内使用 ibgp 协议互联,柜顶交换机与核心交换机使用 ebgp互联。

数据中心BGP路由协议规划

BGP 最初是为不同自治系统之间的互通设计的,它也可以用在数据中心内部。现代数据中心中使用最广泛的路由协议就是 BGP。但在 BGP 引入到数据中心场景时,网络规划需要考虑不少问题。

如下图是某厂商典型的三级CLOS数据中心组网:

img

BGP设计要点大致如下:

  • 为Tor、Leaf、Spine设备规划 AS号
  • 设备间建立 BGP 邻居
  • 为CLOS网络生成 ECMP 等价路由
  • 对不同类型的 BGP 路由进行路由属性控制
  • 指定路由传递规则
  • 使用双向转发检测协议(BFD)加快故障收敛

对于以上涉及要点,Kubernetes 裸金属需要完成如下基础配置:

  • BGP计时器,keepalive/hold timer

    配置为1S/3S,在数据中心内部,故障的快速收敛更为重要,配置为1S/3S加快收敛。

  • 发布路由通告的间隔,Advertisement Interval

    配置为0,立即发布。

  • bgp log-neighbor-changes

    启用交换机侧BGP日志功能,BGP邻居关系建立以及断开时会生成日志信息。

  • BGP ECMP

    不同交换机支持可能不同,一般默认为8,推荐配置为 32。

抓包

假定主机使用 bond0 网卡与集群内外部互访,使用如下命令抓包

$ sudo tcpdump -w `hostname`-` date "+%m%d-%H%M"`-bgp.pcap -i bond0

由于配置了BGP keepalive为1s,我们观察到每隔1s会有一次心跳包检测。

1587390182337

路由更新

在这里我简单地用两个 Tor ebgp 互联,架构如下:

   (ebgp)|-------------------------|(ebgp)
         |                         |
   (172.16.0.26/30)          (172.16.0.22/30)
        Tor1(AS65412)            Tor2(AS65413)
  (10.90.0.254/25)            (10.90.0.126/25)
          |                       |       |
          |(ibgp)                 |(ibgp) |(ibgp)
        ▲131                      ▲85     ▲86

▲为服务器,Tor1和Tor2为柜顶交换机

86节点向 Tor2 广播明10.90.20.128/27 的路由:

1587390389534

交换机 Tor2 添加originator_ID和Cluster_list,用于IBGP防环。而后它 使用 update 信息传给同一个 AS 内的 peer 85

1587390976168

同时 Tor2 向 Tor1 发 ebgp 请求,由于 ebgp 的特性,路由携带的Cluster-list和originator全部消失。

(截图空缺=。=)

然后Tor1向 同一个 AS 内的 peer 发送 update 信息:

1587391270606

结束

本文对 bgp 的 update 报文做了简单的记录,便于新人理解 bgp 协议更新过程。

参考资料