DockOne微信分享(二四五):电视云服务的容器化实践

【编者的话】天脉聚源拥有全球最大规模电视云数据中心,为国内电视台、视频网站、相关机构等提供视频内容加工、互动平台等服务,综合运用视频云计算、大数据挖掘、移动互联网构建“电视+互联网”全新体系,搭建电视媒体连接移动互联网的平台,推动传统电视行业在新的时代背景下酝酿更具生命力的全新生态。

简介

天脉聚源2008年成立至今,深耕广电领域,通过打造电视与移动互联网连接的入口、搭建电视媒体连接移动互联网的平台,推动传统电视行业在新的互联网时代背景下酝酿更具生命力的全新生态。
作为“电视+互联网”核心服务之一是帮助电视台构建全新媒体互动平台,通过摇电视、扫电视等入口将观众和电视与智能手机终端建立实时联系,通过APP、小程序等方式让观众围绕电视当前主题展开互动,比如消息沟通、有奖答题、电视红包等等。从技术角度上,面临的难点主要是因为各个电视台需求差异较大,所以体系复杂众多;另方面,因为电视播出的内容随时间不同,所以其流量负载波动巨大,比如在元旦晚会或农历春晚这些活动中,负载可以在几分钟内从零快速增加到百万级QPS。
后端业务方面,天脉聚源具有目前最大规模的电视云数据中心,处理国内300+套电视节目,依托自主研发的视频云计算、大数据挖掘平台,为电视台,视频网站,媒体机构等提供视频内容加工服务。从技术角度看,涉及大规模存储、任务调度、语音处理、图像处理、自然语言处理、搜索引擎、数据分发等,近几年随着AI技术的快速推进,业务类型也趋于复杂多样。

前端业务系统的容器化实践

天脉聚源的电视互动平台是一个巨大的 TV-SaaS 系统, 每一个租户对应一个电视台、或一档电视栏目,所以需求多,开发团队多,技术栈多(PHP、Go、Nodejs、Java……), 所以平台各个系统和模块复杂多样,对系统的主要诉求是:提高性能,降低成本,稳定可靠。

物理主机 + KVM (虚拟主机)

起初应用部署于自建机房,对于负载较大的场景,可能遇到以下情况:

  1. KVM 本身的操作系统会消耗资源,包括 CPU、内存、IO与存储。很多前端微服务对资源的消耗甚至小于 KVM/OS 的系统资源占用,比如消息服务推送接口单一实例, 并发到 20000 QPS 时,内存占用也不超过 500MB,用 KVM/OS 来调度资源成本上吃亏。
  2. 对于负载波动巨大的系统来说,集群整体硬件资源利用率不高,不能短时间内扩展性能。因为“电视+互联网”应用场景中存在短时负载波动巨大的特点,所以接入公有云服务商是当前最好的选择,当重要节目播出时可快速增加虚拟主机规模以承载巨大的业务请求。

公有云 VPS + 容器

当业务系统托管到公有云后,资源使用方式变成虚拟主机(VPS)+ 容器(Dcoker),每一个容器对应一个独立的功能模块,比如:用户身份验证、互动消息、电视红包、互动答题等等; 当然,大多数业务模块,特别是高负载模块还要具备横向扩展,通过副本(容器数)的增减来调整性能配额。
基于多个容器共享 VPS 资源的部署方式,首要收益就是降低成本,虽然模块众多,但存在时间热度, 一般情况下,同一个 VPS 会部署多个类型容器,流量负载进入集群后,同一时间的 VPS 内部只有小部分容器处于较高负载。
举个例子:一个 VPS 同时有用户登录和互动消息两个应用,当一档电视栏目刚开始时,主要的负载是新用户登录,这个时候 VPS 的 CPU 和 IO 资源主要被用户模块容器所占用;随着节目的进行,登录用户在取得 jwt-token 登录状态后,开始在移动端参与互动讨论,这个时候 VPS 的资源便逐渐转移到互动消息容器。

基于业务重要性,对容器运行资源做差异化分级配置

前面提到,平台模块复杂众多,并且共享 VPS 资源以提高利用率,这里会存在以下情况:

  1. 各个模块软件质量存在差异,系统整体资源极有可能被个别低级错误的应用耗尽,使集群整体性能降低甚至不可用
  2. 如果遇到春晚这种突发负载,公有云服务商可能短时间创建不了大量 VPS,这个时候就需要将部分非关键业务降级甚至暂时关停

所以,需要有资源分级配额方法:

  1. 所有容器不能无限制使用资源,有最大资源限制,超过后强制重启。Docker 原生支持 CPU、RAM、IO 这些配额设置,对整体可用性和稳定性的提高简单有效
  2. 关键业务多给资源,次要业务逐级降低资源配额

