-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Replaced event queue based openshift sdn resource watches with shared informers #16766
Replaced event queue based openshift sdn resource watches with shared informers #16766
Conversation
f8c3c25
to
6a1a983
Compare
6a1a983
to
4add321
Compare
/retest |
@openshift/networking PTAL |
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.
Cool
Is this targeted for 3.7 or for after the branch?
@@ -66,17 +70,21 @@ func New(options configapi.NodeConfig, clusterDomain string, proxyConfig *compon | |||
|
|||
var sdnNode network.NodeInterface | |||
var sdnProxy network.ProxyInterface | |||
var internalNetworkInformers networkinformers.SharedInformerFactory |
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 could get rid of these temporary variables if you moved the creation of config
up here, and then in the "if IsOpenShiftNetworkPlugin" section, you could just set config.SDNNode
, config.SDNProxy
, and config.InternalNetworkInformers
@@ -29,6 +33,11 @@ func NewSDNInterfaces(options configapi.NodeConfig, networkClient networkclient. | |||
// SDN's hostport handling when run under CRI-O. | |||
enableHostports := !strings.Contains(runtimeEndpoint, "crio") | |||
|
|||
sdnInformers := sdncommon.SDNInformers{ |
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.
The SDNInformers type is useful to the SDN code itself for passing to RegisterSharedInformer, but I don't think you should use it from outside code like this; just pass the two informers separately.
networkClient := ctx.ClientBuilder.OpenshiftInternalNetworkClientOrDie(bootstrappolicy.InfraSDNControllerServiceAccountName) | ||
sdnInformers := sdncommon.SDNInformers{ | ||
KubeInformers: ctx.InternalKubeInformers, | ||
NetworkInformers: networkinformers.NewSharedInformerFactory(networkClient, networkapi.DefaultInformerResyncPeriod), |
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.
All the other OpenShift API groups already have an Informers field in ControllerContext. We should make NetworkInformers work the same way. (Add a field to ControllerContext and initialize it (and eventually call Start() on it) from pkg/cmd/server/start/.)
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 originally took this route. Adding a new field in existing informers struct in start/informers.go. All the informers are initialized in NewInformers() and passed to newCreateControllerContext() and ControllerContext runs all controllers(including SDN). But the problem is we don't want to run our network informers if the user is not using openshift-sdn, so I started sprinkling IsOpenShiftNetworkPlugin() in few places. Then I realized why NewInformers/ControllerContext() need to know about openshift network plugin? SDN RunController() is the starting point for openshift sdn and putting network informers there seems the right place.
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.
Hm... it's weird to treat pkg/networking/apis differently from all the other API groups, but then again, pkg/networking/apis is different from all the other API groups; it's only used by our network plugins, not by the OpenShift core or by third-party network plugins. So, OK, I guess this makes sense.
pkg/network/apis/network/types.go
Outdated
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
const ( | ||
ClusterNetworkDefault = "default" | ||
EgressNetworkPolicyMaxRules = 50 | ||
DefaultInformerResyncPeriod = 30 * time.Minute |
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.
pkg/network/apis/network/ is public; it's for things that external software might need to use. Stuff that is only used by other parts of origin goes in pkg/network/plugin.go. (They used to be all mixed up together but this got fixed recently along with the other reorgs.)
(OTOH it's not clear to me that we should be using our own default value here anyway, rather than picking up some shared origin-wide default from somewhere else...)
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.
OpenShift is using 10 mins as informer resync period and k8s is using 15 mins. These values seems aggressive to me. Resync is intended to help the case where you failed to process the first time due to some transient error and hoping that processing again after sometime will succeed. If the watch code is smart enough to recognize nothing has changed, frequent resync is okay but if we are unnecessarily updating iptables, etc. then I think we should resync less often. Moved 30 min resync period constant to pkg/network/plugin.go
pkg/network/master/subnets.go
Outdated
hs := obj.(*networkapi.HostSubnet) | ||
log.V(5).Infof("Watch %s event for HostSubnet %q", eventType, hs.Name) | ||
|
||
if _, ok := hs.Annotations[networkapi.AssignHostSubnetAnnotation]; ok { |
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.
flip this; just return from the function early if the annotation is not present. (likewise in handleDeleteSubnet)
pkg/network/node/egressip.go
Outdated
oc *ovsController | ||
localIP string | ||
oc *ovsController | ||
informers common.SDNInformers | ||
|
||
networkClient networkclient.Interface |
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.
networkClient
is unused now; it's replaced by informers
. (And you should put informers
in the group where networkClient
is now, not the group with localIP
and oc
)
pkg/network/node/egressip.go
Outdated
@@ -57,10 +57,11 @@ type egressIPWatcher struct { | |||
testModeChan chan string | |||
} | |||
|
|||
func newEgressIPWatcher(localIP string, oc *ovsController) *egressIPWatcher { | |||
func newEgressIPWatcher(localIP string, oc *ovsController, informers common.SDNInformers) *egressIPWatcher { |
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.
don't pass informers here, pass it to Start(); that way you don't have to create a fake-not-actually-usable informers arg to pass here in egressip_test.go (because egressip_test.go never calls Start(), because it just calls the update funcs by hand)
4add321
to
bcc276c
Compare
bcc276c
to
c6c2dc9
Compare
@danwinship |
pkg/network/master/subnets.go
Outdated
hs := obj.(*networkapi.HostSubnet) | ||
log.V(5).Infof("Watch %s event for HostSubnet %q", watch.Deleted, hs.Name) | ||
|
||
if _, ok := hs.Annotations[networkapi.AssignHostSubnetAnnotation]; !ok { |
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.
Earlier we were releasing subnet when networkapi.AssignHostSubnetAnnotation is not present. (Refer: https://github.com/openshift/origin/blob/master/pkg/network/master/subnets.go#L276)
I'm guessing that's a existing bug in the code or my understanding could be wrong.
@rajatchopra can you confirm?
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.
Not a bug. We do not want to release if the annotation is present (because that indicates a manually created hostsubnet). Otherwise we do want to release it. Who will release it otherwise?
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.
Discussed with @rajatchopra on IRC,
AssignHostSubnetAnnotation
is for F5 use case and there is no real node exists in the cluster.
HostSubnet is manually created with this special annotation to assign a subnet.
Since we don't get a node deletion event in this case, delete HostSubnet is not triggered.
When user manually deletes this created HostSubnet and if the special annotation is present, then we need to release the subnet (existing bug).
I will add some comments to make it clear.
cb5bf5f
to
60f072d
Compare
/retest |
/hold |
pkg/network/master/vnids.go
Outdated
origNetns := obj.(*networkapi.NetNamespace) | ||
log.V(5).Infof("Watch %s event for NetNamespace %q", eventType, origNetns.Name) | ||
|
||
// Informer cache should not be mutated, so get a copy of the object |
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's updateVNID that mutates it, so I feel like we should do the copy there
pkg/network/master/vnids.go
Outdated
return | ||
} | ||
netns, ok := objCopy.(*networkapi.NetNamespace) | ||
if !ok { |
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.
Just assume that it's the right type. (We don't double-check the typecast at the start of the function, so there's no reason to double-check this one either.)
60f072d
to
7bc3558
Compare
…nd proxy. - Callers of SDN master and proxy need to check if the network plugin is openshift specific before calling any openshift SDN methods. For sdn master, SDNControllerConfig.RunController() and for sdn proxy, NetworkConfig.New() already does these checks.
c39f8bf
to
99cb946
Compare
@smarterclayton @danwinship @knobunc Currently we don't need to sync the informers because in case of dependency we are not looking at the other resource's shared informer cache, we make a direct API call. For example, sdn master gets node event, it will make an API call to check existence of corresponding hostsubnet and then updates or creates hostsubnet as needed. Yes, we could have used Hostsubnet shared informer if the cache is populated. For this, we need to maintain additional queue for each watching resource and start consuming items on the queue only after the caches are populated. This needs decent amount of changes as we watch for several resources in SDN. I prefer this to be handled in a separate pr, created trello card: https://trello.com/c/Uifetuz3 |
@pravisankar: The following tests failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
/retest |
You should always wait for sync (see my last comment) for every resource, no exceptions. It's possible some controllers don't need it, but there is no reason not to wait. |
Just waiting for informer sync after informer start will not help because informer start launches informer queue asynchronously and all the registered event handlers will start receiving the items. In this case, informer sync will be in the main thread which slows the (1) Openshift master/node main thread calls SDN mater/node initalization.
(5) Main thread is done with SDN and runs other stuff This is what was proposed in https://trello.com/c/Uifetuz3 . Current PR only replaces existing event queue which had few issues but doesn't wait for informer sync before processing. Note that older event queue implementation doesn't handle synchronization as well. And the reason for not having issues before is that one event-queue/informer did not depend on another event-queue/informer. So the question is whether to merge this pr together with https://trello.com/c/Uifetuz3 or can we do incremental push by merging this pr in 3.9 release and handling informer sync in another pr? @smarterclayton what do you prefer? |
Ok, you don't have workers yet. If you're only making live calls then we can live with this for now. |
/lgtm |
Need blessings from pkg/cmd/OWNERS |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: danwinship, knobunc, pravisankar, smarterclayton The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these OWNERS Files:
You can indicate your approval by writing |
/hold |
Yeah, this isn't a feature. It's fixing a bug |
Automatic merge from submit-queue (batch tested with PRs 18075, 17725, 16766, 18070, 18113). |
This was introduced in openshift#16766
This was introduced in openshift#16766
This was introduced in openshift/origin#16766
event queue which has some issues:
openshift/origin: Issue Panic observed in master: *errors.errorString: invalid state transition: Added -> Added #16080
openshift/origin: Issue *errors.errorString: invalid state transition: Updated -> Sync #13879
(HostSubnet/NetNamespace/EgressNetworkPolicy) and each watch consumes a go routine.
Shared informer reduces bandwidth/cpu/memory footprint by running only one go
routine per resource watch and allows multiple subscribers.