版权声明:本文为博主原创文章,博客地址:
https://blog.csdn.net/zxy_666/article/details/79958948,未经博主允许不得转载。
背景
公司容器云项目需在平台界面上提供一个ping工具,实现从任何pod内ping指定IP.
背景说明:
· 容器云项目
容器云项目是基于kubernetes(简称k8s)集群搭建的应用容器管理平台。集群中的节点是虚拟机或物理机,节点分为master节点和node节点(worker节点)。node节点上运行着pod(k8s集群的最小工作单元),pod中运行着容器,数量随意,但公司的项目里一个pod只运行一个容器。其中,容器是应用提供服务的载体,应用打成镜像后用于创建相应的容器。
每个节点都有各自的IP,每个pod也有他们的IP,通常在集群搭建时,给pod分配指定网段内的IP,由flannel实现集群间网络的通信,flannel保证集群中的每个pod都被分配不同的IP,避免了网络冲突。· ping需求分析
集群外可以ping通集群的节点IP,却ping不通集群内的ip,如pod的ip。但是应用方pod提供的服务会出现访问不了的情况,运维人员需登录到集群上查看响应pod的网络情况。为了解放运维人员的部分劳动力,故有此需求,希望在容器云平台界面直接提供一个ping工具,让使用者可以直接从每个pod实例发出ping命令,ping目标的IP由使用者提供,发出ping请求的源pod的ip就是该pod在集群中创建时被分配的ip,前端可获取得到。
实现历程:
依次尝试了3种方案,直到第三种才实现了需求。
- 用go实现ping原理
- (使用go提供的终端登录,在目标终端直接发出ping命令)/(在本机直接发出ping命令)
- 在每个pod内引入故障诊断容器
方案一 :实现ping的原理
方案:
用go实现ping的原理,参数是ping的目标IP和ping请求次数。限制是:该方案ping的源IP无法更改,默认就是发出ping操作的机器IP。
实现:
注:本程序(ICMP协议)需要root权限才可执行,启动需要sudo权限
package main
import (
"net"
"time"
"fmt"
"strconv"
"os"
)
type PingOption struct{
Count int
Size int
Timeout int64
Nerverstop bool
}
func NewPingOption()*PingOption{
return &PingOption{
Count:4,
Size:32,
Timeout:1000,
Nerverstop:false,
}
}
func main(){
//argsmap:=map[string]interface{}{}
//ping3("www.yeepay.com",argsmap)//10.151.30.227 不存在:67.4.3.2(现在又存在了) 公网IP:63.142.250.4(通)
argsmap:=map[string]interface{}{}
p:=NewPingOption()
p.ping3("www.baidu.com",argsmap)
}
//ping连接用的协议是ICMP,原理:
//Ping的基本原理是发送和接受ICMP请求回显报文。接收方将报文原封不动的返回发送方,发送方校验报文,校验成功则表示ping通。
//一台主机向一个节点发送一个类型字段值为8的ICMP报文,如果途中没有异常(如果没有被路由丢弃,目标不回应ICMP或者传输失败),
//则目标返回类型字段值为0的ICMP报文,说明这台主机可达
func (p *PingOption)ping3(host string, args map[string]interface{}) {
//要发送的回显请求数
var count int = 4
//要发送缓冲区大小,单位:字节
var size int = 32
//等待每次回复的超时时间(毫秒)
var timeout int64 = 1000
//Ping 指定的主机,直到停止
var neverstop bool = false
fmt.Println(args,"args")
if len(args)!=0{
count = args["n"].(int)
size = args["l"].(int)
timeout = args["w"].(int64)
neverstop = args["t"].(bool)
}
//查找规范的dns主机名字 eg.www.baidu.com->www.a.shifen.com
cname, _ := net.LookupCNAME(host)
starttime := time.Now()
//此处的链接conn只是为了获得ip := conn.RemoteAddr(),显示出来,因为后面每次连接都会重新获取conn,todo 但是每次重新获取的conn,其连接的ip保证一致么?
conn, err := net.DialTimeout("ip4:icmp", host, time.Duration(timeout*1000*1000))
//每个域名可能对应多个ip,但实际连接时,请求只会转发到某一个上,故需要获取实际连接的远程ip,才能知道实际ping的机器是哪台
// ip := conn.RemoteAddr()
// fmt.Println("正在 Ping " + cname + " [" + ip.String() + "] 具有 32 字节的数据:")
var seq int16 = 1
id0, id1 := genidentifier3(host)
//ICMP报头的长度至少8字节,如果报文包含数据部分则大于8字节。
//ping命令包含"请求"(Echo Request,报头类型是8)和"应答"(Echo Reply,类型是0)2个部分,由ICMP报头的类型决定
const ECHO_REQUEST_HEAD_LEN = 8
//记录发送次数
sendN := 0
//成功应答次数
recvN := 0
//记录失败请求数
lostN := 0
//所有请求中应答时间最短的一个
shortT := -1
//所有请求中应答时间最长的一个
longT := -1
//所有请求的应答时间和
sumT := 0
for count > 0 || neverstop {
sendN++
//ICMP报文长度,报头8字节,数据部分32字节
var msg []byte = make([]byte, size+ECHO_REQUEST_HEAD_LEN)
//第一个字节表示报文类型,8表示回显请求
msg[0] = 8 // echo
//ping的请求和应答,该code都为0
msg[1] = 0 // code 0
//校验码占2字节
msg