樣式功能來看,整體不是很復雜,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小時內與您取得聯系。