เป็นไปได้มากว่าทุกวันนี้ไม่มีใครถามว่าทำไมจึงจำเป็นต้องรวบรวมตัวชี้วัดการบริการ ขั้นตอนตรรกะถัดไปคือการตั้งค่าการแจ้งเตือนสำหรับเมตริกที่รวบรวม ซึ่งจะแจ้งเตือนเกี่ยวกับการเบี่ยงเบนของข้อมูลในช่องที่สะดวกสำหรับคุณ (เมล, Slack, Telegram) ในบริการจองโรงแรมออนไลน์
Kapacitor เป็นส่วนหนึ่งของสแต็ก TICK ที่สามารถประมวลผลเมตริกจาก InfluxDB สามารถเชื่อมโยงการวัดต่างๆ เข้าด้วยกัน (รวม) คำนวณสิ่งที่มีประโยชน์จากข้อมูลที่ได้รับ เขียนผลลัพธ์กลับไปยัง InfluxDB ส่งการแจ้งเตือนไปยัง Slack/Telegram/mail
กองทั้งหมดนั้นเจ๋งและมีรายละเอียด
float & int ข้อผิดพลาดในการคำนวณ
ปัญหามาตรฐานอย่างแน่นอน แก้ไขผ่านวรรณะ:
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
การใช้ค่าเริ่มต้น ()
หากไม่ได้กรอกแท็ก/ฟิลด์ ข้อผิดพลาดในการคำนวณจะเกิดขึ้น:
|default()
.tag('status', 'empty')
.field('value', 0)
กรอกเข้าร่วม (ภายใน vs ภายนอก)
ตามค่าเริ่มต้น การเข้าร่วมจะละทิ้งจุดที่ไม่มีข้อมูล (ภายใน)
ด้วยการเติม ('null') การรวมภายนอกจะดำเนินการ หลังจากนั้นคุณจะต้องทำค่าเริ่มต้น () และกรอกค่าว่าง:
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
ยังคงมีความแตกต่างกันนิดหน่อยที่นี่ ในตัวอย่างข้างต้น หากหนึ่งในซีรีส์ (res1 หรือ res2) ว่างเปล่า ซีรีส์ผลลัพธ์ (ข้อมูล) ก็จะว่างเปล่าเช่นกัน มีตั๋วหลายใบในหัวข้อนี้บน Github (
การใช้เงื่อนไขในการคำนวณ (หากเป็นแลมบ์ดา)
|eval(lambda: if("value" > 0, true, false)
ห้านาทีสุดท้ายจากไปป์ไลน์สำหรับงวด
ตัวอย่างเช่น คุณต้องเปรียบเทียบค่าของห้านาทีที่ผ่านมากับสัปดาห์ก่อนหน้า คุณสามารถนำข้อมูลสองชุดออกเป็นสองชุดแยกกันหรือดึงข้อมูลบางส่วนจากช่วงเวลาที่ใหญ่กว่า:
|where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)
อีกทางเลือกหนึ่งในช่วงห้านาทีสุดท้ายคือการใช้ BarrierNode ซึ่งจะตัดข้อมูลก่อนเวลาที่กำหนด:
|barrier()
.period(5m)
ตัวอย่างการใช้เทมเพลต Go ในข้อความ
เทมเพลตสอดคล้องกับรูปแบบจากแพ็คเกจ
ถ้าอื่น
เราจัดสิ่งต่าง ๆ ตามลำดับและไม่เรียกผู้คนด้วยข้อความอีกต่อไป:
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
ตัวเลขสองหลักหลังจุดทศนิยมในข้อความ
ปรับปรุงความสามารถในการอ่านข้อความ:
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
การขยายตัวแปรในข้อความ
เราแสดงข้อมูลเพิ่มเติมในข้อความเพื่อตอบคำถาม “ตะโกนทำไม”?
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
ตัวระบุการแจ้งเตือนที่ไม่ซ้ำ
นี่เป็นสิ่งที่จำเป็นเมื่อมีมากกว่าหนึ่งกลุ่มในข้อมูล ไม่เช่นนั้นระบบจะสร้างการแจ้งเตือนเพียงรายการเดียวเท่านั้น:
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
ตัวจัดการแบบกำหนดเอง
รายการตัวจัดการจำนวนมากรวมถึง exec ซึ่งช่วยให้คุณสามารถรันสคริปต์ของคุณด้วยพารามิเตอร์ที่ส่งผ่าน (stdin) - ความคิดสร้างสรรค์และไม่มีอะไรเพิ่มเติม!
ธรรมเนียมอย่างหนึ่งของเราคือสคริปต์ Python ขนาดเล็กสำหรับส่งการแจ้งเตือนไปยังจุดหย่อน
ในตอนแรก เราต้องการส่งรูปภาพกราฟาน่าที่มีการป้องกันการอนุญาตทางข้อความ หลังจากนั้น ให้เขียน OK ในชุดข้อความไปยังการแจ้งเตือนก่อนหน้าจากกลุ่มเดียวกัน และไม่ใช่เป็นข้อความแยกต่างหาก อีกไม่นาน - เพิ่มข้อความถึงข้อผิดพลาดที่พบบ่อยที่สุดในช่วง X นาทีที่ผ่านมา
หัวข้อที่แยกออกมาคือการสื่อสารกับบริการอื่นๆ และการดำเนินการใดๆ ที่เกิดจากการแจ้งเตือน (เฉพาะในกรณีที่การตรวจสอบของคุณทำงานได้ดีเพียงพอ)
ตัวอย่างของคำอธิบายตัวจัดการ โดยที่ slack_handler.py เป็นสคริปต์ที่เราเขียนเอง:
topic: slack_graph
id: slack_graph.alert
match: level() != INFO AND changed() == TRUE
kind: exec
options:
prog: /sbin/slack_handler.py
args: ["-c", "CHANNELID", "--graph", "--search"]
วิธีการแก้ไขข้อบกพร่อง?
ตัวเลือกที่มีเอาต์พุตบันทึก
|log()
.level("error")
.prefix("something")
ดู (cli): kapacitor -url
ตัวเลือกที่มี httpOut
แสดงข้อมูลในไปป์ไลน์ปัจจุบัน:
|httpOut('something')
ดู (รับ):
รูปแบบการดำเนินการ
- แต่ละงานส่งคืนแผนผังการดำเนินการพร้อมตัวเลขที่มีประโยชน์ในรูปแบบ
กราฟวิซ . - ใช้บล็อก
จุด . - วางลงในโปรแกรมดู
สนุก .
คุณสามารถหาคราดได้ที่ไหนอีก?
การประทับเวลาใน influxdb ในการเขียนกลับ
ตัวอย่างเช่น เราตั้งค่าการแจ้งเตือนสำหรับผลรวมของคำขอต่อชั่วโมง (groupBy(1h)) และต้องการบันทึกการแจ้งเตือนที่เกิดขึ้นใน influxdb (เพื่อแสดงข้อเท็จจริงของปัญหาอย่างสวยงามบนกราฟใน grafana)
influxDBOut() จะเขียนค่าเวลาจากการแจ้งเตือนไปยังการประทับเวลา ดังนั้น จุดบนแผนภูมิจะถูกเขียนก่อนหรือหลังกว่าที่การแจ้งเตือนมาถึง
เมื่อจำเป็นต้องมีความแม่นยำ: เราจะแก้ไขปัญหานี้โดยการเรียกตัวจัดการแบบกำหนดเอง ซึ่งจะเขียนข้อมูลไปยัง influxdb ด้วยการประทับเวลาปัจจุบัน
นักเทียบท่า การสร้าง และการปรับใช้
เมื่อเริ่มต้น kapacitor สามารถโหลดงาน เทมเพลต และตัวจัดการจากไดเร็กทอรีที่ระบุในการกำหนดค่าในบล็อก [load]
หากต้องการสร้างงานอย่างถูกต้อง คุณต้องมีสิ่งต่อไปนี้:
- ชื่อไฟล์ – ขยายเป็นรหัส/ชื่อสคริปต์
- ประเภท – สตรีม/แบทช์
- dbrp – คีย์เวิร์ดเพื่อระบุว่าฐานข้อมูล + นโยบายใดที่สคริปต์ทำงาน (dbrp “ซัพพลายเออร์” “autogen”)
หากงานแบตช์บางงานไม่มีบรรทัดที่มี dbrp บริการทั้งหมดจะปฏิเสธที่จะเริ่มต้นและจะเขียนเกี่ยวกับงานดังกล่าวในบันทึกโดยสุจริต
ในทางกลับกัน chronograf ไม่ควรมีบรรทัดนี้และไม่ได้รับการยอมรับผ่านอินเทอร์เฟซและทำให้เกิดข้อผิดพลาด
แฮ็กเมื่อสร้างคอนเทนเนอร์: Dockerfile ออกด้วย -1 หากมีบรรทัดด้วย //.+dbrp ซึ่งจะช่วยให้คุณเข้าใจสาเหตุของความล้มเหลวได้ทันทีเมื่อประกอบบิวด์
เข้าร่วมหนึ่งต่อหลาย
งานตัวอย่าง: คุณต้องใช้เวลาในการให้บริการเป็นเปอร์เซ็นไทล์ที่ 95 เป็นเวลาหนึ่งสัปดาห์ เปรียบเทียบแต่ละนาทีของ 10 นาทีสุดท้ายด้วยค่านี้
คุณไม่สามารถทำการเข้าร่วมแบบหนึ่งต่อกลุ่มได้ สุดท้าย/เฉลี่ย/มัธยฐานเหนือกลุ่มจุดจะเปลี่ยนโหนดให้เป็นสตรีม ข้อผิดพลาด “ไม่สามารถเพิ่มขอบที่ไม่ตรงกันลูก: ชุด -> สตรีม” จะถูกส่งกลับ
ผลลัพธ์ของแบตช์ซึ่งเป็นตัวแปรในนิพจน์แลมบ์ดาจะไม่ถูกแทนที่เช่นกัน
มีตัวเลือกในการบันทึกหมายเลขที่จำเป็นจากชุดแรกลงในไฟล์ผ่าน udf และโหลดไฟล์นี้ผ่านไซด์โหลด
เราแก้ปัญหาอะไรกับสิ่งนี้?
เรามีซัพพลายเออร์โรงแรมประมาณ 100 ราย แต่ละรายสามารถมีการเชื่อมต่อได้หลายทาง เรียกได้ว่าเป็นช่องทางกันเลยทีเดียว มีช่องประมาณ 300 ช่อง แต่ละช่องอาจหลุดได้ จากตัวชี้วัดที่บันทึกไว้ทั้งหมด เราจะตรวจสอบอัตราข้อผิดพลาด (คำขอและข้อผิดพลาด)
ทำไมไม่กราฟาน่าล่ะ?
การแจ้งเตือนข้อผิดพลาดที่กำหนดค่าใน Grafana มีข้อเสียหลายประการ บางอย่างก็สำคัญ บางอย่างคุณสามารถหลับตาได้ ขึ้นอยู่กับสถานการณ์
Grafana ไม่รู้วิธีคำนวณระหว่างการวัด + การแจ้งเตือน แต่เราต้องการอัตรา (คำขอ-ข้อผิดพลาด)/คำขอ
ข้อผิดพลาดดูน่ารังเกียจ:
และความชั่วร้ายน้อยลงเมื่อดูด้วยการร้องขอที่ประสบความสำเร็จ:
โอเค เราสามารถคำนวณอัตราล่วงหน้าในบริการก่อนกราฟาน่าได้ และในบางกรณีก็จะได้ผล แต่ไม่ใช่ของเราเพราะว่า... สำหรับแต่ละช่องอัตราส่วนของตัวเองถือว่า "ปกติ" และการแจ้งเตือนทำงานตามค่าคงที่ (เรามองด้วยตาเปล่าเปลี่ยนหากมีการแจ้งเตือนบ่อยครั้ง)
นี่คือตัวอย่าง "ปกติ" สำหรับช่องต่างๆ:
เราเพิกเฉยต่อประเด็นก่อนหน้าและถือว่าภาพ "ปกติ" นั้นคล้ายคลึงกันสำหรับซัพพลายเออร์ทุกราย ตอนนี้ทุกอย่างเรียบร้อยดี แล้วเราจะผ่านการแจ้งเตือนในกราฟาน่าไปได้หรือยัง?
เราทำได้ แต่เราไม่ต้องการจริงๆ เพราะเราต้องเลือกหนึ่งในตัวเลือก:
ก) สร้างกราฟจำนวนมากสำหรับแต่ละช่องแยกกัน (และติดตามอย่างเจ็บปวด)
b) ออกจากแผนภูมิเดียวพร้อมทุกช่อง (และหลงไปกับเส้นสีสันสดใสและการแจ้งเตือนที่ปรับแต่งเอง)
คุณทำได้อย่างไร?
มีตัวอย่างการเริ่มต้นที่ดีอีกครั้งในเอกสารประกอบ (
สิ่งที่เราทำในท้ายที่สุด:
- เข้าร่วมสองซีรีส์ในเวลาไม่กี่ชั่วโมง โดยจัดกลุ่มตามช่อง
- กรอกข้อมูลตามลำดับกลุ่มหากไม่มีข้อมูล
- เปรียบเทียบค่ามัธยฐานของ 10 นาทีที่ผ่านมากับข้อมูลก่อนหน้า
- เราตะโกนหากพบบางสิ่ง
- เราเขียนอัตราที่คำนวณได้และการแจ้งเตือนที่เกิดขึ้นใน influxdb
- ส่งข้อความที่เป็นประโยชน์ถึงหย่อน
ในความคิดของฉัน เราสามารถบรรลุทุกสิ่งที่เราต้องการได้ในตอนท้าย (และมากกว่านั้นอีกเล็กน้อยด้วยตัวจัดการแบบกำหนดเอง) ให้สวยงามที่สุดเท่าที่จะเป็นไปได้
คุณสามารถดูได้ที่ github.com
ตัวอย่างของรหัสผลลัพธ์:
dbrp "supplier"."autogen"
var name = 'requests.rate'
var grafana_dash = 'pczpmYZWU/mydashboard'
var grafana_panel = '26'
var period = 8h
var todayPeriod = 10m
var every = 1m
var warnAlert = 15
var warnReset = 5
var reqQuery = 'SELECT sum("count") AS value FROM "supplier"."autogen"."requests"'
var errQuery = 'SELECT sum("count") AS value FROM "supplier"."autogen"."errors"'
var prevErr = batch
|query(errQuery)
.period(period)
.every(every)
.groupBy(1m, 'channel', 'supplier')
var prevReq = batch
|query(reqQuery)
.period(period)
.every(every)
.groupBy(1m, 'channel', 'supplier')
var rates = prevReq
|join(prevErr)
.as('req', 'err')
.tolerance(1m)
.fill('null')
// заполняем значения нулями, если их не было
|default()
.field('err.value', 0.0)
.field('req.value', 0.0)
// if в lambda: считаем рейт, только если ошибки были
|eval(lambda: if("err.value" > 0, 100.0 * (float("req.value") - float("err.value")) / float("req.value"), 100.0))
.as('rate')
// записываем посчитанные значения в инфлюкс
rates
|influxDBOut()
.quiet()
.create()
.database('kapacitor')
.retentionPolicy('autogen')
.measurement('rates')
// выбираем данные за последние 10 минут, считаем медиану
var todayRate = rates
|where(lambda: duration((unixNano(now()) - unixNano("time")) / 1000, 1u) < todayPeriod)
|median('rate')
.as('median')
var prevRate = rates
|median('rate')
.as('median')
var joined = todayRate
|join(prevRate)
.as('today', 'prev')
|httpOut('join')
var trigger = joined
|alert()
.warn(lambda: ("prev.median" - "today.median") > warnAlert)
.warnReset(lambda: ("prev.median" - "today.median") < warnReset)
.flapping(0.25, 0.5)
.stateChangesOnly()
// собираем в message ссылку на график дашборда графаны
.message(
'{{ .Level }}: {{ index .Tags "channel" }} err/req ratio ({{ index .Tags "supplier" }})
{{ if eq .Level "OK" }}It is ok now{{ else }}
'+string(todayPeriod)+' median is {{ index .Fields "today.median" | printf "%0.2f" }}%, by previous '+string(period)+' is {{ index .Fields "prev.median" | printf "%0.2f" }}%{{ end }}
http://grafana.ostrovok.in/d/'+string(grafana_dash)+
'?var-supplier={{ index .Tags "supplier" }}&var-channel={{ index .Tags "channel" }}&panelId='+string(grafana_panel)+'&fullscreen&tz=UTC%2B03%3A00'
)
.id('{{ index .Tags "name" }}/{{ index .Tags "channel" }}')
.levelTag('level')
.messageField('message')
.durationField('duration')
.topic('slack_graph')
// "today.median" дублируем как "value", также пишем в инфлюкс остальные филды алерта (keep)
trigger
|eval(lambda: "today.median")
.as('value')
.keep()
|influxDBOut()
.quiet()
.create()
.database('kapacitor')
.retentionPolicy('autogen')
.measurement('alerts')
.tag('alertName', name)
ข้อสรุปคืออะไร?
Kapacitor ยอดเยี่ยมในการดำเนินการแจ้งเตือนการตรวจสอบด้วยกลุ่มกลุ่ม คำนวณเพิ่มเติมตามตัวชี้วัดที่บันทึกไว้แล้ว ดำเนินการแบบกำหนดเอง และเรียกใช้สคริปต์ (udf)
อุปสรรคในการเข้าไม่สูงมาก - ลองใช้หากกราฟาน่าหรือเครื่องมืออื่น ๆ ไม่ตรงตามความต้องการของคุณ
ที่มา: will.com