Docker 资源配额的设置

Docker 官方支持多种方式对资源配额做出控制:CLI 默认命令行工具;基于 HTTP APIs 接口;基于 Golang 的 SDK。

CPU 配额,核心数绑定
Docker 对 CPU 时间片使用可以细化到毫秒,大多数负载波动较大的场景中,这个参数可以超额配置(对应 docker-cli –cpus 、 –cpu-period 或 –cpu-quota 参数)。
同时,如果宿主机CPU核心较多,负载又高的机器,需要绑定CPU序号到容器,目的是降低操作系统在 CPU 任务调度上额外耗费(对应 docker-cli –cpuset-cpus)。需要注意:绑定核心数与配额时间片应保持对应,并考虑多个容器的资源竞争。

Memory 配额

设置容器的内存配额,对应 docker-cli –memory-*,默认 out-of-memory(OOM)时容器自动被系统 KILL。
注:

  • 创建容器时需要保证开启 –restart always,自动重启因各种异常而停止的容器
  • 很多应用中间件会读取系统资源信息来自动设置内部的内存使用量,即便在 –memory 设置的情况下,其读取的内存容量也是宿主机器的值(其它如 CPU、Proc 状态也是如此),这个问题会引起不必要的 OOM 逻辑误差,解决方法参考下面 LXCFS 部分说明。

IO 配额

ulimits 配置在容器场景中也很重要,但与物理操作系统优化方向相反:宿主操作系统一般将 ulimit 设置为最大值,以提高性能;但多容器共享 VPS 场景下,要考虑隔离资源竞争,则要设置为保守值,配合业务优先级的规划,关键业务容器多给配额,次要业务降低配额。

Network 配置

采用 Docker/Network 原生的 Bridge 或 Host 模式,这样对于性能更友好,足够简单又无第三方服务依赖。
对于端口的分配和地址映射,通过中心化配置再推送到各节点。

利用 LXCFS 增强 Docker 容器隔离性和资源可见性

LXCFS 实现了容器对资源状态信息的修正,使 CPU、内存、进程等资源状态与实际设置一致。LXCFS 基于 fuse 实现用户态文件系统,以 docker volume/mount 方式将各个状态文件 mount 到容器中,详情: https://github.com/lxc/lxcfs

如何控制前端负载流量

使用公有云提供商原生的 Cloud Load Balancer 代理全网流量, 内部再经过 OpenResty/Lua 集群细化流量控制,比如具备业务形态的缓存等,同时容器副本规模的增减也关联到负载均衡配置的更新。

统一 Docker Image 镜像

更多情况下把 Docker 当作运行环境的容器,主要功能是提供基础运行库及资源隔离,而不是打包工具。所以只需要少量几个通用的镜像(类似云服务商提供的 AppEngine 运行环境),每个 VPS Docker Daemon 预置了数量有限的通用Image,实际部署不需要远程 Pull Image,直接选取白名单里面的image名称,创建启动即可。

容器安全

除了资源配额的隔离措施外,所有容器都以非 root 权限启动应用。

配置分发

通过中心配置工具,批量生成配置文件,并推送到各个VPS节点,即:将正确的文件放置在正确的地方,并启动它!

状态监控

基于 docker sdk/stats 接口从 docker daemon 异步获取容器 CPU、RAM、IO 状态信息、异步写入时间序列数据库,通过 WebUI 查看监控数据。
综上所述:前端业务系统的容器化实践主要思路就是资源共享,分级隔离,目的是提高整体性能并降低成本。

后端数据平台的容器化实践

天脉聚源具有目前最大规模的电视云数据中心,构建于自建机房,后端数据平台使用容器的场景在数据挖掘和索引服务方面,对容器的使用经验与前端场景类同,也有自身特点: 负载线性可控,大数据 IO,存储和 CPU 密集型应用较多

Docker Image 内部镜像服务器

相对于前端,后端系统 Docker Image 的依赖库更重量,比如视频、音频、图片依赖的库非常庞大,特别是近几年各类深度学习框架引入后,简单的几个通用 AppEngine Image 已经不能满足需求了,所以部署了内部 Docker Register 服务。
即便如此,内部 Docker Register 服务提供的镜像也仍然力求通用性,仅以白名单方式提供服务。

数据落盘 Volume Mount

应用场景中存在大量数据处理业务,所以容器中需要持久化数据,这个场景下依然遵循“容器是运行环境”方式,数据不写入 Docker 系统盘,单独通过 Volume/Mount 方式存入本地普通存储卷。物理服务器一般有多块 HDD/SSD 设备,或者 RAID 卷设备,这个地方使用 Volume/Mount 方式简单高效。

使用 XFS – Quota 配置容器数据

