Zabbix下发往钉钉告警

zabbix 是很熟悉的东西,但是实际上博主已经跳过了这个东西,直接蹦到 Prometheus 去了 但是,存在即合理,当下公司用的是这个,那么用就用吧,zabbix发到钉钉告警。 那么我们也研究一下如何发到钉钉告警,而且好看一些 原理: 原理就是用 post 向钉钉机器人的 webhook 地址提交 Markdown 的 json 信息 首先我们要建立个钉钉群,然后在群中添加一个群机器人,这里就会有两个选择,一个是这个机器人只接受特定的词语,二是向机器人发送消息的机器的ip是固定的。 阿里云建议的是关键词:云监控、云服务、监控、Monitor、ECS、报警 当然,这里更加建议IP,IP是死的,报警里带关键词意味着发送内容被部分固定了。 如上,我们会得到一个钉钉机器人的Webhook地址: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxx 然后我们要准备一个发送的脚本,python很合适 #!/usr/bin/env python #coding:utf-8 #zabbix钉钉报警 import requests,json,sys,os,datetime #说明:这里改为自己创建的机器人的webhook webhook="https://oapi.dingtalk.com/robot/send?access_token=xxxxxx" def log(info): if os.path.isfile("/tmp/dingding.log") == False: f = open(log_file, 'a+') else: f = open(log_file,'w+') f.write(info) f.close() def msg(text,user): json_text= { "msgtype": "markdown", "markdown": { "title": "zabbix monitor", "text": text }, "at": { "atMobiles": [ user ], "isAtAll": False } } headers = {'Content-Type': 'application/json'} r=requests.post(url=webhook,data=json.dumps(json_text),headers=headers).json() code = r["errcode"] time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) if code == 0: log(time + ":消息发送成功 返回码:" + str(code) + "\n") else: log(time + ":消息发送失败 返回码:" + str(code) + "\n") exit(3) if __name__ == '__main__': user = sys.argv[1] #用户 subject = sys.argv[2] #标题 text = sys.argv[3] #消息 msg(text,user) 我们把上面的文件内容放到 /usr/lib/zabbix/alertscripts 目录下,名字就叫做 dingding.py ,只能是这个目录,因为这是 zabbix 缺省外挂脚本文件的目录。 ...

2022年11月30日 · 2 分钟 · 234 字 · 八戒

kubernetes使用nfs做持久化卷存储

用NFS来做kubernetes的持久化卷虽然不是最佳实践,但存在即合理。在现在的公司就是这么用的,记录一下,可用但不是最佳。 一、搭建NFS服务端和所有工作节点安装客户端 #NFS服务端 (centos7) yum install nfs-utils rpcbind -y mkdir -p /data/nfs/{download,bakup,www} echo "/data/nfs 10.10.0.0/16(rw,all_squash,insecure,sync)" >> /etc/exports exportfs -r systemctl enable rpcbind nfs-server systemctl restart rpcbind nfs-server showmount -e localhost #客户端根本不装也可以,客户端不需要启动任何nfs服务 #只是为了使用showmount工具来验证 #NFS客户端(CentOS,所有WorkNode节点都需要执行) yum install -y nfs-utils #NFS客户端(Ubuntu,所有WorkNode节点都需要执行) apt install -y nfs-common NFS 服务端的参数如下: rw 能读能写 insecure NFS通过1024以上的端口发送 root_squash root会被变成noboby,其他人不变 no_root_squash root身份保持不变,其他人不变。(不能用这个,容易被黑) all_squash 不论登入NFS的使用者身份为何,他的身份都会被压缩成为匿名使用者,通常也就是nobody sync 数据直接落盘,性能略损。 async 数据先落内存,然后落盘,性能略升。 综上,保险的参数就是 /data/nfs 10.10.0.0/16(rw,insecure,all_squash,sync) 注意:修改了/etc/exports后,并不需要重启nfs服务,只要用exportfs重新扫描一次/etc/exports,并且重新加载即可 exports -rv 二、POD内直接使用 由于nfs是内核自带的东西,所以最简单的使用方法就是直接在pod内使用 apiVersion: v1 kind: Pod metadata: name: test labels: app.kubernetes.io/name: alpine spec: containers: - name: alpine image: alpine:latest command: - touch - /data/test volumeMounts: - name: nfs-volume mountPath: /data volumes: - name: nfs-volume nfs: server: 10.10.247.38 path: /data readOnly: no 这个pod是一次性运行的,运行完后就可以看到nfs中多了个test文件 ...

