#!/usr/bin/env bash # ============================================================================ # LokiStack 一键安装(Linux x86_64 / arm64) # # 用法: # bash -c "$(curl -fsSL https://gitea.91ai.io/rlwj-lab/LokiStack/raw/main/scripts/quick_start.sh)" # # 可覆盖(环境变量): # INSTALL_DIR 安装目录,默认 /opt/lokistack # VERSION 版本 tag,默认 latest(自动解析最新 release) # BASE_URL 下载源,默认 Gitea Release # PORT 服务端口,默认 8080 # SKIP_SERVICE=1 仅解压不注册 systemd(调试用) # ============================================================================ set -euo pipefail REPO="rlwj-lab/LokiStack" GITEA="https://gitea.91ai.io" INSTALL_DIR="${INSTALL_DIR:-/opt/lokistack}" VERSION="${VERSION:-latest}" BASE_URL="${BASE_URL:-${GITEA}}" PORT="${PORT:-8080}" info() { printf '\033[1;36m==>\033[0m %s\n' "$*"; } ok() { printf '\033[1;32m✅\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m⚠️\033[0m %s\n' "$*"; } die() { printf '\033[1;31m❌\033[0m %s\n' "$*" >&2; exit 1; } # ---- 1. 环境自检 ---- [ "$(id -u)" = "0" ] || die "请用 root 运行(sudo bash $0)" case "$(uname -m)" in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; *) die "不支持的架构: $(uname -m)(支持 x86_64 / arm64)" ;; esac command -v curl >/dev/null || die "缺少 curl,请先安装(apt/yum install curl)" command -v tar >/dev/null || die "缺少 tar,请先安装" if [ "${SKIP_SERVICE:-0}" != "1" ]; then command -v systemctl >/dev/null || die "未检测到 systemd,可设 SKIP_SERVICE=1 手动运行" fi ok "环境就绪: $(uname -m) -> ${ARCH}, $(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo Linux)" # ---- 2. 解析版本并下载 ---- if [ "$VERSION" = "latest" ]; then info "解析最新版本..." VERSION="$(curl -fsSL "$GITEA/api/v1/repos/$REPO/releases/latest" | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4)" [ -n "$VERSION" ] || die "无法获取最新版本(检查网络或手动指定 VERSION=vX.Y.Z)" fi PKG="lokistack-linux-${ARCH}.tar.gz" URL="$BASE_URL/$REPO/releases/download/$VERSION/$PKG" TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT info "下载 ${URL}" curl -fSL --retry 3 --connect-timeout 10 -o "$TMP/$PKG" "$URL" || die "下载失败(可设 BASE_URL 指定镜像源)" ok "下载完成: $(du -h "$TMP/$PKG" | cut -f1)" # ---- 3. 安装(重复执行 = 升级) ---- if systemctl is-active lokistack >/dev/null 2>&1; then info "检测到已有 LokiStack 服务,执行升级..." systemctl stop lokistack || true [ -d "$INSTALL_DIR/data" ] && cp -r "$INSTALL_DIR/data" "${INSTALL_DIR}/data.bak.$(date +%Y%m%d%H%M%S)" fi mkdir -p "$INSTALL_DIR" tar -xzf "$TMP/$PKG" -C "$TMP" SRC="$TMP/lokistack-linux-${ARCH}" mv "$SRC/lokistack" "$SRC/docs" "$SRC/homepage" "$SRC/README.md" "$INSTALL_DIR/" 2>/dev/null || true chmod +x "$INSTALL_DIR/lokistack" mkdir -p "$INSTALL_DIR/data" ok "安装到 $INSTALL_DIR(数据目录 $INSTALL_DIR/data)" # ---- 4. 密钥:自动生成随机密钥并持久化(显式已存在则不动) ---- SECRETS="$INSTALL_DIR/data/.secrets.env" gen() { head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'; } if [ ! -f "$SECRETS" ]; then umask 077 { echo "# LokiStack 自动生成,删除后重新执行脚本可重置" echo "JWT_SECRET=$(gen)"; echo "ENCRYPTION_KEY=$(gen)"; } > "$SECRETS" ok "已生成随机密钥: $SECRETS" else ok "沿用已有密钥: $SECRETS" fi # ---- 5. systemd 服务 ---- if [ "${SKIP_SERVICE:-0}" != "1" ]; then cat > /etc/systemd/system/lokistack.service </dev/null; then break; fi sleep 1 [ "$i" = "30" ] && warn "服务 30s 内未就绪,请查看: journalctl -u lokistack -e" done IP="$(hostname -I 2>/dev/null | awk '{print $1}')" PASS_FILE="$INSTALL_DIR/data/initial-admin-password.txt" echo "" printf '\033[1;32m================================================\033[0m\n' printf ' LokiStack 安装完成!\n' printf '\033[1;32m================================================\033[0m\n' printf ' 访问地址: http://%s:%s\n' "${IP:-<服务器IP>}" "$PORT" printf ' 初始账号: admin\n' [ -f "$PASS_FILE" ] && printf ' 初始密码: %s\n' "$(cat "$PASS_FILE")" || printf ' 初始密码: 见 %s\n' "$PASS_FILE" printf ' 用户手册: http://%s:%s/docs/\n' "${IP:-<服务器IP>}" "$PORT" printf '\n 常用命令:\n' printf ' systemctl status lokistack # 状态\n' printf ' journalctl -u lokistack -f # 日志\n' printf ' 重新执行本脚本 # 升级(数据自动备份)\n' printf '\033[1;32m================================================\033[0m\n'