diff --git a/README.md b/README.md index 62a0ea42e..f3c6861fd 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,8 @@ Flags: --node=STRING Name node the process is running on. If on Kubernetes, this must match the Kubernetes node name. + --external-label=KEY=VALUE;... + Label(s) to attach to all profiles. --store-address=STRING gRPC address to send profiles and symbols to. --bearer-token=STRING Bearer token to authenticate with store. --bearer-token-file=STRING diff --git a/cmd/parca-agent/main.go b/cmd/parca-agent/main.go index d0b15341e..e31cd0db2 100644 --- a/cmd/parca-agent/main.go +++ b/cmd/parca-agent/main.go @@ -46,19 +46,20 @@ import ( ) type flags struct { - LogLevel string `kong:"enum='error,warn,info,debug',help='Log level.',default='info'"` - HttpAddress string `kong:"help='Address to bind HTTP server to.',default=':7071'"` - Node string `kong:"required,help='Name node the process is running on. If on Kubernetes, this must match the Kubernetes node name.'"` - StoreAddress string `kong:"help='gRPC address to send profiles and symbols to.'"` - BearerToken string `kong:"help='Bearer token to authenticate with store.'"` - BearerTokenFile string `kong:"help='File to read bearer token from to authenticate with store.'"` - Insecure bool `kong:"help='Send gRPC requests via plaintext instead of TLS.'"` - InsecureSkipVerify bool `kong:"help='Skip TLS certificate verification.'"` - SamplingRatio float64 `kong:"help='Sampling ratio to control how many of the discovered targets to profile. Defaults to 1.0, which is all.',default='1.0'"` - Kubernetes bool `kong:"help='Discover containers running on this node to profile automatically.',default='true'"` - PodLabelSelector string `kong:"help='Label selector to control which Kubernetes Pods to select.'"` - SystemdUnits []string `kong:"help='SystemD units to profile on this node.'"` - TempDir string `kong:"help='Temporary directory path to use for object files.',default='/tmp'"` + LogLevel string `kong:"enum='error,warn,info,debug',help='Log level.',default='info'"` + HttpAddress string `kong:"help='Address to bind HTTP server to.',default=':7071'"` + Node string `kong:"required,help='Name node the process is running on. If on Kubernetes, this must match the Kubernetes node name.'"` + ExternalLabel map[string]string `kong:"help='Label(s) to attach to all profiles.'"` + StoreAddress string `kong:"help='gRPC address to send profiles and symbols to.'"` + BearerToken string `kong:"help='Bearer token to authenticate with store.'"` + BearerTokenFile string `kong:"help='File to read bearer token from to authenticate with store.'"` + Insecure bool `kong:"help='Send gRPC requests via plaintext instead of TLS.'"` + InsecureSkipVerify bool `kong:"help='Skip TLS certificate verification.'"` + SamplingRatio float64 `kong:"help='Sampling ratio to control how many of the discovered targets to profile. Defaults to 1.0, which is all.',default='1.0'"` + Kubernetes bool `kong:"help='Discover containers running on this node to profile automatically.',default='true'"` + PodLabelSelector string `kong:"help='Label selector to control which Kubernetes Pods to select.'"` + SystemdUnits []string `kong:"help='SystemD units to profile on this node.'"` + TempDir string `kong:"help='Temporary directory path to use for object files.',default='/tmp'"` } func main() { @@ -102,6 +103,7 @@ func main() { if flags.Kubernetes { pm, err = agent.NewPodManager( logger, + flags.ExternalLabel, node, flags.PodLabelSelector, flags.SamplingRatio, @@ -123,6 +125,7 @@ func main() { node, flags.SystemdUnits, flags.SamplingRatio, + flags.ExternalLabel, ksymCache, wc, dc, diff --git a/pkg/agent/podmanager.go b/pkg/agent/podmanager.go index 2725544e6..7cf77fc36 100644 --- a/pkg/agent/podmanager.go +++ b/pkg/agent/podmanager.go @@ -31,6 +31,8 @@ import ( type PodManager struct { logger log.Logger + externalLabels map[string]string + // node where this instance is running nodeName string ksymCache *ksym.KsymCache @@ -97,6 +99,7 @@ func (g *PodManager) Run(ctx context.Context) error { logger := log.With(g.logger, "namespace", container.Namespace, "pod", container.PodName, "container", container.ContainerName) containerProfiler := NewCgroupProfiler( logger, + g.externalLabels, g.ksymCache, g.writeClient, g.debugInfoClient, @@ -160,6 +163,7 @@ func (g *PodManager) Run(ctx context.Context) error { func NewPodManager( logger log.Logger, + externalLabels map[string]string, nodeName string, podLabelSelector string, samplingRatio float64, @@ -182,6 +186,7 @@ func NewPodManager( } g := &PodManager{ logger: logger, + externalLabels: externalLabels, nodeName: nodeName, samplingRatio: samplingRatio, ksymCache: ksymCache, diff --git a/pkg/agent/profile.go b/pkg/agent/profile.go index 534a24584..7234545e6 100644 --- a/pkg/agent/profile.go +++ b/pkg/agent/profile.go @@ -98,11 +98,12 @@ type DebugInfoClient interface { } type CgroupProfiler struct { - logger log.Logger - ksymCache *ksym.KsymCache - target CgroupProfilingTarget - sink func(Record) - cancel func() + logger log.Logger + externalLabels map[string]string + ksymCache *ksym.KsymCache + target CgroupProfilingTarget + sink func(Record) + cancel func() pidMappingFileCache *maps.PidMappingFileCache writeClient profilestorepb.ProfileStoreServiceClient @@ -117,6 +118,7 @@ type CgroupProfiler struct { func NewCgroupProfiler( logger log.Logger, + externalLabels map[string]string, ksymCache *ksym.KsymCache, writeClient profilestorepb.ProfileStoreServiceClient, debugInfoClient DebugInfoClient, @@ -126,6 +128,7 @@ func NewCgroupProfiler( ) *CgroupProfiler { return &CgroupProfiler{ logger: logger, + externalLabels: externalLabels, ksymCache: ksymCache, target: target, sink: sink, @@ -164,10 +167,19 @@ func (p *CgroupProfiler) Stop() { } func (p *CgroupProfiler) Labels() []*profilestorepb.Label { - return append(p.target.Labels(), &profilestorepb.Label{ - Name: "__name__", - Value: "cpu", - }) + labels := append(p.target.Labels(), + &profilestorepb.Label{ + Name: "__name__", + Value: "cpu", + }) + for key, value := range p.externalLabels { + labels = append(labels, &profilestorepb.Label{ + Name: key, + Value: value, + }) + } + + return labels } func (p *CgroupProfiler) Run(ctx context.Context) error { diff --git a/pkg/agent/systemdmanager.go b/pkg/agent/systemdmanager.go index ede5ecdac..850d12049 100644 --- a/pkg/agent/systemdmanager.go +++ b/pkg/agent/systemdmanager.go @@ -36,6 +36,7 @@ type SystemdManager struct { logger log.Logger nodeName string samplingRatio float64 + externalLabels map[string]string ksymCache *ksym.KsymCache writeClient profilestorepb.ProfileStoreServiceClient debugInfoClient DebugInfoClient @@ -70,6 +71,7 @@ func NewSystemdManager( nodeName string, units []string, samplingRatio float64, + externalLabels map[string]string, ksymCache *ksym.KsymCache, writeClient profilestorepb.ProfileStoreServiceClient, debugInfoClient DebugInfoClient, @@ -85,6 +87,7 @@ func NewSystemdManager( logger: logger, nodeName: nodeName, samplingRatio: samplingRatio, + externalLabels: externalLabels, ksymCache: ksymCache, writeClient: writeClient, debugInfoClient: debugInfoClient, @@ -181,6 +184,7 @@ func (m *SystemdManager) reconcileUnit(ctx context.Context, unit string) error { logger := log.With(m.logger, "systemdunit", unit) p := NewCgroupProfiler( logger, + m.externalLabels, m.ksymCache, m.writeClient, m.debugInfoClient,