Skip to content

Quan sát

Stack monitoring chạy trong namespace nx-watcher. Staging cung cấp metrics và log cơ bản; production bổ sung distributed tracing với Tempo và OpenTelemetry Collector.

Tổng quan Kiến trúc

Staging

Chưa Triển khai

Stack observability cho staging (Prometheus, Grafana, Loki, Promtail) đã được lên kế hoạch nhưng chưa triển khai. Namespace nx-watcher tồn tại nhưng không có pod nào. Các manifest bên dưới mô tả kiến trúc mục tiêu.

Production

Staging vs Production

Thành phầnStagingProduction
Prometheus
Grafana
Loki + Promtail
TempoKhông
OTel CollectorKhông
Lên lịch nodePool default (không taint)Node monitoring chuyên dụng (dedicated=monitoring:NoSchedule)
AlertmanagerTùy chọn

Lên lịch Node

Staging

Pod monitoring chạy trên node default cùng với workload ứng dụng. Không cần taint hay toleration.

yaml
spec:
  nodeSelector:
    node.kubernetes.io/pool: default

Production

Pod monitoring chạy trên node chuyên dụng với taint dedicated=monitoring:NoSchedule. Tất cả workload monitoring phải bao gồm toleration.

Pod monitoring chạy trên node chuyên dụng với taint `dedicat...
yaml
spec:
  nodeSelector:
    node.kubernetes.io/pool: monitoring
  tolerations:
    - key: dedicated
      operator: Equal
      value: monitoring
      effect: NoSchedule

Prometheus

Thu thập metrics từ tất cả dịch vụ, Traefik, và các thành phần tầng dữ liệu.

Deployment

StatefulSet: nx-prometheus
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nx-prometheus
  namespace: nx-watcher
spec:
  serviceName: nx-prometheus
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: prometheus
  template:
    spec:
      serviceAccountName: prometheus
      nodeSelector:
        node.kubernetes.io/pool: default  # staging: default, production: monitoring
      # Chỉ Production:
      # tolerations:
      #   - key: dedicated
      #     operator: Equal
      #     value: monitoring
      #     effect: NoSchedule
      containers:
        - name: prometheus
          image: prom/prometheus:v3.2.1
          args:
            - --config.file=/etc/prometheus/prometheus.yml
            - --storage.tsdb.path=/prometheus
            - --storage.tsdb.retention.time=30d
            - --web.enable-lifecycle
          ports:
            - containerPort: 9090
          resources:
            requests:
              cpu: 200m
              memory: 512Mi
            limits:
              cpu: 500m
              memory: 1Gi
          volumeMounts:
            - name: prometheus-config
              mountPath: /etc/prometheus
            - name: prometheus-data
              mountPath: /prometheus
      volumes:
        - name: prometheus-config
          configMap:
            name: prometheus-config
  volumeClaimTemplates:
    - metadata:
        name: prometheus-data
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: csi-sc-vnpaycloud
        resources:
          requests:
            storage: 10Gi

Cấu hình Thu thập

ConfigMap: prometheus-config
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: nx-watcher
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      evaluation_interval: 15s

    scrape_configs:
      # Metrics Traefik (trong nx-internal)
      - job_name: traefik
        static_configs:
          - targets: ['nx-traefik.nx-internal.svc.cluster.local:8080']
        metrics_path: /metrics

      # Dịch vụ backend (tự động khám phá qua K8s service discovery)
      - job_name: nx-backend
        kubernetes_sd_configs:
          - role: pod
            namespaces:
              names: [nx-backend]
        relabel_configs:
          - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_component]
            regex: backend
            action: keep
          - source_labels: [__meta_kubernetes_pod_name]
            target_label: pod
          - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
            target_label: service

      # PostgreSQL exporter
      - job_name: postgresql
        static_configs:
          - targets: ['nx-pg-exporter.nx-persistent.svc.cluster.local:9187']

      # Redis exporter
      - job_name: redis
        static_configs:
          - targets: ['nx-redis-exporter.nx-broker.svc.cluster.local:9121']

      # Kafka (JMX exporter)
      - job_name: kafka
        static_configs:
          - targets:
              - 'nx-kafka-0.nx-kafka-headless.nx-broker.svc.cluster.local:9404'
              - 'nx-kafka-1.nx-kafka-headless.nx-broker.svc.cluster.local:9404'
              - 'nx-kafka-2.nx-kafka-headless.nx-broker.svc.cluster.local:9404'

    alerting:
      alertmanagers:
        - static_configs:
            - targets: ['nx-alertmanager.nx-watcher.svc.cluster.local:9093']

    rule_files:
      - /etc/prometheus/alerts/*.yml

Grafana

Deployment

Deployment: nx-grafana
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nx-grafana
  namespace: nx-watcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: grafana
  template:
    spec:
      nodeSelector:
        node.kubernetes.io/pool: default  # staging: default, production: monitoring
      # Chỉ Production:
      # tolerations:
      #   - key: dedicated
      #     operator: Equal
      #     value: monitoring
      #     effect: NoSchedule
      containers:
        - name: grafana
          image: grafana/grafana:11.5.2
          ports:
            - containerPort: 3000
          env:
            - name: GF_SECURITY_ADMIN_USER
              valueFrom:
                secretKeyRef:
                  name: grafana-secret
                  key: admin-user
            - name: GF_SECURITY_ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: grafana-secret
                  key: admin-password
            - name: GF_USERS_ALLOW_SIGN_UP
              value: "false"
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 300m
              memory: 512Mi
          volumeMounts:
            - name: grafana-data
              mountPath: /var/lib/grafana
            - name: grafana-datasources
              mountPath: /etc/grafana/provisioning/datasources
            - name: grafana-dashboards-config
              mountPath: /etc/grafana/provisioning/dashboards
            - name: grafana-dashboards
              mountPath: /var/lib/grafana/dashboards
      volumes:
        - name: grafana-data
          persistentVolumeClaim:
            claimName: grafana-data
        - name: grafana-datasources
          configMap:
            name: grafana-datasources
        - name: grafana-dashboards-config
          configMap:
            name: grafana-dashboards-config
        - name: grafana-dashboards
          configMap:
            name: grafana-dashboards

Nguồn Dữ liệu

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: nx-watcher
data:
  datasources.yaml: |
    apiVersion: 1
    datasources:
      - name: Prometheus
        type: prometheus
        access: proxy
        url: http://nx-prometheus.nx-watcher.svc.cluster.local:9090
        isDefault: true
      - name: Loki
        type: loki
        access: proxy
        url: http://nx-loki.nx-watcher.svc.cluster.local:3100
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: nx-watcher
data:
  datasources.yaml: |
    apiVersion: 1
    datasources:
      - name: Prometheus
        type: prometheus
        access: proxy
        url: http://nx-prometheus.nx-watcher.svc.cluster.local:9090
        isDefault: true
      - name: Loki
        type: loki
        access: proxy
        url: http://nx-loki.nx-watcher.svc.cluster.local:3100
      - name: Tempo
        type: tempo
        access: proxy
        url: http://nx-tempo.nx-watcher.svc.cluster.local:3200
        jsonData:
          tracesToLogsV2:
            datasourceUid: loki
            filterByTraceID: true
          tracesToMetrics:
            datasourceUid: prometheus
          serviceMap:
            datasourceUid: prometheus

Dashboard

DashboardMô tả
Tổng quan TraefikTần suất request, độ trễ (p50/p95/p99), tỉ lệ lỗi, kết nối đang hoạt động
Dịch vụ BackendCPU, bộ nhớ, số lượng request, thời gian phản hồi theo từng dịch vụ
PostgreSQLKết nối đang hoạt động, thời gian truy vấn, tỉ lệ cache hit, sử dụng đĩa
RedisSử dụng bộ nhớ, tỉ lệ hit/miss, client kết nối, tần suất lệnh
KafkaSức khỏe broker, partition thiếu bản sao, consumer lag, throughput
Tài nguyên NodeCPU, bộ nhớ, đĩa, mạng theo từng node
BullMQ JobĐộ sâu hàng đợi, tốc độ xử lý, job thất bại, số lần thử lại
Bản đồ Dịch vụ(Chỉ Production) Tự động tạo từ trace Tempo

Loki + Promtail

Tổng hợp log sử dụng Loki để lưu trữ và Promtail làm agent thu thập.

Deployment Loki

StatefulSet: nx-loki
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nx-loki
  namespace: nx-watcher
spec:
  serviceName: nx-loki
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: loki
  template:
    spec:
      nodeSelector:
        node.kubernetes.io/pool: default  # staging: default, production: monitoring
      # Chỉ Production:
      # tolerations:
      #   - key: dedicated
      #     operator: Equal
      #     value: monitoring
      #     effect: NoSchedule
      containers:
        - name: loki
          image: grafana/loki:3.4.2
          args:
            - -config.file=/etc/loki/loki.yaml
          ports:
            - containerPort: 3100
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 300m
              memory: 512Mi
          volumeMounts:
            - name: loki-config
              mountPath: /etc/loki
            - name: loki-data
              mountPath: /loki
      volumes:
        - name: loki-config
          configMap:
            name: loki-config
  volumeClaimTemplates:
    - metadata:
        name: loki-data
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: csi-sc-vnpaycloud
        resources:
          requests:
            storage: 5Gi

DaemonSet Promtail

Promtail chạy trên tất cả node (bao gồm node system, stateful, và monitoring trong production) để thu thập log container. Nó sử dụng toleration để lên lịch trên node có taint.

DaemonSet: promtail
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: promtail
  namespace: nx-watcher
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: promtail
  template:
    spec:
      tolerations:
        # Chấp nhận TẤT CẢ taint để Promtail chạy trên mọi node
        - operator: Exists
      containers:
        - name: promtail
          image: grafana/promtail:3.4.2
          args:
            - -config.file=/etc/promtail/promtail.yaml
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 100m
              memory: 128Mi
          volumeMounts:
            - name: promtail-config
              mountPath: /etc/promtail
            - name: varlog
              mountPath: /var/log
              readOnly: true
            - name: containers
              mountPath: /var/lib/docker/containers
              readOnly: true
      volumes:
        - name: promtail-config
          configMap:
            name: promtail-config
        - name: varlog
          hostPath:
            path: /var/log
        - name: containers
          hostPath:
            path: /var/lib/docker/containers

INFO

Mục tolerations: [{ operator: Exists }] có nghĩa Promtail chấp nhận tất cả taint, bao gồm dedicated=system:NoSchedule trên node system và dedicated=monitoring:NoSchedule trên node monitoring. Điều này đảm bảo log được thu thập từ mọi node trong cluster.

Cấu hình Promtail

ConfigMap: promtail-config
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: promtail-config
  namespace: nx-watcher
data:
  promtail.yaml: |
    server:
      http_listen_port: 9080

    positions:
      filename: /tmp/positions.yaml

    clients:
      - url: http://nx-loki.nx-watcher.svc.cluster.local:3100/loki/api/v1/push

    scrape_configs:
      - job_name: kubernetes-pods
        kubernetes_sd_configs:
          - role: pod
        relabel_configs:
          - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
            target_label: service
          - source_labels: [__meta_kubernetes_namespace]
            target_label: namespace
          - source_labels: [__meta_kubernetes_pod_name]
            target_label: pod
        pipeline_stages:
          - docker: {}

Tempo (Chỉ Production)

Grafana Tempo lưu trữ distributed trace được gửi bởi OpenTelemetry Collector.

Deployment

StatefulSet: nx-tempo
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nx-tempo
  namespace: nx-watcher
spec:
  serviceName: nx-tempo
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: tempo
  template:
    spec:
      nodeSelector:
        node.kubernetes.io/pool: monitoring
      tolerations:
        - key: dedicated
          operator: Equal
          value: monitoring
          effect: NoSchedule
      containers:
        - name: tempo
          image: grafana/tempo:2.7.1
          args:
            - -config.file=/etc/tempo/tempo.yaml
          ports:
            - name: http
              containerPort: 3200
            - name: otlp-grpc
              containerPort: 4317
          resources:
            requests:
              cpu: 200m
              memory: 512Mi
            limits:
              cpu: 500m
              memory: 1Gi
          volumeMounts:
            - name: tempo-config
              mountPath: /etc/tempo
            - name: tempo-data
              mountPath: /var/tempo
      volumes:
        - name: tempo-config
          configMap:
            name: tempo-config
  volumeClaimTemplates:
    - metadata:
        name: tempo-data
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: csi-sc-vnpaycloud
        resources:
          requests:
            storage: 10Gi

Cấu hình Tempo

ConfigMap: tempo-config
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: tempo-config
  namespace: nx-watcher
data:
  tempo.yaml: |
    server:
      http_listen_port: 3200

    distributor:
      receivers:
        otlp:
          protocols:
            grpc:
              endpoint: 0.0.0.0:4317

    storage:
      trace:
        backend: local
        local:
          path: /var/tempo/traces
        wal:
          path: /var/tempo/wal

    compactor:
      compaction:
        block_retention: 72h

    metrics_generator:
      registry:
        external_labels:
          source: tempo
      storage:
        path: /var/tempo/generator/wal
        remote_write:
          - url: http://nx-prometheus.nx-watcher.svc.cluster.local:9090/api/v1/write

OpenTelemetry Collector (Chỉ Production)

OTel Collector nhận trace từ Traefik và dịch vụ backend qua OTLP gRPC và chuyển tiếp đến Tempo.

Deployment

Deployment: nx-otel-collector
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nx-otel-collector
  namespace: nx-watcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: otel-collector
  template:
    spec:
      nodeSelector:
        node.kubernetes.io/pool: monitoring
      tolerations:
        - key: dedicated
          operator: Equal
          value: monitoring
          effect: NoSchedule
      containers:
        - name: otel-collector
          image: otel/opentelemetry-collector-contrib:0.118.0
          args:
            - --config=/etc/otel/otel-collector.yaml
          ports:
            - name: otlp-grpc
              containerPort: 4317
            - name: otlp-http
              containerPort: 4318
            - name: metrics
              containerPort: 8888
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 300m
              memory: 512Mi
          volumeMounts:
            - name: otel-config
              mountPath: /etc/otel
      volumes:
        - name: otel-config
          configMap:
            name: otel-collector-config

Cấu hình OTel Collector

ConfigMap: otel-collector-config
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-collector-config
  namespace: nx-watcher
data:
  otel-collector.yaml: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

    processors:
      batch:
        timeout: 5s
        send_batch_size: 1024
      memory_limiter:
        check_interval: 5s
        limit_mib: 400
        spike_limit_mib: 100
      resource:
        attributes:
          - key: cluster
            value: bana-production
            action: upsert

    exporters:
      otlp/tempo:
        endpoint: nx-tempo.nx-watcher.svc.cluster.local:4317
        tls:
          insecure: true

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch, resource]
          exporters: [otlp/tempo]
      telemetry:
        metrics:
          address: 0.0.0.0:8888

Service OTel

Service: nx-otel-collector
yaml
apiVersion: v1
kind: Service
metadata:
  name: nx-otel-collector
  namespace: nx-watcher
spec:
  selector:
    app.kubernetes.io/name: otel-collector
  ports:
    - name: otlp-grpc
      port: 4317
      targetPort: 4317
    - name: otlp-http
      port: 4318
      targetPort: 4318
    - name: metrics
      port: 8888
      targetPort: 8888
  type: ClusterIP

Nguồn Trace

Trace được gửi đến OTel Collector từ hai nguồn:

Traefik — cấu hình qua tham số CLI trong Traefik Deployment:

yaml
args:
  - --tracing.otlp=true
  - --tracing.otlp.grpc.endpoint=nx-otel-collector.nx-watcher.svc.cluster.local:4317
  - --tracing.otlp.grpc.insecure=true

Dịch vụ backend — cấu hình qua biến môi trường trong ConfigMap dùng chung:

yaml
OTEL_EXPORTER_OTLP_ENDPOINT: http://nx-otel-collector.nx-watcher.svc.cluster.local:4317
OTEL_SERVICE_NAME: bana

Mỗi dịch vụ IGNIS đọc OTEL_EXPORTER_OTLP_ENDPOINT và tự động instrument các HTTP request, truy vấn database, và lệnh Redis.

Quy tắc Cảnh báo

ConfigMap: prometheus-alerts
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-alerts
  namespace: nx-watcher
data:
  alerts.yml: |
    groups:
      - name: bana
        rules:
          # Pod liên tục crash
          - alert: PodCrashLooping
            expr: rate(kube_pod_container_status_restarts_total{namespace=~"nx-.*"}[15m]) > 0
            for: 5m
            labels:
              severity: warning
            annotations:
              summary: "Pod {{ $labels.pod }} đang liên tục crash"

          # Tỉ lệ lỗi cao
          - alert: HighErrorRate
            expr: |
              sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service)
              / sum(rate(traefik_service_requests_total[5m])) by (service) > 0.05
            for: 5m
            labels:
              severity: critical
            annotations:
              summary: "Dịch vụ {{ $labels.service }} có tỉ lệ lỗi >5%"

          # Kết nối PostgreSQL gần giới hạn
          - alert: PostgreSQLHighConnections
            expr: pg_stat_activity_count > 80
            for: 5m
            labels:
              severity: warning
            annotations:
              summary: "Kết nối PostgreSQL vượt 80"

          # Sử dụng bộ nhớ Redis
          - alert: RedisHighMemory
            expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.9
            for: 5m
            labels:
              severity: warning
            annotations:
              summary: "Sử dụng bộ nhớ Redis vượt 90%"

          # Kafka partition thiếu bản sao
          - alert: KafkaUnderReplicated
            expr: kafka_server_replicamanager_underreplicatedpartitions > 0
            for: 10m
            labels:
              severity: critical
            annotations:
              summary: "Kafka có partition thiếu bản sao"

          # Sử dụng đĩa
          - alert: HighDiskUsage
            expr: |
              (kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes) > 0.85
            for: 10m
            labels:
              severity: warning
            annotations:
              summary: "PVC {{ $labels.persistentvolumeclaim }} sử dụng vượt 85%"

          # Dịch vụ identity ngừng hoạt động
          - alert: IdentityServiceDown
            expr: up{job="nx-backend", service="identity"} == 0
            for: 1m
            labels:
              severity: critical
            annotations:
              summary: "Dịch vụ identity ngừng hoạt động — mọi xác thực sẽ thất bại"

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.