导读:中国五矿和阿里巴巴联手打造的钢铁服务专业平台五阿哥,通过集结阿里巴巴在大数据、电商平台和互联网产品技术上的优势,为终端用户带来一站式采购体验。本文是五阿哥运维技术团队针对Docker容器技术在如何在持续交付过程中探索和实践,目前已经将发布部署权限开放给应用开发的owner,实现7*24小时“一站式”的持续交付,整体提高了公司研发过程的交付能力。
作为创业公司和推行DevOps工程师们来说,都遇到过这样的问题:
基于Docker容器技术,运维技术团队开发了五阿哥网站的容器云平台。通过容器云平台95%的应用服务已经实现容器化部署。这些应用支持业务按需拓展,秒级伸缩,提供与用户友好的交互过程,规范了测试和生产的发布流程,让开发和测试同学从基础的环境配置和发布解放出来,使其更聚焦自己的项目开发和测试。
结合五阿哥容器云平台和Docker容器技术的实践,本文先介绍如何实现7*24小时“一站式”的持续交付,实现产品的上线。关于容器云平台的介绍,请关注:https://zhuanlan.zhihu.com/idevops
众所周知,Docker的镜像是分层的。对镜像分层进行约定:
经验总结:如何让自己的镜像变的更小,PUSH的更快?
Rancher图形化管理界面,部署简单、方便, 可以与AD、LDAP、GITHUB集成,基于用户或用户组进行访问控制,快速将系统的编排工具升级至Kubernetes或者Swarm,同时有专业的技术团队进行支持,降低容器技术入门的难度。
基于以上优点我们选择Rancher作为我们容器云平台的编排工具,在对应用的容器实例进行统一的编排调度时,配合Docker-Compose组件,可以在同一时间对多台宿主机执行调度操作。同时,在服务访问出现峰值和低谷时,利用特有的rancher-compose.yml文件调用“SCALE”特性,对应用集群执行动态扩容和缩容,让应用按需求处理不同的请求。https:/zhuanlan.zhihu.com/p/29093407
由于后端开发基于阿里的HSF框架,生产者和消费者之间需要网络可达,对网络要求比较高,需要以真实IP地址进行注册和拉取服务。所以在选择容器网络时,我们使用了Host模式,在容器启动过程中会执行脚本检查宿主机并分配给容器一个独立的端口,来避免冲突的问题。
静态扫描结果:
基于上述图9架构,我们定义了持续部署规范的流程:
(1)开发同学向gitlab提交代码;
(2)拉取项目代码和配置项文件,执行编译任务;
(3)拉取基础镜像,将编译好的应用包打入生成最新的应用镜像,推送到镜像仓库;
(4)根据当前应用及所属环境定制化生成docker-compose.yml文件,基于这个文件执行rancher-compose命令,将应用镜像部署到预发环境(发布生产前的测试环境,相关配置、服务依赖关系和生产环境一致)。
(6)预发环境测试通过后将应用镜像部署至线上环境,测试结果通知后端测试同学。
应用容器现在已经部署到线上环境,那么在整个容器的生命周期中,还需要解决下面两个问题:
(1) 如何保存应用程序产生的运行日志和其它业务日志;
(2) 如何在后端服务出现变化后nginx能够自动发现并完成配置更新。
容器在运行时会在只读层之上创建读写层,所有对应用程序的写操作都在这层进行。当容器重启后,读写层中的数据(包含日志)也会一并被清除。虽然可以通过将容器中日志目录挂载到宿主机解决此类问题,但当容器在多个宿主机间频繁漂移时,每个宿主机上都会有留存应用名的部分日志,增加了开发同学查看、排查问题的难度。
综上所属,日志服务平台作为五阿哥网站日志仓库,将应用运行过程中产生的日志统一存储,并且支持多种方式的查询操作。
通过在日志服务的管理界面配置日志采集路径,在容器中部署agent把应用日志统一投递到logstore中,再在logstore中配置全文索引和分词符,以便开发同学能够通过关键字搜索、查询想要的日志内容。
经验总结:如何避免日志的重复采集问题?
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主动释放无效的实例信息,来看一下注册中心刷新的机制,代码直接奉上:
#!/usr/bin/env python
import etcd
import sys
arg_l=sys.argv[1:]
etcd_clt=etcd.Client(host='172.18.0.7')
def set_key(key,value,ttl=10):
try:
return etcd_clt.write(key,value,ttl)
except TypeError:
print 'key or vlaue is null'
def refresh_key(key,ttl=10):
try:
return etcd_clt.refresh(key,ttl)
except TypeError:
print 'key is null'
def del_key(key):
try:
return etcd_clt.delete(key)
except TypeError:
print 'key is null'
if arg_l:
if len(arg_l) == 3:
key,value,ttl=arg_l
set_key(key,value,ttl)
elif len(arg_l) == 2:
key,ttl=arg_l
refresh_key(key,ttl)
elif len(arg_l) == 1:
key=arg_l[0]
del_key(key)
else:
raise TypeError,'Only three parameters are needed here'
else:
raise Exception('args is null')
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是集群部署,有多个节点,而我又不想把confd的指令搞的又臭又长,所以将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程序使配置生效。(代码请参考:https://zhuanlan.zhihu.com/idevops)
本文是五阿哥运维技术团队针对Docker容器技术在如何在持续交付过程中探索和实践,目前已经将发布部署权限开放给应用开发的owner,实现7*24小时“一站式”的持续交付,整体提高了公司的研发过程的交付能力。
接下来会不断优化持续交付过程中遇到的各种场景,逐渐完善容器云平台,同时会将容器云平台各种功能,总结的经验和教训不断分享给大家,给大家在工作中一些参考,避免走重复的“弯路”。
作者简介:刘晓明,五阿哥钢铁电商平台(wuage.com)运维技术负责人,拥有10年的互联网开发和运维经验。一直致力于运维工具的开发和运维专家服务的推进,赋能开发,不断提高研发效能。
本文来自《程序员》原创文章,谢绝转载,如需订阅,请点击这里(责编/魏伟)
PS:推荐一个容器技术线上直播,讲师来自腾讯、华为、思科、58同城、蘑菇街、当当等6位一线专家,议题涵盖容器云、微服务、servicemesh等最新实践,欢迎报名参加