From e6238b580b479ae145e41082ed461cd36b7762df Mon Sep 17 00:00:00 2001 From: rare-magma Date: Fri, 27 Sep 2024 17:14:17 +0200 Subject: [PATCH] add kv stats Signed-off-by: rare-magma --- Makefile | 2 + README.md | 21 +++- cloudflare-exporter.service | 1 + cloudflare_exporter.sh | 170 +++++++++++++++++++++++++++++ cloudflare_kv_namespaces_list.conf | 0 docker-compose.yml | 1 + 6 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 cloudflare_kv_namespaces_list.conf diff --git a/Makefile b/Makefile index 96541d3..0f7a816 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ install: && chmod +x $${HOME}/.local/bin/cloudflare_exporter.sh \ && cp --no-clobber cloudflare_exporter.conf $${HOME}/.config/cloudflare_exporter.conf \ && cp --no-clobber cloudflare_zone_list.json $${HOME}/.config/cloudflare_zone_list.json \ + && cp --no-clobber cloudflare_kv_namespaces_list.conf $${HOME}/.config/cloudflare_kv_namespaces_list.conf \ && chmod 400 $${HOME}/.config/cloudflare_exporter.conf \ && cp cloudflare-exporter.timer $${HOME}/.config/systemd/user/ \ && cp cloudflare-exporter.service $${HOME}/.config/systemd/user/ \ @@ -16,6 +17,7 @@ uninstall: @rm -f $${HOME}/.local/bin/cloudflare_exporter.sh \ && rm -f $${HOME}/.config/cloudflare_exporter.conf \ && rm -f $${HOME}/.config/cloudflare_zone_list.json \ + && rm -f $${HOME}/.config/cloudflare_kv_namespaces_list.conf \ && systemctl --user disable --now cloudflare-exporter.timer \ && rm -f $${HOME}/.config/.config/systemd/user/cloudflare-exporter.timer \ && rm -f $${HOME}/.config/systemd/user/cloudflare-exporter.service diff --git a/README.md b/README.md index 84b7da4..7c988b6 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ Bash script that uploads the Cloudflare Analytics API data to influxdb on an hou docker build . --tag cloudflare-exporter ``` -1. Configure `cloudflare_exporter.conf` and `cloudflare_zone_list.json` (see the configuration section below). +1. Configure `cloudflare_exporter.conf`, `cloudflare_zone_list.json` and `cloudflare_kv_namespaces.conf` (see the configuration section below). 1. Run it. ```bash - docker run --rm --init --tty --interactive --read-only --cap-drop ALL --security-opt no-new-privileges:true --cpus 2 -m 64m --pids-limit 16 --volume ./cloudflare_exporter.conf:/app/cloudflare_exporter.conf:ro --volume ./cloudflare_zone_list.json:/app/cloudflare_zone_list.json:ro ghcr.io/rare-magma/cloudflare-exporter:latest + docker run --rm --init --tty --interactive --read-only --cap-drop ALL --security-opt no-new-privileges:true --cpus 2 -m 64m --pids-limit 16 --volume ./cloudflare_exporter.conf:/app/cloudflare_exporter.conf:ro --volume ./cloudflare_zone_list.json:/app/cloudflare_zone_list.json:ro --volume ./cloudflare_kv_namespaces_list.conf:/app/cloudflare_kv_namespaces_list.conf:ro ghcr.io/rare-magma/cloudflare-exporter:latest ``` ### With the Makefile @@ -61,13 +61,14 @@ For convenience, you can install this exporter with the following command or fol make install $EDITOR $HOME/.config/cloudflare_exporter.conf $EDITOR $HOME/.config/cloudflare_zone_list.json +$EDITOR $HOME/.config/cloudflare_kv_namespaces_list.conf ``` ### Manually 1. Copy `cloudflare_exporter.sh` to `$HOME/.local/bin/` and make it executable. -2. Copy `cloudflare_exporter.conf` and `cloudflare_zone_list.json` to `$HOME/.config/`, configure them (see the configuration section below) and make them read only. +2. Copy `cloudflare_exporter.conf`, `cloudflare_zone_list.json` and `cloudflare_kv_namespaces_list.conf` to `$HOME/.config/`, configure them (see the configuration section below) and make them read only. 3. Copy the systemd unit and timer to `$HOME/.config/systemd/user/`: @@ -124,6 +125,15 @@ The zone list file should contain a list of zone ids and domain names in json fo ] ``` +### KV namespaces list file + +The KV namespaces list file is optional and should contain a KV namespace id per line: + +```plaintext +999999aba99dd9999ef99ab78965ab1c +111111aba11dd1111ef11ab11111ab1c +``` + ## Troubleshooting Run the script manually with bash set to trace: @@ -148,6 +158,8 @@ systemctl --user list-timers - cloudflare_stats_responses: Request statistics broken down by response status code - cloudflare_stats: General request statistics - cloudflare_stats_workers: Workers statistics grouped by hour +- cloudflare_stats_kv_ops: KV operation statistics grouped by hour +- cloudflare_stats_kv_storage: KV storage statistics ## Exported metrics example @@ -159,6 +171,8 @@ cloudflare_stats_ip,zone="example.com",ipType="searchEngine" requests=21 1703894 cloudflare_stats_responses,zone="example.com",status=403 requests=1 1703894400 cloudflare_stats,zone="example.com" bytes=2032039,cachedBytes=40607,cachedRequests=17,encryptedBytes=2020727,encryptedRequests=251,pageViews=178,requests=266,threats=0,uniqueVisitors=2 1703894400' cloudflare_stats_workers,account=aa0a0aa000a0000aa00a00aa0e000a0a,worker=worker-name status="scriptThrewException",cpuTimeP50=1246,cpuTimeP99=1246,durationP50=0.001246,durationP99=0.001246,responseBodySizeP50=0,responseBodySizeP99=0,wallTimeP50=1605,wallTimeP99=1605,clientDisconnects=0,cpuTimeUs=1246,duration=0.001246,errors=1,requests=1,responseBodySize=0,subrequests=0,wallTime=1605 1727340566 +cloudflare_stats_kv_ops,account=aa0a0aa000a0000aa00a00aa0e000a0a,namespace=999999aba99dd9999ef99ab78965ab1c actionType="read",result="hot_read",responseStatusCode=200,latencyMsP50=116,latencyMsP99=116,objectBytes=1737,requests=1 1727445600 +cloudflare_stats_kv_storage,account=aa0a0aa000a0000aa00a00aa0e000a0a,namespace=999999aba99dd9999ef99ab78965ab1c byteCount=5369,keyCount=1 1727442000 ``` ## Example grafana dashboard @@ -197,6 +211,7 @@ Delete the following files: ~/.local/bin/cloudflare_exporter.sh ~/.config/cloudflare_exporter.conf ~/.config/cloudflare_zone_list.json +~/.config/cloudflare_kv_namespaces_list.conf ~/.config/systemd/user/cloudflare-exporter.timer ~/.config/systemd/user/cloudflare-exporter.service ``` diff --git a/cloudflare-exporter.service b/cloudflare-exporter.service index f444705..bcdc458 100644 --- a/cloudflare-exporter.service +++ b/cloudflare-exporter.service @@ -6,6 +6,7 @@ After=network-online.target ExecStart=/home/%u/.local/bin/cloudflare_exporter.sh LoadCredential=creds:/home/%u/.config/cloudflare_exporter.conf LoadCredential=list:/home/%u/.config/cloudflare_zone_list.json +LoadCredential=namespaces_list:/home/%u/.config/cloudflare_kv_namespaces_list.conf Type=oneshot # Security hardening options diff --git a/cloudflare_exporter.sh b/cloudflare_exporter.sh index 0418162..47d1796 100755 --- a/cloudflare_exporter.sh +++ b/cloudflare_exporter.sh @@ -20,13 +20,16 @@ JQ=$(command -v jq) if [[ "${RUNNING_IN_DOCKER}" ]]; then source "/app/cloudflare_exporter.conf" CLOUDFLARE_ZONE_LIST=$($CAT /app/cloudflare_zone_list.json) + CLOUDFLARE_KV_NAMESPACES=$($CAT /app/cloudflare_kv_namespaces_list.conf) elif [[ -f $CREDENTIALS_DIRECTORY/creds ]]; then #shellcheck source=/dev/null source "$CREDENTIALS_DIRECTORY/creds" CLOUDFLARE_ZONE_LIST=$($CAT $CREDENTIALS_DIRECTORY/list) + CLOUDFLARE_KV_NAMESPACES=$($CAT $CREDENTIALS_DIRECTORY/namespaces_list) else source "./cloudflare_exporter.conf" CLOUDFLARE_ZONE_LIST=$($CAT ./cloudflare_zone_list.json) + CLOUDFLARE_KV_NAMESPACES=$($CAT ./cloudflare_kv_namespaces_list.conf) fi [[ -z "${INFLUXDB_HOST}" ]] && echo >&2 "INFLUXDB_HOST is empty. Aborting" && exit 1 @@ -42,6 +45,7 @@ fi RFC_CURRENT_DATE=$($DATE --rfc-3339=date) ISO_CURRENT_DATE_TIME=$($DATE --iso-8601=seconds) ISO_CURRENT_DATE_TIME_1H_AGO=$($DATE --iso-8601=seconds --date "1 hour ago") +ISO_CURRENT_DATE_TIME_2H_AGO=$($DATE --iso-8601=seconds --date "2 hour ago") INFLUXDB_URL="https://$INFLUXDB_HOST/api/v2/write?precision=s&org=$ORG&bucket=$BUCKET" CF_URL="https://api.cloudflare.com/client/v4/graphql" @@ -365,3 +369,169 @@ if [[ $cf_nb_invocations -gt 0 ]]; then --data-binary @- fi + +if [[ -n "${CLOUDFLARE_KV_NAMESPACES}" ]]; then + + for kv_namespace_id in $(echo "${CLOUDFLARE_KV_NAMESPACES}"); do + KV_GRAPHQL_QUERY=$( + cat <&2 + exit 1 + fi + + cf_kv_json_parsed=$(echo $cf_kv_json | $JQ ".data.viewer.accounts[0].kvOperationsAdaptiveGroups") + cf_stats_kv=$( + echo "$cf_kv_json_parsed" | + $JQ --raw-output " + (.[] | + [\"${CLOUDFLARE_ACCOUNT_TAG}\", + .dimensions.namespaceId, + .dimensions.actionType, + .dimensions.result, + .dimensions.responseStatusCode, + .quantiles.latencyMsP50, + .quantiles.latencyMsP99, + .sum.objectBytes, + .sum.requests, + (.dimensions.datetimeHour | fromdateiso8601) + ]) + | @tsv" | + $AWK '{printf "cloudflare_stats_kv_ops,account=%s,namespace=%s actionType=\"%s\",result=\"%s\",responseStatusCode=%s,latencyMsP50=%s,latencyMsP99=%s,objectBytes=%s,requests=%s %s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10}' + ) + + echo "$cf_stats_kv" | $GZIP | + $CURL --silent --fail --show-error \ + --request POST "${INFLUXDB_URL}" \ + --header 'Content-Encoding: gzip' \ + --header "Authorization: Token $INFLUXDB_API_TOKEN" \ + --header "Content-Type: text/plain; charset=utf-8" \ + --header "Accept: application/json" \ + --data-binary @- + + KV_STORAGE_GRAPHQL_QUERY=$( + cat <&2 + exit 1 + fi + + cf_kv_storage_json_parsed=$(echo $cf_kv_storage_json | $JQ ".data.viewer.accounts[0].kvStorageAdaptiveGroups") + cf_stats_kv_storage=$( + echo "$cf_kv_storage_json_parsed" | + $JQ --raw-output " + (.[] | + [\"${CLOUDFLARE_ACCOUNT_TAG}\", + .dimensions.namespaceId, + .max.byteCount, + .max.keyCount, + (.dimensions.datetimeHour | fromdateiso8601) + ]) + | @tsv" | + $AWK '{printf "cloudflare_stats_kv_storage,account=%s,namespace=%s byteCount=%s,keyCount=%s %s\n", $1, $2, $3, $4, $5}' + ) + + echo "$cf_stats_kv_storage" | $GZIP | + $CURL --silent --fail --show-error \ + --request POST "${INFLUXDB_URL}" \ + --header 'Content-Encoding: gzip' \ + --header "Authorization: Token $INFLUXDB_API_TOKEN" \ + --header "Content-Type: text/plain; charset=utf-8" \ + --header "Accept: application/json" \ + --data-binary @- + done + +fi diff --git a/cloudflare_kv_namespaces_list.conf b/cloudflare_kv_namespaces_list.conf new file mode 100644 index 0000000..e69de29 diff --git a/docker-compose.yml b/docker-compose.yml index 2bd7e81..17ceb57 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,5 +35,6 @@ services: volumes: - ./cloudflare_exporter.conf:/app/cloudflare_exporter.conf:ro - ./cloudflare_zone_list.json:/app/cloudflare_zone_list.json:ro + - ./cloudflare_kv_namespaces_list.conf:/app/cloudflare_kv_namespaces_list.conf:ro labels: net.reddec.scheduler.cron: "5 * * * *"