Skip to content

Commit

Permalink
Add ability to run k8snetlook as a Job within k8s
Browse files Browse the repository at this point in the history
+ client-go initialization uses service account if available
+ add example k8s manifest
+ added more information to readme
  • Loading branch information
sarun87 committed Sep 16, 2020
1 parent 7d3c8ee commit a7be2d2
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 20 deletions.
55 changes: 44 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ docker run --privileged --pid=host --net=host -v /var/run/docker.sock:/var/run/d
```
Notes:
* The above command assumes that the $KUBECONFIG environment variable is pointing to a valid kubeconfig & mounts it within the container
* Mounts docker socket to be able to interact with docker daemon.
* Mounts docker socket needed interact with docker daemon.
* Needs privileged context to be set to access pod's network namespace.
* --net=host: Should run in host network namespace, --pid=host: Run in host pid (proc & sys paths are mounted which is needed to obtain handles to Pod's network namespace)

Expand All @@ -72,22 +72,29 @@ valid subcommands
host Debug host networking only
```

## How to build from source
To build tool from source, run `make` as follows:
## Run within K8s
There are advantages & disadvantages to running a K8s network debugging tool within k8s. Ease of deployment and not requiring ssh access to the host running the problem pod are clear advantages. But the underlying problem could prevent deployment of k8snetlook on the host (Eg: communication from the host to k8s api server is down).

The [examples folder](https://github.com/sarun87/k8snetlook/tree/master/examples) contains a yaml manifest that creates a K8s `Job` and runs to completion. All of the required RBAC objets and the Job itself is deployed in the `k8snetlook` namespace. Steps to run k8snetlook in k8s:

* Change the value of key `command` under `containers` section with the required arguments to k8snetlook
* Change the value of key `kubernetes.io/hostname` under `nodeSelector` section in the yaml spec to the host on which the problem pod is running. Then run the following commands:
* Apply to cluster using:
```
make all
kubectl apply -f examples/run-k8s.yaml
```
The binary named `k8snetlook` will be built under `root-dir/bin/`

To clean existing binaries and supporting files, run:
* Check results by pulling logs of the completed job
```
make clean
kubectl -n k8snetlook get pods
```

To speed up development, there is a darwin target defined as well. To build a darwin compatible binary, run:
```
make k8snetlook-osx
kubectl -n k8snetlook logs <pod-name>
```
* Delete k8snetlook after the run
```
kubectl delete -f examples/run-k8s.yaml
```
* To rerun the test, delete k8snetlook and re-apply.

## Checks currently supported by the tool

Expand All @@ -105,6 +112,32 @@ By having to initialize kubernetes client-set, the tool intrinsically performs A
| | Path MTU discovery between Src & Dst Pod (icmp) |
| | Path MTU discovery between Src Pod & External IP (icmp) |

## How to build from source
To build tool from source, run `make` as follows:
```
make all
```
The binary named `k8snetlook` will be built under `root-dir/bin/`

To clean existing binaries and supporting files, run:
```
make clean
```

To speed up development, there is a darwin target defined as well. To build a darwin compatible binary, run:
```
make k8snetlook-osx
```

To create a zipped release binary, run:
```
make release
```

To create a docker image, run:
```
make docker-image
```

## Contribute
PRs welcome :)
Expand Down
8 changes: 2 additions & 6 deletions cmd/k8snetlook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ func main() {
log.SetLogLevel(logLevel)

if err := k8snetlook.Init(k8snetlook.Cfg.KubeconfigPath); err != nil {
log.Error("Unable to initialize k8snetlook due to:%v\n", err)
log.Error("Unable to initialize k8snetlook\n")
log.Error("%v\n", err)
return
}
defer k8snetlook.Cleanup()
Expand All @@ -98,11 +99,6 @@ func main() {

func validateArgs() {
fmt.Println("")
if k8snetlook.Cfg.KubeconfigPath == "" {
fmt.Println("error: KUBECONFIG env variable not set. Please pass kubeconfig using -config")
printUsage()
os.Exit(1)
}
if podDebugging && (k8snetlook.Cfg.SrcPod.Name == "" || k8snetlook.Cfg.SrcPod.Namespace == "") {
fmt.Printf("error: srcpodname flag and srcpodns required for pod debugging\n\n")
podCmd.Usage()
Expand Down
74 changes: 74 additions & 0 deletions examples/run-k8s.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
apiVersion: v1
kind: Namespace
metadata:
name: k8snetlook

---

apiVersion: v1
kind: ServiceAccount
metadata:
name: k8snetlook
namespace: k8snetlook

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: k8snetlook
rules:
- apiGroups: [""]
resources: ["pods", "nodes", "endpoints", "services", "serviceaccounts", "secrets"]
verbs: ["get", "list"]

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8snetlook
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: k8snetlook
subjects:
- kind: ServiceAccount
name: k8snetlook
namespace: k8snetlook

---

apiVersion: batch/v1
kind: Job
metadata:
name: k8snetlook
namespace: k8snetlook
spec:
template:
spec:
hostNetwork: true
hostPID: true
serviceAccountName: k8snetlook
nodeSelector:
## Change hostname to represent the host that is running the src pod
kubernetes.io/hostname: ip-10-0-1-253.us-west-2.compute.internal
containers:
- name: k8snetlook
image: sarun87/k8snetlook:v0.2
## Edit command suite your debugging needs
command: ["/k8snetlook", "host"]
## Pod debugging example
#command: ["/k8snetlook", "pod", "-srcpodname=nginx-6db489d4b7-2hww8", "-srcpodns=default", "-dstpodname=nginx-6db489d4b7-9l264", "-dstpodns=default", "-externalip=8.8.8.8"]
volumeMounts:
- mountPath: /var/run/docker.sock
name: docker-socket
securityContext:
privileged: true
volumes:
- name: docker-socket
hostPath:
path: /var/run/docker.sock
type: Socket
restartPolicy: Never
backoffLimit: 0
17 changes: 14 additions & 3 deletions k8snetlook/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,27 @@ import (
log "github.com/sarun87/k8snetlook/logutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

var clientset *kubernetes.Clientset

func initKubernetesClient(kubeconfigPath string) error {
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
// check if running in-cluster. If so initialize client-set using incluster method
config, err := rest.InClusterConfig()
if err != nil {
return err
log.Debug("Not running in Pod or unable to fetch config via incluster method. Error:%v", err)
log.Debug("Trying from kubeconfig specified via command line flag")
// Fall back to using config provided as part of command line arguments
// use the current context in kubeconfig
if kubeconfigPath == "" {
return fmt.Errorf("Not running in Pod & kubeconfig not specified using -config flag")
}
config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
return err
}
}
config.Timeout = time.Second * 4
clientset, err = kubernetes.NewForConfig(config)
Expand Down

0 comments on commit a7be2d2

Please sign in to comment.