Introduction

Monitoring my Synology NAS has been at the back of my mind for a very long time. While Synology does provide some basic observability in DSM, it is rather cumbersome to click around every time just to find all the metrics and dashboards I care about. I wanted to a single pane of glass when it comes to observing my homelab. In my K3s cluster, I already have Prometheus and Grafana provisioned to monitor my cluster’s health. My goal is to have relevant Synology NAS metrics scraped by Prometheus, and eventually visualized on Grafana.

Synology NAS monitoring

As shown above, I have decided to use the Prometheus SNMP exporter to get metrics from my NAS. In short, Simple Network Management Protocol (SNMP) is a protocol used for managing and monitoring of network-connected devices, such as a switch, UPS, and NAS.

Enabling SNMP on Synology DSM

Synology SNMP

Login to your Synology DSM and enable SNMP from the Control Planel as shown above. For security purposes:

  • SNMPv3 is recommended over SNMPv1 and SNMPv2c
  • SHA is recommended over MD5
  • AES is recommended over DES

If you have enabled firewall, you will have to allow SNMP port 161 so that the SNMP exporter can access without issues.

Generating SNMP config file

In this section, we will prepare the SNMP config file which is required when you run the SNMP exporter later.

  1. Clone the SNMP exporter repository.
    git clone https://github.com/prometheus/snmp_exporter.git
    
  2. Checkout the release tag.
    git checkout tags/v0.26.0
    
  3. Edit generator/generator.yml. The changes I have done:
    • auths.synology to specify the proper authentication required as configured previously in Synology DSM
    • Removed all modules except modules.if_mib, modules.ip_mib, and modules.synology
    • Added OIDs for host resource system and storage to modules.synology.walk to get some host resource metrics for the Grafana dashboard later
    • Replace hrStorageIndex label with hrStorageDescr using modules.synology.lookups to be compatible with the Grafana dashboard later
    auths:
        synology:
            version: 3
            username: REPLACE_ME
            security_level: authPriv
            password: REPLACE_ME
            auth_protocol: SHA
            priv_protocol: AES
            priv_password: REPLACE_ME
    modules:
        # Default IF-MIB interfaces table with ifIndex.
        if_mib:
            walk: [sysUpTime, interfaces, ifXTable]
            lookups:
                - source_indexes: [ifIndex]
                    lookup: ifAlias
                - source_indexes: [ifIndex]
                    # Uis OID to avoid conflict with PaloAlto PAN-COMMON-MIB.
                    lookup: 1.3.6.1.2.1.2.2.1.2 # ifDescr
                - source_indexes: [ifIndex]
                    # Use OID to avoid conflict with Netscaler NS-ROOT-MIB.
                    lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName
            overrides:
                ifAlias:
                    ignore: true # Lookup metric
                ifDescr:
                    ignore: true # Lookup metric
                ifName:
                    ignore: true # Lookup metric
                ifType:
                    type: EnumAsInfo
        # Default IP-MIB with ipv4InterfaceTable for example.
        ip_mib:
            walk: [ipv4InterfaceTable]
        # Synology MIBs can be found here:
        #   http://www.synology.com/support/snmp_mib.php
        #   http://dedl.synology.com/download/Document/MIBGuide/Synology_MIB_File.zip
        synology:
            walk:
                - laNames
                - laLoadInt
                - ssCpuUser
                - ssCpuSystem
                - ssCpuIdle
                - memory
                - 1.3.6.1.4.1.6574.1 # synoSystem
                - 1.3.6.1.4.1.6574.2 # synoDisk
                - 1.3.6.1.4.1.6574.3 # synoRaid
                - 1.3.6.1.4.1.6574.4 # synoUPS
                - 1.3.6.1.4.1.6574.5 # synologyDiskSMART
                - 1.3.6.1.4.1.6574.6 # synologyService
                - 1.3.6.1.4.1.6574.101 # storageIO
                - 1.3.6.1.4.1.6574.102 # spaceIO
                - 1.3.6.1.4.1.6574.104 # synologyiSCSILUN
                - 1.3.6.1.2.1.25.1 # hrSystem
                - 1.3.6.1.2.1.25.2 # hrStorage
            lookups:
                - source_indexes: [spaceIOIndex]
                    lookup: spaceIODevice
                    drop_source_indexes: true
                - source_indexes: [storageIOIndex]
                    lookup: storageIODevice
                    drop_source_indexes: true
                - source_indexes: [serviceInfoIndex]
                    lookup: serviceName
                    drop_source_indexes: true
                - source_indexes: [diskIndex]
                    lookup: diskID
                    drop_source_indexes: true
                - source_indexes: [raidIndex]
                    lookup: raidName
                    drop_source_indexes: true
                - source_indexes: [laIndex]
                    lookup: laNames
                    drop_source_indexes: true
                - source_indexes: [hrStorageIndex]
                    lookup: hrStorageDescr
                    drop_source_indexes: true
            overrides:
                diskModel:
                    type: DisplayString
                diskSMARTAttrName:
                    type: DisplayString
                diskSMARTAttrStatus:
                    type: DisplayString
                diskSMARTInfoDevName:
                    type: DisplayString
                diskType:
                    type: DisplayString
                modelName:
                    type: DisplayString
                raidFreeSize:
                    type: gauge
                raidName:
                    type: DisplayString
                raidTotalSize:
                    type: gauge
                serialNumber:
                    type: DisplayString
                serviceName:
                    type: DisplayString
                version:
                    type: DisplayString
    
  4. (Optional) Edit generator/Makefile. Remove the unnecessary MIBs so they won’t be downloaded when generating the SNMP config. Specifically, I have left these in the mibs rule.
    mibs: \
        $(MIBDIR)/IANA-CHARSET-MIB.txt \
        $(MIBDIR)/IANA-IFTYPE-MIB.txt \
        $(MIBDIR)/IANA-PRINTER-MIB.txt \
        $(MIBDIR)/.net-snmp \
        $(MIBDIR)/.synology
    
  5. Generate the SNMP config.
    # if using docker
    make docker-generate
    # if running locally
    make generate
    
  6. You should see your SNMP config in generator/snmp.yml.

