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

Refactoring logging and metrics #835

Merged
merged 3 commits into from
Nov 23, 2021
Merged

Conversation

felix-zhe-huang
Copy link
Contributor

@felix-zhe-huang felix-zhe-huang commented Nov 22, 2021

1. Issue, if available:
Logging and Metrics improvements #811
Closes #811

2. Description of changes:

  • Change controller names to lower cases
  • Reword log line to include reconcile resource name in the inline section
  • Add reconcile resource name (provisioner) as label in the prometheus measurement

3. Does this change impact docs?

  • Yes, PR includes docs updates
  • Yes, issue opened: link to issue
  • No

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@netlify
Copy link

netlify bot commented Nov 22, 2021

✔️ Deploy Preview for karpenter-docs-prod canceled.

🔨 Explore the source changes: fd52ec2

🔍 Inspect the deploy log: https://app.netlify.com/sites/karpenter-docs-prod/deploys/619d1bddcd68c100070f0042

@felix-zhe-huang felix-zhe-huang requested review from ellistarn and removed request for ellistarn November 22, 2021 16:48
@@ -75,7 +75,7 @@ func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reco
func (c *Controller) Register(_ context.Context, m manager.Manager) error {
return controllerruntime.
NewControllerManagedBy(m).
Named(controllerName).
Named(strings.ToLower(controllerName)).
Copy link
Contributor

Choose a reason for hiding this comment

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

consider just making the variable lower, rather than lowering it in both places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -59,7 +59,7 @@ type Controller struct {

// Reconcile executes a reallocation control loop for the resource
func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
ctx = logging.WithLogger(ctx, logging.FromContext(ctx).Named("Node"))
ctx = logging.WithLogger(ctx, logging.FromContext(ctx).Named("node").With("node", req.String()))
Copy link
Contributor

Choose a reason for hiding this comment

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

With this change, I think we can change lines like 105 from

fmt.Errorf("patching node %s, %w", node.Name, err)

to

fmt.Errorf("patching node, %w", err)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -74,7 +75,7 @@ type Packing struct {
// It follows the First Fit Decreasing bin packing technique, reference-
// https://en.wikipedia.org/wiki/Bin_packing_problem#First_Fit_Decreasing_(FFD)
func (p *Packer) Pack(ctx context.Context, schedule *scheduling.Schedule, instances []cloudprovider.InstanceType) []*Packing {
defer metrics.Measure(packDuration)()
defer metrics.Measure(packDuration.WithLabelValues(ctx.Value("provisioner").(string)))()
Copy link
Contributor

Choose a reason for hiding this comment

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

consider making a helper function to avoid casting inline. Maybe something like:
inject.GetResource(ctx)
injection.WithResource(ctx)
There are some other helpers that you could move into this pattern like
injection.GetRestConfig(ctx) and injection.GetOptions

Check out the pattern defined by knative in /cmd/webhook/main.go injection.WithNamespaceScope.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. I created one under utility. There are a few similar helper functions under utility already. I learned from that pattern.

limitations under the License.
*/

package reconcilename
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we merge all of these into a single injection package? I really like the knative approach I linked. I think it'll cause this to read really nicely.

injection.WithFoo(ctx, foo)
injection.GetFoo(ctx)

type contextKey string

func Inject(ctx context.Context, contextkey string, resourcename string) context.Context {
return context.WithValue(ctx, contextKey(contextkey), resourcename)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally your callers wouldn't need to be aware of the string. The concept of a reconciled object name is general to every controller. There's a bit of a hiccup since some objects can have both names and namespaces, but for us provisioners and nodes are both un-namespaced concepts. I might name it something like

injection.WithResourceKey() // returns "name" or "namespace/name"

We could also go totally crazy and stuff in a pointer to the full object like

injection.WithObject(ctx, provisioner)
injection.GetObject(ctx).GetName()

I'm not sure how I feel about this. It gives every piece of the code access to the resource that's being reconciled, which is kind of nice. It may be a little bit of an abuse of the context mechanism. Happy to discuss.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree injecting the entire object seems to be abusing the mechanism. Injecting the type.NamespacedName will give us a good starting point. As you suggested, now we have a unified injection package that handles options, restconfig and the resource namespaced name. It looks much cleaner this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This injection package will also be the perfect place to implement a generic injection function that allows us to pass multiple labels down the call path (one for each node when there are multiple nodes in the defragmentation reconcile loop). It will be very useful later when we implement the Karpenter health metrics dashboard. In that case, there may be a need to let karpenter manage the injection key. We may pass the injection key through argument dynamically. Let's revisit this later when we come to the implementation details.

@@ -20,6 +20,7 @@ import (
"github.com/awslabs/karpenter/pkg/apis"
"github.com/awslabs/karpenter/pkg/cloudprovider"
"github.com/awslabs/karpenter/pkg/cloudprovider/registry"
karpenterinjection "github.com/awslabs/karpenter/pkg/utils/injection"
Copy link
Contributor

Choose a reason for hiding this comment

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

consider injection for karpenter and kinjection or knativeinjection for knative

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


type resourceKey struct{}

func WithResource(ctx context.Context, namespacedname types.NamespacedName) context.Context {
Copy link
Contributor

Choose a reason for hiding this comment

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

consider WithNamespacedName, GetNamespacedName

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


func GetResource(ctx context.Context) types.NamespacedName {
retval := ctx.Value(resourceKey{})
if retval == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this if check necessary compared to e.g. line 45

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. In fact there should be a nil check at line 45 as well. Or it will fail if GetOptions is called without calling WithOptions first. May need to beef up nil checking for all the places that call GetOptions.

ellistarn
ellistarn previously approved these changes Nov 23, 2021
Copy link
Contributor

@ellistarn ellistarn left a comment

Choose a reason for hiding this comment

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

Nice! Some optional comments

@@ -67,8 +67,8 @@ func main() {

// Set up logger and watch for changes to log level
ctx := LoggingContextOrDie(config, clientSet)
ctx = restconfig.Inject(ctx, config)
ctx = options.Inject(ctx, opts)
ctx = karpenterinjection.WithConfig(ctx, config)
Copy link
Contributor

Choose a reason for hiding this comment

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

same here re: injection, knativeinjection

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

@ellistarn ellistarn left a comment

Choose a reason for hiding this comment

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

Nicely done!

@ellistarn ellistarn merged commit 71ce076 into aws:main Nov 23, 2021
@felix-zhe-huang felix-zhe-huang deleted the issue#811 branch January 11, 2022 16:18
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.

Logging and Metrics improvements
2 participants