Skip to content

Commit

Permalink
feat: support pinger
Browse files Browse the repository at this point in the history
  • Loading branch information
oilbeater committed Sep 27, 2019
1 parent 200b90c commit e23bd55
Show file tree
Hide file tree
Showing 258 changed files with 19,470 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist/images/kube-ovn-controller
dist/images/kube-ovn-daemon
dist/images/kube-ovn-gateway
dist/images/kube-ovn-webhook
dist/images/kube-ovn-pinger
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ GOFILES_NOVENDOR=$(shell find . -type f -name '*.go' -not -path "./vendor/*")
GO_VERSION=1.13

REGISTRY=index.alauda.cn/alaudak8s
ROLES=node controller cni db webhook
ROLES=node controller cni db webhook pinger
DEV_TAG=dev
RELEASE_TAG=$(shell cat VERSION)

Expand All @@ -19,6 +19,7 @@ build-go:
CGO_ENABLED=0 GOOS=linux go build -o $(PWD)/dist/images/kube-ovn-controller -ldflags "-w -s" -v ./cmd/controller
CGO_ENABLED=0 GOOS=linux go build -o $(PWD)/dist/images/kube-ovn-daemon -ldflags "-w -s" -v ./cmd/daemon
CGO_ENABLED=0 GOOS=linux go build -o $(PWD)/dist/images/kube-ovn-webhook -ldflags "-w -s" -v ./cmd/webhook
CGO_ENABLED=0 GOOS=linux go build -o $(PWD)/dist/images/kube-ovn-pinger -ldflags "-w -s" -v ./cmd/pinger

release: build-go
@for role in ${ROLES} ; do \
Expand Down
23 changes: 23 additions & 0 deletions cmd/pinger/pinger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"fmt"
"github.com/alauda/kube-ovn/pkg/pinger"
"github.com/prometheus/client_golang/prometheus/promhttp"
"k8s.io/klog"
"net/http"
)

func main() {
config, err := pinger.ParseFlags()
if err != nil {
klog.Fatalf("parse config failed %v", err)
}
if config.Mode == "server" {
http.Handle("/metrics", promhttp.Handler())
go func() {
klog.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", config.Port), nil))
}()
}
pinger.StartPinger(config)
}
33 changes: 33 additions & 0 deletions dist/images/Dockerfile.pinger
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM centos:7

ENV PYTHONDONTWRITEBYTECODE yes

RUN yum install -y \
PyYAML bind-utils \
openssl \
numactl-libs \
firewalld-filesystem \
libpcap \
hostname \
iproute strace socat nc \
unbound unbound-devel && \
yum clean all

ENV OVS_VERSION=2.11.4
ENV OVS_SUBVERSION=1

RUN rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/openvswitch-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/openvswitch-devel-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/ovn-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/ovn-common-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/ovn-vtep-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/ovn-central-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm && \
rpm -ivh https://github.com/alauda/ovs/releases/download/v${OVS_VERSION}-${OVS_SUBVERSION}/ovn-host-${OVS_VERSION}-${OVS_SUBVERSION}.el7.x86_64.rpm

RUN mkdir -p /var/run/openvswitch
WORKDIR /kube-ovn

CMD ["/kube-ovn/kube-ovn-pinger"]

COPY kube-ovn-pinger /kube-ovn/kube-ovn-pinger
RUN chmod +x /kube-ovn/kube-ovn-pinger
37 changes: 35 additions & 2 deletions dist/images/kubectl-ko
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ showHelp(){
echo " sbctl [ovn-sbctl options ...] invoke ovn-sbctl"
echo " tcpdump {namespace/podname} [tcpdump options ...] capture pod traffic"
echo " trace {namespace/podname} {target ip address} {icmp|tcp|udp} [target tcp or udp port] trace ovn microflow of specific packet"
echo " diagnose {all|node} [nodename] diagnose connectivity of all nodes or a specific node"
}

