记一次阿里云arms监控和log-pilot日志系统冲突及解决

post thumb
Golang
作者 Louis 发表于 2021年6月29日

log-pilot分析

说道这个问题, 首先得了解log-pilot的收集日志的原理。

Log-pilot will monitor docker events, and parse log labels on new docker conatainer, and generate appropriate log configuration and notify fluentd or filebeat process to reload the new configuration.

翻译过来就是, log-pilot通过观测容器启动, 然后分析log labels, 然后再生成filebeat的配置, 然后通过filebeat收集日志。

代码分析: 主要就是获取日志配置的时候报错, 因此只需要分析这个函数即可。

issue: https://github.com/AliyunContainerService/log-pilot/issues/331


func (p *Pilot) getLogConfigs(jsonLogPath string, mounts []types.MountPoint, labels map[string]string) ([]*LogConfig, error) {
	var ret []*LogConfig

	mountsMap := make(map[string]types.MountPoint)
	for _, mount := range mounts {
		mountsMap[mount.Destination] = mount
	}

	var labelNames []string
	//sort keys
	for k := range labels {
		labelNames = append(labelNames, k)
	}

	customConfigs := make(map[string]string)

	sort.Strings(labelNames)
	root := newLogInfoNode("")
	// 通过遍历容器的 log 标签, 去匹配 前缀有 aliyun.log
	for _, k := range labelNames {
		for _, prefix := range p.logPrefix {
			customConfig := fmt.Sprintf(LABEL_SERVICE_LOGS_CUSTOME_CONFIG_TEMPL, prefix)
			if customConfig == k {
				configs := strings.Split(labels[k], "\n")
				for _, c := range configs {
					if c == "" {
						continue
					}
					customLabel := strings.SplitN(c, "=", 2)
					customConfigs[customLabel[0]] = customLabel[1]
				}
				continue
			}

			serviceLogs := fmt.Sprintf(LABEL_SERVICE_LOGS_TEMPL, prefix)
			if !strings.HasPrefix(k, serviceLogs) || strings.Count(k, ".") == 1 {
				continue
			}

			logLabel := strings.TrimPrefix(k, serviceLogs)
			// 这个地方, 会通过logLabel去初始化节点。
			if err := root.insert(strings.Split(logLabel, "."), labels[k]); err != nil {
				return nil, err
			}
		}
	}

	for name, node := range root.children {
		logConfig, err := p.parseLogConfig(name, node, jsonLogPath, mountsMap)
		if err != nil {
			return nil, err
		}
		CustomConfig(name, customConfigs, logConfig)
		ret = append(ret, logConfig)
	}
	return ret, nil
}

// 这个是去插入tags和初始化lognode节点相关
// 如果只有tags配置, 没有父节点, 
// 也就是 没有 aliyun_log_xxxx, 只有 aliyun_log_xxxx_tags. 则报错xxx has no parent node 。
func (node *LogInfoNode) insert(keys []string, value string) error {
	if len(keys) == 0 {
		return nil
	}
	key := keys[0]
	if len(keys) > 1 {
		if child, ok := node.children[key]; ok {
			child.insert(keys[1:], value)
		} else {
			return fmt.Errorf("%s has no parent node", key)
		}
	} else {
		child := newLogInfoNode(value)
		node.children[key] = child
	}
	return nil
}

arms相关

使用阿里云的arms很简单, 直接在deployment上面添加anotations即可

sepc:  
  template:
    metadata:
      annotations:
        armsPilotAutoEnable: "on"
        armsPilotCreateAppName: staging-demo-service

底层是通过anotations去pod启动的时候注入initcontianer。配置java_tools等参数, 还做了一些很奇怪的事情, 比如添加环境变量。然后链接arms服务。

结论

开启arms。会在pod启动的时候,注入环境变量如下:

aliyun_logs_armsappid_tags=appid=dtqic8hnej@fc640b40f8635e2, 同时 会在改该容器打一个aliyun.logs.armsappid.tags:appid=dtqic8hnej@fc640b40f8635e2 标签。

导致log-pilot程序回去寻找 aliyun.logs.armsappid.tags 的父节点。 然而, arms的父节点没有定义, 因此报错。

修复方法

因此,临时修复的方法是, 如果label匹配了arms, 我就直接跳过初始化节点操作。


			logLabel := strings.TrimPrefix(k, serviceLogs)
			
			// 如果logLabel带有arms的,直接跳过, 不做校验。
			// 因此需要避免 索引起 *arms* 名字, 导致匹配这条,因此不会向es输出日志
			if strings.Contains(logLabel, "arms") {
				continue
			}
			
			// 这个地方, 会通过logLabel去初始化节点。
			if err := root.insert(strings.Split(logLabel, "."), labels[k]); err != nil {
				return nil, err
			}

阿里云官方

在arms的pilot的deploymet上, 添加 ARMS_SLS_TAG_SWITCH = “off” 这个环境变量, 重启arms-pilot即可。

上一篇
记一次dcdn加apisix的sni报错。