Skip to content

Commit 4fd7af4

Browse files
committed
add function to make k8stcpdump invoke containetcpdump to capture the traces
1 parent ada445c commit 4fd7af4

File tree

1 file changed

+118
-59
lines changed

1 file changed

+118
-59
lines changed

cmd/k8stcpdump/k8stcpdump.go

+118-59
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ import (
3939
var cfgFile string
4040
var parFile string
4141

42+
type container struct {
43+
Name string `json:"Name"`
44+
}
45+
46+
type containers struct {
47+
Containers []container `json:"Containers"`
48+
Duration string `json:"Duration"`
49+
}
50+
4251
type target struct {
4352
Name string `json:"Name"`
4453
Namespace string `json:"Namespace"`
@@ -59,10 +68,15 @@ type targetPods struct {
5968
Pods []target `json:"Pods"`
6069
}
6170

62-
func dropErr(e error) {
63-
if e != nil {
64-
panic(e)
65-
}
71+
/*
72+
func getNode(client *kubernetes.Clientset) *nodeSet {
73+
nodes, err := client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
74+
return nodes
75+
}*/
76+
77+
func createNodeSet(nodeSet map[string][]string, targetPod *target) error {
78+
nodeSet[targetPod.Node] = append(nodeSet[targetPod.Node], "k8s_POD_"+targetPod.Name+"_"+targetPod.Namespace+"_"+targetPod.Uid)
79+
return nil
6680
}
6781

6882
func getPodStatus(client *kubernetes.Clientset, data *targets) *[]target {
@@ -126,64 +140,89 @@ func parse(p string) (*rest.Config, *kubernetes.Clientset, *targets) {
126140
return restConfig, client, &data
127141
}
128142

129-
func getPodDef(pod *target, duration string) *apicore.Pod {
143+
func createManifests(node string, targetContainers []string, duration string) (*apicore.Pod, *apicore.ConfigMap) {
130144
n := 2
131145
temp := make([]byte, n)
132146
rand.Read(temp)
133147
suffix := hex.EncodeToString(temp)
134148
var privileged bool
135149
privileged = true
136-
var command string
150+
//var command string
137151
var probeCommand []string
138-
command = "rm -rf /tmp/" + pod.Name + "_" + pod.Namespace + ".cap; rm -rf /tmp/complete-" + pod.Name + "_" + pod.Namespace + "; nsenter -t $(docker inspect $(docker ps |grep '" + pod.Uid + "'|grep -v pause|awk '{print $1}')| grep '\"Pid\":' | grep -Eo '[0-9]*') -n timeout " + duration + " tcpdump -i any -w /tmp/" + pod.Name + "_" + pod.Namespace + ".cap; sleep 2;touch /tmp/complete-" + pod.Name + "_" + pod.Namespace + "; tail -f /dev/null"
152+
//command = "rm -rf /tmp/" + pod.Name + "_" + pod.Namespace + ".cap; rm -rf /tmp/complete-" + pod.Name + "_" + pod.Namespace + "; nsenter -t $(docker inspect $(docker ps |grep '" + pod.Uid + "'|grep -v pause|awk '{print $1}')| grep '\"Pid\":' | grep -Eo '[0-9]*') -n timeout " + duration + " tcpdump -i any -w /tmp/" + pod.Name + "_" + pod.Namespace + ".cap; sleep 2;touch /tmp/complete-" + pod.Name + "_" + pod.Namespace + "; tail -f /dev/null"
139153
//log.Info(command)
140-
probeCommand = []string{"ls", "/tmp/complete-" + pod.Name + "_" + pod.Namespace}
154+
probeCommand = []string{"ls", "/tmp/containerTcpdumpComplete"}
155+
cmContainers := containers{}
156+
cmContainer := container{}
157+
for _, targetContainer := range targetContainers {
158+
cmContainer.Name = targetContainer
159+
cmContainers.Containers = append(cmContainers.Containers, cmContainer)
160+
}
161+
cmContainers.Duration = duration
162+
cmContainersJson, _ := json.Marshal(cmContainers)
141163
//log.Info(probeCommand)
142164
return &apicore.Pod{
143-
ObjectMeta: metav1.ObjectMeta{
144-
Name: pod.Name + "-" + suffix,
145-
Namespace: pod.Namespace,
146-
Labels: map[string]string{
147-
"tdn": pod.Name,
148-
"tdns": pod.Namespace,
165+
ObjectMeta: metav1.ObjectMeta{
166+
Name: node + "-" + suffix,
167+
Namespace: "default",
168+
Labels: map[string]string{
169+
"k8stcpdump": "true",
170+
},
149171
},
150-
},
151-
Spec: apicore.PodSpec{
152-
Containers: []apicore.Container{
153-
{
154-
Name: pod.Name,
155-
Image: "docker.io/library/alpine",
156-
ImagePullPolicy: apicore.PullIfNotPresent,
157-
Command: []string{
158-
"nsenter",
159-
"-t",
160-
"1",
161-
"-m",
162-
"-u",
163-
"-i",
164-
"-n",
165-
"-p",
166-
"--",
167-
"bash",
168-
"-c",
169-
command,
170-
},
171-
SecurityContext: &apicore.SecurityContext{
172-
Privileged: &privileged,
172+
Spec: apicore.PodSpec{
173+
Containers: []apicore.Container{
174+
{
175+
Name: "k8stcpdump",
176+
Image: "shawnlu/containertcpdump:20201130",
177+
ImagePullPolicy: apicore.PullIfNotPresent,
178+
SecurityContext: &apicore.SecurityContext{
179+
Privileged: &privileged,
180+
},
181+
ReadinessProbe: &apicore.Probe{
182+
Handler: apicore.Handler{
183+
Exec: &apicore.ExecAction{
184+
Command: probeCommand,
185+
},
186+
},
187+
},
188+
VolumeMounts: []apicore.VolumeMount{
189+
{
190+
Name: "containers",
191+
MountPath: "/mnt/containerTcpdump/containers.json",
192+
SubPath: "containers.json",
193+
},
194+
},
173195
},
174-
ReadinessProbe: &apicore.Probe{
175-
Handler: apicore.Handler{
176-
Exec: &apicore.ExecAction{
177-
Command: probeCommand,
196+
},
197+
NodeName: node,
198+
HostPID: true,
199+
Volumes: []apicore.Volume{
200+
{
201+
Name: "containers",
202+
VolumeSource: apicore.VolumeSource{
203+
ConfigMap: &apicore.ConfigMapVolumeSource{
204+
LocalObjectReference: apicore.LocalObjectReference{
205+
Name: node + "-" + suffix,
206+
},
178207
},
179208
},
180209
},
181210
},
182211
},
183-
NodeName: pod.Node,
184-
HostPID: true,
185212
},
186-
}
213+
&apicore.ConfigMap{
214+
ObjectMeta: metav1.ObjectMeta{
215+
Name: node + "-" + suffix,
216+
Namespace: "default",
217+
Labels: map[string]string{
218+
"k8stcpdump": "true",
219+
},
220+
},
221+
BinaryData: map[string][]byte{
222+
"containers.json": cmContainersJson,
223+
},
224+
}
225+
187226
}
188227

189228
func watchPodStatus(client *kubernetes.Clientset, tcpdumpPod *apicore.Pod) wait.ConditionFunc {
@@ -206,19 +245,24 @@ func watchPodStatus(client *kubernetes.Clientset, tcpdumpPod *apicore.Pod) wait.
206245
}
207246
}
208247

209-
func createPod(client *kubernetes.Clientset, targetPod *target, duration string) (*apicore.Pod, error) {
210-
podDefinition := getPodDef(targetPod, duration)
211-
tcpdumpPod, err := client.CoreV1().Pods(podDefinition.ObjectMeta.Namespace).Create(context.TODO(), podDefinition, metav1.CreateOptions{})
248+
func createPod(client *kubernetes.Clientset, node string, containers []string, duration string) (*apicore.Pod, error) {
249+
podManifest, cmManifest := createManifests(node, containers, duration)
250+
_, err := client.CoreV1().ConfigMaps(cmManifest.ObjectMeta.Namespace).Create(context.TODO(), cmManifest, metav1.CreateOptions{})
251+
if err != nil {
252+
log.Warn(fmt.Sprintf("Failed to create the configmap for node %q due to %q", node, err))
253+
return nil, err
254+
}
255+
tcpdumpPod, err := client.CoreV1().Pods(podManifest.ObjectMeta.Namespace).Create(context.TODO(), podManifest, metav1.CreateOptions{})
212256
if err == nil {
213257
log.Info(fmt.Sprintf("Pod '%s' in the namespace '%s' has been created.", tcpdumpPod.Name, tcpdumpPod.Namespace))
214258
} else {
215-
log.Warn(fmt.Sprintf("Pod '%s' in the namespace '%s' failed to be created due to '%s'.", podDefinition.ObjectMeta.Name, podDefinition.ObjectMeta.Namespace, err.Error()))
259+
log.Warn(fmt.Sprintf("Pod '%s' in the namespace '%s' failed to be created due to '%s'.", podManifest.ObjectMeta.Name, podManifest.ObjectMeta.Namespace, err.Error()))
216260
}
217261
return tcpdumpPod, err
218262
}
219263

220-
func downloadFromPod(restConfig *rest.Config, client *kubernetes.Clientset, tcpdumpPod *apicore.Pod) error {
221-
path := "/tmp/" + tcpdumpPod.Spec.Containers[0].Name + "_" + tcpdumpPod.ObjectMeta.Namespace + ".cap"
264+
func downloadFromPod(restConfig *rest.Config, client *kubernetes.Clientset, tcpdumpPod *apicore.Pod, targetContainer string) error {
265+
path := "/tmp/" + targetContainer + ".cap"
222266
command := []string{"tar", "cf", "-", path}
223267
req := client.CoreV1().RESTClient().Post().Namespace(tcpdumpPod.ObjectMeta.Namespace).Resource("pods").Name(tcpdumpPod.ObjectMeta.Name).SubResource("exec").VersionedParams(&apicore.PodExecOptions{
224268
Container: tcpdumpPod.Spec.Containers[0].Name,
@@ -255,7 +299,8 @@ func downloadFromPod(restConfig *rest.Config, client *kubernetes.Clientset, tcpd
255299
}
256300
break
257301
}
258-
destFileName := "./" + tcpdumpPod.Spec.Containers[0].Name + "-" + tcpdumpPod.ObjectMeta.Namespace + ".cap"
302+
destFileName := "./" + targetContainer + ".cap"
303+
//log.Info(fmt.Sprintf("Create" + destFileName))
259304
outFile, err := os.Create(destFileName)
260305
if err != nil {
261306
log.Warn(fmt.Sprintf("Error while creating the local dump file for pod '%s'", tcpdumpPod.ObjectMeta.Name))
@@ -278,6 +323,10 @@ func cleanUp(client *kubernetes.Clientset, tcpdumpPod *apicore.Pod) error {
278323
//var GracePeriodSeconds int64
279324
//GracePeriodSeconds = 0
280325
err := client.CoreV1().Pods(tcpdumpPod.ObjectMeta.Namespace).Delete(context.TODO(), tcpdumpPod.ObjectMeta.Name, metav1.DeleteOptions{})
326+
if err != nil {
327+
return err
328+
}
329+
err = client.CoreV1().ConfigMaps(tcpdumpPod.ObjectMeta.Namespace).Delete(context.TODO(), tcpdumpPod.ObjectMeta.Name, metav1.DeleteOptions{})
281330
return err
282331
}
283332

@@ -297,9 +346,9 @@ func cleanUp(client *kubernetes.Clientset, tcpdumpPod *apicore.Pod) error {
297346
// return false
298347
//}
299348

300-
func podOperation(workerGroup *sync.WaitGroup, restConfig *rest.Config, client *kubernetes.Clientset, targetPod *target, duration string, sleepTime time.Duration) error {
349+
func podOperation(workerGroup *sync.WaitGroup, restConfig *rest.Config, client *kubernetes.Clientset, node string, containers []string, duration string, sleepTime time.Duration) error {
301350
defer workerGroup.Done()
302-
tcpdumpPod, err := createPod(client, targetPod, duration)
351+
tcpdumpPod, err := createPod(client, node, containers, duration)
303352
if err == nil {
304353

305354
err = wait.PollImmediate(time.Second*1, sleepTime, watchPodStatus(client, tcpdumpPod))
@@ -326,10 +375,15 @@ func podOperation(workerGroup *sync.WaitGroup, restConfig *rest.Config, client *
326375
log.Warn(fmt.Sprintf("Timeout while waiting tcpdump for pod '%s' in the namespace '%s' to complete", tcpdumpPod.ObjectMeta.Name, tcpdumpPod.ObjectMeta.Namespace))
327376
//log.Fatal(err)
328377
} else {
329-
err = downloadFromPod(restConfig, client, tcpdumpPod)
330-
if err != nil {
331-
log.Warn(fmt.Sprintf("Failed to download dump file from pod '%s' in the namespace '%s'", tcpdumpPod.ObjectMeta.Name, tcpdumpPod.ObjectMeta.Namespace))
378+
for _, container := range containers {
379+
err = downloadFromPod(restConfig, client, tcpdumpPod, container)
380+
if err != nil {
381+
log.Warn(fmt.Sprintf("Failed to download dump file for the container %q from pod '%s' in the namespace '%s'", container, tcpdumpPod.ObjectMeta.Name, tcpdumpPod.ObjectMeta.Namespace))
382+
} else {
383+
log.Info(fmt.Sprintf("Download the dump file for the container %q successfully", container))
384+
}
332385
}
386+
333387
}
334388
err = cleanUp(client, tcpdumpPod)
335389
if err != nil {
@@ -361,13 +415,18 @@ func Run(parFile string) {
361415
podList := *targetPods
362416
count := len(podList)
363417

364-
var workerGroup sync.WaitGroup
418+
nodeSet := make(map[string][]string)
365419
for i := 0; i < count; i++ {
420+
err = createNodeSet(nodeSet, &podList[i])
421+
}
422+
423+
var workerGroup sync.WaitGroup
424+
for node, targetContainers := range nodeSet {
366425
workerGroup.Add(1)
367-
go podOperation(&workerGroup, restConfig, client, &podList[i], duration, sleepTime)
426+
go podOperation(&workerGroup, restConfig, client, node, targetContainers, duration, sleepTime)
368427
}
369-
//fmt.Println("Wait for workers")
370428
workerGroup.Wait()
429+
371430
//fmt.Println("All workers have completed")
372431
log.Info("All operations have been completed. EXIT now.")
373432
}

0 commit comments

Comments
 (0)