tcpdump(){
Expand All @@ -36,7 +37,7 @@ tcpdump(){
exit 1
fi

ovnCni=$(kubectl get pod -n $KUBE_OVN_NS -o wide| grep kube-ovn-cni| grep "$nodeName" | awk '{print $1}')
ovnCni=$(kubectl get pod -n $KUBE_OVN_NS -o wide| grep kube-ovn-cni| grep " $nodeName " | awk '{print $1}')
if [ -z "$ovnCni" ]; then
echo "kube-ovn-cni not exist on node $nodeName"
exit 1
Expand Down Expand Up @@ -111,6 +112,35 @@ trace(){
esac
}

diagnose(){
type="$1"
case $type in
all)
pingers=$(kubectl get pod -n $KUBE_OVN_NS | grep kube-ovn-pinger | awk '{print $1}')
for pinger in $pingers
do
nodeName=$(kubectl get pod "$pinger" -n "$KUBE_OVN_NS" -o jsonpath={.spec.nodeName})
echo "### start to diagnose node $nodeName"
kubectl exec -n $KUBE_OVN_NS -it "$pinger" -- /kube-ovn/kube-ovn-pinger --mode=job
echo "### finish diagnose node $nodeName"
echo ""
done
;;
node)
node="$2"
pinger=$(kubectl get pod -n $KUBE_OVN_NS -o wide | grep kube-ovn-pinger | grep " $node " | awk '{print $1}')
echo "### start to diagnose node $node"
kubectl exec -n $KUBE_OVN_NS -it "$pinger" -- /kube-ovn/kube-ovn-pinger --mode=job
echo "### finish diagnose node $node"
echo ""
;;
*)
echo "type $type not supported"
echo "kubectl ko diagnose {all|node} [nodename]"
;;
esac
}

