Add Apache status monitoring to Helm chart
Publish PHP Apache image / Build, test and push (push) Successful in 1m10s
Publish PHP Apache image / Build, test and push (push) Successful in 1m10s
This commit is contained in:
@@ -13,7 +13,7 @@ upgrade also upgrades the runtime image.
|
||||
Add and refresh the Gitea chart repository with:
|
||||
|
||||
```sh
|
||||
helm repo add brunner https://code.brunner.ninja/api/packages/feedc0de/helm
|
||||
helm repo add brunner https://brunner.ninja/charts
|
||||
helm repo update brunner
|
||||
```
|
||||
|
||||
@@ -40,6 +40,7 @@ helm upgrade --install example-site brunner/php-homepage \
|
||||
--namespace default \
|
||||
--values /path/to/example-site/values.yaml \
|
||||
--take-ownership \
|
||||
--force-conflicts \
|
||||
--wait
|
||||
```
|
||||
|
||||
@@ -55,7 +56,21 @@ With `persistence.existingClaim` empty, the chart creates an RWX PVC using
|
||||
uninstall does not discard site content. Set `persistence.retain: false` only
|
||||
when deleting the release should also delete its dynamically created claim.
|
||||
|
||||
Ingress, TLS, probes, scheduling, storage size, and the image tag are all
|
||||
configurable through `values.yaml`. TCP probes are used by default because
|
||||
individual websites may redirect `/`, require authentication, or select their
|
||||
own HTTP status behavior.
|
||||
Ingress, TLS, probes, scheduling, storage size, Apache's `ServerAdmin`, and the
|
||||
image tag are all configurable through `values.yaml`. TCP startup, readiness,
|
||||
and liveness probes are enabled by default because individual websites may
|
||||
redirect `/`, require authentication, or select their own HTTP status behavior.
|
||||
|
||||
## Apache metrics
|
||||
|
||||
The image provides an extended `mod_status` endpoint on pod-local loopback port
|
||||
8080. It is deliberately not exposed directly by the Service because extended
|
||||
status can contain current client and request details.
|
||||
|
||||
Set `metrics.enabled` to add an Apache exporter sidecar and expose its sanitized
|
||||
Prometheus metrics on the Service. `metrics.serviceMonitor.enabled` and
|
||||
`metrics.prometheusRule.enabled` render the corresponding Prometheus Operator
|
||||
resources when their CRDs exist. The rules cover exporter scrape failures,
|
||||
Apache status failures, and worker saturation. No Prometheus namespace, release
|
||||
label, or Alertmanager is hard-coded; selection and alert routing remain the
|
||||
responsibility of the cluster's Prometheus installation.
|
||||
|
||||
@@ -36,10 +36,18 @@ spec:
|
||||
- name: {{ include "php-homepage.fullname" . }}
|
||||
image: "{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: APACHE_SERVER_ADMIN
|
||||
value: {{ .Values.apache.serverAdmin | quote }}
|
||||
- name: APACHE_STATUS_PORT
|
||||
value: {{ .Values.apache.statusPort | quote }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
- name: apache-status
|
||||
containerPort: {{ .Values.apache.statusPort }}
|
||||
protocol: TCP
|
||||
{{- if .Values.probes.startup.enabled }}
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
@@ -73,6 +81,36 @@ spec:
|
||||
- name: webroot
|
||||
mountPath: {{ .Values.persistence.mountPath }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.enabled }}
|
||||
- name: apache-exporter
|
||||
image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.metrics.image.pullPolicy }}
|
||||
args:
|
||||
- "--scrape_uri=http://127.0.0.1:{{ .Values.apache.statusPort }}/server-status?auto"
|
||||
- "--web.listen-address=:{{ .Values.metrics.port }}"
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: {{ .Values.metrics.port }}
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
{{- with .Values.metrics.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
volumes:
|
||||
- name: webroot
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1/PrometheusRule") }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: {{ include "php-homepage.fullname" . }}
|
||||
labels:
|
||||
{{- include "php-homepage.labels" . | nindent 4 }}
|
||||
{{- with .Values.metrics.prometheusRule.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.metrics.prometheusRule.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
groups:
|
||||
- name: {{ include "php-homepage.fullname" . }}.apache
|
||||
rules:
|
||||
{{- if .Values.metrics.prometheusRule.exporterDown.enabled }}
|
||||
- alert: ApacheExporterDown
|
||||
expr: up{namespace={{ .Release.Namespace | quote }},service={{ include "php-homepage.fullname" . | quote }},endpoint="metrics"} == 0
|
||||
for: {{ .Values.metrics.prometheusRule.exporterDown.for }}
|
||||
labels:
|
||||
severity: {{ .Values.metrics.prometheusRule.exporterDown.severity | quote }}
|
||||
annotations:
|
||||
summary: {{ printf "Apache exporter for %s is unavailable" (include "php-homepage.fullname" .) | quote }}
|
||||
description: {{ printf "Prometheus has not been able to scrape the Apache exporter in namespace %s." .Release.Namespace | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.prometheusRule.apacheDown.enabled }}
|
||||
- alert: ApacheDown
|
||||
expr: apache_up{namespace={{ .Release.Namespace | quote }},service={{ include "php-homepage.fullname" . | quote }}} == 0
|
||||
for: {{ .Values.metrics.prometheusRule.apacheDown.for }}
|
||||
labels:
|
||||
severity: {{ .Values.metrics.prometheusRule.apacheDown.severity | quote }}
|
||||
annotations:
|
||||
summary: {{ printf "Apache for %s is unavailable" (include "php-homepage.fullname" .) | quote }}
|
||||
description: {{ printf "The exporter cannot read Apache server-status in namespace %s." .Release.Namespace | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.prometheusRule.workerSaturation.enabled }}
|
||||
- alert: ApacheWorkerSaturation
|
||||
expr: (sum(apache_workers{namespace={{ .Release.Namespace | quote }},service={{ include "php-homepage.fullname" . | quote }},state="busy"}) / clamp_min(sum(apache_workers{namespace={{ .Release.Namespace | quote }},service={{ include "php-homepage.fullname" . | quote }}}), 1)) > {{ .Values.metrics.prometheusRule.workerSaturation.threshold }}
|
||||
for: {{ .Values.metrics.prometheusRule.workerSaturation.for }}
|
||||
labels:
|
||||
severity: {{ .Values.metrics.prometheusRule.workerSaturation.severity | quote }}
|
||||
annotations:
|
||||
summary: {{ printf "Apache workers for %s are nearly exhausted" (include "php-homepage.fullname" .) | quote }}
|
||||
description: {{ printf "The ratio of busy to available Apache workers has exceeded the configured threshold (%v) for the configured duration." .Values.metrics.prometheusRule.workerSaturation.threshold | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -11,5 +11,11 @@ spec:
|
||||
port: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
targetPort: http
|
||||
{{- if .Values.metrics.enabled }}
|
||||
- name: metrics
|
||||
port: {{ .Values.metrics.port }}
|
||||
protocol: TCP
|
||||
targetPort: metrics
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "php-homepage.selectorLabels" . | nindent 4 }}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1/ServiceMonitor") }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ include "php-homepage.fullname" . }}
|
||||
labels:
|
||||
{{- include "php-homepage.labels" . | nindent 4 }}
|
||||
{{- with .Values.metrics.serviceMonitor.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.metrics.serviceMonitor.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "php-homepage.selectorLabels" . | nindent 6 }}
|
||||
endpoints:
|
||||
- port: metrics
|
||||
path: /metrics
|
||||
interval: {{ .Values.metrics.serviceMonitor.interval }}
|
||||
scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }}
|
||||
{{- end }}
|
||||
@@ -1,10 +1,18 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"required": ["image", "persistence", "service", "ingress"],
|
||||
"required": ["apache", "image", "metrics", "persistence", "service", "ingress"],
|
||||
"properties": {
|
||||
"replicaCount": { "type": "integer", "minimum": 1 },
|
||||
"fullnameOverride": { "type": "string" },
|
||||
"apache": {
|
||||
"type": "object",
|
||||
"required": ["serverAdmin", "statusPort"],
|
||||
"properties": {
|
||||
"serverAdmin": { "type": "string", "minLength": 3 },
|
||||
"statusPort": { "type": "integer", "minimum": 1, "maximum": 65535 }
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"required": ["repository", "tag", "pullPolicy"],
|
||||
@@ -14,6 +22,39 @@
|
||||
"pullPolicy": { "enum": ["Always", "IfNotPresent", "Never"] }
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"required": ["enabled", "image", "port", "serviceMonitor", "prometheusRule"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
|
||||
"image": {
|
||||
"type": "object",
|
||||
"required": ["repository", "tag", "pullPolicy"],
|
||||
"properties": {
|
||||
"repository": { "type": "string", "minLength": 1 },
|
||||
"tag": { "type": "string", "minLength": 1 },
|
||||
"pullPolicy": { "enum": ["Always", "IfNotPresent", "Never"] }
|
||||
}
|
||||
},
|
||||
"serviceMonitor": {
|
||||
"type": "object",
|
||||
"required": ["enabled", "interval", "scrapeTimeout"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"interval": { "type": "string", "minLength": 2 },
|
||||
"scrapeTimeout": { "type": "string", "minLength": 2 }
|
||||
}
|
||||
},
|
||||
"prometheusRule": {
|
||||
"type": "object",
|
||||
"required": ["enabled"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistence": {
|
||||
"type": "object",
|
||||
"required": ["enabled", "existingClaim", "mountPath"],
|
||||
|
||||
@@ -16,6 +16,10 @@ fullnameOverride: ""
|
||||
deploymentAnnotations: {}
|
||||
podAnnotations: {}
|
||||
|
||||
apache:
|
||||
serverAdmin: webmaster@localhost
|
||||
statusPort: 8080
|
||||
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
@@ -26,6 +30,38 @@ service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
metrics:
|
||||
enabled: false
|
||||
image:
|
||||
repository: quay.io/lusitaniae/apache-exporter
|
||||
tag: v1.1.1
|
||||
pullPolicy: IfNotPresent
|
||||
port: 9117
|
||||
resources: {}
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
labels: {}
|
||||
annotations: {}
|
||||
prometheusRule:
|
||||
enabled: false
|
||||
labels: {}
|
||||
annotations: {}
|
||||
exporterDown:
|
||||
enabled: true
|
||||
for: 5m
|
||||
severity: warning
|
||||
apacheDown:
|
||||
enabled: true
|
||||
for: 5m
|
||||
severity: critical
|
||||
workerSaturation:
|
||||
enabled: true
|
||||
threshold: 0.9
|
||||
for: 10m
|
||||
severity: warning
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
existingClaim: ""
|
||||
|
||||
Reference in New Issue
Block a user