K8 Deployment¶
The Helm chart is a reference
The Helm chart below is a reference. Use it as is, change it to fit your environment, or deploy Harvest on Kubernetes any other way you prefer.
This page walks through one way to run Harvest on Kubernetes, using an example Helm chart from the Harvest repository. You can use the chart as it is, adjust it for your environment, or use it as a guide and write your own manifests. The chart's values.yaml describes every value you can set.
Each cluster you list runs in its own pod. The pod reads its password from a Kubernetes Secret, so the password is never in your values file.
Prerequisites¶
- Kubernetes 1.21 or later and Helm 3.10 or later
- Network access from the pods to the management address of each monitored system
- A user on each system with the permissions Harvest needs, see Prepare monitored systems
- To use the default Prometheus integration, the Prometheus Operator, for example kube-prometheus-stack. No Prometheus yet? See Install Prometheus.
Get the example chart¶
git clone --depth 1 https://github.com/NetApp/harvest.git
cd harvest
Install Prometheus (skip if you already have one)¶
Skip this if you already run the Prometheus Operator. This installs kube-prometheus-stack into the namespace
monitoring. It keeps data for one year or until it reaches 45GiB, whichever comes first. The data is stored on a
50Gi persistent volume from the cluster's default StorageClass, so it survives a pod restart. There are no memory
limits. Grow storage and retentionSize together to fit your clusters. The volume can be expanded later if the
StorageClass allows it.
cat > prometheus.yaml <<'EOF'
prometheus:
prometheusSpec:
retention: 1y # keep one year of data
retentionSize: 45GiB # stop before the volume is full
resources:
requests:
memory: 400Mi # no limits
storageSpec: # keep data when the pod restarts
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi # <====== adjust to your clusters
EOF
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace --values prometheus.yaml
The volume comes from the default StorageClass of your cluster. If kubectl get storageclass shows none marked
(default), add storageClassName: <name> under spec.
Wait until the pod prometheus-kube-prometheus-stack-prometheus-0 is Running:
kubectl get pods --namespace monitoring
The chart labels its PodMonitors release: kube-prometheus-stack, which is what this Prometheus selects. If your
Prometheus was installed with another release name, see PodMonitor.
Quick start¶
This installs Harvest for one ONTAP cluster into the namespace harvest.
1. Create the namespace and a Secret with the password
Replace <ontap-password> with the password of the user Harvest logs in with.
kubectl create namespace harvest
kubectl create secret generic cluster-a-credentials \
--namespace harvest \
--from-literal=password='<ontap-password>'
2. Write a values file
Change the lines marked <======: the cluster address and the username. Then paste the whole block. It creates
values.yaml.
cat > values.yaml <<'EOF'
harvestConfig:
Defaults:
use_insecure_tls: true # <====== adjust as needed to enable/disable TLS checks
Pollers:
cluster-a:
datacenter: DC-01
addr: cluster-a.example.com # <====== your cluster address
username: harvest # <====== your username
password: ${CLUSTER_A_PASSWORD} # placeholder, not the password
collectors:
- Rest
- RestPerf
extraEnvVars: # fills the placeholder from the Secret
- name: CLUSTER_A_PASSWORD
valueFrom:
secretKeyRef:
name: cluster-a-credentials
key: password
EOF
No Prometheus Operator? The install in the next step fails because the PodMonitor type does not exist. First create
no-operator.yaml from "No Prometheus Operator" under Prometheus integration, then add
--values no-operator.yaml to the install command.
3. Install
helm install harvest ./deploy/helm/harvest --namespace harvest --values values.yaml
This runs the Harvest release the chart was built for (the chart's appVersion), so every pod runs the same version.
Because the release is named harvest, the pod's name starts with harvest-harvest-poller-cluster-a. Use another
release name such as ontap to get shorter names.
To run another Harvest release, add its tag. Release tags end in -1, for example 26.08.0-1:
helm install harvest ./deploy/helm/harvest --namespace harvest --values values.yaml \
--set image.tag=<release-tag>
For the newest release or a nightly build, use a moving tag and pull it every time a pod starts:
helm install harvest ./deploy/helm/harvest --namespace harvest --values values.yaml \
--set image.tag=latest --set image.pullPolicy=Always
Use --set image.tag=nightly for a nightly build.
4. Verify
kubectl get pods --namespace harvest
Wait until the pod is Running and 1/1. Then look at the metrics:
kubectl port-forward --namespace harvest deploy/harvest-harvest-poller-cluster-a 12990:12990
In another terminal:
curl -s http://localhost:12990/metrics
Metrics appear after the first collection finishes, usually within a minute.
Check that Prometheus has the data. Start a port-forward to Prometheus:
kubectl port-forward --namespace monitoring svc/kube-prometheus-stack-prometheus 9090:9090
In another terminal, ask Prometheus whether the scrape works (1 means up):
curl -s 'http://localhost:9090/api/v1/query' --data-urlencode 'query=up{namespace="harvest"}'
Then ask for Harvest data from your cluster. A number above zero means the data is there:
curl -s 'http://localhost:9090/api/v1/query' --data-urlencode 'query=count({datacenter="DC-01"})'
The queries need a minute or two after the pod starts: the first collection plus one scrape.
You can also open http://localhost:9090, run the same queries on the Query page, or open Status > Targets and look
for podMonitor/harvest/... with state UP.
Optional settings¶
None of these are needed for the quick start.
Credentials from files (optional)
Harvest expands ${VAR} in harvest.yml from the environment when it starts
(variable expansion). The quick start uses this to keep the
password in a Secret. Create the Secret any way you like: kubectl, External Secrets,
Vault, or sealed-secrets. The chart only references it by name.
Certificates, a CA bundle and a credentials file are files. Mount
them from a Secret with extraVolumes and extraVolumeMounts, then point the poller setting at the mounted path.
This example switches cluster-a to
certificate authentication. Replace the file names in
the first command with your own:
kubectl create secret generic cluster-a-tls --namespace harvest \
--from-file=tls.crt=cluster-a.crt --from-file=tls.key=cluster-a.key
cat > certs.yaml <<'EOF'
harvestConfig:
Pollers:
cluster-a:
auth_style: certificate_auth
ssl_cert: /opt/harvest/certs/tls.crt
ssl_key: /opt/harvest/certs/tls.key
extraVolumes:
- name: certs
secret:
secretName: cluster-a-tls
extraVolumeMounts:
- name: certs
mountPath: /opt/harvest/certs
readOnly: true
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values certs.yaml
Helm merges the files in order, so certs.yaml only adds to cluster-a and values.yaml stays as it is.
Resources (optional)
Each poller requests 100m CPU and 256Mi memory by default and has no limits, so a large cluster is never killed for using more memory. Adjust the requests to your clusters and add limits for production. They apply to every poller:
cat > resources.yaml <<'EOF'
poller:
resources:
requests: {cpu: 200m, memory: 512Mi}
limits: {memory: 1Gi}
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values resources.yaml
Collectors and templates (optional)
Choose collectors per poller with collectors. To change a collector's schedule or enable more objects, add
collectorsExtensions to the poller, see
extend an existing object:
cat > collectors.yaml <<'EOF'
harvestConfig:
Pollers:
cluster-a:
collectorsExtensions:
Rest:
jitter: 1m
schedule:
- data: 5m
RestPerf:
objects:
Qtree: qtree.yaml
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values collectors.yaml
Private registry (optional)
To use a mirror or private registry, name a docker-registry Secret that exists in the harvest namespace:
cat > registry.yaml <<'EOF'
image:
registry: registry.example.com
repository: team/harvest
pullSecrets: [my-regcred]
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values registry.yaml
Prometheus integration¶
The default needs nothing more than the quick start. The sections below are alternatives, use at most one of them.
The chart creates one PodMonitor for each cluster, so ten clusters give ten PodMonitors. A PodMonitor is a small Prometheus Operator object that tells Prometheus which pod to scrape. It does not run anything itself. If you do not run the Prometheus Operator, turn the PodMonitors off and scrape the pods yourself.
The commands in each section below start from the quick start values.yaml.
PodMonitor (default)¶
Nothing to add, this is what the quick start installed. Each PodMonitor scrapes its poller's container port http on
/metrics every minute. Change the interval, labels and more under monitoring.scrape.podMonitor, or per poller with
the podMonitor key. Set podMonitor.enabled: false on a poller to skip its PodMonitor.
The PodMonitor sets the Prometheus instance label to the poller name, for example cluster-a. This keeps instance
the same when a pod restarts and gets a new IP address, so your graphs and queries keep working. To change it, add your
own entry under relabelings, which runs after the chart's.
Your Prometheus must select the PodMonitors. The chart labels them release: kube-prometheus-stack. Find the label your
Prometheus expects:
kubectl get prometheus --all-namespaces -o yaml | grep -A3 podMonitorSelector
If it shows another value, set the label to match:
cat > prometheus-label.yaml <<'EOF'
monitoring:
scrape:
podMonitor:
labels:
release: <your-prometheus-release>
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values prometheus-label.yaml
Or tell kube-prometheus-stack to select every PodMonitor:
helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack --namespace monitoring --reuse-values \
--set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false
Admin service discovery (optional)
Instead of PodMonitors, you can let pollers register with a small admin pod that Prometheus reads using
HTTP service discovery. New and removed pollers are
picked up without touching Prometheus. Enable one or the other, not both.
cat > admin-sd.yaml <<'EOF'
monitoring:
scrape:
adminSD:
enabled: true
podMonitor:
enabled: false
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values admin-sd.yaml
kubectl get pods,svc --namespace harvest
The chart creates the admin pod, a headless Service and a ScrapeConfig. It also adds the settings pollers need to
find the admin pod to the generated harvest.yml, see
heart_beat_url.
The ScrapeConfig is labeled release: kube-prometheus-stack, change it with monitoring.scrape.adminSD.labels in the
same way as above. It also sets the instance label to the poller name, the same as the PodMonitor does.
Check which pollers registered. Prometheus refreshes the list about once a minute, so allow a few minutes after an install or restart:
kubectl port-forward --namespace harvest svc/harvest-harvest-admin-sd 8887:8887
In another terminal:
curl -s http://localhost:8887/api/v1/sd
To go back to PodMonitors, upgrade without admin-sd.yaml.
Basic authentication for admin service discovery (optional)
To protect the list with basic authentication, set Admin.httpsd.auth_basic and give the password to the admin pod,
every poller, and Prometheus. The Secret must be in the harvest namespace:
kubectl create secret generic sd-credentials --namespace harvest \
--from-literal=username=sd --from-literal=password='<sd-password>'
cat > admin-auth.yaml <<'EOF'
harvestConfig:
Admin:
httpsd:
listen: :8887
auth_basic:
username: sd
password: ${SD_PASSWORD}
poller:
extraEnvVars: # every poller pod
- name: SD_PASSWORD
valueFrom:
secretKeyRef: {name: sd-credentials, key: password}
admin:
extraEnvVars: # the admin pod
- name: SD_PASSWORD
valueFrom:
secretKeyRef: {name: sd-credentials, key: password}
monitoring:
scrape:
adminSD:
httpSDConfig: # Prometheus
basicAuth:
username: {name: sd-credentials, key: username}
password: {name: sd-credentials, key: password}
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest \
--values values.yaml --values admin-sd.yaml --values admin-auth.yaml
Check the list with the same username and password. Start a port-forward to the admin Service:
kubectl port-forward --namespace harvest svc/harvest-harvest-admin-sd 8887:8887
In another terminal:
curl -s -u sd:<sd-password> http://localhost:8887/api/v1/sd
Without -u the admin pod returns 401.
A poller without the password sends its heartbeat with empty credentials, so the admin pod rejects it and the poller is missing from the list.
TLS for admin service discovery (optional)
To serve the list over https, the admin pod needs a certificate and key. Every poller needs the certificate too: it
trusts the admin pod with it, and a poller exits at startup if the file is missing. Prometheus needs the certificate
as its CA. The certificate must name the admin Service, <release>-harvest-admin-sd.<namespace>.svc, in its
subject alternative names. With the release harvest in the namespace harvest that is
harvest-harvest-admin-sd.harvest.svc. Harvest needs a TLS 1.3 certificate.
This creates a self-signed one and stores it in a Secret:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -days 365 \
-keyout tls.key -out tls.crt -subj "/CN=harvest-harvest-admin-sd" \
-addext "subjectAltName=DNS:harvest-harvest-admin-sd.harvest.svc"
kubectl create secret generic sd-tls --namespace harvest \
--from-file=tls.crt=tls.crt --from-file=tls.key=tls.key
cat > admin-tls.yaml <<'EOF'
harvestConfig:
Admin:
httpsd:
tls:
cert_file: /opt/harvest/sd-tls/tls.crt
key_file: /opt/harvest/sd-tls/tls.key
admin:
extraVolumes: # the admin pod gets the certificate and the key
- name: sd-tls
secret:
secretName: sd-tls
extraVolumeMounts:
- name: sd-tls
mountPath: /opt/harvest/sd-tls
readOnly: true
poller:
extraVolumes: # every poller gets the certificate only
- name: sd-tls
secret:
secretName: sd-tls
items:
- key: tls.crt
path: tls.crt
extraVolumeMounts:
- name: sd-tls
mountPath: /opt/harvest/sd-tls
readOnly: true
monitoring:
scrape:
adminSD:
httpSDConfig: # Prometheus
tlsConfig:
ca:
secret: {name: sd-tls, key: tls.crt}
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest \
--values values.yaml --values admin-sd.yaml --values admin-tls.yaml
The chart switches the heartbeat URL and the ScrapeConfig URL to https when cert_file is set. Add admin-auth.yaml
to the command if you also use basic authentication. Check the list. Start a port-forward to the admin Service:
kubectl port-forward --namespace harvest svc/harvest-harvest-admin-sd 8887:8887
In another terminal:
curl -s --cacert tls.crt --resolve harvest-harvest-admin-sd.harvest.svc:8887:127.0.0.1 \
https://harvest-harvest-admin-sd.harvest.svc:8887/api/v1/sd
No Prometheus Operator (optional)
Turn both off. The chart then creates only the pollers, and you point Prometheus at the pods yourself.
cat > no-operator.yaml <<'EOF'
monitoring:
scrape:
podMonitor:
enabled: false
adminSD:
enabled: false
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values no-operator.yaml
Every pod serves metrics on its container port named http (default 12990) at /metrics. In plain Prometheus, discover
them with kubernetes_sd_configs of role pod and keep the ones labeled app.kubernetes.io/name: harvest.
Dashboards¶
The chart does not install Grafana dashboards. Import the dashboards from the grafana/dashboards directory of the
Harvest release, see Import Grafana dashboards.
Managing clusters¶
Everything is declarative: describe the clusters you want, then run helm upgrade. Helm creates, updates or deletes
the pollers to match.
Note
Helm does not remember which --values files you used. Pass the same ones on every helm upgrade. Without them
the release goes back to the chart defaults and your pollers are removed. See what is applied with
helm get values harvest --namespace harvest.
Add a cluster¶
Create its Secret, describe the cluster in a new file, and upgrade. Replace the password and the two marked values:
kubectl create secret generic cluster-b-credentials --namespace harvest \
--from-literal=password='<ontap-password>'
cat > cluster-b.yaml <<'EOF'
harvestConfig:
Pollers:
cluster-b:
datacenter: DC-02
addr: cluster-b.example.com # <====== your cluster address
username: harvest # <====== your username
password: ${CLUSTER_B_PASSWORD}
collectors:
- Rest
- RestPerf
extraEnvVars:
- name: CLUSTER_B_PASSWORD
valueFrom:
secretKeyRef:
name: cluster-b-credentials
key: password
EOF
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml --values cluster-b.yaml
kubectl get pods --namespace harvest
The pod of cluster-b is created, and the pod of cluster-a is not restarted. E-Series, StorageGRID and other systems
are added the same way. Set collectors as described in the Harvest configuration,
for example [Eseries, EseriesPerf] for E-Series.
Change a cluster¶
Edit the cluster's entry, for example change datacenter in values.yaml, then upgrade:
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml
Add a --values flag for every other file you use, such as --values cluster-b.yaml.
Only the pod of the cluster you changed restarts. Changing a shared section (Exporters, Defaults, Admin)
restarts all pollers.
Remove a cluster¶
Leave the cluster's file out of the next upgrade. The pod and PodMonitor are deleted. The Secret is not managed by the chart, so delete it yourself:
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml
kubectl delete secret cluster-b-credentials --namespace harvest
If the cluster is in values.yaml, delete its entry there instead.
Poller names and ports¶
- A poller name may contain upper case letters and underscores (
AFF250,Dc_1). Kubernetes object names use a lower case form (dc-1),--pollerandharvest.ymlkeep the name you wrote. - Two names that become the same Kubernetes name (
Dc_1anddc-1), or a name that makes the object name longer than 63 characters, failhelm installwith a message that names the poller. prom_portis optional. Each poller is in its own pod, so they can all usepoller.defaultPromPort(12990).
Upgrades¶
Run helm upgrade with the same --values files whenever you change your values or want a newer chart:
git pull
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml
helm history harvest --namespace harvest
Each poller uses the Recreate strategy, so the old pod stops before the new one starts and a poller is never running
twice, which would duplicate metrics. Expect a short gap in metrics when a poller restarts.
Update the Harvest image¶
By default the chart runs the Harvest release in its appVersion, and the pods stay on that version until you upgrade.
Running git pull and then helm upgrade moves them to the version of the newer chart. To run another release, upgrade
with its tag. Release tags end in -1, for example 26.08.0-1:
helm upgrade harvest ./deploy/helm/harvest --namespace harvest --values values.yaml \
--set image.tag=<release-tag>
To see the image the pods run:
kubectl get pods --namespace harvest -o 'custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image,IMAGEID:.status.containerStatuses[0].imageID'
If you use a moving tag (latest or nightly), add --set image.pullPolicy=Always as well. The pods then pull the tag
every time they start, so they can run different versions. To move all of them to the newest image, restart the pods:
kubectl rollout restart deployment --namespace harvest -l app.kubernetes.io/instance=harvest
To go back to the chart's version, run helm upgrade without the --set image.* flags.
Roll back¶
To roll back to the previous revision, or to one from helm history:
helm rollback harvest --namespace harvest
helm rollback harvest 1 --namespace harvest
A rollback restores your values, including the image tag. The exception is a moving tag such as latest or nightly: a
rollback keeps the tag but not the image behind it. To run an older version, run helm upgrade with
--set image.tag=<version> as shown above.
Troubleshooting¶
Start with the pods and their logs. Harvest logs to the container's standard error.
kubectl get pods --namespace harvest
kubectl describe pod --namespace harvest -l app.kubernetes.io/name=harvest
kubectl logs --namespace harvest deploy/harvest-harvest-poller-cluster-a
The image is a distroless debug image, so there is a shell if you need one:
kubectl exec -it --namespace harvest deploy/harvest-harvest-poller-cluster-a -- sh
| Symptom | Cause and fix |
|---|---|
helm install fails with invalid Kubernetes name |
A poller name makes the object name longer than 63 characters. Shorten the name or set fullnameOverride |
helm install fails with both map to the Kubernetes name |
Two poller names only differ by case or punctuation, rename one |
helm install fails with invalid prom_port |
prom_port is not a number from 1 to 65535 |
Pod CrashLoopBackOff, log shows an authentication error |
Wrong user or password, or the Secret or key does not exist. Check kubectl describe pod for secret ... not found |
| Pod running, log shows connection timeouts to the system | The pod cannot reach the management address. Test from the namespace with kubectl run -it --rm curl --image=curlimages/curl -- curl -vk https://<addr>; check NetworkPolicies and egress proxies |
Pod ImagePullBackOff or ErrImagePull |
The tag does not exist (release tags end in -1, for example 26.08.0-1), or the registry needs image.pullSecrets. Check kubectl describe pod |
Pod is Running but not Ready |
The metrics port is not open yet. The startup probe allows about a minute; look at the poller log for errors |
TLS errors such as x509: certificate signed by unknown authority |
Mount your CA bundle and set ca_cert, or set use_insecure_tls: true under harvestConfig.Defaults as in the quick start |
Poller is UP in Kubernetes but there is no Prometheus target |
Prometheus does not select the PodMonitor. Compare kubectl get podmonitor -n harvest --show-labels with the Prometheus podMonitorSelector |
up is 1 but count({datacenter="DC-01"}) returns nothing |
The poller is scraped but has not collected data yet, or cannot reach the system. Wait a minute, then look at the poller log for errors |
Admin service discovery: /api/v1/sd returns [] |
heart_beat_url is missing or wrong, or the poller cannot reach the admin Service. Look for Failed connecting to admin node in the poller log |
Uninstall¶
helm uninstall harvest --namespace harvest
kubectl delete namespace harvest
This deletes the pods, ConfigMaps, PodMonitors and the admin resources. Deleting the namespace also deletes the Secrets you created in it.
If you installed Prometheus above and no longer need it:
helm uninstall kube-prometheus-stack --namespace monitoring
kubectl delete namespace monitoring