install-promtail.sh
· 15 KiB · Bash
Orginalformat
#!/bin/bash
# Universal Promtail Installation and Configuration Script
# Supports: Debian, Ubuntu, CentOS, RHEL, Fedora, Arch Linux, Alpine, Raspberry Pi OS
# Usage: bash -c "$(curl -fsSL <your-gist-url>/install-promtail.sh)"
set -e
# Configuration
LOKI_ENDPOINT="loki.pfotenballen.de"
LOKI_PORT="3100"
PROMTAIL_VERSION="2.9.2"
PROMTAIL_USER="promtail"
PROMTAIL_DIR="/opt/promtail"
CONFIG_DIR="/etc/promtail"
LOG_DIR="/var/log/promtail"
# OS Detection Variables
OS=""
DIST=""
PACKAGE_MANAGER=""
SERVICE_MANAGER=""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Detect OS and Distribution
detect_os() {
log_info "Detecting operating system..."
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$ID
DIST=$VERSION_ID
elif command -v lsb_release >/dev/null 2>&1; then
OS=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
DIST=$(lsb_release -sr)
elif [[ -f /etc/redhat-release ]]; then
OS="rhel"
DIST=$(cat /etc/redhat-release | sed 's/.*release \([0-9]\).*/\1/')
else
log_error "Cannot detect operating system"
exit 1
fi
# Determine package manager
if command -v apt-get >/dev/null 2>&1; then
PACKAGE_MANAGER="apt"
elif command -v yum >/dev/null 2>&1; then
PACKAGE_MANAGER="yum"
elif command -v dnf >/dev/null 2>&1; then
PACKAGE_MANAGER="dnf"
elif command -v pacman >/dev/null 2>&1; then
PACKAGE_MANAGER="pacman"
elif command -v apk >/dev/null 2>&1; then
PACKAGE_MANAGER="apk"
else
log_error "No supported package manager found"
exit 1
fi
# Determine service manager
if command -v systemctl >/dev/null 2>&1 && systemctl --version >/dev/null 2>&1; then
SERVICE_MANAGER="systemd"
elif command -v service >/dev/null 2>&1; then
SERVICE_MANAGER="sysv"
elif command -v rc-service >/dev/null 2>&1; then
SERVICE_MANAGER="openrc"
else
log_error "No supported service manager found"
exit 1
fi
log_info "Detected: OS=$OS, Package Manager=$PACKAGE_MANAGER, Service Manager=$SERVICE_MANAGER"
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
}
# Check if promtail is already installed
check_promtail_installed() {
if command -v promtail &> /dev/null || [[ -f "/usr/local/bin/promtail" ]] || [[ -f "$PROMTAIL_DIR/promtail" ]]; then
return 0
else
return 1
fi
}
# Test Loki endpoint connectivity
test_loki_connectivity() {
log_info "Testing connectivity to Loki endpoint: $LOKI_ENDPOINT:$LOKI_PORT"
if command -v nc >/dev/null 2>&1; then
# Use netcat if available
if timeout 10 nc -z "$LOKI_ENDPOINT" "$LOKI_PORT" 2>/dev/null; then
log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT"
return 0
fi
elif command -v telnet >/dev/null 2>&1; then
# Use telnet as fallback
if timeout 10 bash -c "echo 'quit' | telnet $LOKI_ENDPOINT $LOKI_PORT" 2>/dev/null | grep -q "Connected"; then
log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT"
return 0
fi
elif command -v curl >/dev/null 2>&1; then
# Use curl as last resort
if timeout 10 curl -s "http://$LOKI_ENDPOINT:$LOKI_PORT/ready" >/dev/null 2>&1; then
log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT"
return 0
fi
fi
log_error "Cannot reach $LOKI_ENDPOINT:$LOKI_PORT"
log_error "Please check your network connection and Loki server status"
return 1
}
# Install dependencies
install_dependencies() {
log_info "Installing dependencies..."
case $PACKAGE_MANAGER in
"apt")
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y wget curl unzip netcat-openbsd || apt-get install -y wget curl unzip netcat
;;
"yum")
yum install -y wget curl unzip nc
;;
"dnf")
dnf install -y wget curl unzip nc
;;
"pacman")
pacman -Sy --noconfirm wget curl unzip netcat
;;
"apk")
apk update
apk add wget curl unzip netcat-openbsd
;;
*)
log_error "Unsupported package manager: $PACKAGE_MANAGER"
exit 1
;;
esac
log_success "Dependencies installed"
}
# Create promtail user
create_promtail_user() {
if ! id "$PROMTAIL_USER" &>/dev/null; then
log_info "Creating promtail user..."
case $OS in
"alpine")
adduser -S -D -H -s /bin/false $PROMTAIL_USER
;;
*)
if command -v useradd >/dev/null 2>&1; then
useradd --system --no-create-home --shell /bin/false $PROMTAIL_USER 2>/dev/null || \
useradd -r -M -s /bin/false $PROMTAIL_USER
else
log_error "Cannot create user - useradd not available"
exit 1
fi
;;
esac
log_success "Promtail user created"
else
log_info "Promtail user already exists"
fi
}
# Download and install promtail
install_promtail() {
log_info "Downloading Promtail v$PROMTAIL_VERSION..."
# Determine architecture
ARCH=$(uname -m)
case $ARCH in
x86_64)
ARCH_SUFFIX="amd64"
;;
aarch64)
ARCH_SUFFIX="arm64"
;;
armv7l)
ARCH_SUFFIX="arm"
;;
arm*)
ARCH_SUFFIX="arm"
;;
*)
log_error "Unsupported architecture: $ARCH"
exit 1
;;
esac
# Create directories
mkdir -p $PROMTAIL_DIR
mkdir -p $CONFIG_DIR
mkdir -p $LOG_DIR
# Download promtail binary
DOWNLOAD_URL="https://github.com/grafana/loki/releases/download/v$PROMTAIL_VERSION/promtail-linux-$ARCH_SUFFIX.zip"
cd /tmp
log_info "Downloading from: $DOWNLOAD_URL"
if ! wget --timeout=30 --tries=3 -q "$DOWNLOAD_URL" -O promtail.zip; then
log_error "Failed to download Promtail. Check internet connection."
exit 1
fi
if ! unzip -q promtail.zip; then
log_error "Failed to extract Promtail archive"
exit 1
fi
# Install binary
chmod +x promtail-linux-$ARCH_SUFFIX
mv promtail-linux-$ARCH_SUFFIX /usr/local/bin/promtail
# Set ownership
chown root:root /usr/local/bin/promtail
chown -R $PROMTAIL_USER:$PROMTAIL_USER $CONFIG_DIR $LOG_DIR
# Cleanup
rm -f promtail.zip
log_success "Promtail installed successfully"
}
# Create promtail configuration
create_config() {
log_info "Creating Promtail configuration..."
# Get the actual hostname
HOSTNAME=$(hostname)
cat > $CONFIG_DIR/promtail.yml << EOF
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /var/lib/promtail/positions.yaml
clients:
- url: http://$LOKI_ENDPOINT:$LOKI_PORT/loki/api/v1/push
scrape_configs:
# Direct /var/log/ files (system logs)
- job_name: system-logs
static_configs:
- targets:
- localhost
labels:
job: system-logs
service: system
host: $HOSTNAME
__path__: /var/log/*.log
# Service-specific logs in subdirectories
- job_name: service-logs
static_configs:
- targets:
- localhost
labels:
job: service-logs
host: $HOSTNAME
__path__: /var/log/*/*.log
pipeline_stages:
- regex:
expression: '/var/log/(?P<service>[^/]+)/.*'
- labels:
service: '{{ .service }}'
# Recursively capture all nested logs (deeper than one level)
- job_name: nested-service-logs
static_configs:
- targets:
- localhost
labels:
job: nested-service-logs
host: $HOSTNAME
__path__: /var/log/**/*.log
pipeline_stages:
- regex:
expression: '/var/log/(?P<service>[^/]+)/.*'
- labels:
service: '{{ .service }}'
EOF
# Create positions directory
mkdir -p /var/lib/promtail
chown $PROMTAIL_USER:$PROMTAIL_USER /var/lib/promtail
# Set proper permissions
chown $PROMTAIL_USER:$PROMTAIL_USER $CONFIG_DIR/promtail.yml
chmod 640 $CONFIG_DIR/promtail.yml
log_success "Configuration created"
}
# Create service
create_service() {
case $SERVICE_MANAGER in
"systemd")
create_systemd_service
;;
"sysv")
create_sysv_service
;;
"openrc")
create_openrc_service
;;
*)
log_error "Unsupported service manager: $SERVICE_MANAGER"
exit 1
;;
esac
}
# Create systemd service
create_systemd_service() {
log_info "Creating systemd service..."
cat > /etc/systemd/system/promtail.service << EOF
[Unit]
Description=Promtail service
Documentation=https://grafana.com/docs/loki/latest/clients/promtail/
After=network.target
[Service]
Type=simple
User=$PROMTAIL_USER
ExecStart=/usr/local/bin/promtail -config.file=$CONFIG_DIR/promtail.yml
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=promtail
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
log_success "Systemd service created"
}
# Create SysV init script
create_sysv_service() {
log_info "Creating SysV init script..."
cat > /etc/init.d/promtail << 'EOF'
#!/bin/bash
# promtail Promtail log collector
# chkconfig: 35 80 20
# description: Promtail log collector for Grafana Loki
. /etc/rc.d/init.d/functions
USER="promtail"
DAEMON="promtail"
ROOT_DIR="/var/lib/promtail"
SERVER="$ROOT_DIR/$DAEMON"
LOCK_FILE="/var/lock/subsys/promtail"
start() {
if [ -f $LOCK_FILE ]; then
echo "promtail is locked."
return 1
fi
echo -n $"Shutting down $DAEMON: "
pid=`ps -aefw | grep "$DAEMON" | grep -v " grep " | awk '{print $2}'`
kill -9 $pid > /dev/null 2>&1
[ $? -eq 0 ] && echo "OK" || echo "FAILED"
}
stop() {
echo -n $"Shutting down $DAEMON: "
pid=`ps -aefw | grep "$DAEMON" | grep -v " grep " | awk '{print $2}'`
kill -9 $pid > /dev/null 2>&1
[ $? -eq 0 ] && echo "OK" || echo "FAILED"
rm -f $LOCK_FILE
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $DAEMON
;;
restart)
stop
start
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $?
EOF
chmod +x /etc/init.d/promtail
chkconfig --add promtail 2>/dev/null || update-rc.d promtail defaults
log_success "SysV service created"
}
# Create OpenRC service
create_openrc_service() {
log_info "Creating OpenRC service..."
cat > /etc/init.d/promtail << EOF
#!/sbin/openrc-run
name="promtail"
description="Promtail log collector"
command="/usr/local/bin/promtail"
command_args="-config.file=$CONFIG_DIR/promtail.yml"
command_user="$PROMTAIL_USER"
command_background="yes"
pidfile="/run/\${RC_SVCNAME}.pid"
depend() {
need net
after firewall
}
EOF
chmod +x /etc/init.d/promtail
log_success "OpenRC service created"
}
# Add promtail user to log access groups
configure_log_access() {
log_info "Configuring log file access..."
# Try to add to common log access groups
for group in adm systemd-journal wheel; do
if getent group $group >/dev/null 2>&1; then
usermod -a -G $group $PROMTAIL_USER 2>/dev/null || true
log_info "Added to group: $group"
fi
done
log_success "Log access configured"
}
# Start and enable service
start_service() {
log_info "Starting Promtail service..."
case $SERVICE_MANAGER in
"systemd")
systemctl enable promtail
systemctl start promtail
sleep 2
if systemctl is-active --quiet promtail; then
log_success "Promtail service is running"
systemctl status promtail --no-pager -l
else
log_error "Failed to start Promtail service"
log_error "Check logs with: journalctl -u promtail -f"
exit 1
fi
;;
"sysv")
service promtail start
chkconfig promtail on 2>/dev/null || update-rc.d promtail enable
if service promtail status >/dev/null 2>&1; then
log_success "Promtail service is running"
else
log_error "Failed to start Promtail service"
exit 1
fi
;;
"openrc")
rc-update add promtail default
rc-service promtail start
if rc-service promtail status >/dev/null 2>&1; then
log_success "Promtail service is running"
else
log_error "Failed to start Promtail service"
exit 1
fi
;;
esac
}
# Main installation process
main() {
echo "=================================="
echo " Promtail Installation Script "
echo "=================================="
echo
check_root
detect_os
if check_promtail_installed; then
log_warning "Promtail appears to be already installed"
if [[ -t 0 ]]; then
echo "Existing installation found. Do you want to continue and reconfigure? (y/N)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
log_info "Installation cancelled"
exit 0
fi
else
log_info "Non-interactive mode: Reconfiguring existing installation"
fi
fi
# Test Loki connectivity first
if ! test_loki_connectivity; then
if [[ -t 0 ]]; then
echo "Do you want to continue anyway? (y/N)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
log_info "Installation cancelled"
exit 1
fi
else
log_warning "Non-interactive mode: Continuing despite connectivity issues"
fi
fi
install_dependencies
create_promtail_user
if ! check_promtail_installed; then
install_promtail
else
log_info "Promtail binary already exists, skipping download"
fi
create_config
create_service
configure_log_access
start_service
echo
echo "=================================="
log_success "Promtail installation completed!"
echo "=================================="
echo
echo "Configuration file: $CONFIG_DIR/promtail.yml"
echo "Service status: systemctl status promtail"
echo "Service logs: journalctl -u promtail -f"
echo "Loki endpoint: http://$LOKI_ENDPOINT:$LOKI_PORT"
echo
echo "To check if logs are being sent to Loki:"
echo "curl -G -s \"http://$LOKI_ENDPOINT:$LOKI_PORT/loki/api/v1/query\" --data-urlencode 'query={job=\"system-logs\"}'"
}
# Execute main function
main "$@"
| 1 | #!/bin/bash |
| 2 | |
| 3 | # Universal Promtail Installation and Configuration Script |
| 4 | # Supports: Debian, Ubuntu, CentOS, RHEL, Fedora, Arch Linux, Alpine, Raspberry Pi OS |
| 5 | # Usage: bash -c "$(curl -fsSL <your-gist-url>/install-promtail.sh)" |
| 6 | |
| 7 | set -e |
| 8 | |
| 9 | # Configuration |
| 10 | LOKI_ENDPOINT="loki.pfotenballen.de" |
| 11 | LOKI_PORT="3100" |
| 12 | PROMTAIL_VERSION="2.9.2" |
| 13 | PROMTAIL_USER="promtail" |
| 14 | PROMTAIL_DIR="/opt/promtail" |
| 15 | CONFIG_DIR="/etc/promtail" |
| 16 | LOG_DIR="/var/log/promtail" |
| 17 | |
| 18 | # OS Detection Variables |
| 19 | OS="" |
| 20 | DIST="" |
| 21 | PACKAGE_MANAGER="" |
| 22 | SERVICE_MANAGER="" |
| 23 | |
| 24 | # Colors for output |
| 25 | RED='\033[0;31m' |
| 26 | GREEN='\033[0;32m' |
| 27 | YELLOW='\033[1;33m' |
| 28 | BLUE='\033[0;34m' |
| 29 | NC='\033[0m' # No Color |
| 30 | |
| 31 | # Logging functions |
| 32 | log_info() { |
| 33 | echo -e "${BLUE}[INFO]${NC} $1" |
| 34 | } |
| 35 | |
| 36 | log_success() { |
| 37 | echo -e "${GREEN}[SUCCESS]${NC} $1" |
| 38 | } |
| 39 | |
| 40 | log_warning() { |
| 41 | echo -e "${YELLOW}[WARNING]${NC} $1" |
| 42 | } |
| 43 | |
| 44 | log_error() { |
| 45 | echo -e "${RED}[ERROR]${NC} $1" |
| 46 | } |
| 47 | |
| 48 | # Detect OS and Distribution |
| 49 | detect_os() { |
| 50 | log_info "Detecting operating system..." |
| 51 | |
| 52 | if [[ -f /etc/os-release ]]; then |
| 53 | . /etc/os-release |
| 54 | OS=$ID |
| 55 | DIST=$VERSION_ID |
| 56 | elif command -v lsb_release >/dev/null 2>&1; then |
| 57 | OS=$(lsb_release -si | tr '[:upper:]' '[:lower:]') |
| 58 | DIST=$(lsb_release -sr) |
| 59 | elif [[ -f /etc/redhat-release ]]; then |
| 60 | OS="rhel" |
| 61 | DIST=$(cat /etc/redhat-release | sed 's/.*release \([0-9]\).*/\1/') |
| 62 | else |
| 63 | log_error "Cannot detect operating system" |
| 64 | exit 1 |
| 65 | fi |
| 66 | |
| 67 | # Determine package manager |
| 68 | if command -v apt-get >/dev/null 2>&1; then |
| 69 | PACKAGE_MANAGER="apt" |
| 70 | elif command -v yum >/dev/null 2>&1; then |
| 71 | PACKAGE_MANAGER="yum" |
| 72 | elif command -v dnf >/dev/null 2>&1; then |
| 73 | PACKAGE_MANAGER="dnf" |
| 74 | elif command -v pacman >/dev/null 2>&1; then |
| 75 | PACKAGE_MANAGER="pacman" |
| 76 | elif command -v apk >/dev/null 2>&1; then |
| 77 | PACKAGE_MANAGER="apk" |
| 78 | else |
| 79 | log_error "No supported package manager found" |
| 80 | exit 1 |
| 81 | fi |
| 82 | |
| 83 | # Determine service manager |
| 84 | if command -v systemctl >/dev/null 2>&1 && systemctl --version >/dev/null 2>&1; then |
| 85 | SERVICE_MANAGER="systemd" |
| 86 | elif command -v service >/dev/null 2>&1; then |
| 87 | SERVICE_MANAGER="sysv" |
| 88 | elif command -v rc-service >/dev/null 2>&1; then |
| 89 | SERVICE_MANAGER="openrc" |
| 90 | else |
| 91 | log_error "No supported service manager found" |
| 92 | exit 1 |
| 93 | fi |
| 94 | |
| 95 | log_info "Detected: OS=$OS, Package Manager=$PACKAGE_MANAGER, Service Manager=$SERVICE_MANAGER" |
| 96 | } |
| 97 | |
| 98 | # Check if running as root |
| 99 | check_root() { |
| 100 | if [[ $EUID -ne 0 ]]; then |
| 101 | log_error "This script must be run as root" |
| 102 | exit 1 |
| 103 | fi |
| 104 | } |
| 105 | |
| 106 | # Check if promtail is already installed |
| 107 | check_promtail_installed() { |
| 108 | if command -v promtail &> /dev/null || [[ -f "/usr/local/bin/promtail" ]] || [[ -f "$PROMTAIL_DIR/promtail" ]]; then |
| 109 | return 0 |
| 110 | else |
| 111 | return 1 |
| 112 | fi |
| 113 | } |
| 114 | |
| 115 | # Test Loki endpoint connectivity |
| 116 | test_loki_connectivity() { |
| 117 | log_info "Testing connectivity to Loki endpoint: $LOKI_ENDPOINT:$LOKI_PORT" |
| 118 | |
| 119 | if command -v nc >/dev/null 2>&1; then |
| 120 | # Use netcat if available |
| 121 | if timeout 10 nc -z "$LOKI_ENDPOINT" "$LOKI_PORT" 2>/dev/null; then |
| 122 | log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT" |
| 123 | return 0 |
| 124 | fi |
| 125 | elif command -v telnet >/dev/null 2>&1; then |
| 126 | # Use telnet as fallback |
| 127 | if timeout 10 bash -c "echo 'quit' | telnet $LOKI_ENDPOINT $LOKI_PORT" 2>/dev/null | grep -q "Connected"; then |
| 128 | log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT" |
| 129 | return 0 |
| 130 | fi |
| 131 | elif command -v curl >/dev/null 2>&1; then |
| 132 | # Use curl as last resort |
| 133 | if timeout 10 curl -s "http://$LOKI_ENDPOINT:$LOKI_PORT/ready" >/dev/null 2>&1; then |
| 134 | log_success "Successfully connected to $LOKI_ENDPOINT:$LOKI_PORT" |
| 135 | return 0 |
| 136 | fi |
| 137 | fi |
| 138 | |
| 139 | log_error "Cannot reach $LOKI_ENDPOINT:$LOKI_PORT" |
| 140 | log_error "Please check your network connection and Loki server status" |
| 141 | return 1 |
| 142 | } |
| 143 | |
| 144 | # Install dependencies |
| 145 | install_dependencies() { |
| 146 | log_info "Installing dependencies..." |
| 147 | |
| 148 | case $PACKAGE_MANAGER in |
| 149 | "apt") |
| 150 | export DEBIAN_FRONTEND=noninteractive |
| 151 | apt-get update -qq |
| 152 | apt-get install -y wget curl unzip netcat-openbsd || apt-get install -y wget curl unzip netcat |
| 153 | ;; |
| 154 | "yum") |
| 155 | yum install -y wget curl unzip nc |
| 156 | ;; |
| 157 | "dnf") |
| 158 | dnf install -y wget curl unzip nc |
| 159 | ;; |
| 160 | "pacman") |
| 161 | pacman -Sy --noconfirm wget curl unzip netcat |
| 162 | ;; |
| 163 | "apk") |
| 164 | apk update |
| 165 | apk add wget curl unzip netcat-openbsd |
| 166 | ;; |
| 167 | *) |
| 168 | log_error "Unsupported package manager: $PACKAGE_MANAGER" |
| 169 | exit 1 |
| 170 | ;; |
| 171 | esac |
| 172 | |
| 173 | log_success "Dependencies installed" |
| 174 | } |
| 175 | |
| 176 | # Create promtail user |
| 177 | create_promtail_user() { |
| 178 | if ! id "$PROMTAIL_USER" &>/dev/null; then |
| 179 | log_info "Creating promtail user..." |
| 180 | |
| 181 | case $OS in |
| 182 | "alpine") |
| 183 | adduser -S -D -H -s /bin/false $PROMTAIL_USER |
| 184 | ;; |
| 185 | *) |
| 186 | if command -v useradd >/dev/null 2>&1; then |
| 187 | useradd --system --no-create-home --shell /bin/false $PROMTAIL_USER 2>/dev/null || \ |
| 188 | useradd -r -M -s /bin/false $PROMTAIL_USER |
| 189 | else |
| 190 | log_error "Cannot create user - useradd not available" |
| 191 | exit 1 |
| 192 | fi |
| 193 | ;; |
| 194 | esac |
| 195 | |
| 196 | log_success "Promtail user created" |
| 197 | else |
| 198 | log_info "Promtail user already exists" |
| 199 | fi |
| 200 | } |
| 201 | |
| 202 | # Download and install promtail |
| 203 | install_promtail() { |
| 204 | log_info "Downloading Promtail v$PROMTAIL_VERSION..." |
| 205 | |
| 206 | # Determine architecture |
| 207 | ARCH=$(uname -m) |
| 208 | case $ARCH in |
| 209 | x86_64) |
| 210 | ARCH_SUFFIX="amd64" |
| 211 | ;; |
| 212 | aarch64) |
| 213 | ARCH_SUFFIX="arm64" |
| 214 | ;; |
| 215 | armv7l) |
| 216 | ARCH_SUFFIX="arm" |
| 217 | ;; |
| 218 | arm*) |
| 219 | ARCH_SUFFIX="arm" |
| 220 | ;; |
| 221 | *) |
| 222 | log_error "Unsupported architecture: $ARCH" |
| 223 | exit 1 |
| 224 | ;; |
| 225 | esac |
| 226 | |
| 227 | # Create directories |
| 228 | mkdir -p $PROMTAIL_DIR |
| 229 | mkdir -p $CONFIG_DIR |
| 230 | mkdir -p $LOG_DIR |
| 231 | |
| 232 | # Download promtail binary |
| 233 | DOWNLOAD_URL="https://github.com/grafana/loki/releases/download/v$PROMTAIL_VERSION/promtail-linux-$ARCH_SUFFIX.zip" |
| 234 | |
| 235 | cd /tmp |
| 236 | log_info "Downloading from: $DOWNLOAD_URL" |
| 237 | if ! wget --timeout=30 --tries=3 -q "$DOWNLOAD_URL" -O promtail.zip; then |
| 238 | log_error "Failed to download Promtail. Check internet connection." |
| 239 | exit 1 |
| 240 | fi |
| 241 | |
| 242 | if ! unzip -q promtail.zip; then |
| 243 | log_error "Failed to extract Promtail archive" |
| 244 | exit 1 |
| 245 | fi |
| 246 | |
| 247 | # Install binary |
| 248 | chmod +x promtail-linux-$ARCH_SUFFIX |
| 249 | mv promtail-linux-$ARCH_SUFFIX /usr/local/bin/promtail |
| 250 | |
| 251 | # Set ownership |
| 252 | chown root:root /usr/local/bin/promtail |
| 253 | chown -R $PROMTAIL_USER:$PROMTAIL_USER $CONFIG_DIR $LOG_DIR |
| 254 | |
| 255 | # Cleanup |
| 256 | rm -f promtail.zip |
| 257 | |
| 258 | log_success "Promtail installed successfully" |
| 259 | } |
| 260 | |
| 261 | # Create promtail configuration |
| 262 | create_config() { |
| 263 | log_info "Creating Promtail configuration..." |
| 264 | |
| 265 | # Get the actual hostname |
| 266 | HOSTNAME=$(hostname) |
| 267 | |
| 268 | cat > $CONFIG_DIR/promtail.yml << EOF |
| 269 | server: |
| 270 | http_listen_port: 9080 |
| 271 | grpc_listen_port: 0 |
| 272 | |
| 273 | positions: |
| 274 | filename: /var/lib/promtail/positions.yaml |
| 275 | |
| 276 | clients: |
| 277 | - url: http://$LOKI_ENDPOINT:$LOKI_PORT/loki/api/v1/push |
| 278 | |
| 279 | scrape_configs: |
| 280 | # Direct /var/log/ files (system logs) |
| 281 | - job_name: system-logs |
| 282 | static_configs: |
| 283 | - targets: |
| 284 | - localhost |
| 285 | labels: |
| 286 | job: system-logs |
| 287 | service: system |
| 288 | host: $HOSTNAME |
| 289 | __path__: /var/log/*.log |
| 290 | |
| 291 | # Service-specific logs in subdirectories |
| 292 | - job_name: service-logs |
| 293 | static_configs: |
| 294 | - targets: |
| 295 | - localhost |
| 296 | labels: |
| 297 | job: service-logs |
| 298 | host: $HOSTNAME |
| 299 | __path__: /var/log/*/*.log |
| 300 | pipeline_stages: |
| 301 | - regex: |
| 302 | expression: '/var/log/(?P<service>[^/]+)/.*' |
| 303 | - labels: |
| 304 | service: '{{ .service }}' |
| 305 | |
| 306 | # Recursively capture all nested logs (deeper than one level) |
| 307 | - job_name: nested-service-logs |
| 308 | static_configs: |
| 309 | - targets: |
| 310 | - localhost |
| 311 | labels: |
| 312 | job: nested-service-logs |
| 313 | host: $HOSTNAME |
| 314 | __path__: /var/log/**/*.log |
| 315 | pipeline_stages: |
| 316 | - regex: |
| 317 | expression: '/var/log/(?P<service>[^/]+)/.*' |
| 318 | - labels: |
| 319 | service: '{{ .service }}' |
| 320 | EOF |
| 321 | |
| 322 | # Create positions directory |
| 323 | mkdir -p /var/lib/promtail |
| 324 | chown $PROMTAIL_USER:$PROMTAIL_USER /var/lib/promtail |
| 325 | |
| 326 | # Set proper permissions |
| 327 | chown $PROMTAIL_USER:$PROMTAIL_USER $CONFIG_DIR/promtail.yml |
| 328 | chmod 640 $CONFIG_DIR/promtail.yml |
| 329 | |
| 330 | log_success "Configuration created" |
| 331 | } |
| 332 | |
| 333 | # Create service |
| 334 | create_service() { |
| 335 | case $SERVICE_MANAGER in |
| 336 | "systemd") |
| 337 | create_systemd_service |
| 338 | ;; |
| 339 | "sysv") |
| 340 | create_sysv_service |
| 341 | ;; |
| 342 | "openrc") |
| 343 | create_openrc_service |
| 344 | ;; |
| 345 | *) |
| 346 | log_error "Unsupported service manager: $SERVICE_MANAGER" |
| 347 | exit 1 |
| 348 | ;; |
| 349 | esac |
| 350 | } |
| 351 | |
| 352 | # Create systemd service |
| 353 | create_systemd_service() { |
| 354 | log_info "Creating systemd service..." |
| 355 | |
| 356 | cat > /etc/systemd/system/promtail.service << EOF |
| 357 | [Unit] |
| 358 | Description=Promtail service |
| 359 | Documentation=https://grafana.com/docs/loki/latest/clients/promtail/ |
| 360 | After=network.target |
| 361 | |
| 362 | [Service] |
| 363 | Type=simple |
| 364 | User=$PROMTAIL_USER |
| 365 | ExecStart=/usr/local/bin/promtail -config.file=$CONFIG_DIR/promtail.yml |
| 366 | Restart=always |
| 367 | RestartSec=10 |
| 368 | StandardOutput=journal |
| 369 | StandardError=journal |
| 370 | SyslogIdentifier=promtail |
| 371 | |
| 372 | [Install] |
| 373 | WantedBy=multi-user.target |
| 374 | EOF |
| 375 | |
| 376 | systemctl daemon-reload |
| 377 | log_success "Systemd service created" |
| 378 | } |
| 379 | |
| 380 | # Create SysV init script |
| 381 | create_sysv_service() { |
| 382 | log_info "Creating SysV init script..." |
| 383 | |
| 384 | cat > /etc/init.d/promtail << 'EOF' |
| 385 | #!/bin/bash |
| 386 | # promtail Promtail log collector |
| 387 | # chkconfig: 35 80 20 |
| 388 | # description: Promtail log collector for Grafana Loki |
| 389 | |
| 390 | . /etc/rc.d/init.d/functions |
| 391 | |
| 392 | USER="promtail" |
| 393 | DAEMON="promtail" |
| 394 | ROOT_DIR="/var/lib/promtail" |
| 395 | |
| 396 | SERVER="$ROOT_DIR/$DAEMON" |
| 397 | LOCK_FILE="/var/lock/subsys/promtail" |
| 398 | |
| 399 | start() { |
| 400 | if [ -f $LOCK_FILE ]; then |
| 401 | echo "promtail is locked." |
| 402 | return 1 |
| 403 | fi |
| 404 | |
| 405 | echo -n $"Shutting down $DAEMON: " |
| 406 | pid=`ps -aefw | grep "$DAEMON" | grep -v " grep " | awk '{print $2}'` |
| 407 | kill -9 $pid > /dev/null 2>&1 |
| 408 | [ $? -eq 0 ] && echo "OK" || echo "FAILED" |
| 409 | } |
| 410 | |
| 411 | stop() { |
| 412 | echo -n $"Shutting down $DAEMON: " |
| 413 | pid=`ps -aefw | grep "$DAEMON" | grep -v " grep " | awk '{print $2}'` |
| 414 | kill -9 $pid > /dev/null 2>&1 |
| 415 | [ $? -eq 0 ] && echo "OK" || echo "FAILED" |
| 416 | rm -f $LOCK_FILE |
| 417 | } |
| 418 | |
| 419 | case "$1" in |
| 420 | start) |
| 421 | start |
| 422 | ;; |
| 423 | stop) |
| 424 | stop |
| 425 | ;; |
| 426 | status) |
| 427 | status $DAEMON |
| 428 | ;; |
| 429 | restart) |
| 430 | stop |
| 431 | start |
| 432 | ;; |
| 433 | *) |
| 434 | echo "Usage: {start|stop|status|restart}" |
| 435 | exit 1 |
| 436 | ;; |
| 437 | esac |
| 438 | |
| 439 | exit $? |
| 440 | EOF |
| 441 | |
| 442 | chmod +x /etc/init.d/promtail |
| 443 | chkconfig --add promtail 2>/dev/null || update-rc.d promtail defaults |
| 444 | log_success "SysV service created" |
| 445 | } |
| 446 | |
| 447 | # Create OpenRC service |
| 448 | create_openrc_service() { |
| 449 | log_info "Creating OpenRC service..." |
| 450 | |
| 451 | cat > /etc/init.d/promtail << EOF |
| 452 | #!/sbin/openrc-run |
| 453 | |
| 454 | name="promtail" |
| 455 | description="Promtail log collector" |
| 456 | |
| 457 | command="/usr/local/bin/promtail" |
| 458 | command_args="-config.file=$CONFIG_DIR/promtail.yml" |
| 459 | command_user="$PROMTAIL_USER" |
| 460 | command_background="yes" |
| 461 | pidfile="/run/\${RC_SVCNAME}.pid" |
| 462 | |
| 463 | depend() { |
| 464 | need net |
| 465 | after firewall |
| 466 | } |
| 467 | EOF |
| 468 | |
| 469 | chmod +x /etc/init.d/promtail |
| 470 | log_success "OpenRC service created" |
| 471 | } |
| 472 | |
| 473 | # Add promtail user to log access groups |
| 474 | configure_log_access() { |
| 475 | log_info "Configuring log file access..." |
| 476 | |
| 477 | # Try to add to common log access groups |
| 478 | for group in adm systemd-journal wheel; do |
| 479 | if getent group $group >/dev/null 2>&1; then |
| 480 | usermod -a -G $group $PROMTAIL_USER 2>/dev/null || true |
| 481 | log_info "Added to group: $group" |
| 482 | fi |
| 483 | done |
| 484 | |
| 485 | log_success "Log access configured" |
| 486 | } |
| 487 | |
| 488 | # Start and enable service |
| 489 | start_service() { |
| 490 | log_info "Starting Promtail service..." |
| 491 | |
| 492 | case $SERVICE_MANAGER in |
| 493 | "systemd") |
| 494 | systemctl enable promtail |
| 495 | systemctl start promtail |
| 496 | sleep 2 |
| 497 | if systemctl is-active --quiet promtail; then |
| 498 | log_success "Promtail service is running" |
| 499 | systemctl status promtail --no-pager -l |
| 500 | else |
| 501 | log_error "Failed to start Promtail service" |
| 502 | log_error "Check logs with: journalctl -u promtail -f" |
| 503 | exit 1 |
| 504 | fi |
| 505 | ;; |
| 506 | "sysv") |
| 507 | service promtail start |
| 508 | chkconfig promtail on 2>/dev/null || update-rc.d promtail enable |
| 509 | if service promtail status >/dev/null 2>&1; then |
| 510 | log_success "Promtail service is running" |
| 511 | else |
| 512 | log_error "Failed to start Promtail service" |
| 513 | exit 1 |
| 514 | fi |
| 515 | ;; |
| 516 | "openrc") |
| 517 | rc-update add promtail default |
| 518 | rc-service promtail start |
| 519 | if rc-service promtail status >/dev/null 2>&1; then |
| 520 | log_success "Promtail service is running" |
| 521 | else |
| 522 | log_error "Failed to start Promtail service" |
| 523 | exit 1 |
| 524 | fi |
| 525 | ;; |
| 526 | esac |
| 527 | } |
| 528 | |
| 529 | # Main installation process |
| 530 | main() { |
| 531 | echo "==================================" |
| 532 | echo " Promtail Installation Script " |
| 533 | echo "==================================" |
| 534 | echo |
| 535 | |
| 536 | check_root |
| 537 | detect_os |
| 538 | |
| 539 | if check_promtail_installed; then |
| 540 | log_warning "Promtail appears to be already installed" |
| 541 | if [[ -t 0 ]]; then |
| 542 | echo "Existing installation found. Do you want to continue and reconfigure? (y/N)" |
| 543 | read -r response |
| 544 | if [[ ! "$response" =~ ^[Yy]$ ]]; then |
| 545 | log_info "Installation cancelled" |
| 546 | exit 0 |
| 547 | fi |
| 548 | else |
| 549 | log_info "Non-interactive mode: Reconfiguring existing installation" |
| 550 | fi |
| 551 | fi |
| 552 | |
| 553 | # Test Loki connectivity first |
| 554 | if ! test_loki_connectivity; then |
| 555 | if [[ -t 0 ]]; then |
| 556 | echo "Do you want to continue anyway? (y/N)" |
| 557 | read -r response |
| 558 | if [[ ! "$response" =~ ^[Yy]$ ]]; then |
| 559 | log_info "Installation cancelled" |
| 560 | exit 1 |
| 561 | fi |
| 562 | else |
| 563 | log_warning "Non-interactive mode: Continuing despite connectivity issues" |
| 564 | fi |
| 565 | fi |
| 566 | |
| 567 | install_dependencies |
| 568 | create_promtail_user |
| 569 | |
| 570 | if ! check_promtail_installed; then |
| 571 | install_promtail |
| 572 | else |
| 573 | log_info "Promtail binary already exists, skipping download" |
| 574 | fi |
| 575 | |
| 576 | create_config |
| 577 | create_service |
| 578 | configure_log_access |
| 579 | start_service |
| 580 | |
| 581 | echo |
| 582 | echo "==================================" |
| 583 | log_success "Promtail installation completed!" |
| 584 | echo "==================================" |
| 585 | echo |
| 586 | echo "Configuration file: $CONFIG_DIR/promtail.yml" |
| 587 | echo "Service status: systemctl status promtail" |
| 588 | echo "Service logs: journalctl -u promtail -f" |
| 589 | echo "Loki endpoint: http://$LOKI_ENDPOINT:$LOKI_PORT" |
| 590 | echo |
| 591 | echo "To check if logs are being sent to Loki:" |
| 592 | echo "curl -G -s \"http://$LOKI_ENDPOINT:$LOKI_PORT/loki/api/v1/query\" --data-urlencode 'query={job=\"system-logs\"}'" |
| 593 | } |
| 594 | |
| 595 | # Execute main function |
| 596 | main "$@" |