diff --git a/README.md b/README.md index 89686cf426c63547206096f0700cddbdee7c3b1e..dd6e0b73d0199553c286207b60af38b9ff3cd80f 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,37 @@ In the above example the configuration has been inlined, but can just as well be }, }).alertmanager.secret ``` +### Static etcd configuration + +In order to configure a static etcd cluster to scrape there is a simple mixin prepared, so only the IPs and certificate information need to be configured. Simply append the `kube-prometheus/kube-prometheus-static-etcd.libsonnet` mixin to the rest of the configuration, and configure the `ips` to be the IPs to scrape, and the `clientCA`, `clientKey` and `clientCert` to values that are valid to scrape etcd metrics with. + +Most likely these certificates are generated somewhere in an infrastructure repository, so using the jsonnet `importstr` function can be useful here. All the sensitive information on the certificates will end up in a Kubernetes Secret. + +[embedmd]:# (examples/etcd.jsonnet) +```jsonnet +local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + + (import 'kube-prometheus/kube-prometheus-static-etcd.libsonnet') + { + _config+:: { + namespace: 'monitoring', + + etcd+:: { + ips: ['127.0.0.1'], + clientCA: importstr 'etcd-client-ca.crt', + clientKey: importstr 'etcd-client.key', + clientCert: importstr 'etcd-client.crt', + serverName: 'etcd.my-cluster.local', + }, + }, +}; + +{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } + +{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } + +{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } + +{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } + +{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } + +{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } + +{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) } +``` ### Customizing Prometheus alerting/recording rules and Grafana dashboards diff --git a/examples/etcd-client-ca.crt b/examples/etcd-client-ca.crt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/etcd-client.crt b/examples/etcd-client.crt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/etcd-client.key b/examples/etcd-client.key new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/etcd.jsonnet b/examples/etcd.jsonnet new file mode 100644 index 0000000000000000000000000000000000000000..c521d1cdc76220fb69c1827ee7681bdf3e9258d6 --- /dev/null +++ b/examples/etcd.jsonnet @@ -0,0 +1,22 @@ +local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + + (import 'kube-prometheus/kube-prometheus-static-etcd.libsonnet') + { + _config+:: { + namespace: 'monitoring', + + etcd+:: { + ips: ['127.0.0.1'], + clientCA: importstr 'etcd-client-ca.crt', + clientKey: importstr 'etcd-client.key', + clientCert: importstr 'etcd-client.crt', + serverName: 'etcd.my-cluster.local', + }, + }, +}; + +{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } + +{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } + +{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } + +{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } + +{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } + +{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } + +{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) } diff --git a/jsonnet/kube-prometheus/jsonnetfile.json b/jsonnet/kube-prometheus/jsonnetfile.json index a7b0a203563893e51aa821a2a9c713556667a88d..719f0e9464b82a7f2aa966169fa5f4143e5d88e9 100644 --- a/jsonnet/kube-prometheus/jsonnetfile.json +++ b/jsonnet/kube-prometheus/jsonnetfile.json @@ -39,6 +39,16 @@ } }, "version": "master" + }, + { + "name": "etcd-mixin", + "source": { + "git": { + "remote": "https://github.com/coreos/etcd", + "subdir": "Documentation/etcd-mixin" + } + }, + "version": "master" } ] } \ No newline at end of file diff --git a/jsonnet/kube-prometheus/kube-prometheus-static-etcd.libsonnet b/jsonnet/kube-prometheus/kube-prometheus-static-etcd.libsonnet new file mode 100644 index 0000000000000000000000000000000000000000..23883c2c5f14a426f98c2fc10d7509582fc83b4f --- /dev/null +++ b/jsonnet/kube-prometheus/kube-prometheus-static-etcd.libsonnet @@ -0,0 +1,95 @@ +local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet'; + +(import 'etcd-mixin/mixin.libsonnet') + { + _config+:: { + etcd: { + ips: [], + clientCA: null, + clientKey: null, + clientCert: null, + serverName: null, + }, + }, + prometheus+:: { + serviceEtcd: + local service = k.core.v1.service; + local servicePort = k.core.v1.service.mixin.spec.portsType; + + local etcdServicePort = servicePort.newNamed('metrics', 2379, 2379); + + service.new('etcd', null, etcdServicePort) + + service.mixin.metadata.withNamespace('kube-system') + + service.mixin.metadata.withLabels({ 'k8s-app': 'etcd' }) + + service.mixin.spec.withClusterIp('None'), + endpointsEtcd: + local endpoints = k.core.v1.endpoints; + local endpointSubset = endpoints.subsetsType; + local endpointPort = endpointSubset.portsType; + + local etcdPort = endpointPort.new() + + endpointPort.withName('metrics') + + endpointPort.withPort(2379) + + endpointPort.withProtocol('TCP'); + + local subset = endpointSubset.new() + + endpointSubset.withAddresses([ + { ip: etcdIP } + for etcdIP in $._config.etcd.ips + ]) + + endpointSubset.withPorts(etcdPort); + + endpoints.new() + + endpoints.mixin.metadata.withName('etcd') + + endpoints.mixin.metadata.withNamespace('kube-system') + + endpoints.mixin.metadata.withLabels({ 'k8s-app': 'etcd' }) + + endpoints.withSubsets(subset), + serviceMonitorEtcd: + { + apiVersion: 'monitoring.coreos.com/v1', + kind: 'ServiceMonitor', + metadata: { + name: 'etcd', + namespace: 'kube-system', + labels: { + 'k8s-app': 'etcd', + }, + }, + spec: { + jobLabel: 'k8s-app', + endpoints: [ + { + port: 'metrics', + interval: '30s', + scheme: 'https', + tlsConfig: { + caFile: '/etc/prometheus/secrets/kube-etcd-client-certs/etcd-client-ca.crt', + keyFile: '/etc/prometheus/secrets/kube-etcd-client-certs/etcd-client.key', + certFile: '/etc/prometheus/secrets/kube-etcd-client-certs/etcd-client.crt', + serverName: $._config.etcd.serverName, + }, + }, + ], + selector: { + matchLabels: { + 'k8s-app': 'etcd', + }, + }, + }, + }, + secretEtcdCerts: + local secret = k.core.v1.secret; + + secret.new('kube-etcd-client-certs', { + 'etcd-client-ca.crt': std.base64($._config.etcd.clientCA), + 'etcd-client.key': std.base64($._config.etcd.clientKey), + 'etcd-client.crt': std.base64($._config.etcd.clientCert), + }) + + secret.mixin.metadata.withNamespace($._config.namespace), + prometheus+: + { + spec+: { + secrets+: [$.prometheus.secretEtcdCerts.metadata.name], + }, + }, + }, +}