Skip to content

Commit ce370ce

Browse files
ChrsMarkAkhigbeEromo
authored andcommitted
[receiver/receiver_creator] Add support for enabling receivers/scrapers from K8s hints (open-telemetry#35617)
1 parent 47c3d4f commit ce370ce

File tree

11 files changed

+1025
-76
lines changed

11 files changed

+1025
-76
lines changed

.chloggen/hints.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: receivercreator
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for starting receivers/scrapers based on provided annotations' hints for metrics' collection
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [34427]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

extension/observer/endpoints.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,11 @@ func (p *Pod) Type() EndpointType {
223223
// PodContainer is a discovered k8s pod's container
224224
type PodContainer struct {
225225
// Name of the container
226-
Name string
226+
Name string `mapstructure:"container_name"`
227227
// Image of the container
228-
Image string
228+
Image string `mapstructure:"container_image"`
229229
// ContainerID is the id of the container exposing the Endpoint
230-
ContainerID string
230+
ContainerID string `mapstructure:"container_id"`
231231
// Pod is the k8s pod in which the container is running
232232
Pod Pod
233233
}

receiver/receivercreator/README.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,191 @@ service:
439439
440440
The full list of settings exposed for this receiver are documented [here](./config.go)
441441
with detailed sample configurations [here](./testdata/config.yaml).
442+
443+
444+
## Generate receiver configurations from provided Hints
445+
446+
Note: When hints feature is enabled if hints are present for an endpoint no receiver templates will be evaluated.
447+
448+
Currently this feature is only supported for K8s environments and the `k8sobserver`.
449+
450+
The discovery feature for K8s is enabled with the following setting:
451+
452+
```yaml
453+
receiver_creator/metrics:
454+
watch_observers: [ k8s_observer ]
455+
discovery:
456+
enabled: true
457+
# Define which receivers should be ignored when provided through annotations
458+
# ignore_receivers: []
459+
```
460+
461+
Find bellow the supported annotations that user can define to automatically enable receivers to start collecting metrics signals from the target Pods/containers.
462+
463+
### Supported metrics annotations
464+
465+
#### Enable/disable discovery
466+
467+
`io.opentelemetry.discovery.metrics/enabled` (Required. `"true"` or `"false"`)
468+
469+
#### Define scraper
470+
471+
`io.opentelemetry.discovery.metrics/scraper` (example: `"nginx"`)
472+
473+
474+
#### Define configuration
475+
476+
`io.opentelemetry.discovery.metrics/config`
477+
478+
For `"endpoint"` setting specifically, it sticks to urls that include
479+
```"`endpoint`"``` as it comes from the Port endpoint which is
480+
in form of `pod_ip:container_port`. This is to ensure that each Pod can only
481+
generate configuration that targets itself and not others.
482+
If no endpoint is provided the Pod's endpoint will be used (in form of `pod_ip:container_port`).
483+
484+
**Example:**
485+
486+
```yaml
487+
io.opentelemetry.discovery.metrics/config: |
488+
endpoint: "http://`endpoint`/nginx_status"
489+
collection_interval: "20s"
490+
initial_delay: "20s"
491+
read_buffer_size: "10"
492+
xyz: "abc"
493+
```
494+
495+
496+
#### Support multiple target containers
497+
498+
Users can target the annotation to a specific container by suffixing it with the name of the port that container exposes:
499+
`io.opentelemetry.discovery.metrics.<container_port>/config`.
500+
For example:
501+
```yaml
502+
io.opentelemetry.discovery.metrics.80/config: |
503+
endpoint: "http://`endpoint`/nginx_status"
504+
```
505+
where `80` is the port that the target container exposes.
506+
507+
If a Pod is annotated with both container level hints and pod level hints the container level hints have priority and
508+
the Pod level hints are used as a fallback (see detailed example bellow).
509+
510+
The current implementation relies on the implementation of `k8sobserver` extension and specifically
511+
the [pod_endpoint](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.111.0/extension/observer/k8sobserver/pod_endpoint.go).
512+
The hints are evaluated per container by extracting the annotations from each [`Port` endpoint](#Port) that is emitted.
513+
514+
515+
516+
### Examples
517+
518+
#### Metrics example
519+
520+
Collector's configuration:
521+
```yaml
522+
receivers:
523+
receiver_creator/metrics:
524+
watch_observers: [ k8s_observer ]
525+
discovery:
526+
enabled: true
527+
receivers:
528+
529+
service:
530+
extensions: [ k8s_observer]
531+
pipelines:
532+
metrics:
533+
receivers: [ receiver_creator ]
534+
processors: []
535+
exporters: [ debug ]
536+
```
537+
538+
Target Pod annotated with hints:
539+
540+
```yaml
541+
apiVersion: v1
542+
kind: ConfigMap
543+
metadata:
544+
name: nginx-conf
545+
data:
546+
nginx.conf: |
547+
user nginx;
548+
worker_processes 1;
549+
error_log /dev/stderr warn;
550+
pid /var/run/nginx.pid;
551+
events {
552+
worker_connections 1024;
553+
}
554+
http {
555+
include /etc/nginx/mime.types;
556+
default_type application/octet-stream;
557+
558+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
559+
'$status $body_bytes_sent "$http_referer" '
560+
'"$http_user_agent" "$http_x_forwarded_for"';
561+
access_log /dev/stdout main;
562+
server {
563+
listen 80;
564+
server_name localhost;
565+
566+
location /nginx_status {
567+
stub_status on;
568+
}
569+
}
570+
include /etc/nginx/conf.d/*;
571+
}
572+
---
573+
apiVersion: apps/v1
574+
kind: Deployment
575+
metadata:
576+
name: redis-deployment
577+
labels:
578+
app: redis
579+
spec:
580+
replicas: 1
581+
selector:
582+
matchLabels:
583+
app: redis
584+
template:
585+
metadata:
586+
labels:
587+
app: redis
588+
annotations:
589+
# redis container port metrics hints
590+
io.opentelemetry.discovery.metrics.6379/enabled: "true"
591+
io.opentelemetry.discovery.metrics.6379/scraper: redis
592+
io.opentelemetry.discovery.metrics.6379/config: |
593+
collection_interval: "20s"
594+
timeout: "10s"
595+
596+
# nginx container port metrics hints
597+
io.opentelemetry.discovery.metrics.80/enabled: "true"
598+
io.opentelemetry.discovery.metrics.80/scraper: nginx
599+
io.opentelemetry.discovery.metrics.80/config: |
600+
endpoint: "http://`endpoint`/nginx_status"
601+
collection_interval: "30s"
602+
timeout: "20s"
603+
spec:
604+
volumes:
605+
- name: nginx-conf
606+
configMap:
607+
name: nginx-conf
608+
items:
609+
- key: nginx.conf
610+
path: nginx.conf
611+
containers:
612+
- name: webserver
613+
image: nginx:latest
614+
ports:
615+
- containerPort: 80
616+
name: webserver
617+
volumeMounts:
618+
- mountPath: /etc/nginx/nginx.conf
619+
readOnly: true
620+
subPath: nginx.conf
621+
name: nginx-conf
622+
- image: redis
623+
imagePullPolicy: IfNotPresent
624+
name: redis
625+
ports:
626+
- name: redis
627+
containerPort: 6379
628+
protocol: TCP
629+
```

receiver/receivercreator/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ type receiverConfig struct {
3535
// userConfigMap is an arbitrary map of string keys to arbitrary values as specified by the user
3636
type userConfigMap map[string]any
3737

38+
type receiverSignals struct {
39+
metrics bool
40+
logs bool
41+
traces bool
42+
}
43+
3844
// receiverTemplate is the configuration of a single subreceiver.
3945
type receiverTemplate struct {
4046
receiverConfig
@@ -46,6 +52,7 @@ type receiverTemplate struct {
4652
// It can contain expr expressions for endpoint env value expansion
4753
ResourceAttributes map[string]any `mapstructure:"resource_attributes"`
4854
rule rule
55+
signals receiverSignals
4956
}
5057

5158
// resourceAttributes holds a map of default resource attributes for each Endpoint type.
@@ -60,6 +67,7 @@ func newReceiverTemplate(name string, cfg userConfigMap) (receiverTemplate, erro
6067
}
6168

6269
return receiverTemplate{
70+
signals: receiverSignals{metrics: true, logs: true, traces: true},
6371
receiverConfig: receiverConfig{
6472
id: id,
6573
config: cfg,
@@ -78,6 +86,12 @@ type Config struct {
7886
// ResourceAttributes is a map of default resource attributes to add to each resource
7987
// object received by this receiver from dynamically created receivers.
8088
ResourceAttributes resourceAttributes `mapstructure:"resource_attributes"`
89+
Discovery DiscoveryConfig `mapstructure:"discovery"`
90+
}
91+
92+
type DiscoveryConfig struct {
93+
Enabled bool `mapstructure:"enabled"`
94+
IgnoreReceivers []string `mapstructure:"ignore_receivers"`
8195
}
8296

8397
func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error {

receiver/receivercreator/config_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func TestLoadConfig(t *testing.T) {
9090
Rule: `type == "port"`,
9191
ResourceAttributes: map[string]any{"one": "two"},
9292
rule: portRule,
93+
signals: receiverSignals{true, true, true},
9394
},
9495
"nop/1": {
9596
receiverConfig: receiverConfig{
@@ -102,6 +103,7 @@ func TestLoadConfig(t *testing.T) {
102103
Rule: `type == "port"`,
103104
ResourceAttributes: map[string]any{"two": "three"},
104105
rule: portRule,
106+
signals: receiverSignals{true, true, true},
105107
},
106108
},
107109
WatchObservers: []component.ID{

0 commit comments

Comments
 (0)