目录
influxdb+kapacitor
通过编写tickscript脚本,从influxdb查询cpu|mem|net|blkio|docker等metric,当满足告警条件的时候就触发邮件推送。
收件地址没有在kapacitor.conf里面配置,而是通过代码获取用户输入的邮箱。
也就是说,用户在页面上填了哪些收件地址,到时候告警邮件就推送到哪里。也就省了每次去修改kapacitor.conf的麻烦。
influxdb, kapacitor都是直接从docker hub拉的镜像,并不是自己用dockerfile生产的。
这个influxdb+kapacitor告警服务是原来就做好的功能,只是一直都没有触发过邮件推送,哪怕已经满足触发告警条件。
告警脚本
var tickScriptStr = `
stream
|from()
.measurement('xx') // 指定measurement
.where(lambda: "condition" == '#condition#') // 指定查询条件,#condition#的具体值在代码里面会被替换
|window()
.period(#span#s) // #span#会在代码里面替换
.every(#span#s)
|mean('xx') // 取的是当前measurement里面xx field的平均值
|eval(lambda: 100.0 - "xx") // 这里是由于百分比,所以这里用了100-
.as('used')
|alert()
.message('dc: #dc# | host: #host# | type: cpu | {{ .Level}}: high cpu usage - {{ index .Fields "used" }}')
.warn(lambda: "used" > #threshold#.0) // #threshold#在代码里面赋值
.post('#arecord#') // 这里把告警信息post到某个http服务器
.log('/root/alerts.log')` // 这里把告警日志存入容器的/root/alerts.log里面
脚本里面没有关于邮件收件地址的信息,是由于在代码里面动态加载的,获取用户输入的邮箱,再在tickSCRIPT脚本里面拼接。
类似这样: .email("aaa@aa")
原因
结合kapacitor 邮件推送文档:https://docs.influxdata.com/kapacitor/v1.5/event_handlers/email
与公司已有的kapacitor.conf,发现没有邮件推送是由于公司使用的kapacitor.conf,并没有enable [smtp]模块。
解决
根据[stmp]模块,是需要smtp服务器的host, port, username, password。
问相关同事得到了公司邮件服务器host, port,由于是测试,username+password暂时用的自己的。
由于同事给的邮件服务器host,port不对,我调试了一下午。没有任何相关err log,还去看了/var/log/messages,也没发现什么相关err。
看了kapacitor源码,在邮件推送相关模块只有出错才会打印日志。
问了几个同事,每个同事给的host + port都没有实现邮件推送。
后来问了之前做这个模块的同事,他说port应该是25,赶紧把端口改25测试,这下终于出现err log了。
err log很明显,已经明确告诉我host不对。
把host修改了,port改为25, ok了,邮件能正常推送了。
kapacitor.conf smtp模块
[smtp]
enabled = true // 一定要启用,设置为true
host = "公司的邮件服务器host"
port = 25
username = "公司某个邮件账号,也可以是你自己的邮件账号"
password = "与username对应的密码"
no-verify = false // 这个我使用的默认的false,没有修改
global = false
state-changes-only = false
from = "发件人的地址"
idle-timeout = "30s"
kapacitor如何开启邮件推送服务
append smtp服务
kapacitor服务启动的时候会append smtp服务
代码位置:github.com/influxdata/kapacitor/cmd/kapacitord/run/commond.go
// Run parses the config from args and runs the server.
func (cmd *Command) Run(args ...string) error {
...
s, err := server.New(config, buildInfo, cmd.logService)
if err != nil {
return fmt.Errorf("create server: %s", err)
}
...
if err := s.Open(); err != nil {
return fmt.Errorf("open server: %s", err)
}
...
}
查看server.New()
// New returns a new instance of Server built from a config.
func New(c *Config, buildInfo BuildInfo, logService logging.Interface) (*Server, error) {
...
s.appendSMTPService()
...
}
开启smtp服务
代码位置:github.com/influxdata/kapacitor/server/server.go
// Open opens all the services.
func (s *Server) Open() error {
...
if err := s.startServices(); err != nil {
s.Close()
return err
}
...
}
// service.Open()是一个interface,根据各自service的实现取开启各自的服务
func (s *Server) startServices() error {
...
if err := service.Open(); err != nil {
return fmt.Errorf("open service %T: %s", service, err)
}
...
}
smtp 服务具体实现
// stmp service open
func (s *Service) Open() error {
s.mu.Lock()
defer s.mu.Unlock()
if s.opened {
return nil
}
s.opened = true
s.mail = make(chan *gomail.Message)
s.wg.Add(1)
go func() {
defer s.wg.Done()
s.runMailer()
}()
return nil
}
func (s *Service) runMailer() {
var idleTimeout time.Duration
var d *gomail.Dialer
d, idleTimeout = s.dialer()
var conn gomail.SendCloser
defer func() {
if conn != nil {
conn.Close()
}
}()
var err error
open := false
for {
timer := time.NewTimer(idleTimeout)
select {
case <-s.updates:
// Close old connection
if conn != nil {
if err := conn.Close(); err != nil {
s.logger.Println("E! error closing connection to old SMTP server:", err)
}
conn = nil
}
// Create new dialer
d, idleTimeout = s.dialer()
open = false
case m, ok := <-s.mail:
if !ok {
return
}
if !open {
if conn, err = d.Dial(); err != nil {
s.logger.Println("E! error connecting to SMTP server", err)
break
}
open = true
}
if err := gomail.Send(conn, m); err != nil {
s.logger.Println("E!", err)
}
// Close the connection to the SMTP server if no email was sent in
// the last IdleTimeout duration.
case <-timer.C:
if open {
if err := conn.Close(); err != nil {
s.logger.Println("E! error closing connection to SMTP server:", err)
}
open = false
}
}
timer.Stop()
}
}
当AlertNode使用过滤(info, warn, crit)消息级别的时候会发现, 即使你直用了warn()但是有时候你收到的推送除了有WARNING还会有OK级别的,这是kapacitor内置的,原文在这:
https://docs.influxdata.com/kapacitor/v1.5/nodes/alert_node/#alert-event-data
这里明确说明了
Events are sent to handlers if the alert is in a state other than ‘OK’ or the alert just changed to the ‘OK’ state from a non ‘OK’ state (a.k.a. the alert recovered). Using the AlertNode.StateChangesOnly property events will only be sent to handlers if the alert changed state.
我看有好多与emil推送相关的问题。
https://github.com/influxdata/kapacitor/issues/1471
https://github.com/influxdata/kapacitor/issues/1472
https://github.com/influxdata/kapacitor/issues/805
https://github.com/influxdata/kapacitor/issues/716
https://github.com/influxdata/kapacitor/issues/602
https://github.com/influxdata/kapacitor/issues/607
https://github.com/influxdata/kapacitor/issues/278
httpport没生效:https://github.com/influxdata/kapacitor/issues/2212