Добавлен OTLP-провайдер, реализовано отбрасывание метрик и трейсов в observability + добавлен перезапуск nginx при пересборке контейнеров (ошибка, когда докер меняет адреса сервисов)
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user