Skip to content

Commit 9c43f63

Browse files
docs(samples): exercise a duplicate Klio server name across namespaces
Adds a fourth deployment to opentelemetry-multi: team-e runs its own Klio server also named "klio-a" (see ../klio_server_a.yaml in "default"). Since a Server's StatefulSet pod name is derived from the Server's own name alone, both servers' pods are named "klio-a-klio-0", giving them an identical host_name label -- a case the dashboard's $server variable cannot disambiguate on its own. bootstrap-remote-server.sh bootstraps this scenario: it copies the shared CA's public certificate (so team-e's independently self-signed server can validate clients signed by the same klio-server-ca) and the OTel collector trust anchor, plus one cluster's client certificate generated in "default" (cert-manager Issuers are namespace-scoped). Confirmed live on Kind: both servers run independently and cluster-e backs up successfully. Querying Prometheus directly shows two distinct series for host_name=klio-a-klio-0 (uptime ~9778s in default vs ~299s in team-e, disambiguated only by k8s_namespace_name); the dashboard's actual "Server uptime" panel query, with $namespace left at its default "All" and $server narrowed to klio-a-klio-0, collapses both into a single value and silently picks one. Assisted-by: Claude Signed-off-by: Gabriele Quaresima <gabriele.quaresima@enterprisedb.com>
1 parent 5303cc2 commit 9c43f63

12 files changed