2022年11月24日 · 1 分钟 · 209 字 · 八戒

钉钉告警发送

#!/usr/bin/env python #coding:utf-8 #zabbix钉钉报警 import requests import json import sys import os import datetime #这里是自己创建的机器人的webhook webhook="https://oapi.dingtalk.com/robot/send?access_token=xxxxxx" user=sys.argv[1] text=sys.argv[3] data={ "msgtype": "text", "text": { "content": text }, "at": { "atMobiles": [ user ], "isAtAll": False } } headers = {'Content-Type': 'application/json'} x=requests.post(url=webhook,data=json.dumps(data),headers=headers) 下面放shell脚本的 #!/bin/bash function ddmsg() { Token="xxxxxxxxxxxx" Weburl="https://oapi.dingtalk.com/robot/send?access_token=" curl -ks -m 2 "${Weburl}${Token}" \ -H 'Content-Type: application/json;charset=utf-8' \ -d "{'msgtype': 'text', 'text': { 'content': '$*'} }" &>/tmp/ddmsg.log if [ `grep "errmsg.*ok" /tmp/ddmsg.log |wc -l` -ne 1 ]; then echo 'send error !';cat /tmp/ddmsg.log;exit 1; fi } #测试内容 echo "@警报 主机:$(hostname) 信息:Node test 时间:$(date +"%F %T") ">.msg cat .msg ddmsg "`cat .msg`"

2022年11月23日 · 1 分钟 · 101 字 · 八戒

Freelancer之aws

客户发了一个需求如下: 用shell 或者python3 控制aws去获取新IP、绑定IP到实例 这个就非常简单了,因为本身aws就提供python的工具,一切都可以api化的 做法如下: #安装aws的工具 cd /tmp curl -kL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install aws --version #配置aws key aws configure #查看配置 aws configure list ---------------------------- #申请一个新IP并且打tag aws ec2 allocate-address --region $Region |tee /tmp/eip.log eip_id=$(jq -r ".AllocationId" /tmp/eip.log) aws ec2 create-tags --resources ${eip_id} --tags Key=Name,Value=eip-01 eip=$(jq -r ".PublicIp" /tmp/eip.log) #给ec2赋ip,前提是知道ec2_id aws ec2 associate-address --instance-id ${ec2_id} --allocation-id ${eip_id} ---------------------------- #启动新ec2的方法: #启动ec2 aws ec2 run-instances \ --region $Region --count 1 \ --instance-type t2.micro \ --subnet-id ${lan_a_public_id} \ --security-group-ids $gid \ --key-name MySshKey \ --image-id ami-04ff9e9b51c1f62ca \ |tee /tmp/ec2.log ec2_id=$(jq -r ".Instances[0].InstanceId" /tmp/ec2.log) 参考: https://www.cnblogs.com/elvi/p/16542406.html

2022年11月23日 · 1 分钟 · 95 字 · 八戒

Freelancer之QRCode

