Skip to content

Commit

Permalink
Instrumentation as resource (#78)
Browse files Browse the repository at this point in the history
This PR changes the way Odigos performs instrumentation to
**Instrumentation as resource**.
Instead of adding instrumentation via initContainer and a shared mount,
odigos now adds a resource that can be allocated via the
`.spec.resources.limits` field.

In addition, this PR upgrades the .NET instrumentation version to v0.5.0
and improves the uninstall process.
  • Loading branch information
edeNFed authored Feb 27, 2023
1 parent 0e75fa3 commit d587f4a
Show file tree
Hide file tree
Showing 182 changed files with 838 additions and 23,117 deletions.
1 change: 0 additions & 1 deletion agents/dotnet/.gitignore

This file was deleted.

5 changes: 0 additions & 5 deletions agents/dotnet/Dockerfile

This file was deleted.

6 changes: 0 additions & 6 deletions agents/dotnet/init.sh

This file was deleted.

11 changes: 0 additions & 11 deletions agents/golang/Dockerfile

This file was deleted.

4 changes: 0 additions & 4 deletions agents/java/Dockerfile

This file was deleted.

12 changes: 0 additions & 12 deletions agents/nodejs/Dockerfile

This file was deleted.

23 changes: 0 additions & 23 deletions agents/python/Dockerfile

This file was deleted.

9 changes: 5 additions & 4 deletions autoscaler/controllers/datacollection/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ func getConfigMapData(apps *odigosv1.InstrumentedApplicationList, dests *odigosv
},
},
Processors: commonconf.GenericMap{
"batch": empty,
"batch": empty,
"odigosresourcename": empty,
},
Extensions: commonconf.GenericMap{
"health_check": empty,
Expand Down Expand Up @@ -260,15 +261,15 @@ func getConfigMapData(apps *odigosv1.InstrumentedApplicationList, dests *odigosv

cfg.Service.Pipelines["logs"] = commonconf.Pipeline{
Receivers: []string{"filelog"},
Processors: []string{"batch"},
Processors: []string{"batch", "odigosresourcename"},
Exporters: []string{"otlp/gateway"},
}
}

if collectTraces {
cfg.Service.Pipelines["traces"] = commonconf.Pipeline{
Receivers: []string{"otlp", "zipkin"},
Processors: []string{"batch"},
Processors: []string{"batch", "odigosresourcename"},
Exporters: []string{"otlp/gateway"},
}
}
Expand All @@ -281,7 +282,7 @@ func getConfigMapData(apps *odigosv1.InstrumentedApplicationList, dests *odigosv

cfg.Service.Pipelines["metrics"] = commonconf.Pipeline{
Receivers: []string{"otlp", "kubeletstats"},
Processors: []string{"batch"},
Processors: []string{"batch", "odigosresourcename"},
Exporters: []string{"otlp/gateway"},
}
}
Expand Down
34 changes: 32 additions & 2 deletions autoscaler/controllers/datacollection/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
const (
collectorLabel = "odigos.io/data-collection"
containerName = "data-collection"
containerImage = "otel/opentelemetry-collector-contrib:0.55.0"
containerCommand = "/otelcol-contrib"
containerImage = "keyval/otel-collector-contrib:v0.2"
containerCommand = "/otelcontribcol"
confDir = "/conf"
configHashAnnotation = "odigos.io/config-hash"
dataCollectionSA = "odigos-data-collection"
Expand Down Expand Up @@ -126,6 +126,14 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, configData st
},
},
},
{
Name: "kubeletpodresources",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/var/lib/kubelet/pod-resources",
},
},
},
},
Containers: []corev1.Container{
{
Expand All @@ -147,6 +155,21 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, configData st
MountPath: "/var/log",
ReadOnly: true,
},
{
Name: "kubeletpodresources",
MountPath: "/var/lib/kubelet/pod-resources",
ReadOnly: true,
},
},
Env: []corev1.EnvVar{
{
Name: "NODE_NAME",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "spec.nodeName",
},
},
},
},
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
Expand All @@ -164,6 +187,9 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, configData st
},
},
},
SecurityContext: &corev1.SecurityContext{
Privileged: boolPtr(true),
},
},
},
HostNetwork: true,
Expand Down Expand Up @@ -206,3 +232,7 @@ func patchDaemonSet(existing *appsv1.DaemonSet, desired *appsv1.DaemonSet, ctx c

return updated, nil
}

func boolPtr(b bool) *bool {
return &b
}
2 changes: 1 addition & 1 deletion autoscaler/controllers/gateway/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

const (
containerName = "gateway"
containerImage = "keyval/otel-collector-contrib:v0.1"
containerImage = "keyval/otel-collector-contrib:v0.2"
containerCommand = "/otelcontribcol"
confDir = "/conf"
configHashAnnotation = "odigos.io/config-hash"
Expand Down
90 changes: 51 additions & 39 deletions cli/cmd/uninstall.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
Expand All @@ -13,11 +10,19 @@ import (
"github.com/keyval-dev/odigos/cli/pkg/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"strings"
"time"

"github.com/spf13/cobra"
)

const (
odigosDeviceName = "instrumentation.odigos.io"
goAgentImage = "keyval/otel-go-agent"
golangKernelDebugVolumeName = "kernel-debug"
)

// uninstallCmd represents the uninstall command
var uninstallCmd = &cobra.Command{
Use: "uninstall",
Expand Down Expand Up @@ -57,6 +62,9 @@ to quickly create a Cobra application.`,
createKubeResourceWithLogging(ctx, fmt.Sprintf("Uninstalling Namespace %s", ns),
client, cmd, ns, uninstallNamespace)

// Wait for namespace to be deleted
waitForNamespaceDeletion(ctx, client, ns)

l := log.Print("Rolling back odigos changes to pods")
err = rollbackPodChanges(ctx, client)
if err != nil {
Expand All @@ -69,6 +77,18 @@ to quickly create a Cobra application.`,
},
}

