Ok, I found a metric kube_cronjob_created (which is here, so tell me if you have it or not). If you have it, you can create an unless expression which would say “give me the series from the left side unless there’s the same series on the right side”. So the idea is we know the left side contains cronjobs and services and the right side contains only cronjobs - the difference of sets will be services.
Left side:
label_replace(count(kube_pod_container_status_terminated_reason{reason='Error'} > 0) by (namespace, pod), "cronjob", "$1", "pod", "(.*)-.*-.*") - to the query you have created we’re adding cronjob label (theoretically we could use already present container but it doesn’t always have to be the same I guess). Also we named it cronjob because we can’t join on different labels - they have to have the same name.
Right side
count(kube_cronjob_created{}) by (cronjob)
Result query:
label_replace(count(kube_pod_container_status_terminated_reason{reason='Error'} > 0) by (namespace, pod), "cronjob", "$1", "pod", "(.*)-.*-.*") unless on(cronjob) count(kube_cronjob_created{}) by (cronjob)
In my mind (and from my tests) it should work, but I don’t have any services in error state. Hope that helps!
Edit: alternatively you can use kube_replicaset_created or kube_deployment_created to join on services (you’d have to check if there are only services, but I don’t think cronjob creates deployments / replicasets). Then you’d use and instead of unless (the rest is the same)