Добавлен OTLP-провайдер, реализовано отбрасывание метрик и трейсов в observability + добавлен перезапуск nginx при пересборке контейнеров (ошибка, когда докер меняет адреса сервисов)

This commit is contained in:
mi
2026-07-29 15:15:40 +03:00
parent 3ed7239efa
commit 41e19005fb
43 changed files with 3016 additions and 83 deletions
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -Eeuo pipefail
PRIVATE_IP="${PRIVATE_IP:-192.168.0.5}"
MIN_MEMORY_KIB=$((4 * 1024 * 1024))
MIN_DISK_KIB=$((30 * 1024 * 1024))
errors=0
ok() { printf 'OK %s\n' "$*"; }
warn() { printf 'WARN %s\n' "$*" >&2; }
fail() { printf 'FAIL %s\n' "$*" >&2; errors=$((errors + 1)); }
echo "=== ОС и ресурсы ==="
. /etc/os-release
[[ "${ID:-}" == ubuntu && "${VERSION_ID:-}" == "24.04" ]] \
&& ok "Ubuntu ${VERSION_ID}" || warn "Проверено для Ubuntu 24.04; найдено ${PRETTY_NAME:-unknown}"
memory_kib="$(awk '/MemTotal/ {print $2}' /proc/meminfo)"
((memory_kib >= MIN_MEMORY_KIB)) && ok "RAM не менее 4 GiB" || fail "SigNoz требует не менее 4 GiB RAM"
disk_kib="$(df -Pk /opt 2>/dev/null | awk 'NR==2 {print $4}')"
((disk_kib >= MIN_DISK_KIB)) && ok "/opt: свободно не менее 30 GiB" \
|| fail "Для начальной установки рекомендуется не менее 30 GiB свободно в /opt"
echo
echo "=== Сеть ==="
ip -br -4 address
ip -4 route
ip -4 addr show | grep -Fq "inet ${PRIVATE_IP}/" \
&& ok "Приватный адрес ${PRIVATE_IP} назначен" \
|| fail "Адрес ${PRIVATE_IP} не найден ни на одном интерфейсе"
if getent hosts archive.ubuntu.com registry-1.docker.io ghcr.io >/dev/null; then
ok "DNS работает"
else
fail "DNS не разрешает адреса репозиториев"
fi
for url in \
https://archive.ubuntu.com \
https://download.docker.com \
https://registry-1.docker.io \
https://github.com \
https://signoz.io; do
if curl -4fsSI --connect-timeout 8 --max-time 15 "$url" >/dev/null; then
ok "доступен $url"
else
fail "нет доступа к $url"
fi
done
echo
echo "=== Занятость портов ==="
for port in 8080 4317 4318; do
if ss -H -lnt "( sport = :$port )" | grep -q .; then
fail "TCP-порт $port уже занят: $(ss -H -lntp "( sport = :$port )")"
else
ok "TCP-порт $port свободен"
fi
done
echo
timedatectl show --property=NTPSynchronized --property=Timezone --no-pager
if ((errors)); then
printf '\nОбнаружено ошибок: %d\n' "$errors" >&2
exit 1
fi
echo "Проверка завершена успешно."
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -Eeuo pipefail
PRIVATE_INTERFACE="${PRIVATE_INTERFACE:-eth1}"
PRIVATE_MAC="${PRIVATE_MAC:-fa:16:3e:b6:c6:70}"
PRIVATE_ADDRESS="${PRIVATE_ADDRESS:-192.168.0.5/24}"
NETPLAN_FILE="/etc/netplan/60-signoz-private.yaml"
if [[ $EUID -ne 0 ]]; then
echo "Запустите через sudo: sudo $0" >&2
exit 1
fi
actual_mac="$(cat "/sys/class/net/${PRIVATE_INTERFACE}/address" 2>/dev/null || true)"
if [[ "$actual_mac" != "$PRIVATE_MAC" ]]; then
echo "Остановка: MAC ${PRIVATE_INTERFACE} равен '${actual_mac:-не найден}', ожидался ${PRIVATE_MAC}." >&2
exit 1
fi
install -d -m 0700 /root/netplan-backup
if [[ -f "$NETPLAN_FILE" ]]; then
cp -a "$NETPLAN_FILE" "/root/netplan-backup/60-signoz-private.yaml.$(date +%Y%m%d-%H%M%S)"
fi
cat >"$NETPLAN_FILE" <<EOF
network:
version: 2
ethernets:
${PRIVATE_INTERFACE}:
match:
macaddress: "${PRIVATE_MAC}"
set-name: "${PRIVATE_INTERFACE}"
addresses:
- "${PRIVATE_ADDRESS}"
mtu: 1500
optional: true
EOF
chmod 0600 "$NETPLAN_FILE"
netplan generate
echo "Создан ${NETPLAN_FILE}:"
cat "$NETPLAN_FILE"
echo
echo "Netplan временно применит конфигурацию на 120 секунд."
echo "Подтвердите её в приглашении только после проверки второй SSH-сессии."
netplan try --timeout 120
echo
ip -br -4 address show "$PRIVATE_INTERFACE"
ip -4 route show dev "$PRIVATE_INTERFACE"
ip route get 192.168.0.1
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ $EUID -ne 0 ]]; then
echo "Запустите через sudo: sudo $0" >&2
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y ca-certificates curl gnupg jq
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
. /etc/os-release
cat >/etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: ${VERSION_CODENAME}
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
install -d -m 0755 /etc/docker
cat >/etc/docker/daemon.json <<'EOF'
{
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
EOF
systemctl enable --now docker
systemctl restart docker
docker version
docker compose version
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -Eeuo pipefail
PRIVATE_IP="${PRIVATE_IP:-192.168.0.5}"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
CASTING="${ROOT_DIR}/casting.yaml"
COMPOSE="${ROOT_DIR}/pours/deployment/compose.yaml"
if [[ $EUID -ne 0 ]]; then
echo "Запустите через sudo: sudo $0" >&2
exit 1
fi
command -v docker >/dev/null || { echo "Сначала установите Docker." >&2; exit 1; }
ip -4 addr show | grep -Fq "inet ${PRIVATE_IP}/" || {
echo "На ВМ отсутствует приватный адрес ${PRIVATE_IP}." >&2
exit 1
}
grep -Fq "${PRIVATE_IP}:4317:4317" "$CASTING" || {
echo "PRIVATE_IP не совпадает с адресом в casting.yaml." >&2
exit 1
}
if ! command -v foundryctl >/dev/null; then
installer="$(mktemp)"
trap 'rm -f "$installer"' EXIT
curl -fsSL https://signoz.io/foundry.sh -o "$installer"
bash "$installer"
if [[ -x /root/.local/bin/foundryctl ]]; then
install -m 0755 /root/.local/bin/foundryctl /usr/local/bin/foundryctl
elif [[ ! -x /usr/local/bin/foundryctl ]]; then
echo "foundryctl не найден после установки." >&2
exit 1
fi
fi
cd "$ROOT_DIR"
foundryctl gauge -f "$CASTING"
foundryctl forge -f "$CASTING"
[[ -f "$COMPOSE" ]] || { echo "Foundry не создал $COMPOSE" >&2; exit 1; }
docker compose -f "$COMPOSE" config --quiet
# Защита от случайной публикации UI или OTLP на всех интерфейсах.
grep -Fq "${PRIVATE_IP}:4317:4317" "$COMPOSE"
grep -Fq "${PRIVATE_IP}:4318:4318" "$COMPOSE"
grep -Fq "127.0.0.1:8080:8080" "$COMPOSE"
if grep -Eq '(^|[[:space:]"-])0\.0\.0\.0:(8080|4317|4318):' "$COMPOSE"; then
echo "Остановка: обнаружена публикация служебного порта на 0.0.0.0." >&2
exit 1
fi
docker compose -f "$COMPOSE" pull
docker compose -f "$COMPOSE" up -d --remove-orphans
echo "Ожидание SigNoz UI..."
deadline=$((SECONDS + 300))
until curl -fsS --max-time 5 http://127.0.0.1:8080/api/v1/health >/dev/null 2>&1; do
if ((SECONDS >= deadline)); then
echo "UI не стал готов за 5 минут." >&2
docker compose -f "$COMPOSE" ps -a
exit 1
fi
sleep 5
done
docker compose -f "$COMPOSE" ps -a
cat <<'EOF'
Базовый SigNoz запущен, но первичная настройка ещё не завершена.
Откройте UI через SSH-туннель и создайте первого администратора/организацию.
После этого перезапустите ingester и выполните scripts/30-verify-signoz.sh.
EOF
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -Eeuo pipefail
PRIVATE_IP="${PRIVATE_IP:-192.168.0.5}"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
COMPOSE="${ROOT_DIR}/pours/deployment/compose.yaml"
errors=0
wait_for() {
local label="$1"
local timeout_seconds="$2"
shift
shift
local deadline=$((SECONDS + timeout_seconds))
printf '%-34s' "$label"
until "$@" >/dev/null 2>&1; do
if ((SECONDS >= deadline)); then
echo "FAIL"
errors=$((errors + 1))
return 0
fi
sleep 5
done
echo "OK"
}
container_healthy() {
[[ "$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$1" 2>/dev/null)" == healthy ]]
}
one_shot_ok() {
local state
state="$(docker inspect --format '{{.State.Status}} {{.State.ExitCode}}' "$1" 2>/dev/null)" || return 1
[[ "$state" == "running 0" || "$state" == "exited 0" ]]
}
[[ -f "$COMPOSE" ]] || { echo "Не найден $COMPOSE" >&2; exit 1; }
echo "=== SigNoz: состояние ==="
docker compose -f "$COMPOSE" ps
echo
wait_for "PostgreSQL healthy" 300 container_healthy \
signoz-metastore-postgres-0
wait_for "ClickHouse Keeper healthy" 300 container_healthy \
signoz-telemetrykeeper-clickhousekeeper-0
wait_for "ClickHouse healthy" 300 container_healthy \
signoz-telemetrystore-clickhouse-0-0
wait_for "SigNoz healthy" 300 container_healthy \
signoz-signoz-0
wait_for "SigNoz API/UI" 300 curl -fsS --max-time 5 \
http://127.0.0.1:8080/api/v1/health
wait_for "OTLP gRPC TCP 4317" 300 timeout 3 bash -c \
"exec 3<>/dev/tcp/${PRIVATE_IP}/4317"
wait_for "OTLP HTTP 4318" 300 curl -fsS --max-time 5 \
-H "Content-Type: application/json" \
--data '{"resourceSpans":[]}' \
"http://${PRIVATE_IP}:4318/v1/traces"
wait_for "User scripts completed" 300 one_shot_ok \
signoz-telemetrystore-clickhouse-user-scripts
wait_for "Migrations running/completed" 300 one_shot_ok \
signoz-telemetrystore-migrator
echo
docker compose -f "$COMPOSE" ps -a
echo
ss -lntp "( sport = :8080 or sport = :4317 or sport = :4318 )"
if ((errors)); then
echo "Проверка завершилась с ошибками: $errors" >&2
echo "Логи: docker compose -f '$COMPOSE' logs --tail=200" >&2
exit 1
fi
echo "SigNoz готов."
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -Eeuo pipefail
PRIVATE_IP="${PRIVATE_IP:-192.168.0.5}"
ADMIN_CIDR="${ADMIN_CIDR:-192.168.0.0/24}"
OTLP_SOURCE="${OTLP_SOURCE:-}"
if [[ $EUID -ne 0 ]]; then
echo "Запустите через sudo." >&2
exit 1
fi
if [[ -z "$OTLP_SOURCE" ]]; then
echo "Укажите приватный IP backend, например:" >&2
echo " sudo OTLP_SOURCE=192.168.0.1 $0" >&2
exit 1
fi
ip -4 addr show | grep -Fq "inet ${PRIVATE_IP}/" || {
echo "Приватный адрес ${PRIVATE_IP} не найден." >&2
exit 1
}
echo "SSH будет разрешён только из ${ADMIN_CIDR}."
echo "OTLP будет разрешён только от ${OTLP_SOURCE}."
echo "До продолжения проверьте отдельную SSH-сессию через приватную сеть."
read -r -p "Введите APPLY для применения правил: " answer
[[ "$answer" == APPLY ]] || { echo "Отменено."; exit 1; }
apt-get update
apt-get install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw logging medium
ufw allow from "$ADMIN_CIDR" to "$PRIVATE_IP" port 22 proto tcp comment "SigNoz admin SSH"
ufw allow from "$OTLP_SOURCE" to "$PRIVATE_IP" port 4317 proto tcp comment "HAN OTLP gRPC"
ufw allow from "$OTLP_SOURCE" to "$PRIVATE_IP" port 4318 proto tcp comment "HAN OTLP HTTP"
ufw --force enable
ufw status verbose
cat <<'EOF'
Важно: Docker-публикации могут обходить цепочку UFW INPUT. Основная фильтрация
источника OTLP должна оставаться в облачной группе безопасности. casting.yaml
дополнительно привязывает OTLP только к приватному IP, а UI — к loopback.
EOF