-
Notifications
You must be signed in to change notification settings - Fork 229
ateom: export telemetry through an atelet unix-socket OTLP relay #809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f667d49
648f5a6
fd57f4e
8ae4b5d
ae8b1ae
20931cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,7 @@ import ( | |
| "github.com/agent-substrate/substrate/internal/ateomnet" | ||
| "github.com/agent-substrate/substrate/internal/ateompath" | ||
| "github.com/agent-substrate/substrate/internal/atunnel" | ||
| "github.com/agent-substrate/substrate/internal/otlprelay" | ||
| "github.com/agent-substrate/substrate/internal/proto/ateompb" | ||
| "github.com/agent-substrate/substrate/internal/serverboot" | ||
| "github.com/agent-substrate/substrate/internal/version" | ||
|
|
@@ -60,6 +61,9 @@ var ( | |
| showVersion = flag.Bool("version", false, "Print version and exit.") | ||
| logLevelFlag = flag.String("log-level", "info", "Minimum log level: debug, info, warn, or error.") | ||
|
|
||
| otlpRelaySocket = flag.String("otlp-relay-socket", ateompath.AteletOTLPSocketPath(), | ||
| "Unix socket of atelet's OTLP relay to export telemetry through, keeping it off the pod network. Empty, or absent at startup, exports directly to OTEL_EXPORTER_OTLP_ENDPOINT instead.") | ||
|
|
||
| atunnelListenAddress = flag.String("atunnel-listen-address", "0.0.0.0:443", "Address for actor ingress HTTPS") | ||
| workerCredentialBundle = flag.String("atunnel-credential-bundle", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "Worker Pod credential bundle used by atunnel for inbound serving and outbound mTLS") | ||
| podIdentityTrustBundle = flag.String("atunnel-trust-bundle", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "Pod identity trust bundle used for router clients and the node-local atelet") | ||
|
|
@@ -101,16 +105,39 @@ func do(ctx context.Context) error { | |
| slog.InfoContext(ctx, "ateom-microvm booting", slog.String("version", version.String())) | ||
|
|
||
| const serviceName = "ateom-microvm" | ||
| // Export through atelet's node-local relay when it is there, so telemetry | ||
| // never touches the worker pod's network. A nil conn means it is not, and | ||
| // both providers fall back to dialing the collector directly. | ||
| // | ||
| // A relay that cannot be dialed is logged rather than fatal, matching both | ||
| // ends of the same decision: Dial already treats an absent socket as a | ||
| // fallback rather than an error, and atelet logs and keeps going when it | ||
| // cannot serve the relay at all. What is lost here is the node-local export | ||
| // path, not the ateom's ability to run actors, and failing the worker pod | ||
| // over its telemetry route would turn a misconfigured flag into an outage. | ||
| relayConn, err := otlprelay.Dial(ctx, *otlpRelaySocket) | ||
| if err != nil { | ||
| slog.ErrorContext(ctx, "Failed to connect to the OTLP relay; exporting telemetry directly over the pod network", | ||
| slog.String("socket", *otlpRelaySocket), slog.Any("err", err)) | ||
| } | ||
| if relayConn != nil { | ||
| defer relayConn.Close() | ||
| } | ||
|
|
||
|
Comment on lines
+108
to
+126
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same. |
||
| tp, err := serverboot.InitTracing(ctx, serverboot.TracingOptions{ | ||
| ServiceName: serviceName, | ||
| Sampling: serverboot.ResolveTraceSampling(ctx, serverboot.ParentRatioSampling(serverboot.ControlPlaneTraceRatio)), | ||
| ServiceName: serviceName, | ||
| Sampling: serverboot.ResolveTraceSampling(ctx, serverboot.ParentRatioSampling(serverboot.ControlPlaneTraceRatio)), | ||
| ExporterConn: relayConn, | ||
| // So the spans say which path they took, including when relayConn is nil | ||
| // because the dial above failed and this ateom is exporting directly. | ||
| RelayCapable: true, | ||
| }) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to initialize tracing", err) | ||
| } | ||
| defer serverboot.ShutdownProvider("TracerProvider", tp.Shutdown) | ||
|
|
||
| mp, err := serverboot.InitMetricsPushOnly(ctx, serviceName) | ||
| mp, err := serverboot.InitMetricsPushOnlyVia(ctx, serviceName, relayConn) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to initialize metrics", err) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,28 @@ func GVisorReleaseDir(sha256 string) string { | |
| return filepath.Join(StaticFilesDir, "gvisor-"+sha256) | ||
| } | ||
|
|
||
| // AteletOTLPSocketPath is the node-scoped unix socket atelet serves the OTLP | ||
| // relay on (see internal/otlprelay). It is node-scoped rather than per-pod | ||
| // because every ateom on the node pushes into the same relay: atelet is a | ||
| // DaemonSet, so one socket collapses N per-pod collector connections into one | ||
| // per-node connection. | ||
| // | ||
| // It sits directly under BasePath, which is the host directory already mounted | ||
| // at the same path into atelet and into every ateom pod, so no new volume is | ||
| // needed for ateom to reach it. Note that BasePath is mounted writable | ||
| // (workerpool_apply.go) and shared with CredentialBrokerSocket and the image | ||
| // cache, so a worker pod can unlink or replace this socket. Confining | ||
| // atelet-owned sockets to a subdirectory mounted read-only would be an | ||
| // improvement, but it is a property of the whole BasePath mount rather than of | ||
| // this socket — a read-only subdir needs its own volume and mount, and the pod | ||
| // keeps CAP_SYS_ADMIN. Tracked separately rather than solved here. | ||
| func AteletOTLPSocketPath() string { | ||
| return filepath.Join( | ||
| BasePath, | ||
| "atelet-otlp.sock", | ||
| ) | ||
| } | ||
|
|
||
|
Comment on lines
+62
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wondering if a subdir only atelet writes, mounted read-only into the pods, would be better here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be better, but I think it would be better to have another PR updating it, since it isn't specific to this socket. Instead, added more comments. WDYT? |
||
| func AteomPath(podUID string) string { | ||
| return filepath.Join( | ||
| BasePath, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright 2026 Google LLC | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package otlprelay | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "io/fs" | ||
| "log/slog" | ||
| "os" | ||
|
|
||
| "google.golang.org/grpc" | ||
| "google.golang.org/grpc/credentials/insecure" | ||
| ) | ||
|
|
||
| // Dial opens the ateom half of the relay: a gRPC connection over atelet's unix | ||
| // socket, to be handed to the OTLP exporters via serverboot's ExporterConn. | ||
| // | ||
| // It returns (nil, nil) when sockPath is empty or absent, which the caller reads | ||
| // as "export directly instead". The existence check is what makes the fallback | ||
| // deterministic at startup: grpc.NewClient is lazy, so a connection to a missing | ||
| // socket would be created happily and only fail later, per export, with the | ||
| // telemetry already lost. Losing spans is not worth failing ateom over either, | ||
| // hence a fallback rather than an error. | ||
| // | ||
| // The connection is plaintext by design. A unix socket cannot leave the node, so | ||
| // there is no transport to protect; access is controlled by the socket's file | ||
| // permissions instead (see socketMode). | ||
| func Dial(ctx context.Context, sockPath string) (*grpc.ClientConn, error) { | ||
| if sockPath == "" { | ||
| return nil, nil | ||
| } | ||
| if err := validateSocketPath(sockPath); err != nil { | ||
| return nil, err | ||
| } | ||
| if _, err := os.Stat(sockPath); err != nil { | ||
| if errors.Is(err, fs.ErrNotExist) { | ||
| slog.WarnContext(ctx, "OTLP relay socket absent, exporting telemetry directly over the pod network", | ||
|
baizhenyu marked this conversation as resolved.
|
||
| slog.String("socket", sockPath)) | ||
| return nil, nil | ||
| } | ||
| return nil, fmt.Errorf("while checking the OTLP relay socket %q: %w", sockPath, err) | ||
| } | ||
|
|
||
| // gRPC resolves a "unix://" target to a unix socket dialer natively, so the | ||
| // OTLP exporters above this connection are unchanged: OTLP is gRPC, and gRPC | ||
| // needs only a reliable byte stream. | ||
| conn, err := grpc.NewClient("unix://"+sockPath, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 nit 🟢 – This only builds a valid target for an absolute
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the validation. |
||
| grpc.WithTransportCredentials(insecure.NewCredentials())) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("while dialing the OTLP relay socket %q: %w", sockPath, err) | ||
| } | ||
| slog.InfoContext(ctx, "Exporting telemetry through the atelet OTLP relay", slog.String("socket", sockPath)) | ||
| return conn, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do the same we have now for atelet to avoid crashes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, changed to log.