# Devops Training Session 16: Setup Observability for Kubernetes ###### tags: `devops` `research` `tutorials` ![image](https://hackmd.io/_uploads/Bk6id4Nrye.png) ## Monitoring and Observability for System >[!Note] >For purpose monitoring and observability the Kubernetes Cluster, nowaday you have many options to handle these configuration, such as > >- Grafana >- Prometheus >- Loki >- Tempo >- Pyroscope >- Promtail Each Tools have different uses like - Detect and log the error which come from system, application with **Loki** - Debug memory leaking, performance issue with **Pyroscope** - Tracing the request of application with **Tempo** - Collect the information about *system, workload, CPU/Memory* of cluster in metrics with **Prometheus** - **Grafana** can be used for visual above information to dashboard, number. Also create the alert base on your rule, that will announce problems of system ## Install and setup MnO system ![image](https://hackmd.io/_uploads/ByrutE4Hyg.png) Most of components on MnO system are open-source project, they provide `helm-chart` that help you easily set up for your cluster. You can check about them on - Grafana + Prometheus: [Chart](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack) - Loki: [Chart](https://artifacthub.io/packages/helm/grafana/loki) - Promtail: [Chart](https://artifacthub.io/packages/helm/grafana/promtail) - Tempo: [Chart](https://artifacthub.io/packages/helm/grafana/tempo) - Pyroscope: [Chart](https://artifacthub.io/packages/helm/grafana/pyroscope) >[!Tip] >You have multiple ways to apply this chart into your cluster, in the good condition, you can use `helm` with `terraform` through `helm-release` provider that truly insane For example, you can configure `loki` like `loki/main.tf` ```terraform!= resource "kubernetes_secret" "loki_storage_account" { metadata { name = "storage-account-key" namespace = "monitoring" } data = { STORAGE_ACCOUNT_KEY = var.remote_state.loki_storage_account_key STORAGE_ACCOUNT_NAME = var.remote_state.loki_storage_account_name } } resource "helm_release" "loki" { name = "loki" repository = "https://grafana.github.io/helm-charts" chart = "loki" version = "2.13.3" create_namespace = true namespace = "monitoring" values = [ <<EOF extraArgs: config.expand-env: true extraEnvFrom: - secretRef: name: storage-account-key rbac: pspEnabled: false nodeSelector: pool: infrapool config: server: grpc_server_max_recv_msg_size: ${var.max_recv_sent_msg_size_server} grpc_server_max_send_msg_size: ${var.max_recv_sent_msg_size_server} schema_config: configs: - from: 2022-07-18 store: boltdb-shipper object_store: azure schema: v11 index: prefix: loki_index_ period: 24h querier: query_timeout: 5m engine: timeout: 8m storage_config: boltdb_shipper: shared_store: azure active_index_directory: /data/loki/boltdb-shipper-active cache_location: /data/loki/boltdb-shipper-cache cache_ttl: 24h filesystem: directory: /data/loki/chunks # To configure the long-term storage to be Azure Blob Storage azure: # Name of container under the storage account container_name: loki # Name of storage account account_name: $${STORAGE_ACCOUNT_NAME} # Storage account key as an env from secret account_key: $${STORAGE_ACCOUNT_KEY} request_timeout: 0 # Needed for Alerting: https://grafana.com/docs/loki/latest/rules/ # This is just a simple example, for more details: https://grafana.com/docs/loki/latest/configuration/#ruler_config ruler: storage: type: local local: directory: /tmp/rules rule_path: /tmp/scratch alertmanager_url: http://kube-prometheus-stack-alertmanager.monitoring.svc:9093 enable_alertmanager_v2: true ring: kvstore: store: inmemory enable_api: true alerting_groups: - name: example rules: - alert: HighThroughputLogStreams expr: sum by(container) (rate({job=~"loki-dev/.*"}[1m])) > 1000 for: 2m EOF ] depends_on = [ kubernetes_secret.loki_storage_account ] } ``` After apply terraform, your component of MnO will release to `namespace=monitoring` You can handle with the same idea with `Grafana`, `Prometheus`, `Promtail`, `Tempo` and `Pyroscope` `kube_stack_prometheus/main.tf` ```terraform!= resource "helm_release" "kube-prometheus-stack" { name = "kube-prometheus-stack" repository = "https://prometheus-community.github.io/helm-charts" chart = "kube-prometheus-stack" version = "41.1.0" create_namespace = true namespace = "monitoring" values = [ <<EOF # Since AKS is a managed Kubernetes service, it doesn’t allow you to see internal components such as the etcd store, the controller manager, the scheduler, etc. # See https://techcommunity.microsoft.com/t5/apps-on-azure-blog/using-azure-kubernetes-service-with-grafana-and-prometheus/ba-p/3020459 kubeEtcd: enabled: false kubeControllerManager: enabled: false kubeScheduler: enabled: false kubeProxy: enabled: false # State metrics kube-state-metrics: image: repository: registry.k8s.io/kube-state-metrics/kube-state-metrics tag: v2.10.0 nodeSelector: pool: infrapool # Prometheus operator prometheusOperator: nodeSelector: pool: infrapool # Alertmanager alertmanager: enabled: false # alertmanagerSpec: # nodeSelector: # pool: infrapool # storage: # volumeClaimTemplate: # spec: # accessModes: ["ReadWriteOnce"] # resources: # requests: # storage: ${var.alertmanager_storage_size} # Prometheus prometheus: prometheusSpec: nodeSelector: pool: infrapool storageSpec: volumeClaimTemplate: spec: # Use Azure Disk or Azure files (NFS) for Prometheus storageClassName: default accessModes: ["ReadWriteOnce"] resources: requests: storage: ${var.prometheus_storage_size} additionalScrapeConfigs: - job_name: 'mongodb-exporter' static_configs: - targets: ['mongo-agent-prometheus-mongodb-exporter.monitoring.svc:9216'] - job_name: 'nginx-ingress-metrics' static_configs: - targets: ['ingress-nginx-controller-metrics.nginx-ingress.svc:10254'] - job_name: 'rabbitmq-exporter' static_configs: - targets: ['rabbitmq.infrastructure.svc:15692'] - job_name: 'es-exporter' static_configs: - targets: ['es-agent-prometheus-elasticsearch-exporter.monitoring.svc:9108'] - job_name: 'redis-exporter' static_configs: - targets: ['redis-agent-prometheus-redis-exporter.monitoring.svc:9121'] enableFeatures: - remote-write-receiver # Grafana grafana: image: tag: "10.0.0" adminPassword: ${random_string.grafana_admin_password.result} rbac: pspEnabled: false persistence: enabled: true size: ${var.grafana_storage_size} nodeSelector: pool: infrapool ingress: enabled: true ingressClassName: nginx annotations: {} labels: {} path: / pathType: Prefix hosts: - ${var.monitoring_domain_config} extraPaths: [] tls: - secretName: https-certificate-monitoring hosts: - ${var.monitoring_domain_config} datasources: datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://kube-prometheus-stack-prometheus:9090/ access: proxy isDefault: false editable: true # - name: AlertManager # type: alertmanager # uid: alertmanager # url: http://kube-stack-prometheus-kube-alertmanager.monitoring:9093/ # access: proxy # isDefault: false # editable: true # jsonData: # implementation: prometheus # exemplarTraceIdDestinations: # - name: trace_id # datasourceUid: tempo # urlDisplayLabel: View in tempo - name: loki type: loki uid: loki_svc url: http://loki.monitoring.svc:3100 access: proxy isDefault: false editable: true jsonData: manageAlerts: true alertmanagerUid: alertmanager derivedFields: - datasourceUid: tempo matcherRegex: ((\w+){16}) name: trace_id url: '$${__value.raw}' - name: tempo type: tempo uid: tempo url: http://tempo.monitoring.svc:3100 access: proxy basicAuth: false isDefault: false editable: true jsonData: httpMethod: GET tracesToLogs: datasourceUid: "loki" filterByTraceID: true filterBySpanID: true lokiSearch: datasourceUid: 'loki' EOF ] } ``` `promtail/main.tf` ```terraform!= resource "helm_release" "promtail" { name = "promtail" repository = "https://grafana.github.io/helm-charts" chart = "promtail" version = "6.2.2" create_namespace = true namespace = "monitoring" values = [ <<EOF config: clients: - url: http://loki:3100/loki/api/v1/push snippets: pipelineStages: - cri: {} # - labeldrop: # - filename - match: selector: '{namespace="default"}' stages: - json: expressions: TimeStamp: TimeStamp level: Level MessageTemplate: MessageTemplate - labels: level: MessageTemplate: - timestamp: source: TimeStamp format: RFC3339Nano - match: selector: '{namespace="monitoring"}' stages: - regex: expression: 'level=(?P<level>\w+)' - labels: level: EOF ] } ``` `pyroscope/main.tf` ```terraform!= resource "helm_release" "pyroscope" { name = "pyroscope-agent" namespace = "monitoring" repository = "https://grafana.github.io/helm-charts" chart = "pyroscope" version = "1.4.0" values = [ <<EOF pyroscope: nodeSelector: pool: infrapool persistence: enabled: true agent: enabled: false EOF ] } ``` `tempo/main.tf` ```terraform!= resource "helm_release" "tempo" { name = "tempo" repository = "https://grafana.github.io/helm-charts" chart = "tempo" version = "1.6.1" create_namespace = true namespace = "monitoring" values = [ <<EOF nodeSelector: pool: infrapool tempo: memBallastSizeMbs: 1024 multitenancyEnabled: false reportingEnabled: true metricsGenerator: enabled: true remoteWriteUrl: "http://kube-prometheus-stack-prometheus.monitoring.svc:9090/api/v1/write" ingester: {} querier: {} queryFrontend: {} retention: 24h global_overrides: max_bytes_per_trace: ${var.max_bytes_per_trace} per_tenant_override_config: /conf/overrides.yaml overrides: {} server: http_listen_port: 3100 storage: trace: blocklist_poll_tenant_index_builders: 1 blocklist_poll_jitter_ms: 500 backend: azure azure: container_name: tempo storage_account_name: ${var.remote_state.tempo_storage_account_name} storage_account_key: ${var.remote_state.tempo_storage_account_key} wal: path: /var/tempo/wal receivers: jaeger: protocols: grpc: endpoint: 0.0.0.0:14250 thrift_binary: endpoint: 0.0.0.0:6832 thrift_compact: endpoint: 0.0.0.0:6831 thrift_http: endpoint: 0.0.0.0:14268 otlp: protocols: grpc: endpoint: "0.0.0.0:4317" http: endpoint: "0.0.0.0:4318" extraArgs: {} extraEnv: [] extraEnvFrom: [] extraVolumeMounts: [] config: | multitenancy_enabled: {{ .Values.tempo.multitenancyEnabled }} usage_report: reporting_enabled: {{ .Values.tempo.reportingEnabled }} compactor: compaction: block_retention: {{ .Values.tempo.retention }} distributor: receivers: {{- toYaml .Values.tempo.receivers | nindent 8 }} ingester: {{- toYaml .Values.tempo.ingester | nindent 6 }} server: {{- toYaml .Values.tempo.server | nindent 6 }} storage: {{- toYaml .Values.tempo.storage | nindent 6 }} querier: {{- toYaml .Values.tempo.querier | nindent 6 }} query_frontend: {{- toYaml .Values.tempo.queryFrontend | nindent 6 }} overrides: {{- toYaml .Values.tempo.global_overrides | nindent 6 }} {{- if .Values.tempo.metricsGenerator.enabled }} metrics_generator_processors: - 'service-graphs' - 'span-metrics' metrics_generator: storage: path: "/tmp/tempo" remote_write: - url: {{ .Values.tempo.metricsGenerator.remoteWriteUrl }} {{- end }} tempoQuery: enabled: true extraArgs: {} extraEnv: [] extraVolumeMounts: [] # persistence: # enabled: false # # storageClassName: local-path # accessModes: # - ReadWriteOnce # size: 10Gi EOF ] } ``` ## Let's talk about Grafana If you want dive into `Grafana`, you will face up lots of topic to learn, including - Dashboard - Alert - Authentication - Integrating with tons of data sources - ... >[!Note] >Therefore, It's making Grafana to become a good option when choose the MnO whole system for free. You can use `Grafana Cloud` for enterprise or managed from Azure/AWS Cloud, but it's up to you :+1: If you want to setup full dashboard for AKS, you can concern a bit with these dashboard to implementing inside your cluster 1. Ingress Dashboard (AKS use Ingress): *Usage: Used to look about quality and latency request, concurrency, …* 2. PostgreSQL Dashboard: *Cuz `Grafana` provide datasource postgres and gain permit to query performance metrics from `PostgreSQL` so you can leverage and create a couple dashboard for yourself. Explore at [Integration Performance Query for MySQL or PostgreSQL](https://hackmd.io/@XeusNguyen/ry_nbYBxh)* 3. MongoDB Dashboard: With `Prometheus` permit to use **exporter**, so you can install **exporter** to expose metric from `MongoDB` cluster and `Prometheus` can scrape and you visualise it into `Grafana`. Explore at [mongodb_exporter](https://github.com/percona/mongodb_exporter) 4. RabbitMQ Dashboard: Same with `MongoDB`, you can install exporter for `RabbitMQ`. Explore at [Monitoring with Prometheus and Grafana](https://www.rabbitmq.com/docs/prometheus) 5. Elasticsearch Dashboard: Same with `MongoDB`, `RabbitMQ`, you can install exporter for `Elasticsearch`. Explore at [elasticsearch_exporter](https://github.com/prometheus-community/elasticsearch_exporter) 6. Redis Dashboard: Can also install exporter for `Redis`. Explore at [redis_exporter](https://github.com/oliver006/redis_exporter) 7. ... If you want dive into `Alert` system with Grafana, don't forget to check it out my blog [Deploy your alert with Grafana by Terraform and some common error with K8s](https://hackmd.io/@XeusNguyen/BJfNdXFWp) ## Troubleshoot MnO System ### Can't not read data **Error** : Occur when your storage has problems with any components >[!Note] >All components in MnO system are already store data to azure-disk with name contain the service **Troubleshoot** : Check on dashboard AKS portal about component, Does component attach with azure-disk ? Does the azure-disk exist or not for service ? Can attach that for your service via value `helm-chart` ? ### Disconnection with prometheus ![image](https://hackmd.io/_uploads/S1mdbr4r1l.png) **Error** : Prometheus is restart or not running in currently **Troubleshoot** : Wait 30s for Prometheus restart and query dashboard again. If not, refer to `kubectl` command to **check status of Prometheus** ### Queries is not valid or datasource have problem ![image](https://hackmd.io/_uploads/SJAOZH4Bkx.png) **Error** : Occur when using wrong queries or **datasource** have not response. **Troubleshoot** : Check queries again, if not, please use `kubectl` to look up what happen with **datasource** you **want to search** *(Ex: Loki, Tempo,...)* ### Loki with long queries time out **Error** : Error when set time queries range to large or log storage in this time range out of range. **Troubleshoot** : Reduce size of time range and choose specifically to increase exact log you want to check. ### Cannot query metric from exporter **Error** : Occur when exporter is restart, failure or DNS scraping not working Troubleshoot: - Check status of exporter, if restart you need wait few second or delete `pod` for restart it if state is failure - Double check scrape configuration on prometheus `helm-chart` , DNS of exporter matching prefix `<name-of-service>.<namespace>.svc:<port-service>` ```bash {15-30} title="kube-stack-prometheus/main.tf" # Prometheus prometheus: prometheusSpec: nodeSelector: pool: infrapool storageSpec: volumeClaimTemplate: spec: # Use Azure Disk or Azure files (NFS) for Prometheus storageClassName: default accessModes: ["ReadWriteOnce"] resources: requests: storage: ${var.prometheus_storage_size} additionalScrapeConfigs: - job_name: 'mongodb-exporter' static_configs: - targets: ['mongo-agent-prometheus-mongodb-exporter.monitoring.svc:9216'] - job_name: 'nginx-ingress-metrics' static_configs: - targets: ['ingress-nginx-controller-metrics.nginx-ingress.svc:10254'] - job_name: 'rabbitmq-exporter' static_configs: - targets: ['rabbitmq.infrastructure.svc:15692'] - job_name: 'es-exporter' static_configs: - targets: ['es-agent-prometheus-elasticsearch-exporter.monitoring.svc:9108'] - job_name: 'redis-exporter' static_configs: - targets: ['redis-agent-prometheus-redis-exporter.monitoring.svc:9121'] enableFeatures: - remote-write-receiver ``` ### Alert announce `NULL` information **Error** : Occur when your alert is spam with over length of query loki **Troubleshoot** : - Turn on the `silent` mode for spam alert - Restart or removing when it occurs error with confusion exception and create again - If check the exception over length, `Silent` alert and ignore them ## Conclusion >[!Note] >On this topic, you will understand about >- Monitoring and Observability of System >- Install and setup Monitoring and Observability System >- Dashboard of Grafana >- Alert System by Grafana >- Trouble the Monitoring and Observability System ## Reference - [helm-chart-grafana-values](https://github.com/grafana/helm-charts/blob/main/charts/grafana/values.yaml) - [helm-chart-prometheus-values](https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack/values.yaml) - [latest-doc-grafana](https://grafana.com/docs/grafana/latest/) - [Grafana with error when using PersistentStorage](https://github.com/helm/charts/issues/1071) - [Feature Request: Support Optional Dashboard Reload](https://github.com/grafana/grafana/issues/1169) - [[stable/grafana] grafana-sc-datasources container in grafana pod restarts due to errors](https://github.com/helm/charts/issues/9136) - [Don't crash on exceptions](https://github.com/kiwigrid/k8s-sidecar/pull/6) - [k8s-sidecar](https://github.com/kiwigrid/k8s-sidecar)