客户发了一个需求如下: TODO: 在sortFiles中,获取最新的域名,生成二维码,覆盖QRCodePath图片 TODO:将QRCodePath图片上传到阿里云0S根目录,重复则覆盖 又具体询问了一下细节,是一个 csharp 的程序 var sortFiles = Directory.GetFiles(zipPath, "*.zip").Select(fn => new FileInfo(fn)).OrderBy(f => f.LastAccessTime); // TODO: 在sortFiles中,获取最新的域名(文件名去掉.zip就是域名),结合logoUrl生成二维码,覆盖QRCodePath图片 // TODO: 将QRCodePath图片上传到阿里云OOS根目录,重复则覆盖, 看了一下,觉得思路如下,这是个管道流,无论徒手写代码生成QRCODE(还有个logo图需要放到二维码中央),还是集成OSS的SDK都麻烦 最快速方法是直接调用外部成程序做这两步,第一步用Go写个程序生成二维码,第二步用阿里的ossutil64工具上传 一、Go程序Gen出QRCode go 有很多库,我们直接拿来用好了,但是基本上都是老代码,所以需要下载 go1.13.15 来用 # 下载go 1.13.15,解压到E:\go,并建立一个E:\go\go-work目录,下载的模块会放到这里 https://dl.google.com/go/go1.13.15.windows-amd64.zip # 设置三个变量 GOROOT=E:\go GOPATH=E:\go\go-work PATH中增加好E:\go\bin的目录 # 设置一下代理,有时候模块拉不下来 go env -w GOPROXY=https://goproxy.cn,direct 然后就准备gen qrcode的程序,main.go package main import ( "flag" "image" "image/draw" "image/jpeg" _ "image/png" "os" "path/filepath" "github.com/LyricTian/logger" "github.com/nfnt/resize" "github.com/skip2/go-qrcode" ) var ( text string logo string percent int size int out string ) func init() { flag.StringVar(&text, "t", "", "二维码内容") flag.StringVar(&logo, "l", "", "二维码Logo(png)") flag.IntVar(&percent, "p", 15, "二维码Logo的显示比例(默认15%)") flag.IntVar(&size, "s", 256, "二维码的大小(默认256)") flag.StringVar(&out, "o", "", "输出文件") } func main() { flag.Parse() if text == "" { logger.Fatalf("请指定二维码的生成内容") } if out == "" { logger.Fatalf("请指定输出文件") } if exists, err := checkFile(out); err != nil { logger.Fatalf("检查输出文件发生错误:%s", err.Error()) } else if exists { logger.Fatalf("输出文件已经存在,请重新指定") } code, err := qrcode.New(text, qrcode.Highest) if err != nil { logger.Fatalf("创建二维码发生错误:%s", err.Error()) } srcImage := code.Image(size) if logo != "" { logoSize := float64(size) * float64(percent) / 100 srcImage, err = addLogo(srcImage, logo, int(logoSize)) if err != nil { logger.Fatalf("增加Logo发生错误:%s", err.Error()) } } outAbs, err := filepath.Abs(out) if err != nil { logger.Fatalf("获取输出文件绝对路径发生错误:%s", err.Error()) } os.MkdirAll(filepath.Dir(outAbs), 0777) outFile, err := os.Create(outAbs) if err != nil { logger.Fatalf("创建输出文件发生错误:%s", err.Error()) } defer outFile.Close() jpeg.Encode(outFile, srcImage, &jpeg.Options{Quality: 100}) logger.Infof("二维码生成成功,文件路径:%s", outAbs) } func checkFile(name string) (bool, error) { _, err := os.Stat(name) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } return true, nil } func resizeLogo(logo string, size uint) (image.Image, error) { file, err := os.Open(logo) if err != nil { return nil, err } defer file.Close() img, _, err := image.Decode(file) if err != nil { return nil, err } img = resize.Resize(size, size, img, resize.Lanczos3) return img, nil } func addLogo(srcImage image.Image, logo string, size int) (image.Image, error) { logoImage, err := resizeLogo(logo, uint(size)) if err != nil { return nil, err } offset := image.Pt((srcImage.Bounds().Dx()-logoImage.Bounds().Dx())/2, (srcImage.Bounds().Dy()-logoImage.Bounds().Dy())/2) b := srcImage.Bounds() m := image.NewNRGBA(b) draw.Draw(m, b, srcImage, image.ZP, draw.Src) draw.Draw(m, logoImage.Bounds().Add(offset), logoImage, image.ZP, draw.Over) return m, nil } 上面很简单,用到了别人的三个模块,需要安装一下 ...

2022年11月23日 · 3 分钟 · 567 字 · 八戒

MySQL数据库的备份和恢复之二

我们来说恢复的第二种情况,就是需要从binlog中指定位置恢复 binlog如何设置不说了,我们假设上次用mysqldump做过一次全量备份。 mysqldump -uroot -p -h 192.168.1.35 -P3306 --opt --triggers -R --hex-blob --single-transaction --flush-logs --master-data=2 -B 库名 > 库名.sql 由于我们在备份中使用了参数–flush-logs –master-data=2,所以 库名.sql 中会有binglog的信息供我们使用。 一、用mysqlbinlog来恢复 我们首先要查到随后的binlog文件是那个,从那时候起又生成多少个binglog文件。 然后去库里查询: show master logs; show master status; 我们要分析一下SQL # show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]; show binlog events in 'javaboy_logbin.000002' limit 5,10; 我们来翻看日志: 可以看到是从 764–>865 ,发生了删除,那么回放到这个764这个position前即可,764不会执行 mysqlbinlog /var/lib/mysql/mysql-bin.000204 --stop-position=764 --database=bbb | mysql -uroot -p –start-position指定从哪里开始恢复,如果不指定,就会从binlog文件开头的position开始 mysqlbinlog /var/lib/mysql/mysql-bin.000204 --start-position=205 --stop-position=764 --database=bbb | mysql -uroot -p 二、用binglog2sql来恢复 binlog2sql 是大众点评公司的DBA 开发的一款基于通过解析binlog将delete 恢复为insert,update 的值 set 字段和where条件做对调的原理来恢复数据的。 使用限制 MySQL的binlog format 必须是row 安装 ...

