样式功能来看,整体不是很复杂,alert 组件主要包括了主题色,title,关闭按钮,关闭事件,居中,加粗等
<template>
<!-- 显示隐藏有动画效果 -->
<!-- 开发没用过,不是很理解为什么使用v-show判断显示 -->
<transition name="d-alert-fade">
<div
class="d-alert"
:class="[typeClass, center ? 'is-center' : '', 'is-' + effect]"
v-show="visible"
role="alert"
>
<!-- 左侧图标 -->
<i
class="d-alert__icon"
:class="[iconClass, isBigIcon]"
v-if="showIcon"
></i>
<!-- title 和 描述 -->
<div class="d-alert__content">
<span
class="d-alert__title"
:class="[isBoldTitle]"
v-if="title || $slots.title"
>
<slot name="title">{{ title }}</slot>
</span>
<p v-if="$slots.default && !description" class="d-alert__description">
<slot></slot>
</p>
<p v-if="description && !$slots.default" class="d-alert__description">
{{ description }}
</p>
<i
class="d-alert__closebtn"
:class="{
'is-customed': closeText !=='',
'd-icon-close': closeText===''
}"
v-show="closable"
@click="close"
>{{ closeText }}</i
>
</div>
</div>
</transition>
</template>
使用 role 属性告诉辅助设备(如屏幕阅读器)这个元素所扮演的角色。本质上是增强语义性,当现有的 HTML标签不能充分表达语义性的时候,就可以借助 role 来说明。
这里不是很理解为什么 title 和 description 使用了属性和 slot 判断,有清楚的朋友可以帮忙解答
setup(props, { emit, slots }) {
// 接受的属性转为响应式
const { description, type }=toRefs(props)
// 使用 v-show 显示隐藏
const visible=ref(true)
// 关闭事件
const close=()=> {
visible.value=false
emit('close')
}
const typeClass=computed(()=> {
return `d-alert--${type.value}`
})
const iconClass=computed(()=> {
return TYPE_CLASSES_MAP[type.value] || 'd-icon-info'
})
const isBigIcon=computed(()=> {
return description.value || slots.default ? 'is-big' : ''
})
const isBoldTitle=computed(()=> {
return description.value || slots.default ? 'is-bold' : ''
})
return {
close,
visible,
typeClass,
iconClass,
isBigIcon,
isBoldTitle
}
}
组件介绍到这里就结束了,比较简单。为了凑字呢,这里在介绍下 transition 组件
大部分朋友都了解这是设置组件动画的内置动画组件。通常有三种使用方式:
我们通常使用的方法,css 配置 enter 和 leave
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition name="fade">
<p v-if="show">我是测试</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
<template>
<div class="app">
<button @click="show=!show">Toggle show</button>
<transition name="bounce">
<p v-if="show">我是测试</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
// reverse 很关键
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
监听 transition 组件的内置方法,js 控制动画
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@before-leave="beforeLeave"
@leave="leave"
css="false"
>
<p v-if="show">hello</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
el.style.opacity=0
el.style.transition='opacity 0.5s ease'
},
enter(el) {
this.$el.offsetHeight
el.style.opacity=1
},
beforeLeave(el) {
el.style.opacity=1
},
leave(el) {
el.style.transition='opacity 0.5s ease'
el.style.opacity=0
}
}
}
</script>
如果形参不指定 done ,则表明用户不手动控制动画的结束,而转由节点的 transition 或者 animationEnd 来标识动画结束,开始回调 afterEnter。
钩子函数的形参的个数大于1,表示形参中有 done, 也就是说用户必须手动控制动画何时结束。所以一旦你配置了 done 形参,则转由你告诉框架,动画何时结束。需要在合适的时机调用 done,否则 afterEnter 接口就没法被调用了。
实例
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
</template>
编译生成的 render 函数(不是使用的模板组件)
import {
createVNode as _createVNode,
openBlock as _openBlock,
createBlock as _createBlock,
createCommentVNode as _createCommentVNode,
Transition as _Transition,
withCtx as _withCtx,
} from "vue";
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (
// 收集动态节点 如 v-if v-for
_openBlock(),
_createBlock("template", null, [
_createVNode("div", { class: "app" }, [
_createVNode(
"button",
{
onClick: ($event)=> (_ctx.show=!_ctx.show),
},
" Toggle render ",
8 /* PROPS */,
["onClick"]
),
_createVNode(
_Transition,
{ name: "fade" },
{
// transition 只有一个子节点,默认插槽。多个子节点报错
default: _withCtx(()=> [
_ctx.show
? (_openBlock(), _createBlock("p", { key: 0 }, "hello"))
: _createCommentVNode("v-if", true),
]),
_: 1,
}
),
]),
])
);
}
那么如何在组建创建和销毁的时候执行事件呢?————创建钩子函数 transition 组件返回的是处理过的第一个子节点
trantion 组件定义
const Transition=(props, { slots })=>
//esolveTransitionProps 函数主要作用是,在我们给 Transition 传递的 Props 基础上做一层封装,然后返回一个新的 Props 对象,由于它包含了所有的 Props 处理
h(BaseTransition, resolveTransitionProps(props), slots);
const BaseTransition={
name: `BaseTransition`,
props: {
mode: String,
appear: Boolean,
persisted: Boolean,
// enter
onBeforeEnter: TransitionHookValidator,
onEnter: TransitionHookValidator,
onAfterEnter: TransitionHookValidator,
onEnterCancelled: TransitionHookValidator,
// leave
onBeforeLeave: TransitionHookValidator,
onLeave: TransitionHookValidator,
onAfterLeave: TransitionHookValidator,
onLeaveCancelled: TransitionHookValidator,
// appear
onBeforeAppear: TransitionHookValidator,
onAppear: TransitionHookValidator,
onAfterAppear: TransitionHookValidator,
onAppearCancelled: TransitionHookValidator,
},
setup(props, { slots }) {
const instance=getCurrentInstance();
const state=useTransitionState();
let prevTransitionKey;
return ()=> {
const children=slots.default && getTransitionRawChildren(slots.default(), true);
if (!children || !children.length) {
return;
}
// Transition 组件只允许一个子元素节点,多个报警告,提示使用 TransitionGroup 组件
if (process.env.NODE_ENV !=="production" && children.length > 1) {
warn(
"<transition> can only be used on a single element or component. Use " +
"<transition-group> for lists."
);
}
// 不需要追踪响应式,所以改成原始值,提升性能
const rawProps=toRaw(props);
const { mode }=rawProps;
// 检查 mode 是否合法
if (
process.env.NODE_ENV !=="production" &&
mode &&
!["in-out", "out-in", "default"].includes(mode)
) {
warn(`invalid <transition> mode: ${mode}`);
}
// 获取第一个子元素节点
const child=children[0];
if (state.isLeaving) {
return emptyPlaceholder(child);
}
// 处理 <transition><keep-alive/></transition> 的情况
const innerChild=getKeepAliveChild(child);
if (!innerChild) {
return emptyPlaceholder(child);
}
const enterHooks=resolveTransitionHooks(
innerChild,
rawProps,
state,
instance
);
setTransitionHooks(innerChild, enterHooks);
const oldChild=instance.subTree;
const oldInnerChild=oldChild && getKeepAliveChild(oldChild);
let transitionKeyChanged=false;
const { getTransitionKey }=innerChild.type;
if (getTransitionKey) {
const key=getTransitionKey();
if (prevTransitionKey===undefined) {
prevTransitionKey=key;
} else if (key !==prevTransitionKey) {
prevTransitionKey=key;
transitionKeyChanged=true;
}
}
if (
oldInnerChild &&
oldInnerChild.type !==Comment &&
(!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)
) {
const leavingHooks=resolveTransitionHooks(
oldInnerChild,
rawProps,
state,
instance
);
// 更新旧树的钩子函数
setTransitionHooks(oldInnerChild, leavingHooks);
// 在两个视图之间切换
if (mode==="out-in") {
state.isLeaving=true;
// 返回空的占位符节点,当离开过渡结束后,重新渲染组件
leavingHooks.afterLeave=()=> {
state.isLeaving=false;
instance.update();
};
return emptyPlaceholder(child);
} else if (mode==="in-out") {
leavingHooks.delayLeave=(el, earlyRemove, delayedLeave)=> {
const leavingVNodesCache=getLeavingNodesForType(
state,
oldInnerChild
);
leavingVNodesCache[String(oldInnerChild.key)]=oldInnerChild;
// early removal callback
el._leaveCb=()=> {
earlyRemove();
el._leaveCb=undefined;
delete enterHooks.delayedLeave;
};
enterHooks.delayedLeave=delayedLeave;
};
}
}
return child;
};
},
};
在渲染的过程中,Transition 组件还会通过 resolveTransitionHooks 去定义组件创建和删除阶段的钩子函数对象,然后再通过 setTransitionHooks 函数去把这个钩子函数对象设置到 vnode.transition 上。
hooks定义
const hooks={
mode,
persisted,
beforeEnter(el) {
let hook=onBeforeEnter;
if (!state.isMounted) {
if (appear) {
hook=onBeforeAppear || onBeforeEnter;
} else {
return;
}
}
if (el._leaveCb) {
el._leaveCb(true /* cancelled */);
}
const leavingVNode=leavingVNodesCache[key];
if (
leavingVNode &&
isSameVNodeType(vnode, leavingVNode) &&
leavingVNode.el._leaveCb
) {
leavingVNode.el._leaveCb();
}
callHook(hook, [el]);
},
enter(el) {
let hook=onEnter;
let afterHook=onAfterEnter;
let cancelHook=onEnterCancelled;
if (!state.isMounted) {
if (appear) {
hook=onAppear || onEnter;
afterHook=onAfterAppear || onAfterEnter;
cancelHook=onAppearCancelled || onEnterCancelled;
} else {
return;
}
}
let called=false;
const done=(el._enterCb=(cancelled)=> {
if (called) return;
called=true;
if (cancelled) {
callHook(cancelHook, [el]);
} else {
callHook(afterHook, [el]);
}
if (hooks.delayedLeave) {
hooks.delayedLeave();
}
el._enterCb=undefined;
});
if (hook) {
hook(el, done);
if (hook.length <=1) {
done();
}
} else {
done();
}
},
leave(el, remove) {
const key=String(vnode.key);
if (el._enterCb) {
el._enterCb(true /* cancelled */);
}
if (state.isUnmounting) {
return remove();
}
callHook(onBeforeLeave, [el]);
let called=false;
const done=(el._leaveCb=(cancelled)=> {
if (called) return;
called=true;
remove();
if (cancelled) {
callHook(onLeaveCancelled, [el]);
} else {
callHook(onAfterLeave, [el]);
}
el._leaveCb=undefined;
if (leavingVNodesCache[key]===vnode) {
delete leavingVNodesCache[key];
}
});
leavingVNodesCache[key]=vnode;
if (onLeave) {
onLeave(el, done);
if (onLeave.length <=1) {
done();
}
} else {
done();
}
},
clone(vnode) {
return resolveTransitionHooks(vnode, props, state, instance);
},
};
钩子函数对象定义了 4 个钩子函数,分别是 beforeEnter,enter,leave 和 clone。在节点 patch 阶段的 mountElement 函数中,在插入节点前且存在过度会执行 vnode.transition 中的 beforeEnter 函数
//beforeEnter 钩子函数主要做的事情就是根据 appear 的值和 DOM 是否挂载,来执行 onBeforeEnter 函数或者是 onBeforeAppear 函数。appear 是否节点现实的时候执行动画
beforeEnter(el) {
let hook=onBeforeEnter
if (!state.isMounted) {
if (appear) {
hook=onBeforeAppear || onBeforeEnter
}
else {
return
}
}
if (el._leaveCb) {
el._leaveCb(true /* cancelled */)
}
const leavingVNode=leavingVNodesCache[key]
if (leavingVNode &&
isSameVNodeType(vnode, leavingVNode) &&
leavingVNode.el._leaveCb) {
leavingVNode.el._leaveCb()
}
callHook(hook, [el])
}
resolveTransitionProps 函数
function resolveTransitionProps(rawProps) {
let {
name="v",
type,
css=true,
duration,
enterFromClass=`${name}-enter-from`,
enterActiveClass=`${name}-enter-active`,
enterToClass=`${name}-enter-to`,
appearFromClass=enterFromClass,
appearActiveClass=enterActiveClass,
appearToClass=enterToClass,
leaveFromClass=`${name}-leave-from`,
leaveActiveClass=`${name}-leave-active`,
leaveToClass=`${name}-leave-to`,
}=rawProps;
const baseProps={};
for (const key in rawProps) {
if (!(key in DOMTransitionPropsValidators)) {
baseProps[key]=rawProps[key];
}
}
if (!css) {
return baseProps;
}
const durations=normalizeDuration(duration);
const enterDuration=durations && durations[0];
const leaveDuration=durations && durations[1];
const {
onBeforeEnter,
onEnter,
onEnterCancelled,
onLeave,
onLeaveCancelled,
onBeforeAppear=onBeforeEnter,
onAppear=onEnter,
onAppearCancelled=onEnterCancelled,
}=baseProps;
const finishEnter=(el, isAppear, done)=> {
removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
done && done();
};
const finishLeave=(el, done)=> {
removeTransitionClass(el, leaveToClass);
removeTransitionClass(el, leaveActiveClass);
done && done();
};
const makeEnterHook=(isAppear)=> {
return (el, done)=> {
const hook=isAppear ? onAppear : onEnter;
const resolve=()=> finishEnter(el, isAppear, done);
hook && hook(el, resolve);
nextFrame(()=> {
removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass);
addTransitionClass(el, isAppear ? appearToClass : enterToClass);
if (!(hook && hook.length > 1)) {
if (enterDuration) {
setTimeout(resolve, enterDuration);
} else {
whenTransitionEnds(el, type, resolve);
}
}
});
};
};
return extend(baseProps, {
onBeforeEnter(el) {
onBeforeEnter && onBeforeEnter(el);
addTransitionClass(el, enterActiveClass);
addTransitionClass(el, enterFromClass);
},
onBeforeAppear(el) {
onBeforeAppear && onBeforeAppear(el);
addTransitionClass(el, appearActiveClass);
addTransitionClass(el, appearFromClass);
},
onEnter: makeEnterHook(false),
onAppear: makeEnterHook(true),
onLeave(el, done) {
const resolve=()=> finishLeave(el, done);
addTransitionClass(el, leaveActiveClass);
addTransitionClass(el, leaveFromClass);
nextFrame(()=> {
removeTransitionClass(el, leaveFromClass);
addTransitionClass(el, leaveToClass);
if (!(onLeave && onLeave.length > 1)) {
if (leaveDuration) {
setTimeout(resolve, leaveDuration);
} else {
whenTransitionEnds(el, type, resolve);
}
}
});
onLeave && onLeave(el, resolve);
},
onEnterCancelled(el) {
finishEnter(el, false);
onEnterCancelled && onEnterCancelled(el);
},
onAppearCancelled(el) {
finishEnter(el, true);
onAppearCancelled && onAppearCancelled(el);
},
onLeaveCancelled(el) {
finishLeave(el);
onLeaveCancelled && onLeaveCancelled(el);
},
});
}
我们来看 onBeforeEnter 函数,它的内部执行了基础 props 传入的 onBeforeEnter 钩子函数,并且给 DOM 元素 el 添加了 enterActiveClass 和 enterFromClass 样式。
其中,props 传入的 onBeforeEnter 函数就是我们写 Transition 组件时添加的 beforeEnter 钩子函数。enterActiveClass 默认值是 v-enter-active,enterFromClass 默认值是 v-enter-from,如果给 Transition 组件传入了 name 的 prop,比如 fade,那么 enterActiveClass 的值就是 fade-enter-active,enterFromClass 的值就是 fade-enter-from。(onBeforeAppear 和 onBeforeEnter 的逻辑类似,就不赘述了,它是在我们给 Transition 组件传入 appear 的 Prop,且首次挂载的时候执行的。执行完 beforeEnter 钩子函数,接着插入元素到页面,然后会执行 vnode.transition 中的 enter 钩子函数,上面的 hooks 中)
在 enter 函数内部,首先执行基础 props 传入的 onEnter 钩子函数,然后在下一帧给 DOM 元素 el 移除了 enterFromClass,同时添加了 enterToClass 样式(动画也就是所谓的样式交替改变)
Transition 组件允许我们传入 enterDuration 这个 prop,它会指定进入过渡的动画时长,当然如果你不指定,Vue.js 内部会监听动画结束事件,然后在动画结束后,执行 finishEnter 函数
来看它的实现
const finishEnter=(el, isAppear, done)=> {
removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
done && done();
};
其实就是给 DOM 元素移除 enterToClass 以及 enterActiveClass,同时执行 done 函数,进而执行 onAfterEnter 钩子函数
leave 钩子主要功能和 enter 相反。小伙伴们可自行查阅。
以上就是对 alert 组件的学习。如有不对欢迎指正。
么是JavaScript
JavaScript是一种基于对象和事件驱动的、并具有安全性能的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。
JavaScript特点
是一种解释性脚本语言(代码不进行预编译)。
主要用来向HTML(标准通用标记语言下的一个应用)页面添加交互行为。
可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。
跨平台特性,在绝大多数浏览器的支持下,可以在多种平台下运行(如Windows、Linux、Mac、Android、iOS等)。
JavaScript组成
JavaScript日常用途
1、嵌入动态文本于HTML页面。
2、对浏览器事件做出响应。
3、读写HTML元素。
4、在数据被提交到服务器之前验证数据。
5、检测访客的浏览器信息。
6、控制cookies,包括创建和修改等。
7、基于Node.js技术进行服务器端编程。
JavaScript的基本结构
<script type="text/javascript"> <!— JavaScript 语句; —> </script >
示例:
…… <title>初学JavaScript</title> </head> <body> <script type="text/javascript"> document.write("初学JavaScript"); document.write("<h1>Hello,JavaScript</h1>"); </script> </body> </html>
<script>…</script>可以包含在文档中的任何地方,只要保证这些代码在被使用前已读取并加载到内存即可
JavaScript的执行原理
网页中引用JavaScript的方式
1、使用<script>标签
2、外部JS文件
<script src="export.js" type="text/javascript"></script>
3.直接在HTML标签中
<input name="btn" type="button" value="弹出消息框" onclick="javascript:alert('欢迎你');"/>
JavaScript核心语法:
1. 变量
①先声明变量再赋值
var width; width=5; var - 用于声明变量的关键字 width - 变量名
②同时声明和赋值变量
var catName="皮皮"; var x, y, z=10;
③不声明直接赋值【一般不使用】
width=5;
变量可以不经声明而直接使用,但这种方法很容易出错,也很难查找排错,不推荐使用。
2. 数据类型
①undefined:示例:var width;
变量width没有初始值,将被赋予值undefined
②null:表示一个空值,与undefined值相等
③number:
var iNum=23; //整数
var iNum=23.0; //浮点数
④Boolean:true和false 但是JS会把他们解析成1;0
⑤String:一组被引号(单引号或双引号)括起来的文本 var string1="This is a string";
3. typeof运算符
typeof检测变量的返回值;typeof运算符返回值如下:
①undefined:变量被声明后,但未被赋值.
②string:用单引号或双引号来声明的字符串。
③boolean:true或false。
④number:整数或浮点数。
⑤object:javascript中的对象、数组和null。
rometheus发出告警时分为两部分。首先,Prometheus按告警规则(rule_files配置块)向Alertmanager发送告警(即告警规则是在Prometheus上定义的)。然后,由Alertmanager来管理这些告警,包括去重(Deduplicating)、分组(Grouping)、静音(silencing)、抑制(inhibition)、聚合(aggregation ),最终将面要发出的告警通过电子邮件、webhook等方式将告警通知路由(route)给对应的联系人。
分组:就是将具有相同性质的告警先分类,然后当作单个通知发送出来。比如A和B两台主机的磁盘(CPU/内存)使用率都在告警,则磁盘的告警就可以合并在一个通知中发送出来。可以想像某个服务部署了100个节点,在一次升版后因为bug,日志中均报同样一类错误,如果不合并这类通知,那就是告警风暴。
抑制:就是某些告警触发后,则抑制(禁止)另一些告警。比如收到一条告警提示集群故障无法访问,那么在该集群上的所有其他警告应该被抑制。
静音(默):将某些在预期内的告警设置为静默(即不发送告警)。静默是基于配置匹配规则。Alertmanager会检查从Prometheus推送过来的告警事件,看这些告警事件是否与配置的静默规则能匹配上,如果匹配则不发送任何通知。配置静默方法是在Alertmanager的Web界面中,也可以使用amtool工具。
总之:Alertmanager制定这一系列规则目的只有一个,就是提高告警质量。
配置Alertmanager来做告警通知主要分三个步骤:
一、安装并配置Alertmanager
# tar -xvf alertmanager-0.20.0.linux-amd64.tar.gz
mv alertmanager-0.20.0.linux-amd64 /usr/local/alertmanager
# cat alertmanager.yml
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.mxhichina.com:465'
smtp_from: 'noreply@demo.com'
smtp_auth_username: 'noreply@demo.com'
smtp_auth_password: '1235698'
smtp_require_tls: false
templates:
- '/usr/local/alertmanager/template/default.tmpl'
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 2m
repeat_interval: 10m
receiver: 'email'
# continue default is false
continue: true
receivers:
- name: 'email'
email_configs:
- to: 'cookingit222@163.com,itcooking222@163.com'
send_resolved: true
html: '{{ template "default.html" . }}'
headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
alertmanager配置简要说明:
global:全局配置,主要配置告警方式,如邮件、webhook等。
route:Prometheus的告警先是到达alertmanager的根路由(route),alertmanager的根路由不能包含任何匹配项,因为根路由是所有告警的入口点。另外,根路由需要配置一个接收器(receiver),用来处理那些没有匹配到任何子路由的告警(如果没有配置子路由,则全部由根路由发送告警),即缺省接收器。告警进入到根route后开始遍历子route节点,如果匹配到,则将告警发送到该子route定义的receiver中,然后就停止匹配了。因为在route中continue默认为false,如果continue为true,则告警会继续进行后续子route匹配。如果当前告警仍匹配不到任何的子route,则该告警将从其上一级(匹配)route或者根route发出(按最后匹配到的规则发出邮件)。
group_by:用于分组聚合,对告警通知按标签(label)进行分组,将具有相同标签或相同告警名称(alertname)的告警通知聚合在一个组,然后作为一个通知发送。如果想完全禁用聚合,可以设置为group_by: [...]
group_wait: 当一个新的告警组被创建时,需要等待'group_wait'后才发送初始通知。这样可以确保在发送等待前能聚合更多具有相同标签的告警,最后合并为一个通知发送。
group_interval: 当第一次告警通知发出后,在新的评估周期内又收到了该分组最新的告警,则需等待'group_interval'时间后,开始发送为该组触发的新告警,可以简单理解为,group就相当于一个通道(channel)。
repeat_interval: 告警通知成功发送后,若问题一直未恢复,需再次重复发送的间隔。
查看你的告警路由树,将alertmanager.yml配置文件复制到对话框,然后点击"Draw Routing Tree"
https://www.prometheus.io/webtools/alerting/routing-tree-editor/
修改好配置文件后,可以使用amtool工具检查配置
# ./amtool check-config alertmanager.yml
Checking 'alertmanager.yml' SUCCESS
# cat >/usr/lib/systemd/system/alertmanager.service <<EOF
[Unit]
Description=alertmanager
[Service]
ExecStart=/usr/local/alertmanager/alertmanager --config.file=/usr/local/alertmanager/alertmanager.yml --storage.path=/usr/local/alertmanager/data --web.listen-address=:9093 --data.retention=120h
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
# systemctl enable alertmanager
# systemctl restart alertmanager
alertmanager默认运行端口是:9093
alertmanager也可以同prometheus一样热加载配置
1)向alertmanager进程发送SIGHUP信号,如:kill -SIGHUP alertmanager_pid
2)curl -X POST http://prometheus_ip:9093/-/reload
二、修改prometheus的配置,关联Alertmanager服务,同时添加对alertmanager的监控
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- monitor01:9093
rule_files:
- "rules/*_rules.yml"
- "rules/*_alerts.yml"
......
- job_name: 'alertmanager'
static_configs:
- targets: ['localhost:9093']
labels:
app: alertmanager
三、修改prometheus的配置,添加记录规则和告警规则。
# cat node_rules.yml
groups:
- name: node_rules
#interval: 15s
rules:
- record: instance:node_cpu_usage
expr: 100 - avg(irate(node_cpu_seconds_total{mode="idle"}[1m])) by (nodename) * 100
labels:
metric_type: CPU_monitor
- record: instance:node_1m_load
expr: node_load1
labels:
metric_type: load1m_monitor
- record: instance:node_mem_usage
expr: 100 - (node_memory_MemAvailable_bytes)/(node_memory_MemTotal_bytes) * 100
labels:
metric_type: Memory_monitor
- record: instance:node_root_partition_predit
expr: round(predict_linear(node_filesystem_free_bytes{device="rootfs",mountpoint="/"}[2h],12*3600)/(1024*1024*1024), 1)
labels:
metric_type: root_partition_monitor
# cat node_alerts.yml
groups:
- name: node_alerts
rules:
- alert: cpu_usage_over_threshold
expr: instance:node_cpu_usage > 80
for: 1m
labels:
severity: warning
annotations:
summary: 主机 {{ $labels.nodename }} 的 CPU使用率持续1分钟超出阈值,当前为 {{humanize $value}} %
- alert: system_1m_load_over_threshold
expr: instance:node_1m_load > 20
for: 1m
labels:
severity: warning
annotations:
summary: 主机 {{ $labels.nodename }} 的 1分负载超出阈值,当前为 {{humanize $value}}
- alert: mem_usage_over_threshold
expr: instance:node_mem_usage > 80
for: 1m
annotations:
summary: 主机 {{ $labels.nodename }} 的 内存 使用率持续1分钟超出阈值,当前为 {{humanize $value}} %
- alert: root_partition_usage_over_threshold
expr: instance:node_root_partition_predit < 60
for: 1m
annotations:
summary: 主机 {{ $labels.nodename }} 的 根分区 预计在12小时使用将达到 {{humanize $value}}GB,超出当前可用空间,请及时扩容!
for 表示告警持续的时长,若持续时长小于该时间就不发给alertmanager了,大于该时间再发。for的值不要小于prometheus中的scrape_interval,例如scrape_interval为30s,for为15s,如果触发告警规则,则再经过for时长后也一定会告警,这是因为最新的度量指标还没有拉取,在15s时仍会用原来值进行计算。另外,要注意的是只有在第一次触发告警时才会等待(for)时长。
例如:10:43:00 触发了集群A中的h1告警;10:43:10又触发了集群A中的h2告警。则在10:44:10后发生一条告警是只包含h1的邮件,在10:44:20时会收到h1和h2聚合后的告警邮件,若h1和h2持续未恢复,则在repeat_interval后仍以聚合方式发送告警。
注:h1和h2告警名相同,只是在不同主机上。
完成上述配置后,主机CPU、负载、内存、磁盘超出阈值时就发触发邮件告警。
上述配置仅是对主机资源做了监控,并且告警只发到了缺省联系人组。设想一下,在实际生产环境中,可能会按产品线或业务功能进行分组来研发,不同的服务出现告警时只发送通知到对应的联系人组,其他不相关的组不需要接告警收通知,这就需要我们修改告警路由规则,而Alertmanager的核心和最复杂的地方就在告警路由规则的设置上。
Prometheus的告警规则在推送给Alertmanager前有的三种状态:
1、没有触发告警阈值时,状态为:inactive
2、触发了告警阈值但未满足告警持续时间(for)时,状态为:pending,如果不配置for或者指定for的值为0,则将跳过pending状态。
3、已触发告警阈值并达到告警持续时间,开始将告警事件推送到Alertmanager,此时状态为:firing。
配置告警静默(silence),用于在预期内的维护升级等操作。
当我们在对系统进行维护升级时,通常不希望触发告警通知;另外,当上游服务出现异常,想让下游的服务不触发告警,Prometheus将用于这种配置称为silence(静默)。silence用于设定一个(维护)时间段,如1小时,也可提前手动触发silence过期,表示维护结束,恢复对应服务的告警通知功能。
设置silence的方式有以下2种:
1、登录到alertmanager的控制台操作
2、通过amtool命令进行操作
配置告警模板
Alertmanager的通知模板是基于Go模板系统,详细可参考官网。
https://golang.org/pkg/text/template/
https://prometheus.io/docs/alerting/latest/notifications/#alert
https://prometheus.io/docs/prometheus/latest/configuration/template_reference/
模板配置步骤:
1、修改alertmanager.yml,配置模板地址,然后在每个receiver引用模板
templates:
- '/usr/local/alertmanager/template/default.tmpl'
...
...
html: '{{ template "default.html" . }}'
headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }
2、配置模板
default.tmpl
{{ define "default.html" }}
{{- if gt (len .Alerts.Firing) 0 -}}
[{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]
{{ range $i, $alert :=.Alerts }}
<pre>
告警节点:{{ index $alert.Labels "nodename" }}
告警服务:{{ index $alert.Labels "alertname" }}
报警详情:{{ index $alert.Annotations "summary" }}
开始时间:{{ $alert.StartsAt }}
</pre>
{{ end }}
{{ end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
[{{ .Status | toUpper }}:{{ .Alerts.Resolved | len }}]
{{ range $i, $alert :=.Alerts }}
<pre>
恢复节点:{{ index $alert.Labels "nodename" }}
恢复服务:{{ index $alert.Labels "alertname" }}
状 态:{{ index $alert.Status }}
开始时间:{{ $alert.StartsAt }}
恢复时间:{{ $alert.EndsAt }}
</pre>
{{ end }}
{{ end }}
{{- end }}
注:告警模板如果配置有问题,会导致邮件发送失败,注意观察日志。
以下是静默设置步骤:
1、登录到alertmanager控制台(http://IP:9093/#/alerts),点击上方菜单栏中Alerts,可看到当前有2个告警。
2、点击右上角"New Silence",创建Silence任务,具体设置如下,匹配规则支持正则。
注:可按instance或job_name等方式来进行正则匹配。
3、Silence创建成功后,可以看到处于Active状态的Silence
总之,多实践。
*请认真填写需求信息,我们会在24小时内与您取得联系。