-
Notifications
You must be signed in to change notification settings - Fork 19
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
Integrate with existing observability systems #44
Conversation
f95d786
to
e8e7b83
Compare
Before this PR, the Kube deployer will automatically start services to provide observability to the user. We start Prometheus, Jaeger, Loki, Promtail and Grafana. While this is great for a simple deployment, in many production scenarios, the user doesn't want to start some of these services (or disable everything for testing/benchmarking performance). The user can run its own custom services that scale well and have complicated configs. This PR enables the user to plugin custom observability services. However, note that we are opinionated in terms of which services they can use, namely Prometheus for metrics, Jaeger for traces, Loki/Promtail for logs and Grafana for nice visualization. We don't enable integration with other services yet.
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.
This looks great Robert. Mainly comments that will make the code more understandable, since it's getting quite complicated and subtle.
internal/impl/observability.go
Outdated
|
||
// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info | ||
// should be generated for service. | ||
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool { |
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.
shouldGenerateServiceConfigs
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.
Done
internal/impl/observability.go
Outdated
// [1] https://helm.sh/ | ||
func generateInfoToExportTraces(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) { | ||
// The user disabled exporting the traces, don't generate anything. | ||
if !shouldGenerateKubeDeploymentInfo(exportTracesURL, cfg) { |
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.
This confused the life out of me, since exportTracesURL
sounds like it represents an actual URL to the Jaeger endpont. Instead, it's the key in the Observability
map.
Can you rename:
`s/exportTracesURL/tracesConfigKey/'
Same for metrics etc.
Actually, even though it's somewhat prone to error, I would be happier to just inline "trace_service" here since it would be clearer.
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.
I am sorry for that. Yeah, the names are tricky and I've spent a lot of time renaming and renaming things.
I would prefer to keep the name under tracesConfigKey, metricsConfigsKey, etc, instead of inlining them if you don't mind, because if we do a small typo we will hate our life because some of these names are propagated everywhere.
I think at some point we should create a subdirectory observability, and put different services in different files. It's getting hairy and hard to follow in general.
internal/impl/observability.go
Outdated
// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info | ||
// should be generated for service. | ||
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool { | ||
return cfg.Observability[service] == "" |
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.
This is confusing since you are checking for "" instead of "none". Could you perhaps just get rid of this method and do actual checks for "auto", "none", "", and "url" everywhere, with comments?
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.
I added a constant auto = "" for checks where kube should generate kubernetes service configs and a constant disabled = "none" for checks when no info for a given observability service (neither configs nor service configs) should be generated. I hope this is better.
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.
Thanks Srdjan for your thorough review.
internal/impl/babysitter.go
Outdated
traceExporter, err = | ||
jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(cfg.TraceExporterService))) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Unable to create trace exporter: %v\n", err) |
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.
Done
internal/impl/kube.go
Outdated
// Compute the URL of the export traces service. | ||
var exportTracesURLInfo string | ||
val := cfg.Observability[exportTracesURL] | ||
exportTracesURLIsSet := val != "" && val != "none" |
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.
Definitely more elegant.Thanks
// implementations for any observability systems. | ||
|
||
const ( | ||
// The names of the observability services that interact with the application. |
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.
I added comments and renamed fields (e.g., jaegerImageName -> autoJagerImageName; jaegerCollectorPort -> defaultJaegerCollectorPort).
For the 'exportTracesURL' et all names I am not sure what's the right name. Also the names 'jaeger_service', etc. bothers me, but I coudln't come up with anything better. Also, I am not sure if it's the most intuitive by default to enable auto generated observability or should generate nothing and the user should explicitly set these variables to "auto" and their "external service name" instead. Maybe for now we leave it as it is, and rethink later, when people try to actually use this.
internal/impl/observability.go
Outdated
|
||
// generateObservabilityInfo generates deployment information needed by the app | ||
// to export metrics, logs, and traces. | ||
func generateObservabilityInfo(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) { |
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.
I call it info because info contains deployment info (e.g., kubernetes deployment and service manifest) and also config info (things that are needed to configure the running service). I renamed everything to configs like:
generatePrometheusConfigs -> to configure Prometheus service
generatePrometheusDeploymentConfigs -> to generate kubernetes deployment configs needed to run the Prometheus service
internal/impl/observability.go
Outdated
// observability = {jaeger_service = "jaeger-all-in-one"} | ||
// | ||
// [1] https://helm.sh/ | ||
func generateInfoToExportTraces(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) { |
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.
Done
internal/impl/observability.go
Outdated
// [1] https://helm.sh/ | ||
func generateInfoToExportTraces(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) { | ||
// The user disabled exporting the traces, don't generate anything. | ||
if !shouldGenerateKubeDeploymentInfo(exportTracesURL, cfg) { |
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.
I am sorry for that. Yeah, the names are tricky and I've spent a lot of time renaming and renaming things.
I would prefer to keep the name under tracesConfigKey, metricsConfigsKey, etc, instead of inlining them if you don't mind, because if we do a small typo we will hate our life because some of these names are propagated everywhere.
I think at some point we should create a subdirectory observability, and put different services in different files. It's getting hairy and hard to follow in general.
internal/impl/observability.go
Outdated
|
||
// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info | ||
// should be generated for service. | ||
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool { |
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.
Done
internal/impl/observability.go
Outdated
// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info | ||
// should be generated for service. | ||
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool { | ||
return cfg.Observability[service] == "" |
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.
I added a constant auto = "" for checks where kube should generate kubernetes service configs and a constant disabled = "none" for checks when no info for a given observability service (neither configs nor service configs) should be generated. I hope this is better.
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.
Looks great!
ReplicaSet: r.name, | ||
ComponentsToStart: r.components, | ||
InternalPort: int32(r.internalPort), | ||
TraceServiceUrl: r.traceServiceURL, |
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.
nit: s/Url/URL/
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.
TraceServiceUrl is the autogenerated proto field trace_service_url
. Unless we define it as trace_serviceURL, I think protoc will convert URL to Url.
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.
Thanks Srdjan!
Before this PR, the Kube deployer will automatically start services to provide observability to the user. We start Prometheus, Jaeger, Loki, Promtail and Grafana.
While this is great for a simple deployment, in many production scenarios, the user doesn't want to start some of these services (or disable everything for testing/benchmarking performance). The user can run its own custom services that scale well and have complicated configs.
This PR enables the user to plugin custom observability services. However, note that we are opinionated in terms of which services they can use, namely Prometheus for metrics, Jaeger for traces, Loki/Promtail for logs and Grafana for nice visualization. We don't enable integration with other services yet.