-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adding support for NodeJS (#70)
* feat: adding support for NodeJS and potentially C/C++ * fix: fixing Dockerfile * chore: change to FlameGraph * fix: making the perf agent actually work * feat: adding the ability to launch profiling from the CLI * fix: brief documentation update * chore: adding nodejs caveat to the README
- Loading branch information
Showing
9 changed files
with
270 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,14 @@ jobs: | |
password: ${{ secrets.DOCKER_HUB_PASSWORD }} | ||
repository: verizondigital/kubectl-flame | ||
tags: ${{ steps.vars.outputs.tag }}-ruby | ||
- name: Build perf Docker Image | ||
uses: docker/build-push-action@v1 | ||
with: | ||
dockerfile: 'agent/docker/perf/Dockerfile' | ||
username: ${{ secrets.DOCKER_HUB_USER }} | ||
password: ${{ secrets.DOCKER_HUB_PASSWORD }} | ||
repository: verizondigital/kubectl-flame | ||
tags: ${{ steps.vars.outputs.tag }}-perf | ||
- name: Setup Go | ||
uses: actions/setup-go@v1 | ||
with: | ||
|
@@ -64,4 +72,4 @@ jobs: | |
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Update new version in krew-index | ||
uses: rajatjindal/[email protected] | ||
uses: rajatjindal/[email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
FROM golang:1.14-buster as agentbuild | ||
WORKDIR /go/src/github.com/VerizonMedia/kubectl-flame | ||
ADD . /go/src/github.com/VerizonMedia/kubectl-flame | ||
RUN go get -d -v ./... | ||
RUN cd agent && go build -o /go/bin/agent | ||
|
||
FROM alpine as builder | ||
RUN apk add --update git | ||
RUN git clone https://github.com/brendangregg/FlameGraph | ||
|
||
FROM alpine | ||
RUN apk add --update perf perl | ||
RUN mkdir -p /app/FlameGraph | ||
COPY --from=agentbuild /go/bin/agent /app/agent | ||
RUN mv /usr/bin/perf /app/perf | ||
COPY --from=builder /FlameGraph /app/FlameGraph | ||
|
||
CMD [ "/app/agent" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package profiler | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"strconv" | ||
|
||
"github.com/VerizonMedia/kubectl-flame/agent/details" | ||
"github.com/VerizonMedia/kubectl-flame/agent/utils" | ||
) | ||
|
||
const ( | ||
perfLocation = "/app/perf" | ||
perfRecordOutputFileName = "/tmp/perf.data" | ||
flameGraphPlLocation = "/app/FlameGraph/flamegraph.pl" | ||
flameGraphStackCollapseLocation = "/app/FlameGraph/stackcollapse-perf.pl" | ||
perfScriptOutputFileName = "/tmp/perf.out" | ||
perfFoldedOutputFileName = "/tmp/perf.folded" | ||
flameGraphPerfOutputFile = "/tmp/perf.svg" | ||
) | ||
|
||
type PerfProfiler struct{} | ||
|
||
func (p *PerfProfiler) SetUp(job *details.ProfilingJob) error { | ||
return nil | ||
} | ||
|
||
func (p *PerfProfiler) Invoke(job *details.ProfilingJob) error { | ||
err := p.runPerfRecord(job) | ||
if err != nil { | ||
return fmt.Errorf("perf record failed: %s", err) | ||
} | ||
|
||
err = p.runPerfScript(job) | ||
if err != nil { | ||
return fmt.Errorf("perf script failed: %s", err) | ||
} | ||
|
||
err = p.foldPerfOutput(job) | ||
if err != nil { | ||
return fmt.Errorf("folding perf output failed: %s", err) | ||
} | ||
|
||
err = p.generateFlameGraph(job) | ||
if err != nil { | ||
return fmt.Errorf("flamegraph generation failed: %s", err) | ||
} | ||
|
||
return utils.PublishFlameGraph(flameGraphPerfOutputFile) | ||
} | ||
|
||
func (p *PerfProfiler) runPerfRecord(job *details.ProfilingJob) error { | ||
pid, err := utils.FindRootProcessId(job) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
duration := strconv.Itoa(int(job.Duration.Seconds())) | ||
cmd := exec.Command(perfLocation, "record", "-p", pid, "-o", perfRecordOutputFileName, "-g", "--", "sleep", duration) | ||
|
||
return cmd.Run() | ||
} | ||
|
||
func (p *PerfProfiler) runPerfScript(job *details.ProfilingJob) error { | ||
f, err := os.Create(perfScriptOutputFileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
cmd := exec.Command(perfLocation, "script", "-i", perfRecordOutputFileName) | ||
cmd.Stdout = f | ||
|
||
return cmd.Run() | ||
} | ||
|
||
func (p *PerfProfiler) foldPerfOutput(job *details.ProfilingJob) error { | ||
f, err := os.Create(perfFoldedOutputFileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
cmd := exec.Command(flameGraphStackCollapseLocation, perfScriptOutputFileName) | ||
cmd.Stdout = f | ||
|
||
return cmd.Run() | ||
} | ||
|
||
func (p *PerfProfiler) generateFlameGraph(job *details.ProfilingJob) error { | ||
inputFile, err := os.Open(perfFoldedOutputFileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer inputFile.Close() | ||
|
||
outputFile, err := os.Create(flameGraphPerfOutputFile) | ||
if err != nil { | ||
return err | ||
} | ||
defer outputFile.Close() | ||
|
||
cmd := exec.Command(flameGraphPlLocation) | ||
cmd.Stdin = inputFile | ||
cmd.Stdout = outputFile | ||
|
||
return cmd.Run() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package job | ||
|
||
import ( | ||
"fmt" | ||
|
||
batchv1 "k8s.io/api/batch/v1" | ||
apiv1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/util/uuid" | ||
|
||
"github.com/VerizonMedia/kubectl-flame/cli/cmd/data" | ||
"github.com/VerizonMedia/kubectl-flame/cli/cmd/version" | ||
) | ||
|
||
type perfCreator struct{} | ||
|
||
func (p *perfCreator) create(targetPod *apiv1.Pod, cfg *data.FlameConfig) (string, *batchv1.Job, error) { | ||
id := string(uuid.NewUUID()) | ||
var imageName string | ||
var imagePullSecret []apiv1.LocalObjectReference | ||
args := []string{ | ||
id, | ||
string(targetPod.UID), | ||
cfg.TargetConfig.ContainerName, | ||
cfg.TargetConfig.ContainerId, | ||
cfg.TargetConfig.Duration.String(), | ||
string(cfg.TargetConfig.Language), | ||
string(cfg.TargetConfig.Event), | ||
} | ||
|
||
if cfg.TargetConfig.Pgrep != "" { | ||
args = append(args, cfg.TargetConfig.Pgrep) | ||
} | ||
|
||
if cfg.TargetConfig.Image != "" { | ||
imageName = cfg.TargetConfig.Image | ||
} else { | ||
imageName = fmt.Sprintf("%s:%s-perf", baseImageName, version.GetCurrent()) | ||
} | ||
|
||
if cfg.TargetConfig.ImagePullSecret != "" { | ||
imagePullSecret = []apiv1.LocalObjectReference{{Name: cfg.TargetConfig.ImagePullSecret}} | ||
} | ||
|
||
commonMeta := metav1.ObjectMeta{ | ||
Name: fmt.Sprintf("kubectl-flame-%s", id), | ||
Namespace: cfg.JobConfig.Namespace, | ||
Labels: map[string]string{ | ||
"kubectl-flame/id": id, | ||
}, | ||
Annotations: map[string]string{ | ||
"sidecar.istio.io/inject": "false", | ||
}, | ||
} | ||
|
||
resources, err := cfg.JobConfig.ToResourceRequirements() | ||
if err != nil { | ||
return "", nil, fmt.Errorf("unable to generate resource requirements: %w", err) | ||
} | ||
|
||
job := &batchv1.Job{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "Job", | ||
APIVersion: "batch/v1", | ||
}, | ||
ObjectMeta: commonMeta, | ||
Spec: batchv1.JobSpec{ | ||
Parallelism: int32Ptr(1), | ||
Completions: int32Ptr(1), | ||
TTLSecondsAfterFinished: int32Ptr(5), | ||
BackoffLimit: int32Ptr(2), | ||
Template: apiv1.PodTemplateSpec{ | ||
ObjectMeta: commonMeta, | ||
Spec: apiv1.PodSpec{ | ||
HostPID: true, | ||
Volumes: []apiv1.Volume{ | ||
{ | ||
Name: "target-filesystem", | ||
VolumeSource: apiv1.VolumeSource{ | ||
HostPath: &apiv1.HostPathVolumeSource{ | ||
Path: cfg.TargetConfig.DockerPath, | ||
}, | ||
}, | ||
}, | ||
}, | ||
ImagePullSecrets: imagePullSecret, | ||
InitContainers: nil, | ||
Containers: []apiv1.Container{ | ||
{ | ||
ImagePullPolicy: apiv1.PullAlways, | ||
Name: ContainerName, | ||
Image: imageName, | ||
Command: []string{"/app/agent"}, | ||
Args: args, | ||
VolumeMounts: []apiv1.VolumeMount{ | ||
{ | ||
Name: "target-filesystem", | ||
MountPath: "/var/lib/docker", | ||
}, | ||
}, | ||
SecurityContext: &apiv1.SecurityContext{ | ||
Privileged: boolPtr(true), | ||
Capabilities: &apiv1.Capabilities{ | ||
Add: []apiv1.Capability{"SYS_PTRACE"}, | ||
}, | ||
}, | ||
Resources: resources, | ||
}, | ||
}, | ||
RestartPolicy: "Never", | ||
NodeName: targetPod.Spec.NodeName, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
if cfg.TargetConfig.ServiceAccountName != "" { | ||
job.Spec.Template.Spec.ServiceAccountName = cfg.TargetConfig.ServiceAccountName | ||
} | ||
|
||
return id, job, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters