-
Notifications
You must be signed in to change notification settings - Fork 107
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
recording webhook: Remove webhook state #1112
Conversation
fa6fc84
to
b3928c9
Compare
Codecov Report
@@ Coverage Diff @@
## main #1112 +/- ##
==========================================
- Coverage 50.68% 50.21% -0.47%
==========================================
Files 42 42
Lines 4735 4809 +74
==========================================
+ Hits 2400 2415 +15
- Misses 2259 2314 +55
- Partials 76 80 +4 |
@@ -37,7 +37,7 @@ import ( | |||
) | |||
|
|||
var ( | |||
replicas int32 = 3 | |||
replicas int32 = 1 |
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 brings a related question -- since we only run a single replica, but the webhook listens to all namespaces, wouldn't it become a bottleneck?
I think the most systematic solution would be to actually remove the replica tracking from the webhook and make it truly stateless. The replica tracking is used for two things (AFAICS):
- when recording with the hook, to use in the filename recorded into
- to discern between different container replicas
For both, I guess we could use a random number instead, but I was thinking if it was acceptable to deprecate support for the hook based recording and make merging of policies the default once we have it? Frankly, I don't see much reason in having separate policies for instances of individual containers, wouldn't you want to have a single policy instead? The hook based recording, if we didn't want to deprecate it, could even use random numbers instead.
Alternatively, we could have a single gRPC endpoint just to track the replicas, but that brings even more complexity.
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'd be up for removing support for the hook. It adds extra complexity and we do have other alternatives to offer. We really shouldn't have anything stateful in the webhook... ideally, if we need to keep track of something, we should have a separate system to track the stateful bits... e.g. we might want to start leveraging Redis and probing it from the webhook. Having a performant distributed cache we could query would help.
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.
Yes, I was considering just moving the sync.Map to the operator (controller) and have it fronted by a gRPC API to get the next replica number, but the simpler we can make the webhook the 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.
That's an option, but I'd just highly suggest using a dedicated service that's meant for caching, e.g. Redis. It's less code we'd have to maintain.
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.
+1 for removing the hook
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.
Great. I'll work on removing the hook and then I'll see if we can adjust the code to get away with the using e.g. random numbers for the individually recorded container replicas. Afterwards, we'll be able to go back to multiple webhook pods.
Please let me know if I should make that work part of this PR or a follow-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.
I think it can be part of this PR :)
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
@@ -352,6 +352,13 @@ spec: | |||
containers: | |||
- name: nginx | |||
image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21 | |||
ports: | |||
- containerPort: 8080 | |||
readinessProbe: |
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 might be something to recommend in our docs
b3928c9
to
7786f81
Compare
oops unit tests |
7786f81
to
42bc933
Compare
welp:
|
42bc933
to
a36685f
Compare
ah! Previously we were installing |
/test pull-security-profiles-operator-test-e2e |
…e actually ready If no readiness probe is used, then we might end up recording the container before it is truly ready and the policy might not contain all the calls we need.
Only one call to set the status was used to set both finalizers and the status, which doesn't work. Let's use a separate call for setting the status and a separate call for setting the finalizer.
Especially with multiple replicas, we've seen the webhooks error out due to a conflict. This is problematic because the webhooks have a hard-fail policy. Let's retry multiple times instead.
Adds a test for the finalizer.
…orded, use labels instead The recording webhook used to track container replicas in a sync.Map, but its usage was problematic because: - all replicas were deleted in case any of replicas with the same generatedName were used. This meant replica numbers used for recorded policies were being reused and not all containers would end up having a recorded policy - if a replica was removed, the profilerecording would lose the status and finalizers - using the replica tracking in the sync.Map means that the webhook is maintaining its state, which it shouldn't. While this patch doesn't remove the state completely it reduces its usage. Instead of the above, this patch maintains the finalizer and the status as a combination of examining the pod being admitted as well as the currently existing pods which are listed during admission. The replica number is then always increasing by one e.g. across scaleups or scaledowns of replicated pods. Finally, because the webhook is watching all namespaces, but the sync.Map was only storing plain names, let's also add namespace as a prefix for the syncMap. This allows to empty the sync.Map once no pods for a single recording exist, keeping the memory usage low over time.
Removing the "hook" option enables us to remove the state from the webhook as well as simplifies the list of the supported matrix of recordings.
Instead of keeping track of the replicas in the webhook, let's use a random nonce to distinguish between different container replicas. When the profile is recorded for a replicated container, we already know the pod name, so let's use the hash of the pod to append to the container. This would also make it easier to cross-reference the pods and the containers. In order to parse the recording annotations easily, we also switch from using dashes in the recording annotations to underscores. Because underscores can't be used in k8s object names, they make it easy to split the annotations along them and are allowed in annotations.
a36685f
to
81a5617
Compare
rebased to pick up the recent fixes for the flaky tests -- most of the recording tests are marked as flaky and I want to make sure they pass as well (they did locally, but I was typically running a subset only) |
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.
Awesome work, thank you so much!
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jhrozek, saschagrunert The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
ubuntu flaky e2e tests passed: |
hmm, in flatcar tests I still see:
going to look into it. But I don't think that flatcar tests anything that fedora and ubuntu wouldn't test.. |
/hold cancel |
What type of PR is this?
/kind bug
What this PR does / why we need it:
This PR contains a self-contained change from the branch where I work on
merging recorded policies. Since the changes are useful (IMO) on their own
and the branch has been getting quite big, I decided to submit them separately.
More information about the individual commits are below and also in the
commit messages.
If no readiness probe is used, then we might end up recording the
container before it is truly ready and the policy might not contain
all the calls we need.
Only one call to set the status was used to set both finalizers and the
status, which doesn't work. Let's use a separate call for setting the
status and a separate call for setting the finalizer.
Especially with multiple replicas, we've seen the webhooks error out due
to a conflict. This is problematic because the webhooks have a hard-fail
policy. Let's retry multiple times instead.
Adds a test for the finalizer.
The recording webhook used to track container replicas in a sync.Map,
but its usage was problematic because:
generatedName were used. This meant replica numbers used for
recorded policies were being reused and not all containers would
end up having a recorded policy
status and finalizers
is maintaining its state, which it shouldn't. While this patch
doesn't remove the state completely it reduces its usage.
Instead of the above, this patch maintains the finalizer and the status as a combination of examining the pod being admitted as well as the currently existing pods which are listed during admission. The replica number is then always increasing by one e.g. across scaleups or scaledowns of replicated pods. Finally, because the webhook is watching all namespaces, but the
sync.Map was only storing plain names, let's also add namespace as a prefix for the syncMap. This allows to empty the sync.Map once no pods for a single recording exist, keeping the memory usage low over time.
recording: Remove "hook" from the list of the supported recording types - Removing the "hook" option enables us to remove the state from the webhook as well as simplifies the list of the supported matrix of recordings
recording: Remove state from the webhook, use a random nonce instead
Instead of keeping track of the replicas in the webhook, let's use a random nonce to distinguish between different container replicas. When the profile is recorded for a replicated container, we already know the pod name, so let's use the hash of the pod to append to the container. This would also make it easier to cross-reference the pods and the containers. In order to parse the recording annotations easily, we also switch from using dashes in the recording annotations to underscores. Because underscores can't be used in k8s object names, they make it easy to split the annotations along them and are allowed in annotations.
Which issue(s) this PR fixes:
Fixes: #744
Does this PR have test?
yes
Special notes for your reviewer:
I'll point them out inline
Does this PR introduce a user-facing change?