diff --git a/Chart.yaml b/Chart.yaml index 73cdce2..1717fef 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 name: pgdog -version: v0.31 +version: v0.32 appVersion: "0.1.22" diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 9a9c4ea..c719adc 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -85,3 +85,29 @@ app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/component: gateway {{- end }} {{- end }} + +{{/* +Common labels for prometheus-collector +*/}} +{{- define "pgdog.prometheusCollector.labels" -}} +{{- if .Values.prometheusCollector.labels }} +{{- toYaml .Values.prometheusCollector.labels }} +{{- else -}} +app.kubernetes.io/name: {{ include "pgdog.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: prometheus-collector +{{- end }} +{{- end }} + +{{/* +Selector labels for prometheus-collector +*/}} +{{- define "pgdog.prometheusCollector.selectorLabels" -}} +{{- if .Values.prometheusCollector.selectorLabels }} +{{- toYaml .Values.prometheusCollector.selectorLabels }} +{{- else -}} +app.kubernetes.io/name: {{ include "pgdog.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: prometheus-collector +{{- end }} +{{- end }} diff --git a/templates/prometheus-collector/config.yaml b/templates/prometheus-collector/config.yaml new file mode 100644 index 0000000..2171b58 --- /dev/null +++ b/templates/prometheus-collector/config.yaml @@ -0,0 +1,55 @@ +{{- if .Values.prometheusCollector.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "pgdog.fullname" . }}-prometheus-collector + labels: + {{- include "pgdog.prometheusCollector.labels" . | nindent 4 }} +data: + prometheus.yml: | + global: + scrape_interval: {{ .Values.prometheusCollector.scrapeInterval | default "15s" }} + evaluation_interval: {{ .Values.prometheusCollector.evaluationInterval | default "15s" }} + {{- if .Values.clusterName }} + external_labels: + cluster: {{ .Values.clusterName | quote }} + {{- end }} + + scrape_configs: + - job_name: 'pgdog' + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - {{ .Release.Namespace }} + relabel_configs: + # Only scrape pods matching pgdog selector labels + - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name] + action: keep + regex: {{ include "pgdog.name" . }} + - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_instance] + action: keep + regex: {{ .Release.Name }} + - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_component] + action: keep + regex: pgdog + # Set the metrics port + - source_labels: [__address__] + action: replace + regex: ([^:]+)(?::\d+)? + replacement: $1:{{ .Values.openMetricsPort }} + target_label: __address__ + # Add pod metadata as labels + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_ip] + action: replace + target_label: pod_ip +{{- end }} diff --git a/templates/prometheus-collector/deployment.yaml b/templates/prometheus-collector/deployment.yaml new file mode 100644 index 0000000..054402f --- /dev/null +++ b/templates/prometheus-collector/deployment.yaml @@ -0,0 +1,122 @@ +{{- if .Values.prometheusCollector.enabled }} +{{- $tlsEnabled := .Values.prometheusCollector.tls.enabled }} +{{- $basicAuthEnabled := .Values.prometheusCollector.basicAuth.enabled }} +{{- $webConfigEnabled := or $tlsEnabled $basicAuthEnabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "pgdog.fullname" . }}-prometheus-collector + labels: + {{- include "pgdog.prometheusCollector.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "pgdog.prometheusCollector.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "pgdog.prometheusCollector.selectorLabels" . | nindent 8 }} + {{- if .Values.prometheusCollector.podAnnotations }} + annotations: + {{- toYaml .Values.prometheusCollector.podAnnotations | nindent 8 }} + {{- end }} + spec: + {{- if .Values.serviceAccount.create }} + serviceAccountName: {{ include "pgdog.serviceAccountName" . }} + {{- end }} + containers: + - name: prometheus + image: {{ .Values.prometheusCollector.image.repository }}:{{ .Values.prometheusCollector.image.tag }} + imagePullPolicy: {{ .Values.prometheusCollector.image.pullPolicy }} + args: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + - "--web.listen-address=:{{ .Values.prometheusCollector.port }}" + - "--web.enable-lifecycle" + - "--storage.tsdb.retention.time={{ .Values.prometheusCollector.retention.time }}" + - "--storage.tsdb.retention.size={{ .Values.prometheusCollector.retention.size }}" + {{- if $webConfigEnabled }} + - "--web.config.file=/etc/prometheus-web/web.yml" + {{- end }} + ports: + - name: prometheus + containerPort: {{ .Values.prometheusCollector.port }} + protocol: TCP + {{- if .Values.prometheusCollector.resources }} + resources: + {{- toYaml .Values.prometheusCollector.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /etc/prometheus + - name: storage + mountPath: /prometheus + {{- if $webConfigEnabled }} + - name: web-config + mountPath: /etc/prometheus-web + readOnly: true + {{- end }} + livenessProbe: + {{- if or $tlsEnabled $basicAuthEnabled }} + exec: + command: + - /bin/sh + - -c + - | + {{- if $basicAuthEnabled }} + wget -q --spider {{ if $tlsEnabled }}--no-check-certificate https{{ else }}http{{ end }}://{{ .Values.prometheusCollector.basicAuth.username }}:$(cat /etc/prometheus-web/password)@localhost:{{ .Values.prometheusCollector.port }}/-/healthy + {{- else }} + wget -q --spider --no-check-certificate https://localhost:{{ .Values.prometheusCollector.port }}/-/healthy + {{- end }} + {{- else }} + httpGet: + path: /-/healthy + port: prometheus + {{- end }} + initialDelaySeconds: 30 + periodSeconds: 15 + readinessProbe: + {{- if or $tlsEnabled $basicAuthEnabled }} + exec: + command: + - /bin/sh + - -c + - | + {{- if $basicAuthEnabled }} + wget -q --spider {{ if $tlsEnabled }}--no-check-certificate https{{ else }}http{{ end }}://{{ .Values.prometheusCollector.basicAuth.username }}:$(cat /etc/prometheus-web/password)@localhost:{{ .Values.prometheusCollector.port }}/-/ready + {{- else }} + wget -q --spider --no-check-certificate https://localhost:{{ .Values.prometheusCollector.port }}/-/ready + {{- end }} + {{- else }} + httpGet: + path: /-/ready + port: prometheus + {{- end }} + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: config + configMap: + name: {{ include "pgdog.fullname" . }}-prometheus-collector + - name: storage + emptyDir: + sizeLimit: {{ .Values.prometheusCollector.storage.size }} + {{- if $webConfigEnabled }} + - name: web-config + secret: + secretName: {{ include "pgdog.fullname" . }}-prometheus-collector + {{- end }} + {{- with .Values.prometheusCollector.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheusCollector.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheusCollector.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/templates/prometheus-collector/secret.yaml b/templates/prometheus-collector/secret.yaml new file mode 100644 index 0000000..8cc8ac2 --- /dev/null +++ b/templates/prometheus-collector/secret.yaml @@ -0,0 +1,34 @@ +{{- if .Values.prometheusCollector.enabled }} +{{- $tlsEnabled := .Values.prometheusCollector.tls.enabled }} +{{- $basicAuthEnabled := .Values.prometheusCollector.basicAuth.enabled }} +{{- if or $tlsEnabled $basicAuthEnabled }} +{{- $cert := genSelfSignedCert "prometheus-collector" nil (list "localhost" (printf "%s-prometheus-collector" (include "pgdog.fullname" .))) 3650 }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "pgdog.fullname" . }}-prometheus-collector + labels: + {{- include "pgdog.prometheusCollector.labels" . | nindent 4 }} +type: Opaque +stringData: + web.yml: | + {{- if $tlsEnabled }} + tls_server_config: + cert_file: /etc/prometheus-web/tls.crt + key_file: /etc/prometheus-web/tls.key + {{- end }} + {{- if $basicAuthEnabled }} + basic_auth_users: + {{ .Values.prometheusCollector.basicAuth.username }}: {{ .Values.prometheusCollector.basicAuth.passwordHash }} + {{- end }} + {{- if $basicAuthEnabled }} + password: {{ .Values.prometheusCollector.basicAuth.password | quote }} + {{- end }} + {{- if $tlsEnabled }} + tls.crt: | +{{ $cert.Cert | indent 4 }} + tls.key: | +{{ $cert.Key | indent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/templates/prometheus-collector/service.yaml b/templates/prometheus-collector/service.yaml new file mode 100644 index 0000000..7174c58 --- /dev/null +++ b/templates/prometheus-collector/service.yaml @@ -0,0 +1,35 @@ +{{- if .Values.prometheusCollector.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "pgdog.fullname" . }}-prometheus-collector + labels: + {{- include "pgdog.prometheusCollector.labels" . | nindent 4 }} + {{- if or .Values.prometheusCollector.service.annotations .Values.prometheusCollector.service.aws.enabled }} + annotations: + {{- if .Values.prometheusCollector.service.aws.enabled }} + service.beta.kubernetes.io/aws-load-balancer-scheme: {{ .Values.prometheusCollector.service.aws.scheme | quote }} + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" + service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true" + {{- end }} + {{- with .Values.prometheusCollector.service.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if .Values.prometheusCollector.service.aws.enabled }} + type: LoadBalancer + loadBalancerClass: service.k8s.aws/nlb + {{- else }} + type: {{ .Values.prometheusCollector.service.type }} + {{- end }} + ports: + - name: prometheus + port: {{ .Values.prometheusCollector.port }} + targetPort: prometheus + protocol: TCP + selector: + {{- include "pgdog.prometheusCollector.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/templates/role.yaml b/templates/role.yaml index ee51251..79e8087 100644 --- a/templates/role.yaml +++ b/templates/role.yaml @@ -14,5 +14,11 @@ rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "watch"] + {{- if .Values.prometheusCollector.enabled }} + # Allow Prometheus to discover pods for scraping + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + {{- end }} {{- end }} diff --git a/templates/service.yaml b/templates/service.yaml index 4f4c8b3..34e83c7 100644 --- a/templates/service.yaml +++ b/templates/service.yaml @@ -8,7 +8,7 @@ metadata: service.beta.kubernetes.io/aws-load-balancer-scheme: {{ .Values.service.aws.scheme | quote }} service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" - service.beta.kubernetes.io/aws-load-balancer-type: "external" + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true" service.beta.kubernetes.io/aws-load-balancer-listener-attributes.TCP-{{ .Values.port }}: tcp.idle_timeout.seconds=1800 {{- if .Values.healthcheckPort }} diff --git a/test/values-prometheus-collector.yaml b/test/values-prometheus-collector.yaml new file mode 100644 index 0000000..a315f38 --- /dev/null +++ b/test/values-prometheus-collector.yaml @@ -0,0 +1,51 @@ +# Test Prometheus collector configuration +prometheusCollector: + enabled: true + port: 9090 + scrapeInterval: 30s + evaluationInterval: 30s + image: + repository: prom/prometheus + tag: v2.48.0 + pullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + storage: + size: 5Gi + retention: + time: 7d + size: 2GB + # basicAuth: + # enabled: true + # username: prometheus + # # bcrypt hash of "testpassword" + # passwordHash: "$2a$10$IpLmUJvKPpVJkhbP6YVmAOQXoKr4kXXXXXXXXXXXXXXXXXXXXXXX" + service: + type: ClusterIP + annotations: {} + aws: + enabled: true + scheme: internal + nodeSelector: + monitoring: "true" + tolerations: + - key: dedicated + operator: Equal + value: monitoring + effect: NoSchedule + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: monitoring + operator: In + values: + - "true" + podAnnotations: + prometheus.io/scrape: "false" diff --git a/values.yaml b/values.yaml index 785d4fc..ceccf3c 100644 --- a/values.yaml +++ b/values.yaml @@ -165,7 +165,7 @@ service: enabled: false # scheme controls whether the load balancer is internet-facing or internal # Valid values: "internet-facing" or "internal" - scheme: "internal" + scheme: "internet-facing" # nodeSelector allows scheduling pods on nodes with specific labels nodeSelector: {} @@ -396,3 +396,82 @@ queryStats: # queryParserEnabled is DEPRECATED - use queryParser instead # queryParserEnabled: true + +# Prometheus Collector configuration +# Deploys a standalone Prometheus instance that collects metrics from all pgdog pods +prometheusCollector: + # enabled controls whether to deploy the Prometheus collector + enabled: false + # labels allows overriding prometheus-collector resource labels + labels: {} + # selectorLabels allows overriding prometheus-collector selector labels + selectorLabels: {} + # podAnnotations allows adding custom annotations to the prometheus-collector pods + podAnnotations: {} + # image contains the Docker image properties for Prometheus + image: + repository: prom/prometheus + tag: latest + pullPolicy: IfNotPresent + # port on which Prometheus will expose metrics + port: 9090 + # scrapeInterval defines how often to scrape targets + scrapeInterval: 15s + # evaluationInterval defines how often to evaluate rules + evaluationInterval: 15s + # resources define resource requests and limits for the Prometheus container + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + # storage configuration for Prometheus TSDB + storage: + # size is the maximum size of the emptyDir volume + size: 10Gi + # retention configuration for Prometheus TSDB + retention: + # time is the maximum duration to keep data (e.g., 15d, 6h) + time: 15d + # size is the maximum size of data to retain (e.g., 5GB, 500MB) + # Prometheus will delete oldest data first when this limit is exceeded + size: 5GB + # tls configuration for enabling HTTPS on the Prometheus endpoint + tls: + # enabled controls whether TLS is enabled for Prometheus + # When enabled, a self-signed certificate is automatically generated + enabled: false + # basicAuth configuration for protecting the Prometheus endpoint + basicAuth: + # enabled controls whether basic auth is required to access Prometheus + enabled: false + # username for basic auth + username: "" + # password is the plaintext password (used for health checks) + password: "" + # passwordHash is the bcrypt hash of the password + # Generate with: htpasswd -nBC 10 "" | tr -d ':\n' + # Or use Python: python -c "import bcrypt; print(bcrypt.hashpw(b'password', bcrypt.gensalt()).decode())" + passwordHash: "" + # service contains the Kubernetes service configuration + service: + # type specifies the type of Kubernetes service (ignored when aws.enabled is true) + type: ClusterIP + # annotations allows adding custom annotations to the service + annotations: {} + # aws configures AWS Load Balancer Controller annotations + # When enabled, service type is automatically set to LoadBalancer + aws: + # enabled controls whether to add AWS LB annotations + enabled: false + # scheme controls whether the load balancer is internet-facing or internal + # Valid values: "internet-facing" or "internal" + scheme: "internet-facing" + # nodeSelector allows scheduling pods on nodes with specific labels + nodeSelector: {} + # tolerations allows pods to be scheduled on nodes with matching taints + tolerations: [] + # affinity rules for pod scheduling + affinity: {}