What k8s bookmark solves?

Viewed 1155

What I'm Trying To Do

I'm trying to make a deployment and watch for k8s events until the deployment is ready using k8s node api (Watch): https://github.com/kubernetes-client/javascript/blob/master/examples/typescript/watch/watch-example.ts


My Questions

I have read this section: https://kubernetes.io/docs/reference/using-api/api-concepts/#watch-bookmarks over and over but I still can't understand, from the client perspective:

  1. What is this feature try to solve?
  2. When should I use it?
  3. Can I not use it and not miss events?
  4. How do I use it?
  5. What should I do when I receive bookmark event.
4 Answers

I'll attempt to answer your questions from the client's perspective:

What is this feature trying to solve? 

Its intended to reduce the load on kube-apiserver from clients that issue Watch requests for a resource type. Bookmarks are intended to let the client know that the server has sent the client all events up to the resourceVersion specified in the Bookmark event. This is to make sure that if the watch on the client side fails or the channel closes (after timeout), the client can resume the watch from resourceVersion last returned in the Bookmark event. It's more like a checkpoint event in the absence of real mutating events like Added, Modified, Deleted.

The client could already be caching the resourceVersion that is received as part of the regular Added, Modified, Deleted events, but consider a case where the resource you're watching has had no updates in the last 30 minutes and the resourceVersion you have cached is "100".  

In the absence of bookmark events, you will attempt to resume from "100", but the server could return a "410 Gone" indicating that it doesn't have that version or it is too old. In that case, the client will have to do a List to get the latest resourceVersion and initiate a watch from there or start the watch from latest by specifying no resourceVersion, which is more expensive for the server to process.

In the presence of bookmark events, the server might attempt to send you a Bookmark event with resourceVersion "150" indicating that you have processed up that version and in the event of a restart you can start processing from there onwards.   This contrived example in the documentation shows just that. You start your watcher at rv="10245", you process some events up to rv="10596" and then the server sends you a bookmark with rv="12746" so in the event of a restart you can start processing from there onwards.

GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245&allowWatchBookmarks=true
---

200 OK 
Transfer-Encoding: chunked 
Content-Type: application/json

{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...}
}
...
{
  "type": "BOOKMARK",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "12746"} }
}

When should I use it?

For efficient processing of change events on the resource your watching, it seems better to process Bookmarks than to not. There are no guarantees round the delivery of Bookmarks though - the server might not support it (if it is running an older version of Kubernetes), or it might not send them at all.

Can I not use and not miss events?

Yes, you could totally do that, but your likelihood of missing events decreases if you use Bookmarks. Also remember that it not only benefits the client but also the kube-apiserver, because it could return events from its local cache if it has the "fresh" resourceVerion you received in a Bookmark, in the absence of which it will have to do a consistent-read from etcd. I recommend this video to understand how Watch events work under the hood.

How do I use it?

Depending on which client library you're suing the usage might be different, but in a raw HTTP request you can specify this query param: allowWatchBookmarks=true

GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245&allowWatchBookmarks=true

If you're using client-go, there is a tool called Reflector, and this is how it requests bookmarks and this is how it processes it.

What should I do when I receive a Bookmark event?

Extract the resourceVersion from the Bookmark event and cache it for when you try to restart your watcher for any reason.

It’s about reconnect mechanism about List-Watch. 
When “watch” connection broken, it will do the reconnect and “resourceversion” is the start point which client need to tell the server(api-server), then the server will return all the event from the point.

But in general, client has filter, that’s means the “resourceverseion” in client side will not change if the event is not care by the client(for example kubelet only care resource in its node). And the etcd only keep 5 min event by default.

Think about one case, a kubelet has the “resourceversion=5” of resource pod, then the “resourceversion” increase in server side to 100 (since these change: 6 - 100 not care by the client, so the “resourceversion” in client side is still 5). Then connection broken, the reconnect start, client tell server its “resourceversion” is 5, but on server side the oldest “resourceversion” is 25, so the server have no idea, since it will cause event missing in client side, so it have to return error: too old version error. Then the client have to clean its cache and List-Watch again, it will cost much time.

With BookMark, the client will get notification(only latest “resourceversion”, no details for event) no matter it care or not when event raised, it make sure the “resourceversion” in client side is fresh. So it avoid to a new List-watch.

Seems Bookmark could reduce the List to 3% compare to no BookMark.

It allows resuming the watch efficiently because you know you've processed everything up to that point. The next time you start a watch you can use that as the starting version and not need to reply every object.

k8s will send to you two things, for example:

resourceVersion=100 (for ADDED event let's say)
resourceVersion=200 (for BOOKMARK event)

This means two things for you as the client. It's easier to understand if we break this into two pieces:

  • resourceVersion=100 for the resource that you are currently watching, is something that you care about. For example if you are watching Pods, then the 100 is a Pod ADDED event - thus you as a client will get that event.

  • The second part means that in the interval 101..200 there are no events that you as the client, care about. So in case of a restart, its safe to start from 200: you either consumed everything in that range (100 for example), or there are no events that you care about (101..200)

Related