func waitForNamespaceDeletion(ctx context.Context, client *kube.Client, ns string) {
l := log.Print("Waiting for namespace to be deleted")
wait.PollImmediate(1*time.Second, 5*time.Minute, func() (bool, error) {
_, err := client.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{})
if err != nil {
l.Success()
return true, nil
}
return false, nil
})
}

func rollbackPodChanges(ctx context.Context, client *kube.Client) error {
deps, err := client.AppsV1().Deployments("").List(ctx, metav1.ListOptions{})
if err != nil {
Expand All @@ -80,10 +100,7 @@ func rollbackPodChanges(ctx context.Context, client *kube.Client) error {
continue
}

if err := rollbackPodTemplateSpec(ctx, client, &dep.Spec.Template); err != nil {
return err
}

rollbackPodTemplateSpec(ctx, client, &dep.Spec.Template)
if _, err := client.AppsV1().Deployments(dep.Namespace).Update(ctx, &dep, metav1.UpdateOptions{}); err != nil {
return err
}
Expand All @@ -99,10 +116,7 @@ func rollbackPodChanges(ctx context.Context, client *kube.Client) error {
continue
}

if err := rollbackPodTemplateSpec(ctx, client, &s.Spec.Template); err != nil {
return err
}

rollbackPodTemplateSpec(ctx, client, &s.Spec.Template)
if _, err := client.AppsV1().StatefulSets(s.Namespace).Update(ctx, &s, metav1.UpdateOptions{}); err != nil {
return err
}
Expand All @@ -111,45 +125,43 @@ func rollbackPodChanges(ctx context.Context, client *kube.Client) error {
return nil
}

func rollbackPodTemplateSpec(ctx context.Context, client *kube.Client, pts *v1.PodTemplateSpec) error {
// Remove odigos volumes
for i, v := range pts.Spec.Volumes {
if strings.Contains(v.Name, "odigos") || strings.Contains(v.Name, "agentdir") {
pts.Spec.Volumes = append(pts.Spec.Volumes[:i], pts.Spec.Volumes[i+1:]...)
}
}

// Remove containers with keyval image
func rollbackPodTemplateSpec(ctx context.Context, client *kube.Client, pts *v1.PodTemplateSpec) {
// Odigos instruments pods in two ways:
// A. For Java/.NET/Python/NodeJS apps, it adds a resource limit to the container
instrumentedViaResourceLimit := false
for i, c := range pts.Spec.Containers {
if strings.Contains(c.Image, "keyval") || strings.Contains(c.Image, "odigos") {
pts.Spec.Containers = append(pts.Spec.Containers[:i], pts.Spec.Containers[i+1:]...)
if c.Resources.Limits != nil {
for val, _ := range c.Resources.Limits {
if strings.Contains(val.String(), odigosDeviceName) {
instrumentedViaResourceLimit = true
delete(pts.Spec.Containers[i].Resources.Limits, val)
}
}
}
}

if len(c.Command) > 0 && c.Command[0] == "/odigos/init" {
// set container command to be container args
pts.Spec.Containers[i].Command = pts.Spec.Containers[i].Args
pts.Spec.Containers[i].Args = nil
}
if instrumentedViaResourceLimit {
return
}

// Remove volume mounts
// B. For Go apps, it adds a sidecar container

// Remove containers with go agent image
for i, c := range pts.Spec.Containers {
for j, vm := range c.VolumeMounts {
if strings.Contains(vm.Name, "odigos") || strings.Contains(vm.Name, "agentdir") {
pts.Spec.Containers[i].VolumeMounts = append(c.VolumeMounts[:j], c.VolumeMounts[j+1:]...)
}
if strings.Contains(c.Image, goAgentImage) {
pts.Spec.Containers = append(pts.Spec.Containers[:i], pts.Spec.Containers[i+1:]...)
}
}

// Remove odigos init containers
for i, c := range pts.Spec.InitContainers {
if strings.Contains(c.Image, "keyval") || strings.Contains(c.Image, "odigos") ||
strings.Contains(c.Image, "otel") {
pts.Spec.InitContainers = append(pts.Spec.InitContainers[:i], pts.Spec.InitContainers[i+1:]...)
// Roll back shared process namespace
pts.Spec.ShareProcessNamespace = nil

// Remove odigos volumes
for i, v := range pts.Spec.Volumes {
if v.Name == golangKernelDebugVolumeName {
pts.Spec.Volumes = append(pts.Spec.Volumes[:i], pts.Spec.Volumes[i+1:]...)
}
}

return nil
}

func uninstallDeployments(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error {
Expand Down
Loading

0 comments on commit d587f4a

Please sign in to comment.