2022年11月22日 · 1 分钟 · 113 字 · 八戒

MySQL数据库的备份和恢复之一

喜大普奔,干了一个月的MySQL DBA的工作,又学到了一部分知识。 记录一下,以备不时只需 mysqldump 的备份其实很麻烦,要考虑很多因素 备份时候不能锁表 恢复的时候要快 有二进制数据的话需要备份二进制数据 有触发器、存储过程的都备份 通常 mysqldump 是做每天的fullbackup,要为之后的 binlog 做好准备,万一要恢复要提前做好准备 我们一点一点来说: 一、备份时不能锁表 --single-transaction 通过将导出操作封装在一个事务内来使得导出的数据是一个一致性快照 该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN不会阻塞任何应用程序且能保证导出时数据库的一致性状态。 它只适用于InnoDB存储引擎。 本选项和--lock-tables选项是互斥的,因为LOCK TABLES会使任何挂起的事务隐含提交。 二、恢复时要尽量快 --opt 等同于--add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, --disable-keys 该选项默认开启, 可以用--skip-opt禁用. --extended-insert, -e --extended-insert=false 使用具有多个VALUES列的INSERT语法。这样使导出文件更小,并加速导入时的速度。 默认为打开状态,使用--skip-extended-insert取消选项。 三、有二进制的按二进制备份 --hex-blob 使用十六进制格式导出二进制字符串字段。如果有二进制数据就必须使用该选项。影响到的字段类型有BINARY、VARBINARY、BLOB。 四、有触发器、存储过程的都备份 --triggers 导出触发器。该选项默认启用,用--skip-triggers禁用它。 --routines, -R 导出存储过程以及自定义函数 五、为之后如果要做binlog恢复提前做好准备 --flush-logs 开始导出之前刷新binlog日志。 请注意:假如一次导出多个数据库(使用选项--databases或者--all-databases),将会逐个数据库刷新日志。 除非使用--lock-all-tables或者--master-data外。在这种情况下,日志将会被刷新一次,相应的所以表同时被锁定。 因此,如果打算同时导出和刷新日志应该使用--lock-all-tables 或者--master-data 和--flush-logs --master-data 在导出的时候同时生成binlog文件名和位置在导出的文件开头。 这个非常重要。binlog 的文件和位置可以从这里拿到。 例如: -- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000001', MASTER_LOG_POS=157; 该选项将binlog的位置和文件名追加到输出文件中。 如果为1,将会输出CHANGE MASTER 命令; 如果为2,输出的CHANGE MASTER命令前添加注释信息。 该选项将打开--lock-all-tables 选项,除非--single-transaction也被指定(在这种情况下,全局读锁在开始导出时获得很短的时间;其他内容参考下面的--single-transaction选项)。 该选项自动关闭--lock-tables选项。 mysqldump -uroot -p --host=localhost --all-databases --master-data=1; mysqldump -uroot -p --host=localhost --all-databases --master-data=2; 那么综上所述,总结一行非常使用的备份语句: ...

2022年11月21日 · 1 分钟 · 107 字 · 八戒

OpenVPN客户端定制IP和路由

