-
Notifications
You must be signed in to change notification settings - Fork 814
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
[kubernetes] Only use kube annotations for sd once per pod #2901
Conversation
e70e5b5
to
118db09
Compare
thanks @mikekap i'll review it shortly |
Hi @mikekap That's an issue I should have caught before. Our lack of integration testing for k8s is getting punishing. Your approach in this PR works, but I wonder if we shouldn't modify the annotation format instead of ignoring all but one container per pod. Running checks against several containers in the same pod should be an option. What do you think of changing annotation keys like so?
This feature is not documented yet so it shouldn't be a problem. |
We could definitely add support for monitoring all containers. It would probably be more intuitive to use the kubernetes container name rather than the image name though. One thing I'm not sure of - is the multi-container model more flexible? Currently, even though we only init checks for one of the containers, you can easily monitor the other containers in a pod by just changing the port (since all containers in a pod share the same ip). On a higher level, the kubernetes model is a bit shoehorned into the docker model here. The "unit" of kubernetes is a pod. Ideally we'd want to monitor pods no matter how many containers are running or not running in those pods. That is, if some container fails to start, or docker is down, the monitoring should still apply. That's a bit outside the scope of this PR to fix though. |
Using the kubernetes container name is fine since it's also quick to extract and guaranteed to exist. Go for it 👍 |
118db09
to
d86da7e
Compare
Done. I haven't tested this outside of unit tests yet since I would need to migrate my existing usage. |
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.
Thanks for the update @mikekap
I left one small comment in the code, looks good otherwise. I'll give it a try tomorrow.
@@ -223,6 +230,14 @@ def _get_additional_tags(self, container_inspect, *args): | |||
tags.append('pod_name:%s' % pod_metadata.get('name')) | |||
return tags | |||
|
|||
def _get_kube_container_name(self, c_id): | |||
pods = self.kubeutil.retrieve_pods_list().get('items', []) |
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 super excited about calling this for every container, can we maybe build the mapping c_id -> c_name
once before entering this loop https://github.com/mikekap/dd-agent/blob/4790dd8df8417900a62fb776f0e10fa3491fb749/utils/service_discovery/sd_docker_backend.py#L258 to reduce api calls? It should be fine since it's coming from the kubelet but with nodes running hundreds of containers if there's a hiccups we'll feel it.
Looks like this scheme is not going to work:
Let's use dots instead of slashes? |
Sounds good. I'm a bit swamped this week so I'll get around to making those changes on the weekend if you don't mind. Sorry for the delay |
@@ -26,7 +26,8 @@ | |||
INIT_CONFIGS = 'init_configs' | |||
INSTANCES = 'instances' | |||
KUBE_ANNOTATIONS = 'kube_annotations' | |||
KUBE_ANNOTATION_PREFIX = 'com.datadoghq.sd/' | |||
KUBE_CONTAINER_NAME = 'kube_container_name' | |||
KUBE_ANNOTATION_PREFIX = 'sd.datadoghq.com' |
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.
typo I guess, it's com.datadoghq.sd
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.
So I may have snuck this one in there. I changed the annotation to be service-discovery.datadoghq.com/checks.XXXX
. I've realized that kube annotations don't follow the "reverse dns" scheme at all and I didn't want to deviate from that. Let me know if you'd prefer to change it back to what you were suggesting.
…r the first container in a pod. Otherwise you could end up instantiating several instances of the same check when the pod contains more than one container. Also get rid of the trace_config madness
Particularly this goes from O(4-5N) calls to the kubernetes API to 1.
4790dd8
to
3ae3d38
Compare
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.
Hey @mikekap sorry for the delay here. I left 2 comments, once they're fixed we'll merge this. Don't worry about the conflicts I'll resolve them at merge time. It's about code I changed recently.
Thanks a bunch!
check_names = json.loads(kube_annotations[KUBE_ANNOTATION_PREFIX + CHECK_NAMES]) | ||
init_config_tpls = json.loads(kube_annotations[KUBE_ANNOTATION_PREFIX + INIT_CONFIGS]) | ||
instance_tpls = json.loads(kube_annotations[KUBE_ANNOTATION_PREFIX + INSTANCES]) | ||
prefix = '{}/{}.'.format(KUBE_ANNOTATION_PREFIX, kube_container_name) |
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.
We shouldn't require the container name here, it's already defined in the metadata.
prefix = '{}.'.format(KUBE_ANNOTATION_PREFIX)
is enough.
and the annotations would look like
annotations:
service-discovery.datadoghq.com.check_names: '["supervisord"]'
service-discovery.datadoghq.com.init_configs: '[{}]'
service-discovery.datadoghq.com.instances: '[{"name": "super", "host": "%%host%%"}]'
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.
HK - I think you might have forgotten some of the context from above. The main point of doing this PR is so we instantiate the checks above once per pod rather than once per container (since pods can have a few containers and you can't annotate containers directly in kubernetes; this was previously creating N instances of the checks above where N=containers in a pod).
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.
welp, yes indeed.
"""Get the list of checks applied to a container from the identifier_to_checks cache in the config store. | ||
Use the DATADOG_ID label or the image.""" | ||
inspect = state.inspect_container(c_id) |
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.
So we actually have another issue to take care of here, which is that if the container which the ID is from is already removed at this point, inspect will be None and we won't reload anything although there's a check that now fails on a missing container.
To be clear it's a pre-existing bug, not yours, but let's fix it here. In this situation we can't really decide which check to reload since the container isn't here anymore, so I think we should trigger a full reload.
Adding this block here does the job:
# if the container was removed we can't tell which check is concerned
# so we have to reload everything
if not inspect:
self.reload_check_configs = True
return
🎉 thanks again @mikekap |
Wow - thanks for getting this in @hkaj! I was just coming back from the break and getting ready to tackle merging this. Sorry you had to go through that. |
No worries, it was an easy conflict. I'll finish polishing a documentation update for it and we should be good to go! |
What does this PR do?
Currently the kube annotations are used for service discovery once per docker container. This causes "duplicate" checks to be instantiated if you have sidecar containers in your kubernetes pod.
This PR changes the annotations to annotate a specific container to monitor (by name).
Motivation
Seeing dup checks in my cluster :X.
Testing Guidelines
Unit tests. I will also be deploying this to my internal cluster and monitoring.
Additional Notes
The various tuples flying around between config & backend are getting a bit old. AFAICT there are 4 distinct sets:
[(source, (check_name, init_config, instance_tpl))]
_get_config_templates
->_get_check_configs
:[(source, (check_name, init_config, instance_tpl, variables))]
_get_check_configs
->get_configs
:[(source, (check_name, init_config, instance))]
It's getting a bit weird. I removed
trace_configs
as a variation because that was getting to be a bit too much. I think there was also a bug where some code didn't handle this case.