本文由 1 月 24 日晚五阿哥(www.wuage.com)公司运维技术负责人刘晓明在 Rancher 官方技术交流群内所做分享的内容整理而成,分享了五阿哥网站的 Docker 容器云平台建设实践经验。文末有下载福利,添加 Rancher 小助手为好友加入技术交流群,还可实时参加下一次技术分享~
引言
五阿哥钢铁电商平台(www.wuage.com)是由钢铁行业第一的中国五矿与互联网第一的阿里巴巴联手打造,并充分运用双方股东优势资源,即:阿里巴巴在大数据、电商运营、互联网产品技术上的巨大优势,尤其是在 B2B 终端买家上的独有市场基础,以及中国五矿 67 年的行业经验,和遍布全球的 200 多个营销和物流网点,致力于为钢铁行业带来全新而持续的发展。
Docker 容器云平台是五阿哥运维技术团队为内部服务整合、开发的一套容器管理平台,支持基础设施私有云和公有云对接,实现云上和云下实例使用一套平台进行管理,业务实例按需弹性扩容和缩容,规范化的项目管理流程、测试、上线流程,旨在将开发、测试人员从基础环境的配置与管理中解放出来,使其更聚焦于自己的业务开发。
本文主要结合在五阿哥业务场景情况,从以下三个部分讲解:
1、为什么使用 Docker 技术
2、Docker 容器云架构方案
3、技术的选型和实践
为什么使用 Docker 技术
1、硬件资源利用率问题造成部分成本浪费
在网站功能中不同的业务场景有计算型的,有 IO 读写型的,有网络型,有内存型的,集中部署应用就会导致资源利用率不合理的问题。比如,一个机器上部署的服务都是内存密集型,那么 CPU 资源就都很容易浪费了。
2、单物理机多应用无法对无法进行有效的隔离,导致应用对资源的抢占和相互影响
一个物理机器跑多个应用,无法进行所使用的 CPU,内存,进程进行限制,如果一个应用出现对资源的抢占问题,就会引起连锁反应,最终导致网站部分功能不可用。
3、环境、版本管理复杂,上线部署流程缺乏,增加问题排查的复杂度
由于内部开发流程的不规范,代码在测试或者上线过程中,对一些配置项和系统参数进行随意的调整,在发布时进行增量发布,一旦出现问题,就会导致测试的代码和线上运行的代码是不一致的,增加了服务上线的风险,也增加了线上服务故障排查的难度。
4、环境不稳定,迁移成本高,增加上线风险
在开发过程中存在多个项目并行开发和服务的依赖问题,由于环境和版本的复杂性很高,不能快速搭建和迁移一个环境,导致无法在测试环境中无法模拟出线上的流程进行测试,很多同学在线上环境进行测试,这里有很高的潜在风险,同时导致开发效率降低。
5、传统虚拟机和物理机占用空间大,启动慢,管理复杂等问题
传统虚拟机和物理机在启动过程进行加载内核,执行内核和 init 进行,导致在启动过程占用很长时间,而且在管理过程中会遇到各种各样的管理问题。
Docker 容器云架构方案
基于 Docker 容器技术,运维技术团队开发了五阿哥网站的容器云平台。整体架构图如下:
基础设施
基础设施包含网络、服务器、存储等计算资源。
多云对接
私有云(VMware)和公有云(aliyun)进行统一托管,包含网络区域配置,VM 实例开通及 Docker 的环境初始化配置等。
弹性调度
Docker 容器云平台集群节点管理,Saltstack 进行配置管理,镜像中心管理业务镜像,统一监控,统一日志管理,定时任务管理。
服务编排
服务注册,服务发现,容器节点在线的扩容和缩容,服务优雅上线,回滚降级,规范 Java,Node,Python,iOS,Android 等规范化的上线。
统一门户
规范化整个业务流程,简洁的用户流程,可动态管理整个云环境的所有资源。
技术选型及实践
镜像标准
众所周知,Docker 的镜像是分层的。对镜像分层进行约定:
第一层是操作系统层,由 CentOS/Alpine 等基础镜像构成,安装一些通用的基础组件;
第二层是中间件层,根据不同的应用程序,安装它们运行时需要使用到的各种中间件和依赖软件包,如,nginx、tomcat 等;
第三层是应用层,这层仅包含已经打好包的各应用程序代码。
经验总结:如何让自己的镜像变的更小,PUSH 的更快?
dockerfile 构建应用镜像,在中间件层遇到一些需要安装的软件包时,尽可能的使用包管理工具(如 yum)或以 git clone 方式下载源码包进行安装,目的是将软件包的 copy 和安装控制在同一层,软件部署成功后清除一些无用的 rpm 包或源码包,让基础镜像的尺寸更小。
Java 应用镜像中并没有将 jdk 软件包打入镜像,将 jdk 部署在每台宿主上,在运行镜像时,通过挂载目录的方式将宿主机上的 java 家目录挂载至容器指定目录下。因为它会把基础镜像撑得非常大;
在构建应用镜像时,docker 会对这两层进行缓存并直接使用,仅会重新创建代码出现变动的应用层,这样就提高了应用镜像的构建速度和构建成功后向镜像仓库推送的速度,从整体流程上提升了应用的部署效率。
编排工具
Rancher 图形化管理界面,部署简单、方便, 可以与 AD、LDAP、GITHUB 集成,基于用户或用户组进行访问控制,快速将系统的编排工具升级至 kubernetes 或者 swarm,同时有专业的技术团队进行支持,降低容器技术入门的难度。
基于以上优点我们选择 Rancher 作为我们容器云平台的编排工具,在对应用的容器实例进行统一的编排调度时,配合 Docker-Compose 组件,可以在同一时间对多台宿主机执行调度操作。同时,在服务访问出现峰值和低谷时,利用特有的 rancher-compose.yml 文件调用“SCALE”特性,对应用集群执行动态扩容和缩容,让应用按需求处理不同的请求。
网络模型
由于后端开发基于阿里的 HSF 框架,生产者和消费者之间需要网络可达,对网络要求比较高,需要以真实 IP 地址进行注册和拉取服务。所以在选择容器网络时,我们使用了 Host 模式,在容器启动过程中会执行脚本检查宿主机并分配给容器一个独立的端口,来避免冲突的问题。
持续集成
监测代码提交状态,对代码进行持续集成,在集成过程中执行单元测试,代码 Sonar 和安全工具进行静态扫描,将结果通知给开发同学同时部署集成环境,部署成功后触发自动化测试。
静态扫描结果:
持续部署
是一种能力,这种能力非常重要,把一个包快速部署在你想要的地方。平台采用分布式构建、部署,master 管理多个 slave 节点,每个 slave 节点分属不同的环境。在 master 上安装并更新插件、创建 job、管理各开发团队权限。slave 用于执行 job。
基于上述架构,我们定义了持续部署规范的流程:
(1)开发同学向 gitlab 提交代码;
(2)拉取项目代码和配置项文件,执行编译任务;
(3)拉取基础镜像,将编译好的应用包打入生成最新的应用镜像,推送到镜像仓库;
(4)根据当前应用及所属环境定制化生成 docker-compose.yml 文件,基于这个文件执行 rancher-compose 命令,将应用镜像部署到预发环境(发布生产前的测试环境,相关配置、服务依赖关系和生产环境一致)。
(5)预发环境测试通过后将应用镜像部署至线上环境,测试结果通知后端测试同学。
监控管理
通过 zabbix 自动注册(AutoRegistration),Grafana 通过调用 zabbix 的 API 接口进行监控指标的统一展示。
日志管理
容器在运行时会在只读层之上创建读写层,所有对应用程序的写操作都在这层进行。当容器重启后,读写层中的数据(包含日志)也会一并被清除。虽然可以通过将容器中日志目录挂载到宿主机解决此类问题,但当容器在多个宿主机间频繁漂移时,每个宿主机上都会有留存应用名的部分日志,增加了开发同学查看、排查问题的难度。
综上所述,日志服务平台作为五阿哥网站日志仓库,将应用运行过程中产生的日志统一存储,并且支持多种方式的查询操作。
通过在日志服务的管理界面配置日志采集路径,在容器中部署 agent 把应用日志统一投递到 logstore 中,再在 logstore 中配置全文索引和分词符,以便开发同学能够通过关键字搜索、查询想要的日志内容。
经验总结:如何避免日志的重复采集问题?
日志服务 agent 需要在配置文件 “ilogtail_config.json” 中增加配置参数 “check_point_filename”,指定 checkpoint 文件生成的绝对路径,并且将此路径挂载至宿主机目录下,确保容器在重启时不会丢失 checkpoint 文件,不会出现重复采集问题。
服务注册
etcd 是一个具备高可用性和强一致性的键值存储仓库,它使用类似于文件系统的树形结构,数据全部以“/”开头。etcd 的数据分为两种类型:key 和 directories,其中 key 下存储单独的字符串值,directories 下则存放 key 的集合或者其他子目录。
在五阿哥环境中,每个向 etcd 注册的应用服务,它们的根目录都以 “/$
{APP_NAME}_$
{ENVIRONMENT}” 命名。根目录下存储每个应用实例的 Key 信息,它们都以 “$
{IP}-$
{PORT}” 的方式命名。
下图是使用上述约定,存储在 etcd 上某应用实例的数据结构:
可以看到我是使用 get 方法向 etcd 发送请求的,请求的是部署在预发环境(PRE)的搜索服务(search);在它的根目录“/search_PRE”下,仅存储了一个应用实例的信息,这个实例的 key 是“172.18.100.31-86”;对应的 value 是“172.18.100.31:86‘’,整个注册过程是这样的:
通过代码为容器应用程序生成随机端口,和宿主机正在使用的端口进行比对,确保端口没有冲突后写入程序配置文件;
把通过 python 和 etcd 模块编写的服务注册工具集成在脚本中,将 IP 地址和上一步获取的随机端口以参数的方式传递给服务注册工具;
待应用程序完全启动后,由服务注册工具以约定好的数据结构将应用实例的写入 etcd 集群,完成服务注册工作;
容器定时向 etcd 发送心跳,报告存活并刷新 ttl 时间;
容器脚本捕获 rancher 发送至应用实例的 singnal terminal 信号,在接收到信号后向 etcd 发送 delete 请求删除实例的数据。
注:在 ttl 基础上增加主动清除功能,在服务正常释放时,可以立刻清除 etcd 上注册信息,不必等待 ttl 时间。
经验总结:容器在重启或者意外销毁时,让我们一起看一下这个过程中容器和注册中心都做了什么事情?
应用在注册是携带 key 和 value 时携带了 ttl 超时属性,就是考虑到当服务集群中的实例宕机后,它在 etcd 中注册的信息也随之失效,若不予清除,失效的信息将会成为垃圾数据被一直保存,而且配置管理工具还会把它当做正常数据读取出来,写入 web server 的配置文件中。要保证存储在 etcd 中的数据始终有效,就需要让 etcd 主动释放无效的实例信息,来看一下注册中心刷新的机制,代码直接奉上:
服务发现
confd 是一个轻量级的配置管理工具,支持 etcd 作为后端数据源,通过读取数据源数据,保证本地配置文件为最新;不仅如此 ,它还可以在配置文件更新后,检查配置文件语法有效性,以重新加载应用程序使配置生效。这里需要说明的是,confd 虽然支持 rancher 作为数据源,但考虑易用性和扩展性等原因,最终我们还是选择了 etcd。
和大多数部署方式一样,我们把 confd 部署在 web server 所在的 ECS 上,便于 confd 在监测到数据变化后及时更新配置文件和重启程序。confd 的相关配置文件和模板文件部署在默认路径/etc/confd 下,目录结构如下:
/etc/confd/
├── conf.d
├── confd.toml
└── templates
confd.toml 是 confd 的主配置文件,使用 TOML 格式编写,因为 etcd 是集群部署,有多个节点,将 interval、nodes 等选项写到了这个配置文件里。
cond.d 目录存放 web server 的模板配置源文件,也使用 TOML 格式编写。该文件用于指定应用模板配置文件路径(src)、应用配置文件路径(dest)、数据源的 key 信息(keys)等。
templates 目录存放 web server 下每个应用的模板配置文件。它使用 Go 支持的 text/template 语言格式进行编写。在 confd 从 etcd 中读取到最新应用注册信息后,通过下面的语句写入模板配置文件中:
{{range getvs “/${APP_NAME}/*”}}
server {{.}};
{{end}}
通过 supervisor 管理 confd 进程。confd 在运行后会每隔 5 秒对 etcd 进行轮询,当某个应用服务的 K/V 更新后,confd 会读取该应用存储在 etcd 中的数据,写入到模板配置文件中,生成这个应用配置文件,最后由 confd 将配置文件写入到目标路径下,重新加载 nginx 程序使配置生效。
结语
以上是五阿哥运维技术团队针对 Docker 容器云平台建设实践,目前已经将权限开放给开发同学,实现云上和云下实例使用一套平台进行管理,业务实例按需弹性扩容和缩容,规范化的项目管理和发布流程,实例和业务可按需进行扩容和缩容,实现 7*24 小时“一站式”的持续交付,开发同学更聚焦于自己的业务开发,提高了公司的研发过程的效能。
接下来我们还会不断优化和适配各种业务场景,逐渐完善容器云平台,同时会将容器云平台各种功能,总结的经验和教训不断分享给大家,给大家在工作中一些参考,避免走重复的“弯路”。
Q & A
Q:你们的监控是怎么做的呢?
A:利用 zabbix 自动注册(AutoRegistration),Grafana 通过调用 zabbix 的 API 接口进行监控指标的统一展示。
Q:您说的网络 Host 模式是在 Rancher 中指定容器的网络模式吗?现在 Rancher 有桥接、托管之类的,
A:rancher 网络模式中的 host 模式。
Q:CICD 是已经集成进来还是单独模块?
A:已经集成,在统一一个平台展示,调用 jenkies 的接口。
Q:请问有用 scaleio 作为 docker 后端存储么?或者说,有用分布式存储用做容器的存储空间吗?
A:暂时未使用分布式存储,目前都跑在 aliyun ecs 和 VMware 的虚拟机上。
Q:rancher 使用了 host 模式后,外部如何访问容器啊?我之前容器启动不指定网络,然后由 pipework 指定网络的。
A:网络共享主机网卡,通过应用的服务注册和发现即可对外提供服务。
Q:日志用什么软件展示内容的?
A:用 ELK。
Q:Zabbix 怎么监控到容器内的计算资源?
A:基于 docker 的 python 模块,编写的监控脚本。来实现容器内计算资源的监控。
Q:现在是否有应用 APM 的监控?
A:有用,性能监控基于阿里的”鹰眼”,结合自己开发来做的。
——
Q:就是 rancher 可以对容器的应用可以监控是吧?结合鹰眼和自己开发是么?
A:基础计算资源监控和网站应用性能监控是分开的。
Q:jenkins 默认的用户名密码是什么?
A:私信我我再告诉你:P
Q:私有云和阿里云之间的联通走的是公网还是专线接入走的内网?
A:内网专线方式
Q:jenkins 那块用了什么插件呢?
A:Git Plugin,Status Monitor Plugin,pipeline plugin 用户认证等插件。
Q:使用 host 模式 rancher 的 haproxy 配置,是不是需要指定多条具体到每个容器?
A:未用 haproxy,使用的是 nginx。
Q:请问是用容器云平台统一管理私有云和阿里云上的虚拟机上的容器吗,用这种方式实现灾备?
A:公有云和私有云主机和容器都可以进行管理,支持业务的快速扩容和缩容。
Q:应用程序产生的日志怎么收集?
A:容器中部署 agent 把应用日志统一投递到日志平台。
——
Q:容器中部署 agent,那岂不是容器变得很重?
A:是重,但会标准。见解不同,实现就好。
Q:服务注册是 jenkins 集成的最后一步吗?
A:不是,在容器启动后自动向 etcd 集群进行注册。
Q:请问在实际企业容器云部署过程中,是不是开发代码都 commit 到一个新的镜像中,持久化数据都单独用容器数据卷的方式,映射到宿主机上进行管理的呢?
A:应用分有状态和无状态,区别对待。
Q:git 持续集成, 开发到测试是否是合并到测试分支, 然后出发执行 jenkies 作业, 测试通过,再把代码再合并到生产分支上吗? 这块您们是怎么做的?
A:开发到测试,发布的分支仍然是开发分支,发布预发和线上是 release 分支,测试通过后合并至 master。
Q:java 应用的弹性具体如何做到的?
A:session 共享和应用优雅上、下线。
Q:nginx 的话,配置是在容器里面配,还是配置好了做成镜像?
A:镜像,配置文件挂载。
Q:请问数据库也是部署在容器云上吗,还是独立的数据库平台?
A:数据库独立部署
Q:采用 host 网络是否主要考虑是方便服务注册?
A:方便服务注册和发现,还有性能。
Q:rancher 中的容器的内存分配多少合适,比如一个用户管理的微服务跑在 3 个实力的容器中,我怎么测试来给每个实力分配多少内存合适的值?
A:应用可能是 CPU 密集型/内存密集型/IO 密集型,不同的应用根据不同的实际情况进行配置。
Q:有集群的权限管理么?比如集群中 a 部分只给 a 业务使用,b 集群给 b 业务使用?
A:有的,类似于 pool。
作者简介
刘晓明,五阿哥(www.wuage.com)公司运维技术负责人。拥有 10 年的互联网开发和运维经验,一直致力于运维工具的开发和运维专家服务的推进,赋能开发,提高效能。知乎号:布道,专栏“开发运维”,还会持续更新更多干货,欢迎翻牌~
评论