#!/bin/bash # ============================================================= # GUARDiA ITSM 설치 스크립트 — CentOS 7 / CentOS Stream 8/9 # ============================================================= # 전제조건: 순수 CentOS OS (최소 설치) # 실행 방법: sudo bash setup_centos.sh # 설치 테스트: bash setup_centos.sh --test # 환경변수: # TOMCAT_VER=9.0.98 : Tomcat 버전 # TOMCAT_MIRROR=http:// : 내부 미러 URL (오프라인 환경) # ============================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" GUARDIA_ROOT="$(dirname "$SCRIPT_DIR")" LOG_FILE="/var/log/guardia_install.log" TEST_MODE="${1:-}" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; } info() { echo -e " $*"; } exec > >(tee -a "$LOG_FILE") 2>&1 echo "==================================================" echo " GUARDiA ITSM 설치 — CentOS" echo " 시작: $(date)" echo "==================================================" [[ $EUID -eq 0 ]] || fail "root 권한으로 실행하세요: sudo bash $0" OS_VER=$(rpm -E %{rhel} 2>/dev/null || echo "7") echo "감지된 CentOS 버전: $OS_VER" # ── 테스트 모드 ────────────────────────────────────────────── if [[ "$TEST_MODE" == "--test" ]]; then echo "=== 설치 검증 모드 ===" PASS=0; FAIL=0 check() { local desc="$1"; shift if "$@" &>/dev/null; then ok "$desc"; ((PASS++)) else echo -e "${RED}[FAIL]${NC} $desc"; ((FAIL++)) fi } check "Java 17 (OpenJDK)" java -version check "Python 3.11+" python3.11 --version check "PostgreSQL" pg_isready -q check "Redis" redis-cli ping check "Tomcat 9 서비스" systemctl is-active tomcat9 check "Tomcat HTTP" bash -c 'curl -sf http://localhost:8080/ -o /dev/null' check "Ollama 서비스" systemctl is-active ollama check "Ollama API" bash -c 'curl -sf http://localhost:11434/api/version -o /dev/null' check "Ollama 모델 존재" bash -c 'ollama list 2>/dev/null | grep -v NAME | grep -q .' check "GUARDiA 서비스" systemctl is-active guardia-itsm check "GUARDiA HTTP" bash -c 'curl -sf http://localhost:8001/ -o /dev/null' check "GUARDiA 로그인 API" bash -c 'curl -sf -X POST http://localhost:8001/api/auth/login \ -H "Content-Type: application/json" -d "{\"username\":\"admin\",\"password\":\"1111\"}" -o /dev/null' check "Fail2ban" systemctl is-active fail2ban check "Chrony NTP" chronyc tracking check "Nginx" nginx -t check "Python UTF-8" bash -c 'PYTHONIOENCODING=utf-8 python3.11 -c "print(\"OK\")" > /dev/null' echo "" echo "검증 결과: 성공 $PASS / 실패 $FAIL" [[ $FAIL -eq 0 ]] && ok "모든 검사 통과" || fail "일부 실패 — 로그: $LOG_FILE" exit 0 fi # ── 1. 시스템 패키지 ───────────────────────────────────────── echo "" echo "[1/10] 시스템 패키지 설치..." yum install -y epel-release 2>/dev/null || dnf install -y epel-release 2>/dev/null || true if [[ "$OS_VER" -ge 8 ]]; then dnf install -y \ curl wget git gcc openssl-devel libffi-dev \ python3.11 python3.11-devel python3-pip \ java-17-openjdk java-17-openjdk-devel \ postgresql-server postgresql-contrib \ redis nginx lsof unzip jq else yum install -y centos-release-scl 2>/dev/null || true yum install -y \ curl wget git gcc openssl-devel libffi-devel \ rh-python38 \ java-17-openjdk java-17-openjdk-devel \ postgresql-server postgresql-contrib \ redis nginx lsof unzip jq fi # JAVA_HOME 설정 JAVA_HOME_PATH=$(alternatives --list 2>/dev/null | grep jre_17 | awk '{print $3}' | head -1) [ -z "$JAVA_HOME_PATH" ] && JAVA_HOME_PATH=$(dirname $(dirname $(readlink -f $(which java)))) cat > /etc/profile.d/java.sh << JHEOF export JAVA_HOME=$JAVA_HOME_PATH export PATH=\$JAVA_HOME/bin:\$PATH JHEOF source /etc/profile.d/java.sh ok "시스템 패키지 + OpenJDK 17 설치 완료" # ── 2. Tomcat 9 설치 ───────────────────────────────────────── echo "" echo "[2/10] Tomcat 9 설치..." TOMCAT_VER="${TOMCAT_VER:-9.0.98}" TOMCAT_HOME="/app/tomcat" TOMCAT_USER="tomcat" id $TOMCAT_USER &>/dev/null || useradd -r -s /bin/false -d "$TOMCAT_HOME" $TOMCAT_USER # CentOS는 tomcat 패키지 버전이 낮을 수 있으므로 수동 설치 기본 TOMCAT_TAR="apache-tomcat-${TOMCAT_VER}.tar.gz" MIRROR="${TOMCAT_MIRROR:-https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VER}/bin}" if [[ ! -d "$TOMCAT_HOME/bin" ]]; then warn "Tomcat ${TOMCAT_VER} 설치 중 (MIRROR: $MIRROR)..." wget -q "$MIRROR/$TOMCAT_TAR" -O /tmp/$TOMCAT_TAR \ || fail "Tomcat 다운로드 실패. TOMCAT_MIRROR=http://내부미러/... 로 설정하세요." mkdir -p "$TOMCAT_HOME" tar -xzf /tmp/$TOMCAT_TAR -C "$TOMCAT_HOME" --strip-components=1 chmod +x "$TOMCAT_HOME/bin/"*.sh fi mkdir -p "$TOMCAT_HOME/logs" "$TOMCAT_HOME/temp" chown -R $TOMCAT_USER:$TOMCAT_USER "$TOMCAT_HOME" # opsagent 계정 추가 USERS_XML="$TOMCAT_HOME/conf/tomcat-users.xml" if [[ -f "$USERS_XML" ]] && ! grep -q "opsagent" "$USERS_XML"; then sed -i 's|| \n \n \n|' "$USERS_XML" fi cat > /etc/systemd/system/tomcat9.service << TCEOF [Unit] Description=Apache Tomcat 9 After=network.target [Service] Type=forking User=$TOMCAT_USER Group=$TOMCAT_USER Environment="JAVA_HOME=$JAVA_HOME_PATH" Environment="CATALINA_HOME=$TOMCAT_HOME" Environment="CATALINA_BASE=$TOMCAT_HOME" Environment="CATALINA_PID=$TOMCAT_HOME/temp/tomcat.pid" Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC" Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom" ExecStart=$TOMCAT_HOME/bin/startup.sh ExecStop=$TOMCAT_HOME/bin/shutdown.sh Restart=on-failure RestartSec=10 SuccessExitStatus=143 [Install] WantedBy=multi-user.target TCEOF systemctl daemon-reload systemctl enable tomcat9 systemctl start tomcat9 ok "Tomcat 9 서비스 등록 완료 (포트 8080)" # ── 3. Python 가상환경 ────────────────────────────────────── echo "" echo "[3/10] Python 가상환경..." PYTHON_BIN=$(command -v python3.11 || command -v python3.8 || command -v python3) mkdir -p /opt/guardia $PYTHON_BIN -m venv /opt/guardia/venv source /opt/guardia/venv/bin/activate pip install --upgrade pip -q pip install -r "$GUARDIA_ROOT/itsm/requirements.txt" -q ok "Python 패키지 설치 완료" # ── 4. PostgreSQL 초기화 ──────────────────────────────────── echo "" echo "[4/10] PostgreSQL 초기화..." if [[ "$OS_VER" -ge 8 ]]; then postgresql-setup --initdb 2>/dev/null || true else postgresql-setup initdb 2>/dev/null || true fi systemctl start postgresql systemctl enable postgresql sudo -u postgres psql -tc "SELECT 1 FROM pg_user WHERE usename='guardia'" | grep -q 1 || \ sudo -u postgres psql -c "CREATE USER guardia WITH PASSWORD 'guardia_secure_pw';" sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='guardia'" | grep -q 1 || \ sudo -u postgres psql -c "CREATE DATABASE guardia OWNER guardia;" ok "PostgreSQL 설정 완료" # ── 5. Redis ──────────────────────────────────────────────── echo "" echo "[5/10] Redis 시작..." systemctl start redis systemctl enable redis ok "Redis 완료" # ── 6. 환경 파일 ──────────────────────────────────────────── echo "" echo "[6/10] 환경 설정 파일..." ENV_FILE="$GUARDIA_ROOT/itsm/.env" if [[ ! -f "$ENV_FILE" ]]; then cat > "$ENV_FILE" << 'ENVEOF' DATABASE_URL=postgresql+asyncpg://guardia:guardia_secure_pw@localhost:5432/guardia SECRET_KEY=change_this_secret_key_in_production_min_32chars ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=480 REDIS_URL=redis://localhost:6379/0 OLLAMA_BASE_URL=http://localhost:11434 GUARDIA_LLM_MODEL=llama3.1:8b MESSENGER_BASE_URL=http://localhost:8002 MESSENGER_OPS_ROOM=ops CATALINA_HOME=/app/tomcat ENVEOF warn ".env 생성됨 — SECRET_KEY 필수 변경" fi # ── 7. DB 초기화 ───────────────────────────────────────────── echo "" echo "[7/10] DB 초기화..." cd "$GUARDIA_ROOT/itsm" source /opt/guardia/venv/bin/activate if ss -tlnp 2>/dev/null | grep -q ':8001'; then warn "포트 8001 사용 중 — 종료..." fuser -k 8001/tcp 2>/dev/null || true; sleep 2 fi PYTHONIOENCODING=utf-8 python tools/db_init.py --force \ && ok "DB 초기화 완료" || fail "DB 초기화 실패" # ── 8. systemd 서비스 ──────────────────────────────────────── echo "" echo "[8/10] GUARDiA ITSM 서비스..." systemctl stop guardia-itsm 2>/dev/null || true cat > /etc/systemd/system/guardia-itsm.service << SVCEOF [Unit] Description=GUARDiA ITSM Server After=network.target postgresql.service redis.service tomcat9.service [Service] Type=exec User=nginx WorkingDirectory=$GUARDIA_ROOT/itsm Environment="PATH=/opt/guardia/venv/bin" Environment="PYTHONIOENCODING=utf-8" Environment="PYTHONUNBUFFERED=1" EnvironmentFile=$GUARDIA_ROOT/itsm/.env ExecStart=/opt/guardia/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8001 --workers 4 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target SVCEOF systemctl daemon-reload systemctl enable guardia-itsm systemctl start guardia-itsm ok "GUARDiA ITSM 서비스 완료" # ── 9. Nginx ───────────────────────────────────────────────── echo "" echo "[9/10] Nginx..." cat > /etc/nginx/conf.d/guardia.conf << 'NGXEOF' server { listen 80; server_name _; client_max_body_size 100M; location / { proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 300s; } } NGXEOF setsebool -P httpd_can_network_connect 1 2>/dev/null || true systemctl enable nginx; systemctl start nginx if systemctl is-active firewalld &>/dev/null; then firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --reload fi ok "Nginx 완료" # ── 10. Ollama (온프레미스 sLLM) ────────────────────────────── echo "" echo "[10/13] Ollama (온프레미스 sLLM 서버) 설치..." OLLAMA_INSTALL="${OLLAMA_INSTALL:-online}" OLLAMA_MODELS="${OLLAMA_MODELS:-llama3.1:8b}" if [[ "$OLLAMA_INSTALL" == "skip" ]]; then warn "Ollama 설치 건너뜀 (OLLAMA_INSTALL=skip)" else if [[ "$OLLAMA_INSTALL" == "offline" ]]; then BIN_PATH="${OLLAMA_BIN_PATH:-/tmp/ollama}" [[ -f "$BIN_PATH" ]] || fail "오프라인: OLLAMA_BIN_PATH에 바이너리 필요" cp "$BIN_PATH" /usr/local/bin/ollama && chmod +x /usr/local/bin/ollama else curl -fsSL https://ollama.com/install.sh | sh \ || { warn "Ollama 설치 실패 — 수동 설치 필요"; OLLAMA_INSTALL=failed; } fi if [[ "$OLLAMA_INSTALL" != "failed" ]]; then systemctl enable ollama 2>/dev/null || true systemctl start ollama 2>/dev/null || true sleep 3 if [[ "$OLLAMA_INSTALL" == "online" ]]; then for model in $OLLAMA_MODELS; do ollama pull "$model" 2>&1 | tail -3 \ || warn "모델 $model 다운로드 실패 — 나중에: ollama pull $model" done fi ok "Ollama 설치 완료 (http://localhost:11434)" fi fi # ── 11. 보안·운영 도구 ─────────────────────────────────────── echo "" echo "[11/13] 보안·운영 도구 (Fail2ban / Chrony / JDK 다중 버전 / Logrotate)..." # Fail2ban if [[ "$OS_VER" -ge 8 ]]; then dnf install -y fail2ban 2>/dev/null || warn "fail2ban 설치 실패" else yum install -y fail2ban 2>/dev/null || warn "fail2ban 설치 실패" fi if command -v fail2ban-server &>/dev/null; then cat > /etc/fail2ban/jail.local << 'F2BEOF' [sshd] enabled = true maxretry = 5 bantime = 3600 findtime = 600 F2BEOF systemctl enable fail2ban; systemctl start fail2ban ok "Fail2ban 설치 완료" fi # Chrony NTP if [[ "$OS_VER" -ge 8 ]]; then dnf install -y chrony 2>/dev/null || true else yum install -y chrony 2>/dev/null || true fi systemctl enable chronyd 2>/dev/null || true systemctl start chronyd 2>/dev/null || true ok "Chrony NTP 설정 완료" # JDK 8/11 (레거시 WAS 지원) if [[ "$OS_VER" -ge 8 ]]; then dnf install -y java-11-openjdk 2>/dev/null || warn "JDK 11 설치 실패" dnf install -y java-1.8.0-openjdk 2>/dev/null || warn "JDK 8 설치 실패" else yum install -y java-11-openjdk java-1.8.0-openjdk 2>/dev/null || true fi ok "JDK 다중 버전 설치 완료 (alternatives --config java 로 전환)" # Logrotate for Tomcat cat > /etc/logrotate.d/tomcat9 << 'LREOF' /app/tomcat/logs/catalina.out { daily rotate 14 compress delaycompress missingok notifempty } /app/tomcat/logs/*.log { weekly rotate 8 compress missingok } LREOF ok "Logrotate 설정 완료 (catalina.out 14일 보관)" # ── 12. 서비스 상태 확인 ───────────────────────────────────── echo "" echo "[12/13] 서비스 상태 확인..." for svc in tomcat9 ollama guardia-itsm nginx postgresql redis; do systemctl is-active "$svc" &>/dev/null && ok "$svc 실행 중" || warn "$svc 미실행" done echo "" echo "==================================================" ok "GUARDiA ITSM 설치 완료 — CentOS [13/13]" info "GUARDiA URL: http://$(hostname -I | awk '{print $1}')" info "Tomcat URL: http://$(hostname -I | awk '{print $1}'):8080 (내부 전용)" info "Ollama URL: http://localhost:11434 (내부 전용)" info "검증: sudo bash $0 --test" echo "=================================================="