Add PodMonitor or ServiceMonitor outside of kube-prometheus-stack helm values - prometheus-operator

Using kube-prometheus-stack helm chart, version 35.2.0. So far, I add my custom PrometheusRules, PodMonitor and ServiceMonitor via helm custom values.
helm install my-kubpromstack prometheus-community/kube-prometheus-stack -n monitoring \
-f my-AlertRules.yaml \
-f my-PodMonitor.yaml
Or in case of changes in the PrometheusRules or PodMonitor, I use helm upgrade. The custom values are defined based on kube-prometheus-stack/values.yaml. Where I define prometheus.additionalPodMonitors and additionalPrometheusRulesMap in separate YAML files
helm upgrade my-kubpromstack -n monitoring \
--reuse-values \
-f my-AlertRules.yaml \
-f my-PodMonitor.yaml
QUESTION: how to make the Prometheus server from kube-prometheus-stack aware of rules, podmonitor, servicemonitor created outside of the helm values?
For example, the PodMonitor definition below is NOT picked-up by Prometheus (ie doesn't appear in the targets in Prometheus UI).
kubectl apply -f - << EOF
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: cluster-operator-metrics
labels:
app: strimzi
spec:
selector:
matchLabels:
strimzi.io/kind: cluster-operator
namespaceSelector:
matchNames:
- my-strimzi
podMetricsEndpoints:
- path: /metrics
port: http
EOF
The pod to monitor has a label strimzi.io/kind: cluster-operator and is located in my-strimzi namespace. I would expect the podmonitor above to be recognized by Prometheus automatically. Because the default podMonitorSelector: {} in kube-prometheus-stack/values.yaml has a comment that says:
## PodMonitors to be selected for target discovery.
## If {}, select all PodMonitors
EDIT: Looks like this question is useful to quite some people. The simplest solution is what Aris Chow suggested below. Set the custom helm values as below:
prometheus:
prometheusSpec:
podMonitorSelectorNilUsesHelmValues: false
probeSelectorNilUsesHelmValues: false
ruleSelectorNilUsesHelmValues: false
serviceMonitorSelectorNilUsesHelmValues: false

If you define prometheus.prometheusSpec.podMonitorSelectorNilUseHelmValues as false (in values.yaml, it is set to true by default) you could achieve your goal. As the value is true, it would just try to set a release label for matching PodMonitor, which your own definition does not include.
Or, you could leave it as true and set prometheus.prometheusSpec.podMonitorSelector as:
matchLabels:
prometheus: "true"
And add label prometheus: "true" in your podmonitor.yaml.
Click here to check the code if you are intereseted in details.
Please note that the chart version in this link is 15.4.4, you should change to the version you are using just in case there are any update.

Add this answer to address the question asked by a commenter in OP. Here is the PodMonitor definition I used + the custom helm values for the kube-prometheus-stack helm chart to have Prometheus operator discover the pod as target. The main point is the label app: strimzi. The comment section show how to configure helm to make Prometheus recognize that label.
#--------------------------------------------------------------------------------
# The kube-prometheus-stack helm chart must have the value
# podMonitorSelector.matchLabels set to match the label `app: strimzi` of the PodMonitors below.
# Otherwise Prometheus operator will not scrape the metrics of the corresponding pods
#
# prometheus:
# prometheusSpec:
# podMonitorSelector:
# matchLabels:
# app: strimzi
#--------------------------------------------------------------------------------
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: strimzi-cluster-operator-metrics
labels:
app: strimzi
spec:
selector:
matchLabels:
strimzi.io/kind: cluster-operator
namespaceSelector:
matchNames:
- strimzi
podMetricsEndpoints:
- path: /metrics
port: http

Related

Automatically update Kubernetes resource if another resource is created

I currently have the following challenge: We are using two ingress controllers in our cloud Kubernetes cluster, a custom Nginx ingress controller, and a cloud ingress controller on the load balancer.
The challenge now is when creating an Nginx-ingress element, that an automatic update on the cloud ingress controller ingress element is triggered. The ingress controller of the cloud provider does not support host specifications like *.example.com, so we have to work around it.
Cloud Provider Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cloudprovider-listener-https
namespace: nginx-ingress-controller
annotations:
kubernetes.io/elb.id: "<loadbalancerid>"
kubernetes.io/elb.port: "<loadbalancerport>"
kubernetes.io/ingress.class: "<cloudprovider>"
spec:
rules:
- host: "customer1.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ingress-nginx-controller
port:
number: 80
property:
ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH
- host: "customer2.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ingress-nginx-controller
port:
number: 80
property:
ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH
- host: "customer3.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ingress-nginx-controller
port:
number: 80
property:
ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH
tls:
- hosts:
- "*.example.com"
secretName: wildcard-cert
Nginx Ingress Config for each Customer
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: <namespace>
annotations:
kubernetes.io/ingress.class: nginx
# ... several nginx-ingress annotations
spec:
rules:
- host: "customer<x>.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: <port>
property:
ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH
Currently, the cloud ingress resource is created dynamically by the helm, but triggered externally and the paths are queried by script "kubectl get ing -A" + magic.
Is there a way to monitor Nginx ingresses internally in the cluster and automatically trigger an update of the cloud ingress for new ingress elements?
Or am I going about this completely wrong?
Hope you guys can help.
I'll describe a solution that requires running kubectl commands from within the Pod.
In short, you can use a script to continuously monitor the .metadata.generation value of the ingress resource, and when this value is increased, you can run your "kubectl get ing -A + magic".
The .metadata.generation value is incremented for all changes, except for changes to .metadata or .status.
Below, I will provide a detailed step-by-step explanation.
To check the generation of the web ingress resource, we can use:
### kubectl get ingress <INGRESS_RESOURCE_NAME> -n default --template '{{.metadata.generation}}'
$ kubectl get ingress web -n default --template '{{.metadata.generation}}'
1
To constantly monitor this value, we can create a Bash script:
NOTE: This script compares generation to newGeneration in a while loop to detect any .metadata.generation changes.
$ cat check-script.sh
#!/bin/bash
generation="$(kubectl get ingress web -n default --template '{{.metadata.generation}}')"
while true; do
newGeneration="$(kubectl get ingress web -n default --template '{{.metadata.generation}}')"
if [[ "${generation}" != "${newGeneration}" ]]; then
echo "Modified !!!" # Here you can additionally add "magic"
generation=${newGeneration}
fi
We want to run this script from inside the Pod, so I converted it to ConfigMap which will allow us to mount this script in a volume (see: Using ConfigMaps as files from a Pod):
$ cat check-script-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: check-script
data:
checkScript.sh: |
#!/bin/bash
generation="$(kubectl get ingress web -n default --template '{{.metadata.generation}}')"
while true; do
newGeneration="$(kubectl get ingress web -n default --template '{{.metadata.generation}}')"
if [[ "${generation}" != "${newGeneration}" ]]; then
echo "Modified !!!"
generation=${newGeneration}
fi
done
$ kubectl apply -f check-script-configmap.yml
configmap/check-script created
For security reasons, I've created a separate ingress-checker ServiceAccount with the view Role assigned and our Pod will run under this ServiceAccount:
NOTE: I've created a Deployment instead of a single Pod.
$ cat all-in-one.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: ingress-checker
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ingress-checker-binding
subjects:
- kind: ServiceAccount
name: ingress-checker
namespace: default
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ingress-checker
name: ingress-checker
spec:
selector:
matchLabels:
app: ingress-checker
template:
metadata:
labels:
app: ingress-checker
spec:
serviceAccountName: ingress-checker
volumes:
- name: check-script
configMap:
name: check-script
containers:
- image: bitnami/kubectl
name: test
command: ["bash", "/mnt/checkScript.sh"]
volumeMounts:
- name: check-script
mountPath: /mnt
After applying the above manifest, the ingress-checker Deployment was created and started monitoring the web ingress resource:
$ kubectl apply -f all-in-one.yaml
serviceaccount/ingress-checker created
clusterrolebinding.rbac.authorization.k8s.io/ingress-checker-binding created
deployment.apps/ingress-checker created
$ kubectl get deploy,pod | grep ingress-checker
deployment.apps/ingress-checker 1/1 1
pod/ingress-checker-6846474c9-rhszh 1/1 Running
Finally, we can check how it works.
From one terminal window I checked the ingress-checker logs with the $ kubectl logs -f deploy/ingress-checker command.
From another terminal window, I modified the web ingress resource.
Second terminal window:
$ kubectl edit ing web
ingress.networking.k8s.io/web edited
First terminal window:
$ kubectl logs -f deploy/ingress-checker
Modified !!!
As you can see, it works as expected. We have the ingress-checker Deployment that monitors changes to the web ingress resource.

Can we NOT set nginx.ingress.kubernetes.io/client-body-timeout: '120' using ingress annotations?

The default value of said annotation is 60 sec; I am looking to change its value to 120 sec. I added this as an annotation in ingress resource file but it doesn't seem to be working.
Since my request body is quite big, I am getting 408 from ingress http server immediately after 60 sec only;
Where else I can define this annotation if it is not allowed in ingress file itself?
The following page doesn't mention this annotation; Is it because it is not meant to be added as an annotation?
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations
Ingress resource snippet:
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /my-app
nginx.ingress.kubernetes.io/client-header-buffer-size: "1M"
nginx.ingress.kubernetes.io/client-header-timeout: "60"
nginx.ingress.kubernetes.io/client-body-buffer-size: "1M"
nginx.ingress.kubernetes.io/client-body-timeout: "120"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header custom-header $1;
spec:
rules:
- http:
paths:
- path: /(UK)/my-app/(.*)$
backend:
serviceName: test
servicePort: 80
To summarize our conversation in comments:
There are two Nginx ingress controllers;
One nginx controller is maintained by kubernetes community and the other one by nginx (the company behind nginx product). Here is the github repo for Nginx ingress controller and and here for kubernetes nginx controller.
Nginx controller provided by kubernetes doesn't allow setting client-body-timeout with annotation. Here is a link to github repo with annotations code. This means that what you are left with is either
setting this parameter globally, or
opening feature request on github and waiting for someone to implement it.
client-body-timeout parameter can only be set through global config (as specified in documentation).
Adding to HelloWorlds answer, if someone is looking to provide this annotation globally with Kubernetes version of Ingress then following steps could be followed:
Check in which namespace ingress pod is running. Mostly the namespace name will be something like -ingress-some-string-.
$ kubectl get ns
Lets say the namespace is: 'ingress-nginx'
Now that namespace is known, check pods inside that namespace.
$ kubectl get pods -n ingress-nginx
Lets say you get a pod something like: 'ingress-nginx-controller-abcdefg'
Check the configmap this pod is using the following command:
$ kubectl get pod ingress-nginx-controller-abcdefg -n ingress-nginx -o yaml | grep configmap
You will get an output something like: --configmap=${POD_NAMESPACE}/nginx-configuration
Now, you have to create a config map with above name with required and supported configurations by Kubernetes Ingress.
$ cat global-configmap.yaml
apiVersion: v1
kind: ConfigMap
meta:
name: nginx-configuration
namespace: ingress-nginx
data:
client-body-timeout: "120" # default value is 60 seconds
Now, apply this config map yaml.
$ kubectl apply -f global-configmap.yaml

Nginx ingress controller not giving metrics for prometheus

I am trying to deploy an nginx ingress controller which can be monitored using prometheus however I am running into an issue that it seems no metrics pod(s) is being created like most posts and docs I have found online show.
I'm using helm to deploy the ingress controller and using a CLI arguement to enable metrics.
helm install ingress stable/nginx-ingress --set controller.metrics.enabled=true
Here is my ingress file
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
# add an annotation indicating the issuer to use.
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-dev"
# needed to allow the front end to talk to the back end
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.domain.com"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"
# needed for monitoring
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
name: dev-ingress
namespace: development
spec:
rules:
- host: api.<domain>.com
http:
paths:
- backend:
serviceName: api
servicePort: 8090
path: /
tls: # < placing a host in the TLS config will indicate a certificate should be created
- hosts:
- api.<domai>.com
secretName: dev-ingress-cert # < cert-manager will store the created certificate in this secre
In case this makes a difference I am using the prometheus operator helm chart with the below command.
helm install monitoring stable/prometheus-operator --namespace=monitoring
All namespaces exist already so that shouldn't be an issue, as for the development vs monitoring name spaces I saw in many places this was acceptable so I went with it to make things easier to figure out what is happening.
I would follow this guide to setup monitoring for Nginx ingress controller. I believe what you are missing is a prometheus.yaml which defines scrape config for the Nginx ingress controller and RBAC for prometheus to be able to scrape the Nginx ingress controller metrics.
Edit: Annotate nginx ingress controller pods
kubectl annotate pods nginx-ingress-controller-pod prometheus.io/scrape=true -n ingress-nginx --overwrite
kubectl annotate pods nginx-ingress-controller-pod prometheus.io/port=10254 -n ingress-nginx --overwrite
I was not using helm but manifests and the method POD annotations to install the Prometheus. I follow the official doc but could not see the metric either.
I believe the Deployment manifest has some issues. The annotation shouldn't be put on the Deployment level but on the pod level
apiVersion: v1
kind: Deployment
..
spec:
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
label:
...
ports:
- name: prometheus
containerPort: 10254
..
Also, I've confirmed the metric for Nginx is enabled by default when using manifest deployment. No extra steps are needed for this.

"How to fix 'Error: must either provide a name or specify --generate-name' in Helm"

How to fix Error: must either provide a name or specify --generate-name in Helm
Created sample helm chart name as mychart and written the deployment.yaml, service.yaml, ingress.yaml with nginx service. After that running the command like $ helm install mychart
service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- name: main
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
deployment.yaml
apiVersion: extensions/v1beta2
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.13
ports:
containerPort: 80
ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx
annotations:
http.port: "443"
spec:
backend:
serviceName: nginx
servicePort: 80
Expected output:
.....
status: DEPLOYED
just to add --generate-name at the end of helm command
According to the helm documentation for v3.x
helm install --help
Usage:
helm install [NAME] [CHART] [flags]
you want to use:
helm install "your release name" chart
For example:
# helm repo add stable https://kubernetes-charts.storage.googleapis.com/
# helm install wordpress-helm-testing stable/wordpress
NAME: wordpress-helm-testing
LAST DEPLOYED: 2019-10-07 15:56:21.205156 -0700 PDT m=+1.763748029
NAMESPACE: default
STATUS: deployed
NOTES:
1. Get the WordPress URL:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch the status with: 'kubectl get svc --namespace default -w wordpress-helm-testing'
export SERVICE_IP=$(kubectl get svc --namespace default wordpress-helm-testing --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
echo "WordPress URL: http://$SERVICE_IP/"
echo "WordPress Admin URL: http://$SERVICE_IP/admin"
2. Login with the following credentials to see your blog
echo Username: user
echo Password: $(kubectl get secret --namespace default wordpress-helm-testing -o jsonpath="{.data.wordpress-password}" | base64 --decode)
#helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART
wordpress-helm-testing default 1 2019-10-07 15:56:21.205156 -0700 PDT deployed wordpress-7.3.9
This is a better operational approach since it eliminates randomness in your release names. You might want to use something like a user name or anything that makes it unique and adds meaning to the release other than the GUID the --generate-name option will give you.
In helm v3 you can use either:
helm install [NAME] [CHART]
or:
helm install [CHART] --generate-name
Examples:
helm install reloader stakater/reloader
helm install stakater/reloader --generate-name
From the help manual:
helm install --help
Usage:
helm install [NAME] [CHART] [flags]
Flags:
-g, --generate-name generate the name (and omit the NAME parameter)
Assuming the chart is in the current directory:
helm install some-name .
Output:
NAME: some-name
LAST DEPLOYED: Sun Jan 5 21:03:25 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
The best/easiest way to fix this would be to append "--generate-name" in the command used to install helm chart.
Add release name
helm install test --dry-run --debug .\mychart\
test is the release name.

Kubernetes persistent storage causing files to disappear inside container

Trying to create a Drupal container on Kubernetes with the apache drupal image.
When a persistent volume is mounted at /var/www/html and inspecting the Drupal container with docker exec -it <drupal-container-name> bash there are no files visible. Thus no files can be served.
Workflow
1 - Create google compute disk
gcloud compute disks create --size=20GB --zone=us-central1-c drupal-1
2 - Register the newly created google compute disk to the kubernetes cluster instance
kubectl create -f gce-volumes.yaml
3 - Create Drupal pod
kubectl create -f drupal-deployment.yaml
The definition files are inspired from the wordpress example, my drupal-deployment.yaml:
apiVersion: v1
kind: Service
metadata:
name: drupal
labels:
app: drupal
spec:
ports:
- port: 80
selector:
app: drupal
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dp-pv-claim
labels:
app: drupal
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: drupal
labels:
app: drupal
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: drupal
tier: frontend
spec:
containers:
- name: drupal
image: drupal:8.2.3-apache
ports:
- name: drupal
containerPort: 80
volumeMounts:
- name: drupal-persistent-storage
mountPath: /var/www/html
volumes:
- name: drupal-persistent-storage
persistentVolumeClaim:
claimName: dp-pv-claim
And the gce-volumes.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: drupal-pv-1
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
gcePersistentDisk:
pdName: drupal-1
fsType: ext4
What's causing the files to disappear? How can I successfully persist the Drupal installation files at /var/www/html inside the container?
You are mounting the persistentStorage at /var/www/html, so if you had files at that location in your base image, the folder is replaced by the mount, and the files from the base image are no longer available.
If your content will be dynamic and you want to save those files in the persistentStorage, this is the way to do it, however you will need to populate the persistentStorage initially.
One solution would be to have the files in a different folder in the base image, and copy them over when you run the Pod, however this will happen every time you start the Pod, and may overwrite your changes, unless your copy script checks first if the folder is empty.
Another option is to have a Job do this only once (before you run your Drupal Pod and mount this storage.)
Note:
GKEPersistentStorage only allows ReadWriteOnce (which can be mounted only on a single Pod) or ReadOnlyMany (i.e. readable only but can be mounted on many Pods) and it cannot be mounted with different modes at the same time, so in the end you can only run one of these Pods. (i.e. it will not scale)

Resources