Files
linux-scripts/install-prometheus-stack.sh
T
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

1653 lines
44 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
#############################################################
#### Prometheus Stack Installer ####
#### For RHEL/Rocky/Alma, Oracle Linux, Debian & Ubuntu ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version: 3.1 ####
#### ####
#### Usage: ./install-prometheus-stack.sh [OPTIONS] ####
#############################################################
# Script defaults
INSTALL_PROMETHEUS=true
INSTALL_NODE_EXPORTER=true
INSTALL_BLACKBOX=true
INSTALL_ALERTMANAGER=true
INSTALL_MYSQL_EXPORTER=false
INSTALL_GRAFANA=true
INSTALL_LOKI=false
INSTALL_ALLOY=false
WEBSERVER="nginx"
INSTALL_WEBSERVER=true
ENABLE_TLS=false
UPDATE_MODE=false
DRY_RUN=false
CONFIG_FILE=""
SKIP_DEPS=false
# System variables
domain="example.com"
bindir="/usr/local/bin"
promdir="/etc/prometheus"
logfile="/var/log/prometheus-install.log"
# MySQL Exporter variables (can be overridden by config file)
mynum=2
myuser="exporter"
mypass="password"
myhost1="db.host1.example"
myhost2="db.host2.example"
myhost3="db.host3.example"
#########################
### Logging Functions ###
#########################
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$logfile"
}
log_error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" | tee -a "$logfile" >&2
}
log_info() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" | tee -a "$logfile"
}
#########################
### Utility Functions ###
#########################
show_help() {
cat << EOF
Prometheus Stack Installer
USAGE:
$0 [OPTIONS]
OPTIONS:
--prometheus Install Prometheus (default: true)
--node-exporter Install node_exporter (default: true)
--blackbox Install blackbox_exporter (default: true)
--alertmanager Install AlertManager (default: true)
--mysql-exporter Install MySQL exporter (default: false)
--grafana Install Grafana (default: true)
--loki Install Loki log storage (default: false)
--alloy Install Alloy log/metrics collector (default: false)
--webserver <type> Install web server: nginx, apache, caddy (default: nginx)
--no-webserver Skip web server installation
--enable-tls Enable TLS/SSL security between components
--all Install all components
--update Update existing installations
--domain <domain> Domain for reverse proxy configs (default: example.com)
--config-file <file> Load configuration from file
--dry-run Show what would be installed without doing it
--skip-deps Skip dependency installation
--help Show this help message
EXAMPLES:
$0 --prometheus --grafana --alloy
$0 --all --skip mysql-exporter
$0 --update --prometheus --grafana
$0 --dry-run --config-file prod.conf
CONFIG FILE FORMAT:
domain=example.com
myuser=dbuser
mypass=dbpassword
myhost1=db1.example.com
EOF
}
cleanup() {
if [[ -d "/tmp/prometheus-install-$$" ]]; then
rm -rf "/tmp/prometheus-install-$$"
fi
}
trap cleanup EXIT
check_component_installed() {
local component=$1
case $component in
"prometheus")
systemctl is-active --quiet prometheus 2>/dev/null || [[ -f "$bindir/prometheus" ]]
;;
"node_exporter")
systemctl is-active --quiet node_exporter 2>/dev/null || [[ -f "$bindir/node_exporter" ]]
;;
"blackbox_exporter")
systemctl is-active --quiet blackbox_exporter 2>/dev/null || [[ -f "$bindir/blackbox_exporter" ]]
;;
"alertmanager")
systemctl is-active --quiet alertmanager 2>/dev/null || [[ -f "$bindir/alertmanager" ]]
;;
"grafana")
systemctl is-active --quiet grafana-server 2>/dev/null || command -v grafana-server >/dev/null 2>&1
;;
"loki")
systemctl is-active --quiet loki 2>/dev/null || [[ -f "$bindir/loki" ]]
;;
"alloy")
systemctl is-active --quiet alloy 2>/dev/null || [[ -f "$bindir/alloy" ]]
;;
esac
}
#########################
### System Detection ###
#########################
detect_os() {
if [[ "$(command -v lsb_release)" ]]; then
OS=$(lsb_release -i | awk '{print $3}' | tr '[:upper:]' '[:lower:]')
OSVER=$(lsb_release -r | awk '{print $2}' | cut -d. -f1)
else
OS=$({ grep PRETTY_NAME /etc/os-release || true; } | sed 's/PRETTY_NAME=//g' | tr -d '="' | awk '{print $1}' | tr '[:upper:]' '[:lower:]')
OSVER=$({ grep VERSION_ID /etc/os-release || true; } | sed 's/VERSION_ID=//g' | tr -d '"' | cut -d. -f1)
fi
log_info "Detected OS: $OS version $OSVER"
}
setup_directories() {
if [[ -d "/usr/lib/systemd/system" ]]; then
psdir='/etc/systemd/system'
else
psdir='/usr/lib/systemd/system'
fi
# Create log directory
mkdir -p "$(dirname "$logfile")"
touch "$logfile"
}
#########################
### Package Management ###
#########################
setup_package_manager() {
case $OS in
"ubuntu"|"debian")
pkgmgr="apt -y"
;;
"red"|"centos"|"oracle"|"rocky"|"almalinux")
if command -v dnf >/dev/null 2>&1; then
pkgmgr="dnf -y"
else
pkgmgr="yum -y"
fi
;;
*)
log_error "Unsupported OS: $OS"
exit 1
;;
esac
log_info "Using package manager: $pkgmgr"
}
#########################
### Permission Check ###
#########################
check_permissions() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root! Login as root, or use sudo."
exit 1
fi
}
#########################
### User Management ###
#########################
create_prometheus_user() {
if ! grep -q prometheus /etc/passwd; then
log_info "Creating prometheus user and group"
groupadd --system prometheus
if [[ "$OS" == "ubuntu" || "$OS" == "debian" ]]; then
useradd -s /sbin/nologin --system -g prometheus prometheus
else
useradd -m -s /bin/false prometheus -g prometheus
fi
else
log_info "Prometheus user already exists"
fi
}
#########################
### Dependencies ###
#########################
install_dependencies() {
if [[ "$SKIP_DEPS" == "true" ]]; then
log_info "Skipping dependency installation"
return
fi
log_info "Installing dependencies"
if [[ ! "$(command -v wget)" ]]; then
$pkgmgr install wget
fi
if [[ ! "$(command -v curl)" ]]; then
$pkgmgr install curl
fi
if [[ ! "$(command -v tar)" ]]; then
$pkgmgr install tar
fi
if [[ ! "$(command -v unzip)" ]]; then
$pkgmgr install unzip
fi
}
##########################
### Install Prometheus ###
##########################
install_prometheus() {
log_info "Installing Prometheus"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install Prometheus"
return
fi
if check_component_installed "prometheus" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "Prometheus already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/prometheus"
mkdir -p "$workdir"
cd "$workdir"
# Create directories
mkdir -p "$promdir" /var/lib/prometheus
chown prometheus /var/lib/prometheus/
for dir in backups rules templates consoles console_libraries; do
mkdir -p "$promdir/${dir}"
chown -R prometheus. "$promdir/${dir}"
chmod -R 755 "$promdir/${dir}"
done
# Download latest Prometheus
log_info "Downloading Prometheus"
curl -s https://api.github.com/repos/prometheus/prometheus/releases/latest | \
grep browser_download_url | \
grep linux-amd64 | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download Prometheus"
exit 1
}
tar -xzf prometheus*.tar.gz
cd prometheus-*/
# Install binaries
mv prometheus promtool "$bindir/"
# Install config if not exists or in update mode
if [[ ! -f "$promdir/prometheus.yml" ]] || [[ "$UPDATE_MODE" == "true" ]]; then
if [[ -f "$promdir/prometheus.yml" ]]; then
cp "$promdir/prometheus.yml" "$promdir/backups/prometheus.yml.$(date +%Y%m%d_%H%M%S)"
fi
mv prometheus.yml "$promdir/"
fi
mv consoles/ console_libraries/ "$promdir/" || true
chown -R prometheus. /var/lib/prometheus/ "$promdir/"
# SELinux context for RHEL 8+
if [[ "$OS" == "red" && "$OSVER" -ge 8 ]]; then
restorecon -rv "$bindir/prometheus" || true
fi
# Create systemd service
create_prometheus_service
systemctl daemon-reload
systemctl enable prometheus
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart prometheus
else
systemctl start prometheus
fi
log_info "Prometheus installation completed"
}
create_prometheus_service() {
cat > "$psdir/prometheus.service" << 'EOF'
[Unit]
Description=Prometheus Time Series Collection and Processing Server
Documentation=https://prometheus.io/docs/introduction/overview/
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/prometheus \
--config.file /etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/data \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090 \
--web.external-url= \
--enable-feature=new-service-discovery-manager,exemplar-storage,extra-scrape-metrics
Restart=always
RestartSec=5s
SyslogIdentifier=prometheus
[Install]
WantedBy=multi-user.target
EOF
# Create default config if it doesn't exist
if [[ ! -f "$promdir/prometheus.yml" ]]; then
create_prometheus_config
fi
}
create_prometheus_config() {
cat > "$promdir/prometheus.yml" << 'EOF'
# Global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
scrape_timeout: 15s # scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape: Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label 'job=<job_name>' to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'server_metrics'
scrape_interval: 5s
static_configs:
- targets: ['localhost:9100']
labels:
alias: Prometheus Server
EOF
}
#############################
### Install node_exporter ###
#############################
install_node_exporter() {
log_info "Installing node_exporter"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install node_exporter"
return
fi
if check_component_installed "node_exporter" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "node_exporter already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/node_exporter"
mkdir -p "$workdir"
cd "$workdir"
# Download latest node_exporter
log_info "Downloading node_exporter"
curl -s https://api.github.com/repos/prometheus/node_exporter/releases/latest | \
grep browser_download_url | \
grep linux-amd64 | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download node_exporter"
exit 1
}
tar -xzf node_exporter*.tar.gz
cd node_exporter-*/
mv node_exporter "$bindir/"
chown prometheus. "$bindir/node_exporter"
# SELinux context for RHEL 8+
if [[ "$OS" == "red" && "$OSVER" -ge 8 ]]; then
restorecon -rv "$bindir/node_exporter" || true
fi
# Create systemd service
create_node_exporter_service
systemctl daemon-reload
systemctl enable node_exporter
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart node_exporter
else
systemctl start node_exporter
fi
log_info "node_exporter installation completed"
}
create_node_exporter_service() {
cat > "$psdir/node_exporter.service" << 'EOF'
[Unit]
Description=Prometheus Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=root
Group=root
Type=simple
ExecStart=/usr/local/bin/node_exporter $OPTIONS
[Install]
WantedBy=multi-user.target
EOF
# Create default options
echo 'OPTIONS="--collector.ethtool --collector.interrupts --collector.processes --collector.systemd --collector.tcpstat"' > /etc/default/node_exporter
}
########################
### Install BlackBox ###
########################
install_blackbox() {
log_info "Installing blackbox_exporter"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install blackbox_exporter"
return
fi
if check_component_installed "blackbox_exporter" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "blackbox_exporter already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/blackbox"
mkdir -p "$workdir"
cd "$workdir"
# Download latest blackbox_exporter
log_info "Downloading blackbox_exporter"
curl -s https://api.github.com/repos/prometheus/blackbox_exporter/releases/latest | \
grep browser_download_url | \
grep linux-amd64 | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download blackbox_exporter"
exit 1
}
tar -xzf blackbox_exporter*.tar.gz
cd blackbox_exporter-*/
mv blackbox_exporter "$bindir/"
chown prometheus. "$bindir/blackbox_exporter"
# Install config
mkdir -p "$promdir"
if [[ ! -f "$promdir/blackbox.yml" ]] || [[ "$UPDATE_MODE" == "true" ]]; then
if [[ -f "$promdir/blackbox.yml" ]]; then
cp "$promdir/blackbox.yml" "$promdir/backups/blackbox.yml.$(date +%Y%m%d_%H%M%S)"
fi
mv blackbox.yml "$promdir/"
fi
chown -R prometheus. "$promdir/"
# SELinux context for RHEL 8+
if [[ "$OS" == "red" && "$OSVER" -ge 8 ]]; then
restorecon -rv "$bindir/blackbox_exporter" || true
fi
# Create systemd service
create_blackbox_service
systemctl daemon-reload
systemctl enable blackbox_exporter
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart blackbox_exporter
else
systemctl start blackbox_exporter
fi
# Add to prometheus config if not already present
add_blackbox_to_prometheus_config
log_info "blackbox_exporter installation completed"
}
create_blackbox_service() {
cat > "$psdir/blackbox_exporter.service" << 'EOF'
[Unit]
Description=Prometheus Blackbox Exporter Http/Https Monitoring
After=network.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/blackbox_exporter \
--config.file /etc/prometheus/blackbox.yml \
--web.listen-address=":9115"
Restart=always
[Install]
WantedBy=multi-user.target
EOF
}
add_blackbox_to_prometheus_config() {
if ! grep -q "job_name.*blackbox" "$promdir/prometheus.yml" 2>/dev/null; then
log_info "Adding blackbox configuration to Prometheus"
cat >> "$promdir/prometheus.yml" << 'EOF'
- job_name: 'blackbox'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
#### Local Targets ####
- http://localhost:9090
#### Remote Targets ####
#- https://google.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9115
EOF
# Restart prometheus to reload config
if systemctl is-active --quiet prometheus; then
systemctl reload prometheus || systemctl restart prometheus
fi
fi
}
#######################
### Install Grafana ###
#######################
install_grafana() {
log_info "Installing Grafana"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install Grafana"
return
fi
if check_component_installed "grafana" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "Grafana already installed, skipping"
return
fi
case $OS in
"ubuntu"|"debian")
install_grafana_debian
;;
"red"|"centos"|"oracle"|"rocky"|"almalinux")
install_grafana_rhel
;;
*)
log_error "Unsupported OS for Grafana installation: $OS"
return 1
;;
esac
systemctl daemon-reload
systemctl enable grafana-server
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart grafana-server
else
systemctl start grafana-server
fi
log_info "Grafana installation completed"
}
install_grafana_debian() {
# Add Grafana APT repository
$pkgmgr update
$pkgmgr install -y software-properties-common wget
wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key
echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | tee -a /etc/apt/sources.list.d/grafana.list
$pkgmgr update
$pkgmgr install grafana
}
install_grafana_rhel() {
# Add Grafana YUM repository
cat > /etc/yum.repos.d/grafana.repo << 'EOF'
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
$pkgmgr install grafana
}
###################
### Install Loki ###
###################
install_loki() {
log_info "Installing Loki"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install Loki"
return
fi
if check_component_installed "loki" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "Loki already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/loki"
mkdir -p "$workdir"
cd "$workdir"
# Download latest Loki
log_info "Downloading Loki"
curl -s https://api.github.com/repos/grafana/loki/releases/latest | \
grep browser_download_url | \
grep loki-linux-amd64.zip | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download Loki"
exit 1
}
unzip loki-linux-amd64.zip
mv loki-linux-amd64 "$bindir/loki"
chown prometheus. "$bindir/loki"
chmod +x "$bindir/loki"
# Create Loki directories
mkdir -p /var/lib/loki/{wal,chunks,index}
chown -R prometheus. /var/lib/loki
# Create Loki config directory
mkdir -p "$promdir"
chown -R prometheus. "$promdir"
# Create Loki config
create_loki_config
# Create systemd service
create_loki_service
systemctl daemon-reload
systemctl enable loki
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart loki
else
systemctl start loki
fi
log_info "Loki installation completed"
}
create_loki_config() {
if [[ ! -f "$promdir/loki.yml" ]] || [[ "$UPDATE_MODE" == "true" ]]; then
if [[ -f "$promdir/loki.yml" ]]; then
cp "$promdir/loki.yml" "$promdir/backups/loki.yml.$(date +%Y%m%d_%H%M%S)"
fi
cat > "$promdir/loki.yml" << 'EOF'
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
path_prefix: /var/lib/loki
storage:
filesystem:
chunks_directory: /var/lib/loki/chunks
rules_directory: /var/lib/loki/rules
replication_factor: 1
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration
# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/
#
# Statistics help us better understand how Loki is used, and they show us performance
# levels for most users. This helps us prioritize features and documentation.
# For more information on what's sent, look at
# https://github.com/grafana/loki/blob/main/pkg/usagestats/stats.go
# Refer to the buildReport method to see what goes into a report.
#
# If you would like to disable reporting, uncomment the following lines:
analytics:
reporting_enabled: false
EOF
chown prometheus. "$promdir/loki.yml"
fi
}
create_loki_service() {
cat > "$psdir/loki.service" << 'EOF'
[Unit]
Description=Loki log aggregation system
After=network.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/loki -config.file /etc/prometheus/loki.yml
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
}
####################
### Install Alloy ###
####################
install_alloy() {
log_info "Installing Grafana Alloy"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install Grafana Alloy"
return
fi
if check_component_installed "alloy" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "Alloy already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/alloy"
mkdir -p "$workdir"
cd "$workdir"
# Download latest Alloy
log_info "Downloading Grafana Alloy"
curl -s https://api.github.com/repos/grafana/alloy/releases/latest | \
grep browser_download_url | \
grep -E "alloy-.*-linux-amd64\.zip" | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download Grafana Alloy"
exit 1
}
unzip alloy-*-linux-amd64.zip
mv alloy-*-linux-amd64 "$bindir/alloy"
chown prometheus. "$bindir/alloy"
chmod +x "$bindir/alloy"
# Create config directory
mkdir -p "$promdir/alloy"
chown -R prometheus. "$promdir/alloy"
# Create basic Alloy config
create_alloy_config
# Create systemd service
create_alloy_service
systemctl daemon-reload
systemctl enable alloy
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart alloy
else
systemctl start alloy
fi
log_info "Grafana Alloy installation completed"
}
create_alloy_config() {
cat > "$promdir/alloy/config.alloy" << 'EOF'
// Basic Alloy configuration for log collection
logging {
level = "info"
format = "logfmt"
}
// Loki logs endpoint
loki.write "default" {
endpoint {
url = "http://localhost:3100/loki/api/v1/push"
}
}
// Local file logs
local.file_match "varlog" {
path_targets = ["/var/log/*.log"]
}
loki.source.file "varlog" {
targets = local.file_match.varlog.targets
forward_to = [loki.write.default.receiver]
}
// Prometheus metrics
prometheus.scrape "default" {
targets = [
{"__address__" = "localhost:9090", "job" = "prometheus"},
{"__address__" = "localhost:9100", "job" = "node"},
]
forward_to = [prometheus.remote_write.default.receiver]
}
prometheus.remote_write "default" {
endpoint {
url = "http://localhost:9090/api/v1/write"
}
}
EOF
chown prometheus. "$promdir/alloy/config.alloy"
}
create_alloy_service() {
cat > "$psdir/alloy.service" << 'EOF'
[Unit]
Description=Grafana Alloy
Documentation=https://grafana.com/docs/alloy/
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/alloy run /etc/prometheus/alloy/config.alloy
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
}
########################
### Install Web Server ###
########################
install_webserver() {
log_info "Installing and configuring $WEBSERVER"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install and configure $WEBSERVER"
return
fi
case $WEBSERVER in
"nginx")
install_nginx
;;
"apache")
install_apache
;;
"caddy")
install_caddy
;;
*)
log_error "Unsupported web server: $WEBSERVER"
exit 1
;;
esac
log_info "$WEBSERVER installation and configuration completed"
}
install_nginx() {
$pkgmgr install nginx
# Determine nginx config directory
if [[ -d "/etc/nginx/sites-available" ]]; then
sitesa="/etc/nginx/sites-available"
sitese="/etc/nginx/sites-enabled/"
elif [[ -d "/etc/nginx/conf.d" ]]; then
sitesa="/etc/nginx/conf.d"
sitese=""
fi
create_nginx_configs
# Test nginx config
if ! nginx -t; then
log_error "Nginx configuration test failed"
exit 1
fi
systemctl enable nginx
systemctl restart nginx
}
install_apache() {
case $OS in
"ubuntu"|"debian")
$pkgmgr install apache2
sitesa="/etc/apache2/sites-available"
sitese="/etc/apache2/sites-enabled"
service_name="apache2"
;;
"red"|"centos"|"oracle"|"rocky"|"almalinux")
$pkgmgr install httpd
sitesa="/etc/httpd/conf.d"
sitese=""
service_name="httpd"
;;
esac
# Enable required modules
if [[ "$OS" == "ubuntu" || "$OS" == "debian" ]]; then
a2enmod proxy proxy_http headers
fi
create_apache_configs
# Test apache config
if ! $service_name -t; then
log_error "Apache configuration test failed"
exit 1
fi
systemctl enable $service_name
systemctl restart $service_name
}
install_caddy() {
# Install Caddy
case $OS in
"ubuntu"|"debian")
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
$pkgmgr update
$pkgmgr install caddy
;;
"red"|"centos"|"oracle"|"rocky"|"almalinux")
$pkgmgr install 'dnf-command(copr)'
$pkgmgr copr enable @caddy/caddy
$pkgmgr install caddy
;;
esac
create_caddy_config
systemctl enable caddy
systemctl restart caddy
}
create_nginx_configs() {
# Prometheus config
cat > "$sitesa/prometheus.conf" << EOF
server {
listen 80;
listen [::]:80;
server_name prometheus.$domain;
location / {
proxy_pass http://localhost:9090/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# Grafana config
cat > "$sitesa/grafana.conf" << EOF
server {
listen 80;
listen [::]:80;
server_name metrics.$domain;
location / {
proxy_pass http://localhost:3000/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# AlertManager config
cat > "$sitesa/alertmanager.conf" << EOF
server {
listen 80;
listen [::]:80;
server_name alerts.$domain;
location / {
proxy_pass http://localhost:9093/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# Loki config
cat > "$sitesa/loki.conf" << EOF
server {
listen 80;
listen [::]:80;
server_name loki.$domain;
location / {
proxy_pass http://localhost:3100/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# Enable sites if using sites-available/sites-enabled structure
if [[ -n "$sitese" ]]; then
ln -sf "$sitesa/prometheus.conf" "$sitese" 2>/dev/null || true
ln -sf "$sitesa/grafana.conf" "$sitese" 2>/dev/null || true
ln -sf "$sitesa/alertmanager.conf" "$sitese" 2>/dev/null || true
ln -sf "$sitesa/loki.conf" "$sitese" 2>/dev/null || true
fi
}
create_apache_configs() {
local protocol="http"
local ssl_config=""
if [[ "$ENABLE_TLS" == "true" ]]; then
protocol="https"
ssl_config="
SSLEngine on
SSLCertificateFile /etc/ssl/certs/prometheus.crt
SSLCertificateKeyFile /etc/ssl/private/prometheus.key"
fi
# Prometheus config
cat > "$sitesa/prometheus.conf" << EOF
<VirtualHost *:80>
ServerName prometheus.$domain
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:9090/
ProxyPassReverse / http://localhost:9090/
ProxyPassReverse / $protocol://prometheus.$domain/
</VirtualHost>
EOF
# Grafana config
cat > "$sitesa/grafana.conf" << EOF
<VirtualHost *:80>
ServerName metrics.$domain
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
ProxyPassReverse / $protocol://metrics.$domain/
</VirtualHost>
EOF
# AlertManager config
cat > "$sitesa/alertmanager.conf" << EOF
<VirtualHost *:80>
ServerName alerts.$domain
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:9093/
ProxyPassReverse / http://localhost:9093/
ProxyPassReverse / $protocol://alerts.$domain/
</VirtualHost>
EOF
# Loki config
cat > "$sitesa/loki.conf" << EOF
<VirtualHost *:80>
ServerName loki.$domain
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:3100/
ProxyPassReverse / http://localhost:3100/
ProxyPassReverse / $protocol://loki.$domain/
</VirtualHost>
EOF
# Enable sites if using sites-available/sites-enabled structure
if [[ -n "$sitese" ]]; then
a2ensite prometheus grafana alertmanager loki 2>/dev/null || true
fi
}
create_caddy_config() {
local protocol="http"
local tls_config=""
if [[ "$ENABLE_TLS" == "true" ]]; then
protocol="https"
tls_config="
tls /etc/ssl/certs/prometheus.crt /etc/ssl/private/prometheus.key"
fi
cat > /etc/caddy/Caddyfile << EOF
# Prometheus
prometheus.$domain {$tls_config
reverse_proxy localhost:9090
}
# Grafana
metrics.$domain {$tls_config
reverse_proxy localhost:3000
}
# AlertManager
alerts.$domain {$tls_config
reverse_proxy localhost:9093
}
# Loki
loki.$domain {$tls_config
reverse_proxy localhost:3100
}
EOF
}
#############################
### Install AlertManager ###
#############################
install_alertmanager() {
log_info "Installing AlertManager"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install AlertManager"
return
fi
if check_component_installed "alertmanager" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "AlertManager already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/alertmanager"
mkdir -p "$workdir"
cd "$workdir"
# Create alertmanager user if doesn't exist
if ! grep -q alertmanager /etc/passwd; then
groupadd --system alertmanager
if [[ "$OS" == "ubuntu" || "$OS" == "debian" ]]; then
useradd -s /sbin/nologin --system -g alertmanager alertmanager
else
useradd -m -s /bin/false alertmanager -g alertmanager
fi
mkdir -p /var/lib/alertmanager
chown alertmanager:alertmanager /var/lib/alertmanager
fi
# Download latest AlertManager
log_info "Downloading AlertManager"
curl -s https://api.github.com/repos/prometheus/alertmanager/releases/latest | \
grep browser_download_url | \
grep linux-amd64 | \
cut -d '"' -f 4 | \
wget -qi - || {
log_error "Failed to download AlertManager"
exit 1
}
tar -xzf alertmanager*.tar.gz
cd alertmanager-*/
mv amtool alertmanager "$bindir/"
chown alertmanager:alertmanager "$bindir/alertmanager" "$bindir/amtool"
# Install config if not exists or in update mode
if [[ ! -f "$promdir/alertmanager.yml" ]] || [[ "$UPDATE_MODE" == "true" ]]; then
if [[ -f "$promdir/alertmanager.yml" ]]; then
cp "$promdir/alertmanager.yml" "$promdir/backups/alertmanager.yml.$(date +%Y%m%d_%H%M%S)"
fi
mv alertmanager.yml "$promdir/"
fi
chown -R alertmanager:alertmanager "$promdir/alertmanager.yml"
# Create systemd service
create_alertmanager_service
systemctl daemon-reload
systemctl enable alertmanager
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart alertmanager
else
systemctl start alertmanager
fi
log_info "AlertManager installation completed"
}
create_alertmanager_service() {
cat > "$psdir/alertmanager.service" << 'EOF'
[Unit]
Description=Prometheus AlertManager Service
Wants=network-online.target
After=network-online.target
[Service]
User=alertmanager
Group=alertmanager
Type=simple
ExecStart=/usr/local/bin/alertmanager \
--config.file /etc/prometheus/alertmanager.yml \
--storage.path /var/lib/alertmanager/ \
--cluster.advertise-address=0.0.0.0:9093
[Install]
WantedBy=multi-user.target
EOF
}
##############################
### Install MySQL Exporter ###
##############################
install_mysql_exporter() {
log_info "Installing MySQL Exporter"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install MySQL Exporter"
return
fi
if check_component_installed "mysqld_exporter" && [[ "$UPDATE_MODE" == "false" ]]; then
log_info "MySQL Exporter already installed, skipping"
return
fi
local workdir="/tmp/prometheus-install-$$/mysql_exporter"
mkdir -p "$workdir"
cd "$workdir"
# Download latest mysqld_exporter
log_info "Downloading MySQL Exporter"
curl -s https://api.github.com/repos/prometheus/mysqld_exporter/releases/latest | \
grep browser_download_url | \
grep linux-amd64.tar.gz | \
awk '{gsub(/"/, "", $2); print $2}' | \
wget -qi - || {
log_error "Failed to download MySQL Exporter"
exit 1
}
tar -xzf mysqld_exporter-*
cd mysqld_exporter-*/
mv mysqld_exporter* "$bindir/mysqld_exporter"
chown prometheus. "$bindir/mysqld_exporter"
# Create MySQL config
create_mysql_exporter_config
# Create systemd service
create_mysql_exporter_service
systemctl daemon-reload
systemctl enable mysqld_exporter
if [[ "$UPDATE_MODE" == "true" ]]; then
systemctl restart mysqld_exporter
else
systemctl start mysqld_exporter
fi
# Add to prometheus config
add_mysql_to_prometheus_config
log_info "MySQL Exporter installation completed"
}
create_mysql_exporter_config() {
touch /etc/.mysqld_exporter.cnf
chown prometheus. /etc/.mysqld_exporter.cnf
chmod 600 /etc/.mysqld_exporter.cnf
# Generate config based on number of hosts
{
for ((i=1; i<=mynum; i++)); do
echo "[client]"
echo "user=$myuser"
echo "password=$mypass"
eval "echo \"host=\$myhost$i\""
echo
done
} > /etc/.mysqld_exporter.cnf
}
create_mysql_exporter_service() {
cat > "$psdir/mysqld_exporter.service" << EOF
[Unit]
Description=MySQL Exporter Service
Wants=network.target
After=network.target
[Service]
User=prometheus
Group=prometheus
Environment="DATA_SOURCE_NAME=mysqld_exporter:$mypass@$myuser:(/var/lib/mysql/mysql.sock)"
Type=simple
ExecStart=/usr/local/bin/mysqld_exporter
Restart=always
[Install]
WantedBy=multi-user.target
EOF
}
add_mysql_to_prometheus_config() {
if ! grep -q "job_name.*mysql" "$promdir/prometheus.yml" 2>/dev/null; then
log_info "Adding MySQL configuration to Prometheus"
cat >> "$promdir/prometheus.yml" << 'EOF'
- job_name: 'mysql_metrics'
scrape_interval: 5s
static_configs:
- targets:
- localhost:9104
EOF
# Restart prometheus to reload config
if systemctl is-active --quiet prometheus; then
systemctl reload prometheus || systemctl restart prometheus
fi
fi
}
#########################
### Parse Arguments ###
#########################
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
--prometheus)
INSTALL_PROMETHEUS=true
shift
;;
--no-prometheus)
INSTALL_PROMETHEUS=false
shift
;;
--node-exporter)
INSTALL_NODE_EXPORTER=true
shift
;;
--no-node-exporter)
INSTALL_NODE_EXPORTER=false
shift
;;
--blackbox)
INSTALL_BLACKBOX=true
shift
;;
--no-blackbox)
INSTALL_BLACKBOX=false
shift
;;
--alertmanager)
INSTALL_ALERTMANAGER=true
shift
;;
--no-alertmanager)
INSTALL_ALERTMANAGER=false
shift
;;
--mysql-exporter)
INSTALL_MYSQL_EXPORTER=true
shift
;;
--no-mysql-exporter)
INSTALL_MYSQL_EXPORTER=false
shift
;;
--grafana)
INSTALL_GRAFANA=true
shift
;;
--no-grafana)
INSTALL_GRAFANA=false
shift
;;
--loki)
INSTALL_LOKI=true
shift
;;
--no-loki)
INSTALL_LOKI=false
shift
;;
--alloy)
INSTALL_ALLOY=true
shift
;;
--no-alloy)
INSTALL_ALLOY=false
shift
;;
--webserver)
WEBSERVER="$2"
INSTALL_WEBSERVER=true
shift 2
;;
--no-webserver)
INSTALL_WEBSERVER=false
shift
;;
--enable-tls)
ENABLE_TLS=true
shift
;;
--all)
INSTALL_PROMETHEUS=true
INSTALL_NODE_EXPORTER=true
INSTALL_BLACKBOX=true
INSTALL_ALERTMANAGER=true
INSTALL_MYSQL_EXPORTER=true
INSTALL_GRAFANA=true
INSTALL_LOKI=true
INSTALL_ALLOY=true
INSTALL_WEBSERVER=true
shift
;;
--update)
UPDATE_MODE=true
shift
;;
--domain)
domain="$2"
shift 2
;;
--config-file)
CONFIG_FILE="$2"
shift 2
;;
--dry-run)
DRY_RUN=true
shift
;;
--skip-deps)
SKIP_DEPS=true
shift
;;
--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
#########################
### Load Config File ###
#########################
load_config_file() {
if [[ -n "$CONFIG_FILE" && -f "$CONFIG_FILE" ]]; then
log_info "Loading configuration from $CONFIG_FILE"
# shellcheck source=/dev/null
source "$CONFIG_FILE"
fi
}
##########################
### Main Installation ###
##########################
main() {
log_info "Starting Prometheus stack installation"
log_info "Command line: $0 $*"
parse_arguments "$@"
load_config_file
check_permissions
detect_os
setup_directories
setup_package_manager
create_prometheus_user
install_dependencies
# Install components based on flags
if [[ "$INSTALL_PROMETHEUS" == "true" ]]; then
install_prometheus
fi
if [[ "$INSTALL_NODE_EXPORTER" == "true" ]]; then
install_node_exporter
fi
if [[ "$INSTALL_BLACKBOX" == "true" ]]; then
install_blackbox
fi
if [[ "$INSTALL_ALERTMANAGER" == "true" ]]; then
install_alertmanager
fi
if [[ "$INSTALL_MYSQL_EXPORTER" == "true" ]]; then
install_mysql_exporter
fi
if [[ "$INSTALL_GRAFANA" == "true" ]]; then
install_grafana
fi
if [[ "$INSTALL_LOKI" == "true" ]]; then
install_loki
fi
if [[ "$INSTALL_ALLOY" == "true" ]]; then
install_alloy
fi
if [[ "$INSTALL_WEBSERVER" == "true" ]]; then
install_webserver
fi
log_info "Installation completed successfully"
if [[ "$DRY_RUN" == "false" ]]; then
echo
echo "=== Installation Summary ==="
echo "Components installed:"
[[ "$INSTALL_PROMETHEUS" == "true" ]] && echo " ✓ Prometheus (http://localhost:9090)"
[[ "$INSTALL_NODE_EXPORTER" == "true" ]] && echo " ✓ Node Exporter (http://localhost:9100)"
[[ "$INSTALL_BLACKBOX" == "true" ]] && echo " ✓ Blackbox Exporter (http://localhost:9115)"
[[ "$INSTALL_ALERTMANAGER" == "true" ]] && echo " ✓ AlertManager (http://localhost:9093)"
[[ "$INSTALL_MYSQL_EXPORTER" == "true" ]] && echo " ✓ MySQL Exporter (http://localhost:9104)"
[[ "$INSTALL_GRAFANA" == "true" ]] && echo " ✓ Grafana (http://localhost:3000)"
[[ "$INSTALL_LOKI" == "true" ]] && echo " ✓ Loki (http://localhost:3100)"
[[ "$INSTALL_ALLOY" == "true" ]] && echo " ✓ Grafana Alloy"
[[ "$INSTALL_WEBSERVER" == "true" ]] && echo "$WEBSERVER Web Server"
echo
echo "Check logs at: $logfile"
echo "Default Grafana credentials: admin/admin"
fi
}
# Run main function
main "$@"