Lines changed: 365 additions & 3 deletions
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env bash
2+
# Bootstraps a namespace that runs its OWN local Klio server (self-signed,
3+
# not copied from "default") which nonetheless needs to: (a) validate client
4+
# certs signed by the shared klio-server-ca, and (b) export OTel data to the
5+
# shared collector in "default". Unlike copy-cross-namespace-secrets.sh
6+
# (which pins a specific EXISTING server's own cert for a remote client),
7+
# this copies the CA's public cert so any locally-issued server certificate
8+
# can validate against it, plus the shared OTel collector/client certs, plus
9+
# one cluster's client cert (generated in "default", where klio-server-ca
10+
# lives, then copied here since Issuers are namespace-scoped).
11+
#
12+
# Usage: bootstrap-remote-server.sh <dest-namespace> <client-secret-name>
13+
#
14+
# Run this after `kubectl apply -k .` and before applying the destination
15+
# namespace's kustomization, e.g.:
16+
# ./bootstrap-remote-server.sh team-e cluster-e-klio-user
17+
set -euo pipefail
18+
19+
if [ $# -ne 2 ]; then
20+
echo "usage: $0 <dest-namespace> <client-secret-name>" >&2
21+
exit 1
22+
fi
23+
24+
SOURCE_NS=default
25+
DEST_NS="$1"
26+
CLIENT_SECRET_NAME="$2"
27+
28+
echo "Waiting for cert-manager to issue the secrets in ${SOURCE_NS}..."
29+
kubectl wait --for=create secret/klio-server-ca -n "${SOURCE_NS}" --timeout=120s
30+
kubectl wait --for=create "secret/${CLIENT_SECRET_NAME}" -n "${SOURCE_NS}" --timeout=120s
31+
kubectl wait --for=create secret/otel-collector-tls -n "${SOURCE_NS}" --timeout=120s
32+
kubectl wait --for=create secret/klio-server-otel-client-tls -n "${SOURCE_NS}" --timeout=120s
33+
34+
kubectl create namespace "${DEST_NS}" --dry-run=client -o yaml | kubectl apply -f -
35+
36+
echo "Copying the shared CA's public certificate (so a locally-issued server cert can validate clients signed by it)..."
37+
kubectl get secret klio-server-ca -n "${SOURCE_NS}" -o jsonpath='{.data.tls\.crt}' | base64 -d |
38+
kubectl create secret generic klio-server-ca -n "${DEST_NS}" --from-file=tls.crt=/dev/stdin \
39+
--dry-run=client -o yaml | kubectl apply -f -
40+
41+
echo "Copying the OTel collector's certificate (public cert only, used as the trust anchor)..."
42+
kubectl get secret otel-collector-tls -n "${SOURCE_NS}" -o jsonpath='{.data.ca\.crt}' | base64 -d |
43+
kubectl create secret generic otel-collector-ca -n "${DEST_NS}" --from-file=ca.crt=/dev/stdin \
44+
--dry-run=client -o yaml | kubectl apply -f -
45+
46+
echo "Copying the shared OTel client certificate (full secret; not verified by the collector)..."
47+
kubectl get secret klio-server-otel-client-tls -n "${SOURCE_NS}" -o json |
48+
jq 'del(.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.creationTimestamp,.metadata.ownerReferences,.metadata.annotations)' |
49+
kubectl apply -n "${DEST_NS}" -f -
50+
51+
echo "Copying the cluster's client certificate (full secret, incl. private key)..."
52+
kubectl get secret "${CLIENT_SECRET_NAME}" -n "${SOURCE_NS}" -o json |
53+
jq 'del(.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.creationTimestamp,.metadata.ownerReferences,.metadata.annotations)' |
54+
kubectl apply -n "${DEST_NS}" -f -
55+
56+
echo "Done. You can now apply ${DEST_NS}'s kustomization."
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Client cert for cluster-e, backed by a SECOND Klio server also named
2+
# "klio-a" (see ../team-e), living in a different namespace than the
3+
# original klio-a in "default". Since a Server's StatefulSet pod name
4+
# equals the Server's own name, this second klio-a's pod is ALSO named
5+
# "klio-a-klio-0" -- an identical host_name to the original, but in a
6+
# different namespace. Generated here because the klio-server-ca Issuer is
7+
# namespace-scoped; copied into team-e by bootstrap-remote-server.sh.
8+
apiVersion: cert-manager.io/v1
9+
kind: Certificate
10+
metadata:
11+
name: cluster-e-klio-user
12+
spec:
13+
commonName: klio@cluster-e
14+
secretName: cluster-e-klio-user
15+
16+
duration: 2160h # 90d
17+
renewBefore: 360h # 15d
18+
19+
isCA: false
20+
usages:
21+
- client auth
22+
23+
issuerRef:
24+
name: klio-server-ca
25+
kind: Issuer
26+
group: cert-manager.io

operator/config/samples/opentelemetry-multi/kustomization.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
#
44
# Deploys two Klio servers (klio-a, klio-b) and two CNPG clusters
55
# (cluster-a -> klio-a, cluster-b -> klio-b) into the "default" namespace,
6-
# sharing one OTel collector / Jaeger / Prometheus stack. See ../team-c for
7-
# the third cluster, which lives in a different namespace and is backed by
8-
# klio-b.
6+
# sharing one OTel collector / Jaeger / Prometheus stack. See:
7+
# ../team-c for a third cluster in a different namespace, backed by klio-b
8+
# ../team-e for a second, independent server also named "klio-a" (so its
9+
# StatefulSet pod, and therefore host_name, collides with the original)
910
resources:
1011
- cluster_klio_otel_config.yaml
1112
- cluster_a.yaml
1213
- cluster_a_klio_client_auth.yaml
1314
- cluster_b.yaml
1415
- cluster_b_klio_client_auth.yaml
1516
- cluster_c_klio_client_auth.yaml
17+
- cluster_e_klio_client_auth.yaml
1618
- issuer.yaml
1719
- jaeger.yaml
1820
- klio_encryption.yaml
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
apiVersion: postgresql.cnpg.io/v1
2+
kind: Cluster
3+
metadata:
4+
name: cluster-e
5+
namespace: team-e
6+
spec:
7+
instances: 3
8+
imagePullPolicy: Always
9+
10+
projectedVolumeTemplate:
11+
sources:
12+
- secret:
13+
name: otel-collector-ca
14+
items:
15+
- key: ca.crt
16+
path: ca.crt
17+
- secret:
18+
name: klio-server-otel-client-tls
19+
items:
20+
- key: tls.crt
21+
path: tls.crt
22+
- key: tls.key
23+
path: tls.key
24+
25+
postgresql:
26+
pg_hba:
27+
- local replication all peer map=local
28+
29+
plugins:
30+
- name: klio.cnpg.io
31+
enabled: true
32+
parameters:
33+
pluginConfigurationRef: client-config-cluster-e
34+
35+
storage:
36+
size: 1Gi
37+
---
38+
apiVersion: klio.cnpg.io/v1alpha1
39+
kind: PluginConfiguration
40+
metadata:
41+
name: client-config-cluster-e
42+
namespace: team-e
43+
spec:
44+
serverAddress: klio-a
45+
clientSecretName: cluster-e-klio-user
46+
serverSecretName: klio-a-tls
47+
clusterName: cluster-e
48+
containers:
49+
- name: klio-plugin
50+
env:
51+
- name: OTEL_SERVICE_NAME
52+
value: "klio-plugin"
53+
envFrom:
54+
- configMapRef:
55+
name: cluster-klio-otel-config
56+
- name: klio-restore
57+
env:
58+
- name: OTEL_SERVICE_NAME
59+
value: "klio-restore"
60+
envFrom:
61+
- configMapRef:
62+
name: cluster-klio-otel-config
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Same settings as ../cluster_klio_otel_config.yaml, but the endpoint is
2+
# fully-qualified since the otel-collector Service lives in "default", not
3+
# in this namespace.
4+
apiVersion: v1
5+
kind: ConfigMap
6+
metadata:
7+
name: cluster-klio-otel-config
8+
namespace: team-e
9+
data:
10+
OTEL_RESOURCE_DETECTORS: "telemetry.sdk,host,os.type,process.executable.name"
11+
OTEL_TRACES_EXPORTER: "otlp"
12+
OTEL_METRICS_EXPORTER: "otlp"
13+
OTEL_EXPORTER_OTLP_PROTOCOL: "grpc"
14+
OTEL_EXPORTER_OTLP_ENDPOINT: "https://otel-collector.default.svc.cluster.local:4317"
15+
OTEL_EXPORTER_OTLP_COMPRESSION: "gzip"
16+
OTEL_EXPORTER_OTLP_TIMEOUT: "10000"
17+
OTEL_EXPORTER_OTLP_INSECURE: "false"
18+
OTEL_EXPORTER_OTLP_CERTIFICATE: "/projected/ca.crt"
19+
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE: "/projected/tls.crt"
20+
OTEL_EXPORTER_OTLP_CLIENT_KEY: "/projected/tls.key"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Local self-signed issuer for this namespace's OWN Klio server identity
2+
# (cert-manager Issuers are namespace-scoped, so ../issuer.yaml in "default"
3+
# can't be reused here).
4+
apiVersion: cert-manager.io/v1
5+
kind: Issuer
6+
metadata:
7+
name: selfsigned-issuer
8+
namespace: team-e
9+
spec:
10+
selfSigned: {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Same content as ../klio_encryption.yaml: reusing the same encryption
2+
# identity across servers is fine for this demo.
3+
apiVersion: v1
4+
stringData:
5+
secret-key: AGE-SECRET-KEY-1P7TWJ8H837DPVLSRVHAP0ZCZVHXJRTUS7S0A5VZAR49URW0Z9Q0SFW4HVM
6+
public-key: age16y2zu5qptkzateyuarcahs0am75qjpu0xd2ty2dvm5jusnfzxg0scjh5uu
7+
encryption-key: |
8+
-----BEGIN AGE ENCRYPTED FILE-----
9+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyMzJERW1QTUhjNGdlNmdi
10+
WmFLZ3FLL1lZclduUWEvZkJncGdJZmtVd1FzCmZwSHVTcWNMRFN6a2xpY0NHNVRv
11+
MlltSlNRY1B4bmNoOUxuZzZ5RENSSmsKLS0tIGNYUUNzSGVqNUZhS3VodnZ1cXNP
12+
T3JHS3JRSUNkR1RyeEROYnZYNnNSV00KUd3HyxvvqCyW6hC1sWNDn/xeptHsoz/e
13+
hxCgWoSJvfSfNk174njMwEeW+ic=
14+
-----END AGE ENCRYPTED FILE-----
15+
kind: Secret
16+
metadata:
17+
name: klio-encryption
18+
namespace: team-e
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Reuses the name "klio-a" (see ../klio_server_a.yaml in "default"). Its
2+
# StatefulSet pod is therefore also named "klio-a-klio-0", identical to the
3+
# original klio-a's host_name -- see team-e/README note in ../README.md.
4+
apiVersion: klio.cnpg.io/v1alpha1
5+
kind: Server
6+
metadata:
7+
name: klio-a
8+
namespace: team-e
9+
spec:
10+
queue:
11+
pvcTemplate:
12+
resources:
13+
requests:
14+
storage: 1Gi
15+
accessModes:
16+
- ReadWriteOnce
17+
tier1:
18+
cache:
19+
pvcTemplate:
20+
resources:
21+
requests:
22+
storage: 1Gi
23+
accessModes:
24+
- ReadWriteOnce
25+
26+
data:
27+
pvcTemplate:
28+
resources:
29+
requests:
30+
storage: 1Gi
31+
accessModes:
32+
- ReadWriteOnce
33+
34+
encryptionKeyFile:
35+
fileReference:
36+
volume:
37+
secret:
38+
secretName: klio-encryption
39+
path: encryption-key
40+
41+
identityFile:
42+
fileReference:
43+
volume:
44+
secret:
45+
secretName: klio-encryption
46+
path: secret-key
47+
48+
caSecretName: klio-server-ca
49+
tlsSecretName: klio-a-tls
50+
51+
image: registry.dev:5000/klio-testing:dev
52+
imagePullPolicy: Always
53+
54+
template:
55+
spec:
56+
containers:
57+
- name: server
58+
env:
59+
- name: NODE_NAME
60+
valueFrom:
61+
fieldRef:
62+
fieldPath: spec.nodeName
63+
- name: POD_UID
64+
valueFrom:
65+
fieldRef:
66+
fieldPath: metadata.uid
67+
- name: OTEL_SERVICE_NAME
68+
value: "klio-server-a-team-e"
69+
- name: OTEL_RESOURCE_ATTRIBUTES
70+
value: "k8s.node.name=$(NODE_NAME),k8s.pod.uid=$(POD_UID)"
71+
envFrom:
72+
- configMapRef:
73+
name: klio-server-otel-config
74+
volumeMounts:
75+
- mountPath: /otel
76+
name: otel
77+
volumes:
78+
- name: otel
79+
projected:
80+
sources:
81+
- secret:
82+
name: otel-collector-ca
83+
items:
84+
- key: ca.crt
85+
path: ca.crt
86+
- secret:
87+
name: klio-server-otel-client-tls
88+
items:
89+
- key: tls.crt
90+
path: tls.crt
91+
- key: tls.key
92+
path: tls.key
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# A SECOND server named "klio-a" (same name as the one in "default"), in a
2+
# different namespace. Its StatefulSet pod is therefore also named
3+
# "klio-a-klio-0" -- an identical host_name to the original klio-a, testing
4+
# whether $server (built from host_name alone) can disambiguate two
5+
# same-named servers across namespaces.
6+
apiVersion: cert-manager.io/v1
7+
kind: Certificate
8+
metadata:
9+
name: klio-a
10+
namespace: team-e
11+
spec:
12+
secretName: klio-a-tls
13+
commonName: klio-a
14+
dnsNames:
15+
- klio-a
16+
- klio-a.team-e
17+
- klio-a.team-e.svc
18+
19+
duration: 2160h # 90d
20+
renewBefore: 360h # 15d
21+
22+
isCA: false
23+
usages:
24+
- server auth
25+
26+
issuerRef:
27+
name: selfsigned-issuer
28+
kind: Issuer
29+
group: cert-manager.io
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Same settings as ../klio_server_otel_config.yaml, but the endpoint is
2+
# fully-qualified since the otel-collector Service lives in "default", not
3+
# in this namespace.
4+
apiVersion: v1
5+
kind: ConfigMap
6+
metadata:
7+
name: klio-server-otel-config
8+
namespace: team-e
9+
data:
10+
OTEL_RESOURCE_DETECTORS: "telemetry.sdk,host,os.type,process.executable.name"
11+
OTEL_TRACES_EXPORTER: "otlp"
12+
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: "grpc"
13+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "https://otel-collector.default.svc.cluster.local:4317"
14+
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION: "gzip"
15+
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: "10000"
16+
OTEL_EXPORTER_OTLP_TRACES_INSECURE: "false"
17+
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE: "/otel/ca.crt"
18+
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE: "/otel/tls.crt"
19+
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY: "/otel/tls.key"
20+
OTEL_METRICS_EXPORTER: "otlp"
21+
OTEL_METRIC_EXPORT_INTERVAL: "60000"
22+
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: "grpc"
23+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: "https://otel-collector.default.svc.cluster.local:4317"
24+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT: "60000"
25+
OTEL_EXPORTER_OTLP_METRICS_INSECURE: "false"
26+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE: "/otel/ca.crt"
27+
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE: "/otel/tls.crt"
28+
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY: "/otel/tls.key"

0 commit comments

Comments
 (0)