Deploying SNMP exporter

I have decided to deploy the SNMP exporter in my K3s cluster, so the instructions will be catered for that. There are no strict requirements where you run the SNMP exporter, as long as it has sufficient resources and access to your NAS, e.g. you could run it on a server using systemd.

Here are the Kubernetes manifests:

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: snmp-config
type: Opaque
stringData:
  snmp.yml: |
    REPLACE_ME_WITH_GENERATED_SNMP_YML    

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: snmp-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: snmp-exporter
  template:
    metadata:
      labels:
        app.kubernetes.io/name: snmp-exporter
    spec:
      containers:
        - name: snmp-exporter
          image: quay.io/prometheus/snmp-exporter:v0.26.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 9116
              name: http
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: 9116
            initialDelaySeconds: 0
            periodSeconds: 10
            timeoutSeconds: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /
              port: 9116
            initialDelaySeconds: 0
            periodSeconds: 10
            timeoutSeconds: 1
            failureThreshold: 3
          resources:
            limits:
              cpu: 250m
              memory: 180Mi
            requests:
              cpu: 50m
              memory: 180Mi
          volumeMounts:
            - mountPath: /etc/snmp_exporter
              name: config
      volumes:
        - name: config
          secret:
            secretName: snmp-config
      serviceAccountName: snmp-exporter

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: snmp-exporter
spec:
  ports:
    - name: http
      port: 9116
      protocol: TCP
      targetPort: 9116
  selector:
    app.kubernetes.io/name: snmp-exporter

# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: snmp-exporter

# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: snmp-exporter
spec:
  endpoints:
    - interval: 5m
      port: http
      path: /snmp
      params:
        target:
          - REPLACE_ME_WITH_NAS_IP
        auth:
          - synology
        module:
          - if_mib
          - synology
  selector:
    matchLabels:
      app.kubernetes.io/name: snmp-exporter

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: monitoring
commonLabels:
  app.kubernetes.io/name: snmp-exporter
resources:
  - deployment.yaml
  - secret.yaml
  - service.yaml
  - serviceaccount.yaml
  - servicemonitor.yaml

The above uses Kustomize to manage and customize the manifests. It also depends on the ServiceMonitor custom resource from the Prometheus operator project. The ServiceMonitor spec is then automatically translated to a Prometheus config and then reloaded in Prometheus once applied.

At this point, Prometheus is aware and begins scraping the /snmp endpoint exposed by SNMP exporter. You can verify everything is working by querying a metric e.g. raidName. If you would like to see the full list of metrics exposed by SNMP exporter, you can port-forward to the SNMP exporter and open the following in your browser: http://localhost:9116/snmp?target=REPLACE_ME_WITH_NAS_IP&auth=synology&module=synology&module=if_mib.

Visualizing on Grafana

Creating dashboards from scratch seems like a daunting task, especially when there are lots of metrics lying around. Thankfully, there are kind folks who shared their dashboards on Grafana and I found a comprehensive one for Synology NAS. After some modifications, fixes, and adaptations to my environment, here is the latest JSON model of my dashboard.

Do note that the scrape interval I have configured is 5 minutes to reduce the data points I collect since it is for my homelab. If you have done the same, you might want to update the scrape interval in the Prometheus data source in Grafana to match accordingly. Otherwise, $__rate_interval used by the dashboard will not yield enough data points for rate calculations. More on that here.

Thanks for following along up till this point! This concludes my guide on monitoring Synology NAS with Prometheus, Grafana, and SNMP exporter. Hope you found this guide useful and happy monitoring!

References