基于Docker持续交付平台建设的实践

综上所属,日志服务平台作为五阿哥网站日志仓库,将应用运行过程中产生的日志统一存储,并且支持多种方式的查询操作。

基于Docker持续交付平台建设的实践10

图10:日志服务平台

通过在日志服务的管理界面配置日志采集路径,在容器中部署agent把应用日志统一投递到logstore中,再在logstore中配置全文索引和分词符,以便开发同学能够通过关键字搜索、查询想要的日志内容。

经验总结:如何避免日志的重复采集问题?

日志服务agent需要在配置文件“ilogtail_config.json”中增加配置参数“check_point_filename”,指定checkpoint文件生成的绝对路径,并且将此路径挂载至宿主机目录下,确保容器在重启时不会丢失checkpoint文件,不会出现重复采集问题。

服务的注册

etcd是一个具备高可用性和强一致性的键值存储仓库,它使用类似于文件系统的树形结构,数据全部以“/”开头。etcd的数据分为两种类型:key和directories,其中key下存储单独的字符串值,directories下则存放key的集合或者其他子目录。

基于Docker持续交付平台建设的实践11

图11 应用注册

在五阿哥环境中,每个向etcd注册的应用服务,它们的根目录都以

”/ ${APP_NAME}_ ${ENVIRONMENT}”

命名。根目录下存储每个应用实例的Key信息,它们都以“IP−{PORT}”的方式命名。

下图是使用上述约定,存储在etcd上某应用实例的数据结构:

基于Docker持续交付平台建设的实践12

图12: 存储在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 pythonimportetcd importsys arg_l=sys.argv[ 1:] etcd_clt=etcd.Client(host= '172.18.0.7') defset_key(key,value,ttl=10):try: returnetcd_clt.write(key,value,ttl) exceptTypeError: print'key or vlaue is null'defrefresh_key(key,ttl=10):try: returnetcd_clt.refresh(key,ttl) exceptTypeError: print'key is null'defdel_key(key):try: returnetcd_clt.delete(key) exceptTypeError: print'key is null'ifarg_l: iflen(arg_l) == 3: key,value,ttl=arg_l set_key(key,value,ttl) eliflen(arg_l) == 2: key,ttl=arg_l refresh_key(key,ttl) eliflen(arg_l) == 1: key=arg_l[ 0] del_key(key) else: raiseTypeError, 'Only three parameters are needed here'else: raiseException( 'args is null')服务的发现