getOvnCentralPod(){
centralPod=$(kubectl get pod -n $KUBE_OVN_NS | grep ovn-central | head -n 1 | awk '{print $1}')
if [ -z "$centralPod" ]; then
Expand Down Expand Up @@ -142,7 +172,10 @@ case $subcommand in
trace)
trace "$@"
;;
diagnose)
diagnose "$@"
;;
*)
showHelp
;;
esac
esac
3 changes: 2 additions & 1 deletion docs/kubectl-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ Available Subcommands:
sbctl [ovn-sbctl options ...] invoke ovn-sbctl
tcpdump {namespace/podname} [tcpdump options ...] capture pod traffic
trace {namespace/podname} {target ip address} {icmp|tcp|udp} [target tcp or udp port]
```
diagnose {all|node} [nodename] diagnose connectivity of all nodes or a specific node
```
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ require (
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef // indirect
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee // indirect
github.com/projectcalico/libcalico-go v0.0.0-20190305235709-3d935c3b8b86 // indirect
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/prometheus/client_golang v0.9.2
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
github.com/prometheus/common v0.2.0 // indirect
github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb // indirect
github.com/sirupsen/logrus v1.4.2
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
github.com/spf13/pflag v1.0.3
github.com/vishvananda/netlink v1.0.0
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c h1:gqEdF4VwBu3lTKGHS9rXE9x1/pEaSwCXRLOZRF6qtlw=
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
Expand Down
90 changes: 90 additions & 0 deletions pkg/pinger/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package pinger

import (
"flag"
"github.com/spf13/pflag"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog"
)

type Configuration struct {
KubeConfigFile string
KubeClient kubernetes.Interface
Port int
DaemonSetNamespace string
DaemonSetName string
Interval int
Mode string
DNS string
}

func ParseFlags() (*Configuration, error) {
var (
argPort = pflag.Int("port", 8080, "metrics port")
argKubeConfigFile = pflag.String("kubeconfig", "", "Path to kubeconfig file with authorization and master location information. If not set use the inCluster token.")
argDaemonSetNameSpace = pflag.String("ds-namespace", "kube-ovn", "kube-ovn-pinger daemonset namespace")
argDaemonSetName = pflag.String("ds-name", "kube-ovn-pinger", "kube-ovn-pinger daemonset name")
argInterval = pflag.Int("interval", 5, "interval seconds between consecutive pings")
argMode = pflag.String("mode", "server", "server or job Mode")
argDns = pflag.String("dns", "kubernetes.default.svc.cluster.local", "check dns from pod")
)

klogFlags := flag.NewFlagSet("klog", flag.ExitOnError)
klog.InitFlags(klogFlags)

// Sync the glog and klog flags.
flag.CommandLine.VisitAll(func(f1 *flag.Flag) {
f2 := klogFlags.Lookup(f1.Name)
if f2 != nil {
value := f1.Value.String()
f2.Value.Set(value)
}
})

pflag.CommandLine.AddGoFlagSet(klogFlags)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()

config := &Configuration{
KubeConfigFile: *argKubeConfigFile,
KubeClient: nil,
Port: *argPort,
DaemonSetNamespace: *argDaemonSetNameSpace,
DaemonSetName: *argDaemonSetName,
Interval: *argInterval,
Mode: *argMode,
DNS: *argDns,
}
if err := config.initKubeClient(); err != nil {
return nil, err
}
return config, nil
}

func (config *Configuration) initKubeClient() error {
var cfg *rest.Config
var err error
if config.KubeConfigFile == "" {
cfg, err = rest.InClusterConfig()
if err != nil {
klog.Errorf("use in cluster config failed %v", err)
return err
}
} else {
cfg, err = clientcmd.BuildConfigFromFlags("", config.KubeConfigFile)
if err != nil {
klog.Errorf("use --kubeconfig %s failed %v", config.KubeConfigFile, err)
return err
}
}

kubeClient, err := kubernetes.NewForConfig(cfg)
if err != nil {
klog.Errorf("init kubernetes client failed %v", err)
return err
}
config.KubeClient = kubeClient
return nil
}
128 changes: 128 additions & 0 deletions pkg/pinger/ping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package pinger

import (
goping "github.com/sparrc/go-ping"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"net"
"os/exec"
"sync"
"time"
)

func StartPinger(config *Configuration) {
for {
checkOvs()
checkOvnController()
ping(config)
if config.Mode != "server" {
break
}
time.Sleep(time.Duration(config.Interval) * time.Second)
}
}

func ping(config *Configuration) {
pingNodes(config.KubeClient)
pingPods(config.KubeClient, config.DaemonSetNamespace, config.DaemonSetName)
nslookup(config.DNS)
}

func pingNodes(client kubernetes.Interface) {
klog.Infof("start to check node connectivity")
nodes, err := client.CoreV1().Nodes().List(metav1.ListOptions{})
if err != nil {
klog.Errorf("failed to list nodes, %v", err)
return
}
wg := sync.WaitGroup{}
for _, no := range nodes.Items {
for _, addr := range no.Status.Addresses {
if addr.Type == v1.NodeInternalIP {
wg.Add(1)
go func(nodeIP, nodeName string) {
defer wg.Done()
pinger, err := goping.NewPinger(nodeIP)
if err != nil {
klog.Errorf("failed to init pinger, %v", err)
return
}
pinger.SetPrivileged(true)
pinger.Count = 5
pinger.Run()
stats := pinger.Statistics()
klog.Infof("ping node: %s %s, count: %d, loss rate %.2f%%, average rtt %.2fms",
nodeName, nodeIP, pinger.Count, stats.PacketLoss*100, float64(stats.AvgRtt)/float64(time.Millisecond))
}(addr.Address, no.Name)
}
}
}
wg.Wait()
}

func pingPods(client kubernetes.Interface, dsNamespace, dsName string) {
klog.Infof("start to check pod connectivity")
ds, err := client.AppsV1().DaemonSets(dsNamespace).Get(dsName, metav1.GetOptions{})
if err != nil {
klog.Errorf("failed to get peer ds: %v", err)
return
}
pods, err := client.CoreV1().Pods(dsNamespace).List(metav1.ListOptions{LabelSelector: labels.Set(ds.Spec.Selector.MatchLabels).String()})
if err != nil {
klog.Errorf("failed to list peer pods: %v", err)
return
}

wg := sync.WaitGroup{}
for _, pod := range pods.Items {
if pod.Status.PodIP != "" {
wg.Add(1)
go func(podIp, podName, podNamespace string) {
defer wg.Done()
pinger, err := goping.NewPinger(podIp)
if err != nil {
klog.Errorf("failed to init pinger, %v", err)
return
}
pinger.SetPrivileged(true)
pinger.Count = 5
pinger.Run()
stats := pinger.Statistics()
klog.Infof("ping pod: %s/%s %s, count: %d, loss rate %.2f, average rtt %.2fms",
podNamespace, podName, podIp, pinger.Count, stats.PacketLoss*100, float64(stats.AvgRtt)/float64(time.Millisecond))
}(pod.Status.PodIP, pod.Name, pod.Namespace)
}
}
wg.Wait()
}

func nslookup(dns string) {
klog.Infof("start to check dns connectivity")
t1 := time.Now()
addrs, err := net.LookupHost(dns)
elpased := time.Since(t1)
if err != nil {
klog.Errorf("failed to resolve dns %s, %v", dns, err)
return
}
klog.Infof("resolve dns %s to %v in %.2fms", dns, addrs, float64(elpased)/float64(time.Millisecond))
}

func checkOvs() {
output, err := exec.Command("/usr/share/openvswitch/scripts/ovs-ctl", "status").CombinedOutput()
if err != nil {
klog.Errorf("check ovs status failed %v, %s", err, string(output))
}
klog.Infof("ovs-vswitchd and ovsdb are up")
}

func checkOvnController() {
output, err := exec.Command("/usr/share/openvswitch/scripts/ovn-ctl", "status_controller").CombinedOutput()
if err != nil {
klog.Errorf("check ovn_controller status failed %v, %s", err, string(output))
}
klog.Infof("ovn_controller is up")
}
1 change: 1 addition & 0 deletions vendor/github.com/sparrc/go-ping/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e23bd55

Please sign in to comment.