前面提到使用 Volume Mount 持久化数据到磁盘,这里也存在资源配额的问题,需要控制容器对磁盘的占用容量,目前的方式是使用原生 XFS 文件系统的 Project Quota 方式实现,针对 Volume Mount 目录设置容量配额。

资源共享

与前端业务类似,后端数据平台也会在宿主机上共享资源,不同的是后台数据处理并不全是 7×24 在线,所以不同业务容器可以分时执行,或者异步执行。
通过对容器状态的启停时间调整,或者缩减容器副本的数量,对这类 CPU、IO 密集型任务控制简单有效。

微服务代理,负载均衡

对于企业私有网络,运用 LVS 可以高效实现任何 TCP 流量控制;对基于 HTTP的微服务,使用 OpenResty/Lua 仍然是一个高效高可用的方式。

服务性能可扩展

应用服务横向扩展通过容器的副本数控制,部分应用涉及到分区,通过 Docker 环境变量传入当前容器副本编号 POD_REP_ID=[0,N) 以及副本配额总数 POD_REP_NUM=N,应用程序通过获取相关参数值来动态适应业务分片逻辑。

自动化管理(资源编排)

在资源编排方面,一般关键系统会同时运行至少两套异构方案,预防未知错误导致系统长期不可用(理论上自动化程度越高的系统遇到错误修正的复杂度也越高)。

通用方案

经典方案,低学习成本,直观理解的方式:集中式 “配置文件 + shell + rsync/push”,辅以分组策略,这种方式大多数运维人员和开发人员都容易理解,易上手。

自动化方案

先后使用过 Docker/Swarm、Google/Kubernetes、Sysinner/InnerStack。
Kubernetes 功能完备,并具备强大的扩展能力,但同时重量级且复杂,普通技术人员要熟练运行 Kubernetes 集群并快速解决基于 Kubernetes 的各类问题有较大学习成本,有小规模测试系统用。
InnerStack 相比 Kubernetes 轻量、简单易用,内置 WebUI、用户管理和容器监控,近一年在构建全新深度视频内容数据挖掘引擎、视频搜索引擎等应用的上线中获得了良好效果。

Q&A

Q:微服务代理,能详细说说吗?
A:一般两个作用,统一服务地址和负载均衡,可以用 LVS + Nginx 。在我们的业务中,有些业务逻辑解耦给了代理服务(统一网关),比如 JWT 用户统一认证等等,还有一些涉及业务逻辑的,比如电视抢红包,用随机算法只将一部分流量放到后端,进而使用了 OpenResty/Lua 组合。
Q:监控使用的啥?
A:当前监控使用 Sysinner/InnerStack 内置的监控功能,基于 docker/sdk-golang 和 leveldb-go,结合一些前端开发工作(主要是 chart 展现)。
Q:微服务改动时,代理是如何感知的?是否要频繁重启代理?这一块是怎么实现的?
A:微服务改动,先将配置推送到代理服务,再操作容器,LVS 和 Nginx/OpenResty 变更配置不需要重启,当然这个地方也需要容器应用配合,避免容器之间直接通信。
Q:使用 XFS 限制容器挂载的目录大小具体是怎么做的呢,能否说下思路和相关参考?
A:一般前端业务,容器挂载目录只存放日志,10GB 左右。后端涉及到数据持久的应用,比如 MySQL ,SSDB 这个按照业务预估,一般 50 ~ 500 GB 。具体可以搜索 “xfs quota , project quota” 。
Q:你们线上基于 Docker 单节点部署服务吗?
A:单节点有测试服务器;其实单结点和集群对于 Docker 的操作时一样的。
Q:有没有根据业务负载来操作容器伸缩(增减)的功能?
A:有,比如元旦、春节前期,按照预估扩容 VPS 时,会预留部分资源,正式上线运行中如果有模块负载高,会实时把这个模块推送到预留资源;如果没有富余机器了,就按照优先级把部分模块降级服务。
Q:CPU 限制这一块能细说一下吗?貌似用的更多的是 CPUshare?
A:比如给一个容器 2 cores 资源,则 –cpu-period=1000000 –cpu-quota=20000000 –cpusets-cpus=0,1 (cat /proc/cpuinfo)。
Q:可以问一下视频存储用什么,还有搜索服务用什么搜索引擎,还有发布版本的时候,数据库结构更改怎么做到不停机更新的,作为一个小白我很好奇?
A:视频存储用传统企业盘柜; 搜索用 sphinxsearch + 定制修改;数据库一般准备两套,如果升级涉及数据库变更(可能锁表),则是暂停写业务,再操作 (这个期间查询业务不受影响)。

以上内容根据2020年1月7日晚微信群分享内容整理。 分享人 王锐,天脉聚源基础平台技术经理
。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。