Skip to content
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

Merged
merged 2 commits into from
Aug 10, 2023
Merged

Integrate with existing observability systems #44

merged 2 commits into from
Aug 10, 2023

Conversation

rgrandl
Copy link
Collaborator

@rgrandl rgrandl commented Aug 9, 2023

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.

@rgrandl rgrandl force-pushed the try_out branch 2 times, most recently from f95d786 to e8e7b83 Compare August 9, 2023 22:08
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.
Copy link
Contributor

@spetrovic77 spetrovic77 left a 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/kube.go Outdated Show resolved Hide resolved
internal/impl/kube.go Outdated Show resolved Hide resolved
internal/impl/observability.go Show resolved Hide resolved
internal/impl/observability.go Outdated Show resolved Hide resolved
internal/impl/observability.go Outdated Show resolved Hide resolved

// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info
// should be generated for service.
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldGenerateServiceConfigs

Copy link
Collaborator Author

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 Show resolved Hide resolved
// [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) {
Copy link
Contributor

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.

Copy link
Collaborator Author

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.

// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info
// should be generated for service.
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool {
return cfg.Observability[service] == ""
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

internal/impl/observability.go Outdated Show resolved Hide resolved
Copy link
Collaborator Author

@rgrandl rgrandl left a 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.

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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Compute the URL of the export traces service.
var exportTracesURLInfo string
val := cfg.Observability[exportTracesURL]
exportTracesURLIsSet := val != "" && val != "none"
Copy link
Collaborator Author

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.
Copy link
Collaborator Author

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.


// generateObservabilityInfo generates deployment information needed by the app
// to export metrics, logs, and traces.
func generateObservabilityInfo(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) {
Copy link
Collaborator Author

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 Show resolved Hide resolved
internal/impl/observability.go Show resolved Hide resolved
// observability = {jaeger_service = "jaeger-all-in-one"}
//
// [1] https://helm.sh/
func generateInfoToExportTraces(dep *protos.Deployment, cfg *KubeConfig) ([]byte, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// [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) {
Copy link
Collaborator Author

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.


// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info
// should be generated for service.
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// shouldGenerateKubeDeploymentInfo returns true iff a Kubernetes deployment info
// should be generated for service.
func shouldGenerateKubeDeploymentInfo(service string, cfg *KubeConfig) bool {
return cfg.Observability[service] == ""
Copy link
Collaborator Author

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.

Copy link
Contributor

@spetrovic77 spetrovic77 left a 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/Url/URL/

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

@rgrandl rgrandl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Srdjan!

@rgrandl rgrandl merged commit a9a65f3 into main Aug 10, 2023
8 checks passed
@rgrandl rgrandl deleted the try_out branch August 10, 2023 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants