Предположим, что «пятая» команда занимает своими метриками в «старом» VMetrics-кластере тенанты с номерами 12, 13, 14. Это означает, что команда занимает тенанты 12:0, 13:0, 14:0.
Пример копирования из старого VMetrics-кластера в новый с изменением номеров тенантов
1. Заметки
-
В старом кластере метрики различных команд хранились в тенантах без учёта номера проекта, то есть одна команда могла занимать несколько номеров
vm_account_id. В парадигме VMetrics, где в случае пропуска номера проекта в номере тенанта, считается, что номер проекта равен нулю. -
В новом кластере «пятой» команде выдлелен лишь один номер
vm_account-id, предположим, что это номер5. В пределах этого номера аккаунта, команда «пять» может занять все 32 бита vm_project_id. То есть для технический метрик выделить тенант5:1. Для каких-либо бизнес-метрик тенанты5:130,5:131,5:132. И т.д.
2. Подготовка к запуску скрипта
-
В переменных скрипта
START_MONTHиEND_MONTHпоменяйте даты начального месяца и последнего месяца копируемого диапазона. При желании скрипт можно переделать на использование в диапазоне дней, часов, и т.д. -
В переменной
ARGS_LISTотредактируйте записи в массиве в виде:
«номер исходного тенанта в старом кластере»
«номер целевого аккаунта»
«номер целевого проекта» -
Помните, что метрики храняться чанками, поэтому при попадании какой-либо метрики в, например, последнюю минуту желаемого диапазона, будет скопирован весь чанк, то есть в новом кластере можно будет увидеть метрики, дата которых выходят за границы скопированного диапазона. Я наблюдал +несколько часов.
-
В результате копирования, в новом VMetrics-кластере могут появиться дублирующие записи. Для решения этой проблемы включите в новом VMetrics-кластере опцию дедупликации равной 1ms и на vmstorage’ах, и на vmselect’ах.
3. Запуск скрипта
Я выполнял запуск с помощью команды:
unbuffer ./vmctl-copying-16.sh | tee script.$(date +%y%m%d-%H%M%S).log
4. Содержимое скрипта
Details
#!/bin/bash
exec 3>script.$(date +%y%m%d-%H%M%S).stderr.log
BASH_XTRACEFD=3
set -xeuo pipefail
# -------------------------------
# Constants
# -------------------------------
DST_ADDRESS='https://mon-vmauth-vip.example.org'
DST_BEARER_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxx'
START_MONTH="2024-09"
END_MONTH="2024-12"
DEFAULT_CHUNK="month"
LOG_FILE="vmctl-migration.$(date +%y%m%d-%H%M%S).log"
DRY_RUN="${DRY_RUN:-false}"
ARGS_LIST=(
"0 0 1"
"1 2 1"
"2 2 2"
"3 2 3"
"4 2 4"
"5 13 1"
"6 7 1"
"7 3 1000"
"8 1 1"
"9 9 1"
"21 7 0"
)
# -------------------------------
# Logging
# -------------------------------
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG_FILE"
}
log_hr() {
echo "--------------------------------------------------------------------------------" | tee -a "$LOG_FILE"
}
log_section() {
echo "================================================================================" | tee -a "$LOG_FILE"
}
log_err() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}
# -------------------------------
# HTTP helper
# -------------------------------
safe_curl() {
local url="$1"
log_err "CMD: curl -sf \"$url\""
local result
if ! result=$(curl -sf "$url"); then
log_err "❌ curl error for: $url"
return 1
fi
log_err "RESP: $result"
printf '%s' "$result"
}
# -------------------------------
# Count check (no filters allowed)
# -------------------------------
check_series_count() {
local src_address=$1
local url="${src_address}/api/v1/series/count"
log "Checking series count: $url"
local response count
if ! response=$(safe_curl "$url"); then
log "⚠ Cannot fetch series count, skipping full-tenant copy"
return 1
fi
if ! count=$(echo "$response" | jq -r '.data[]' 2>/dev/null); then
log "⚠ Cannot parse series count, skipping full-tenant copy"
return 1
fi
if [[ -z "$count" ]]; then
log "⚠ Empty series count, skipping full-tenant copy"
return 1
fi
log "Series count: $count"
if (( count >= 1000000 )); then
log "⚠ Too many series ($count), skipping full-tenant copy"
return 1
fi
return 0
}
# -------------------------------
# Run vmctl
# -------------------------------
run_vmctl() {
local src=$1 dst=$2 tok=$3 acc=$4 prj=$5
local ts=$6 te=$7 filt=$8 step=$9
log_hr
log "▶ vmctl: step=${step}, filter=${filt:-<none>}"
if [ "$DRY_RUN" = "true" ]; then
log "(dry-run) skipping vmctl"
return 0
fi
if ! vmctl-prod vm-native -s \
-vm-native-backoff-retries 1 \
-vm-native-src-addr "$src" \
-vm-native-dst-addr "$dst" \
-vm-native-dst-bearer-token "$tok" \
-vm-extra-label "vm_account_id=${acc}" \
-vm-extra-label "vm_project_id=${prj}" \
-vm-native-filter-time-start "$ts" \
-vm-native-filter-time-end "$te" \
-vm-native-step-interval="$step" \
${filt:+-vm-native-filter-match "$filt"}; then
log "❌ vmctl failed"
return 1
fi
log "✔ vmctl succeeded"
return 0
}
# -------------------------------
# Metadata fetcher: metrics only
# -------------------------------
fetch_metric_names_for_range() {
local src=$1 start_ts=$2 end_ts=$3
local url="${src}/api/v1/label/__name__/values?start=${start_ts}&end=${end_ts}"
log_err "Fetch metrics via: $url"
local resp
resp=$(safe_curl "$url") || return 1
echo "$resp" | jq -r '.data[]' 2>/dev/null | sort -u
}
# -------------------------------
# Main
# -------------------------------
for entry in "${ARGS_LIST[@]}"; do
read -r SRC_ID DST_ID PRJ_ID <<< "$entry"
SRC="http://127.0.0.1:8000/select/${SRC_ID}/prometheus"
TIME_START="${START_MONTH}-01T00:00:00+03:00"
TIME_END=$(date -d "$(date -d "${END_MONTH}-01 +1 month" +%Y-%m-%d) -1 sec" +%Y-%m-%dT%H:%M:%S%:z)
TS_START=$(date -d "$TIME_START" +%s)
TS_END=$(date -d "$TIME_END" +%s)
log_section
log "TENANT ${SRC_ID} → ${DST_ID}:${PRJ_ID}"
# 1) Full-tenant copy
if check_series_count "$SRC"; then
run_vmctl "$SRC" "$DST_ADDRESS" "$DST_BEARER_TOKEN" "$DST_ID" "$PRJ_ID" \
"$TIME_START" "$TIME_END" "" "month" && continue
else
log "⏭ skip full-tenant"
fi
log_hr
log "▶ per-metric copy"
# 2) Fetch metrics list
METRICS=$(fetch_metric_names_for_range "$SRC" "$TS_START" "$TS_END") || continue
for METRIC in $METRICS; do
FILTER="{__name__=\"$METRIC\"}"
log_hr
log "Metric: $METRIC"
# 3) Try steps in sequence
for STEP in month week day hour minute; do
if run_vmctl "$SRC" "$DST_ADDRESS" "$DST_BEARER_TOKEN" "$DST_ID" "$PRJ_ID" \
"$TIME_START" "$TIME_END" "$FILTER" "$STEP"; then
# success at this granularity, go to next metric
continue 2
fi
done
# 4) All steps failed
log "❌ Failed to migrate: $METRIC"
done
done
set +x
exec 3>&-
5. Объяснение работы скрипта (по памяти)
-
Поначалу я пытался лить как есть с помощью одной команды. Это вызывало большую нагрузку на исходный кластер, а на некоторых метриках скрипт падал с ошибкой.
-
В шестнадцатой версии скрипта пришёл к проверке тенанта на количество временных рядов и, если их больше 100000?, копирование продолжается по одной метрике.