-
Notifications
You must be signed in to change notification settings - Fork 107
GATEWAYS-4306: exporting metrics for conntrack per zone #137
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: master
Are you sure you want to change the base?
Conversation
Please provide a descriptive commit message. |
sure! for now updated the description |
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.
Pull Request Overview
This PR implements a new connection tracking monitoring system that leverages netlink to directly access kernel conntrack data. The system provides zone-based monitoring capabilities for granular network traffic analysis and DDoS detection.
- Adds a new
ConntrackService
that uses netlink to query kernel conntrack entries - Integrates the conntrack service into the main OVS client with proper lifecycle management
- Updates dependencies to support the new conntrack functionality
Reviewed Changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
File | Description |
---|---|
ovsnl/conntrack.go | New service implementing conntrack entry retrieval and conversion from kernel data |
ovsnl/client.go | Integration of ConntrackService into main client with initialization and cleanup |
go.mod | Dependency updates including ti-mo/conntrack library and Go version upgrade |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
ovsnl/conntrack.go
Outdated
// Start dump in goroutine | ||
go func() { | ||
defer close(flowChan) | ||
flows, err := s.client.Dump(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.
I am curious how well Dump scales, and whether you should be using DumpFilter or DumpExpect instead (or maybe even some new variant that just counts if needed)
How many entries did you scale to , in your test setup?
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 did a POC, did not test yet on heavy traffic. Will check on DumpFilter or DumpExpect as well. Let me get back to you
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.
You won't need heavy traffic. Just scale up to like a million or two Conntrack entries and see if it performs well.
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.
updated the code. Checked with 1 million conntrack entries. It is working. The code needs a lot of cleanup. Just a heads up.
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.
That's encouraging. While you are at it, could you try the max conntrack limit as well?
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.
Looking at the snapshot from your scaled run, it does indicate the irq plateau for the duration of the run. I am assuming there were no CPU lockup messages from the kernel during this run, correct?
Did you get a chance to optimize the frequency of metrics collection?
On another note, this collection should be controlled with a config knob and we should slow roll this carefully.
Also cc @jcooperdo for another pair of eyes.
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.
@do-msingh was working on the issue you caught from screenshot. It was not properly doing conntrack count refresh. It looks under control now. As of now I tested seeding conntracks to a specific droplet in a hyperviser. In this screenshot you will see only around 400K jump because for easy testing I kept the timeout 10 mins. But actually created 2.6 Million conntracks only. I will test scenarios like adding timeout for 1 hr, seeding conntracks to multiple droplets (10-20), and see how the system performs. Will keep posted here.
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.
While testing with 1 hr timeout with 2.6M conntracks created against 1 droplet in a single zone. There are some small discrepencies due to processing delay: ( For example we have missed 12k events to count while running sync for 2.6M conntracks )
I can try to fix it later as an improvement task
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.
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.
in the same way when I tested creating conntracks without my changes in openvswitch_exporter the graph looks like above
The build is failing due to go version in my local machine. In this repository we are using older version. When the code is signed off I will try to install the older version and push it. Kept it like this for now. |
ovsnl/conntrack.go
Outdated
|
||
// NewZoneMarkAggregator creates a new aggregator with its own listening connection. | ||
func NewZoneMarkAggregator(s *ConntrackService) (*ZoneMarkAggregator, error) { | ||
log.Printf("Creating new conntrack zone mark aggregator...") |
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.
Could you remove these logs?
ovsnl/conntrack.go
Outdated
return nil, fmt.Errorf("failed to create listening connection: %w", err) | ||
} | ||
|
||
log.Printf("Successfully created conntrack listening connection") |
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.
same as above
ovsnl/conntrack.go
Outdated
// Start dump in goroutine | ||
go func() { | ||
defer close(flowChan) | ||
flows, err := s.client.Dump(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.
This script would be nice to integrate with chef and maybe export some metrics using node exporter so we can build some dashboards around it. In your tests, could you run at scale for an extended period like a couple of hours and check average CPU util? Do you only see CPU spikes around the time metrics are collected? For how long? Also @jcooper had a suggestion to reduce the frequency of collecting the metrics, or maybe optimizing it to reduce load.
Lastly, can you check dmesg output as well at scale to make sure we are not missing anything?
ovsnl/conntrack.go
Outdated
} | ||
|
||
// ForceSync performs a manual sync (disabled for large tables) | ||
func (a *ZoneMarkAggregator) ForceSync() 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.
This method doesn't appear to be used, is it needed?
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.
removed it.
ovsnl/conntrack.go
Outdated
} | ||
|
||
// IsHealthy checks if the aggregator is in a healthy state | ||
func (a *ZoneMarkAggregator) IsHealthy() 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.
This method doesn't appear to be used, is it needed?
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.
removed it. missed cleaning it up. Added this for debugging purpose.
ovsnl/conntrack.go
Outdated
CPUs int | ||
} | ||
|
||
// ConntrackService manages the connection to the kernel's conntrack via Netlink. |
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.
How does ConntrackService
do what the comment suggests? Its only references I can find are no-op constructors/closers.
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.
refactored
ovsnl/conntrack.go
Outdated
// primary counts (zone -> mark -> count) | ||
mu sync.RWMutex | ||
counts map[uint16]map[uint32]int |
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.
Do we gain any benefit from mapping by zone->mark? Could we instead simplify this by mapping by zmKey
?
ovsnl/conntrack.go
Outdated
return out | ||
} | ||
|
||
// GetTotalCount returns the total counted entries (best-effort) |
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.
Is this used by anything? Would it return the same as nf_conntrack_count
?
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.
removed it. missed cleaning it up. Added this for debugging purpose.
Co-authored-by: jcooperdo <[email protected]>
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.
Pull Request Overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
ovsnl/conntrack.go
Outdated
deltas := a.destroyDeltas | ||
a.destroyDeltas = make(map[ZmKey]int) | ||
a.deltaMu.Unlock() |
Copilot
AI
Oct 10, 2025
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.
Similar race condition as in applyDeltasImmediately: the mutex is unlocked before the deltas map is fully processed. Other goroutines could start adding to the new destroyDeltas map while the old deltas are still being applied, potentially causing inconsistent state.
Copilot uses AI. Check for mistakes.
ovsnl/client.go
Outdated
if len(errs) > 0 { | ||
return fmt.Errorf("errors closing client: %v", errs) | ||
} |
Copilot
AI
Oct 10, 2025
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.
[nitpick] The error handling could be improved by using a more structured approach. Consider using errors.Join (Go 1.20+) or a similar pattern to properly combine multiple errors instead of formatting them into a single string.
Copilot uses AI. Check for mistakes.
Co-authored-by: Anit Gandhi <[email protected]> Co-authored-by: Copilot <[email protected]>
Co-authored-by: Anit Gandhi <[email protected]>
Co-authored-by: Anit Gandhi <[email protected]>
ovsnl/conntrack_linux.go
Outdated
|
||
go func() { | ||
a.initialSnapshotComplete = true | ||
a.initialSnapshotError = 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.
initialSnapshotError
seems like it's always nil
. it defaults to nil
and then is set again here, and then seemingly never used
same goes for initialSnapshotComplete
also, why are these set in a goroutine?
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.
my bad. those variables I used at the time of debugging issues under heavy load. I missed cleaning those up. I will clean it up in next commit.
ovsnl/conntrack_linux.go
Outdated
if atomic.LoadInt64(&a.eventCount)%100 == 0 { | ||
runtime.Gosched() | ||
} |
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.
what is this for?
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 was testing with 2.6M conntracks sent to a single vm and also was conducting a test when multiple vms are receiving heavy conntrack. My intention is to tell the scheduler to give up the current goroutine's time slice and let others run. Found out this can be used to improve concurrency and responsiveness in high-throughput or tight-loop scenarios. Doing it for every event will be inefficient so I wanted to have the if condition
ovsnl/conntrack_linux_test.go
Outdated
} | ||
|
||
// Clean up | ||
agg.Stop() |
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.
would recommend replacing this with t.Cleanup(agg.Stop)
up on line 58
ovsnl/conntrack_linux.go
Outdated
for i := 0; i < eventWorkerCount; i++ { | ||
a.wg.Add(1) | ||
go a.eventWorker(i) | ||
} | ||
|
||
a.wg.Add(1) | ||
go a.destroyFlusher() | ||
|
||
a.wg.Add(1) | ||
go a.startHealthMonitoring() |
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.
nowadays in Go 1.25+, you can use a.wg.Go
, without having to do a.wg.Add
+ defer a.wg.Done
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
ovsnl/conntrack_common.go
Outdated
// metrics / health | ||
eventCount int64 | ||
lastEventTime time.Time | ||
eventRate float64 |
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.
it looks like eventRate
gets protected by countsMu
. assuming that's intentional, it should be grouped up at the top like
countsMu sync.RWMutex
counts map[ZmKey]int // primary counts (zmKey -> count) - simplified flat mapping
eventRate float64
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
ovsnl/conntrack_common.go
Outdated
destroyDeltas map[ZmKey]int | ||
|
||
// metrics / health | ||
eventCount int64 |
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.
would recommend using atomic.Int64
as the type, and using the methods on that type
since go 1.19 that's been the more recommended pattern for atomics
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
Co-authored-by: Anit Gandhi <[email protected]>
This PR implements a new connection tracking monitoring system that leverages netlink to directly access kernel conntrack data. The implementation provides zone-based monitoring capabilities, allowing for more granular network traffic analysis