公司用的 OpenVPN ,研发部门和普通员工的权限不一样,技术部门需要访问服务器,普通员工则不需要 这样的话就需要根据用户定制路由了,技术部门推送特定的路由,普通员工不推送。 那么做法如下,在 server.conf 做如下设定: ifconfig-pool-persist /etc/openvpn/conf/ipp.txt client-config-dir /etc/openvpn/conf/static 第一行是指定记录 ip 地址的文件,作用是如果服务器进程重启了,重启后会读取这个文件,客户端重新拨号后获得的 IP 就不会变,还是重启前的 IP。 第二行是指定了客户端的单独配置 我们假设一个用户是 zhangranrui,那么就去 /etc/openvpn/conf/static 下建立一个 zhangranrui 的文本文件,内容如下: # 前面是客户端固定IP地址,后面是网关地址 ifconfig-push 172.18.0.10 172.18.0.9 # 推送路由,后面是掩码 push "route 10.10.0.0 255.255.0.0" # 重定向客户端的所有流量,否则访问服务端内网要像上面一样推送路由 # push "redirect-gateway def1" # 推送给客户端的DNS解析地址 # push "dhcp-option DNS 223.5.5.5" # push "dhcp-option DNS 114.114.114.114" 上面我们固定了IP,也推送了特定路由,那么怎么知道一个客户的 ip 呢,去看 ipp 文件: cat /etc/openvpn/conf/ipp.txt zhangranrui,172.18.0.8 注意,上面文件里的 172.18.0.8 不是 ip ,是 网段 地址 172.18.0.9 是网关地址,172.18.0.10才是真正的 IP 地址,所以不要搞错了 ...

2022年11月19日 · 1 分钟 · 73 字 · 八戒

英语的符号表达

面试 infosys 的时候是一个印度老哥做考官,他问我的问题我是一句都听不懂的,确实是听不懂。 然后他把问题在 webex 中打出来我回答的。 其中有一个问题是:如何用CLI查出系统中内存占用最高的5个进程 ps aux --sort=-%mem | head -n 5 上面一句如何用英语表达出来呢,不好说啊 所以记录一下,以备将来不时之需 . period 句号 , comma 逗号 : colon 冒号 ; semicolon 分号 ! exclamation 惊叹号 ? question 问号 - hyphen 连字符 * asterisk 星号 ' apostrophe 所有格符号,单词内部的省略 — dash 破折号 _ underscore ‘ ’ single quotation marks 单引号 “ ” double quotation marks 双引号 ( ) round brackets 圆括号 [ ] square brackets 方括号 <> Angle brackets 尖括号 {} curly brackets or braces 大括号 《 》French quotes 法文引号;书名号 ... ellipsis 省略号 ¨ tandem colon 双点号 " ditto 同上 ‖ parallel 双线号 / slash 斜线号 & ampersand = and ~ tilde 波浪号 § section; division 分节号 → arrow 箭号;参见号 | vertical bar 竖线 backslash 反斜线 % percent 百分号

2022年11月17日 · 1 分钟 · 123 字 · 八戒

阿里云IPSec与锐捷路由器得连接

现在也开始搞起桌面运维了,其实还感觉蛮有意思的。 公司的主路由器是锐捷的NBR6205-E,作为IPSec的服务端,而阿里云作为客户端。 首先先普及一下IPSec的知识,IPSec目前只支持IPv4单播: 密钥安全有 IKE 负责;数据安全方面,有 IPSec 负责。 但是IKE也是 IPSec 的 一 部分,不能独立存在。存在两个SA分别为 ISAKMP Security Association(IKE SA) IPsec Security Association(IPsec SA) 在这里注意一点就是IKE SA=ISAKMP SA IKE SA的 lifetime 默认为 86400 秒,即一天,默认没有volume limit。 用户的数据流量真正是在 IPsec SA 上传递的,而不是在IKE SA; IPsec SA直接为用户数据流服务,IPsec SA中的所有安全策略都是为了用户数据流的安全。 IPsec SA的 lifetime 默认为3600 秒,即1小时;默认volume limit为4608000 Kbytes,即4.608 Gbyte。 因为SA有两个,分为IKE SA和IPsec SA,两个SA分别定义了如何保护密钥以及如何保护数据,其实这两个SA都是由IKE建立起来的,所以将IKE的整个运行过程分成了两个Phase(阶段),即 : IKE Phase Two IKE Phase One 一、IKE Phase One第一阶段 IKE Phase One的主要工作就是建立IKE SA(ISAKMP SA),IKE SA的服务对象并不是用户数据,而是密钥流量,以及为IPsec SA服务的;IKE SA的协商阶段被称为main mode(主模式)还有 aggressive 野蛮模式,IKE也是需要保护自己的流量安全的(这些流量并非用户流量),所以IKE SA之间也需要协商出一整套安全策略,否则后续的密钥和IPsec SA的建立就不能得到安全保证; ...

2022年11月15日 · 1 分钟 · 189 字 · 八戒