feat(setup): 폐쇄망 완전 오프라인 설치 지원
[download_packages.sh — 인터넷 서버에서 실행] - OS별 패키지 다운로드 (ubuntu .deb / centos .rpm) - pip wheel 전체 다운로드 (-r requirements.txt) - Tomcat 9 tar.gz, Ollama 바이너리, Chart.js 다운로드 - Ollama 모델(llama3.1:8b, codellama:7b) 압축 패키지 - Docker 이미지 tar (docker_package.sh 연동) - setup/offline/ 에 계층적으로 저장, README.md 자동 생성 [설치 스크립트 오프라인 지원 강화] - setup_ubuntu.sh: OFFLINE_PKG_DIR 환경변수 지원 (.deb 로컬 설치) - setup_centos.sh: OFFLINE_PKG_DIR 환경변수 지원 (.rpm 로컬 설치) - 기존 TOMCAT_MIRROR=file://..., OLLAMA_INSTALL=offline 유지 [Chart.js 오프라인 폴백] - index.html: CDN 실패 시 /static/chart.umd.min.js 로컬 폴백 - db_init.py: 오프라인 패키지에서 chart.umd.min.js 자동 복사 폐쇄망 설치 절차: 인터넷 서버: bash setup/download_packages.sh all USB 복사: tar -czf offline.tar.gz setup/offline/ 폐쇄망 설치: OFFLINE_PKG_DIR=./offline/ubuntu bash setup/setup_ubuntu.sh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eea643ce40
commit
d196886752
@ -5,6 +5,22 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>GUARDiA ITSM</title>
|
<title>GUARDiA ITSM</title>
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
<!-- Chart.js — 대시보드 차트 (CDN 실패 시 로컬 폴백) -->
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.src = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js';
|
||||||
|
s.crossOrigin = 'anonymous';
|
||||||
|
s.onerror = function() {
|
||||||
|
// 폐쇄망 폴백: /static/chart.umd.min.js (오프라인 설치 시 복사됨)
|
||||||
|
var local = document.createElement('script');
|
||||||
|
local.src = '/static/chart.umd.min.js';
|
||||||
|
local.onerror = function() { window._chartjsUnavailable = true; };
|
||||||
|
document.head.appendChild(local);
|
||||||
|
};
|
||||||
|
document.head.appendChild(s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<!-- F-4: PWA -->
|
<!-- F-4: PWA -->
|
||||||
<link rel="manifest" href="/static/manifest.json">
|
<link rel="manifest" href="/static/manifest.json">
|
||||||
<meta name="theme-color" content="#4f8ef7">
|
<meta name="theme-color" content="#4f8ef7">
|
||||||
@ -143,59 +159,163 @@
|
|||||||
|
|
||||||
<!-- Views -->
|
<!-- Views -->
|
||||||
<div id="view-dashboard" class="view active">
|
<div id="view-dashboard" class="view active">
|
||||||
<div class="stats-row" id="stats-row">
|
<!-- KPI 카드 행 -->
|
||||||
<!-- filled by JS -->
|
<div class="stats-row" id="stats-row"><!-- filled by JS --></div>
|
||||||
|
|
||||||
|
<!-- ── 대시보드 탭 ── -->
|
||||||
|
<div class="dash-tab-nav" id="dash-tab-nav">
|
||||||
|
<button class="dash-tab active" data-tab="ops" onclick="switchDashTab('ops')">📊 운영 현황</button>
|
||||||
|
<button class="dash-tab" data-tab="infra" onclick="switchDashTab('infra')">🖥️ 인프라</button>
|
||||||
|
<button class="dash-tab" data-tab="security" onclick="switchDashTab('security')">🔒 보안</button>
|
||||||
|
<button class="dash-tab" data-tab="ai" onclick="switchDashTab('ai')">🤖 AI 인사이트</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-grid">
|
|
||||||
<div class="card" id="recent-list-card">
|
<!-- ═══ 탭 1: 운영 현황 ═══ -->
|
||||||
<div class="card-header">최근 SR</div>
|
<div class="dash-tab-panel active" id="dash-panel-ops">
|
||||||
<div class="card-body" id="recent-list"></div>
|
<div class="chart-grid-2">
|
||||||
</div>
|
<!-- SR 상태 도넛 -->
|
||||||
<div class="card" id="status-chart-card">
|
<div class="card chart-card">
|
||||||
<div class="card-header">상태별 현황</div>
|
<div class="card-header">SR 상태 분포</div>
|
||||||
<div class="card-body" id="status-chart"></div>
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-sr-status"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 엔지니어 워크로드 패널 -->
|
<!-- 우선순위 바 -->
|
||||||
<div class="card" id="workload-card" style="margin-top:18px">
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">우선순위별 SR</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-sr-priority"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 7일 SR 추이 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
||||||
<span>👷 엔지니어 워크로드</span>
|
<span>📈 최근 7일 SR 추이</span>
|
||||||
<button class="btn btn-secondary" style="font-size:12px;padding:4px 10px"
|
<button class="btn btn-secondary" style="font-size:11px;padding:3px 8px"
|
||||||
onclick="loadWorkload()">새로고침</button>
|
onclick="loadDashboardCharts()">새로고침</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body" style="padding:14px 16px">
|
||||||
|
<canvas id="chart-sr-trend" style="max-height:200px"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 엔지니어 워크로드 + 최근 SR -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">👷 엔지니어 워크로드</div>
|
||||||
<div class="card-body" id="workload-body">
|
<div class="card-body" id="workload-body">
|
||||||
<div style="color:var(--text-muted);font-size:13px;padding:12px 18px">로딩 중…</div>
|
<div style="color:var(--text-muted);font-size:13px;padding:8px">로딩 중…</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 7일 SR 추이 차트 (ADMIN/PM 전용 — JS 제어) -->
|
<div class="card chart-card">
|
||||||
<div id="trend-chart-card" class="card" style="margin-top:18px;display:none">
|
<div class="card-header">최근 SR</div>
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
<div class="card-body" id="recent-list" style="overflow-y:auto;max-height:260px"></div>
|
||||||
<span>📈 최근 7일 SR 추이</span>
|
|
||||||
<span id="trend-last-updated" style="font-size:11px;color:var(--text-muted)"></span>
|
|
||||||
</div>
|
|
||||||
<div class="card-body" style="padding:16px 18px">
|
|
||||||
<div id="trend-chart" class="trend-chart"></div>
|
|
||||||
<div class="trend-legend">
|
|
||||||
<div class="trend-legend-item">
|
|
||||||
<div class="trend-legend-dot" style="background:#818cf8"></div><span>생성</span>
|
|
||||||
</div>
|
|
||||||
<div class="trend-legend-item">
|
|
||||||
<div class="trend-legend-dot" style="background:#34d399"></div><span>완료</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 2: 인프라 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-infra">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- 기관별 SR 수평 바 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">기관별 SR 현황 (Top 10)</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-inst-sr"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<!-- 7일 추이 차트 (ADMIN/PM 전용 — JS가 표시 여부 제어) -->
|
|
||||||
<div id="trend-chart-card" class="card" style="margin-top:16px;display:none">
|
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
|
||||||
<span>📈 최근 7일 SR 추이</span>
|
|
||||||
<span id="trend-last-updated" style="font-size:11px;color:var(--text-muted)"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" style="padding:16px">
|
<!-- 서버 OS 분포 -->
|
||||||
<div id="trend-chart" class="trend-chart"></div>
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">서버 OS 분포</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-os-dist"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- CMDB 서버 상태 그리드 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
|
<div class="card-header">서버 헬스 현황</div>
|
||||||
|
<div class="card-body" id="server-health-grid"
|
||||||
|
style="display:flex;flex-wrap:wrap;gap:8px;padding:14px 16px">
|
||||||
|
<div style="color:var(--text-muted);font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- SSL 만료 현황 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
|
<div class="card-header">🔐 SSL 인증서 만료 현황</div>
|
||||||
|
<div class="card-body chart-body" style="max-height:220px">
|
||||||
|
<canvas id="chart-ssl-expiry"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 3: 보안 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-security">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- 취약점 심각도 바 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">취약점 심각도 분포</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-vuln-severity"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 패치율 도넛 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">패치 적용 현황</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-patch-rate"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- PAM 세션 + 감사 로그 -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🔑 PAM 세션 현황</div>
|
||||||
|
<div class="card-body" id="pam-status-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">📋 최근 감사 로그</div>
|
||||||
|
<div class="card-body" id="audit-body"
|
||||||
|
style="overflow-y:auto;max-height:260px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 4: AI 인사이트 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-ai">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- AI 티켓 분류 분포 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🤖 AI 티켓 분류 현황</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-ai-category"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 이상 탐지 추이 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">⚡ 이상 탐지 추이 (7일)</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-anomaly-trend"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 예측 유지보수 + 학습 루프 -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🔧 예측 유지보수 위험 서버</div>
|
||||||
|
<div class="card-body" id="predictive-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">📚 학습 루프 현황</div>
|
||||||
|
<div class="card-body" id="learning-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /view-dashboard -->
|
||||||
|
|
||||||
<div id="view-board" class="view">
|
<div id="view-board" class="view">
|
||||||
<div id="kanban-board">
|
<div id="kanban-board">
|
||||||
|
|||||||
@ -112,5 +112,16 @@ async def main():
|
|||||||
print("[OK] DB 초기화 완료")
|
print("[OK] DB 초기화 완료")
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_offline_assets():
|
||||||
|
"""폐쇄망 정적 파일 복사 (있는 경우)."""
|
||||||
|
import shutil
|
||||||
|
offline_chart = ITSM_DIR.parent / "setup" / "offline" / "common" / "chart.umd.min.js"
|
||||||
|
target_chart = ITSM_DIR / "static" / "chart.umd.min.js"
|
||||||
|
if offline_chart.exists() and not target_chart.exists():
|
||||||
|
shutil.copy2(offline_chart, target_chart)
|
||||||
|
print(f"[OK] Chart.js 오프라인 파일 복사: {target_chart}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
_copy_offline_assets()
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
375
setup/download_packages.sh
Normal file
375
setup/download_packages.sh
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# GUARDiA 폐쇄망 설치 패키지 다운로드 스크립트
|
||||||
|
# ============================================================
|
||||||
|
# 인터넷이 연결된 Linux 서버에서 실행하세요.
|
||||||
|
# 모든 필요 파일을 setup/offline/ 에 다운로드합니다.
|
||||||
|
#
|
||||||
|
# 사용법:
|
||||||
|
# bash setup/download_packages.sh [OS유형]
|
||||||
|
# bash setup/download_packages.sh ubuntu # Ubuntu 20/22/24
|
||||||
|
# bash setup/download_packages.sh centos # CentOS 7/8/Stream
|
||||||
|
# bash setup/download_packages.sh rhel # RHEL 8/9
|
||||||
|
# bash setup/download_packages.sh all # 세 OS 모두
|
||||||
|
#
|
||||||
|
# 결과 디렉토리:
|
||||||
|
# setup/offline/
|
||||||
|
# ├── ubuntu/ deb 패키지 + pip 휠 + 바이너리
|
||||||
|
# ├── centos/ rpm 패키지 + pip 휠 + 바이너리
|
||||||
|
# ├── rhel/ rpm 패키지 + pip 휠 + 바이너리
|
||||||
|
# ├── common/ 공통 바이너리 (Tomcat, Ollama, Chart.js 등)
|
||||||
|
# └── python/ pip 휠 파일 (OS 공통)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
GUARDIA_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
OFFLINE_DIR="$SCRIPT_DIR/offline"
|
||||||
|
OS_TYPE="${1:-all}"
|
||||||
|
|
||||||
|
TOMCAT_VER="${TOMCAT_VER:-9.0.98}"
|
||||||
|
PYTHON_VER="${PYTHON_VER:-3.11}"
|
||||||
|
|
||||||
|
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 " $*"; }
|
||||||
|
|
||||||
|
echo "=================================================="
|
||||||
|
echo " GUARDiA 폐쇄망 패키지 다운로드"
|
||||||
|
echo " 대상 OS: $OS_TYPE"
|
||||||
|
echo " 저장 위치: $OFFLINE_DIR"
|
||||||
|
echo "=================================================="
|
||||||
|
|
||||||
|
mkdir -p "$OFFLINE_DIR/common" "$OFFLINE_DIR/python"
|
||||||
|
|
||||||
|
# ── 공통 바이너리 다운로드 ──────────────────────────────
|
||||||
|
|
||||||
|
download_common() {
|
||||||
|
echo ""
|
||||||
|
echo "=== 공통 바이너리 다운로드 ==="
|
||||||
|
|
||||||
|
# Tomcat 9
|
||||||
|
TOMCAT_URL="https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz"
|
||||||
|
if [[ ! -f "$OFFLINE_DIR/common/apache-tomcat-${TOMCAT_VER}.tar.gz" ]]; then
|
||||||
|
echo " Tomcat $TOMCAT_VER 다운로드..."
|
||||||
|
wget -q "$TOMCAT_URL" -O "$OFFLINE_DIR/common/apache-tomcat-${TOMCAT_VER}.tar.gz" \
|
||||||
|
&& ok "Tomcat $TOMCAT_VER" || warn "Tomcat 다운로드 실패"
|
||||||
|
else
|
||||||
|
info "Tomcat $TOMCAT_VER 이미 존재"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ollama Linux AMD64
|
||||||
|
OLLAMA_VER="${OLLAMA_VER:-latest}"
|
||||||
|
if [[ ! -f "$OFFLINE_DIR/common/ollama-linux-amd64" ]]; then
|
||||||
|
echo " Ollama 바이너리 다운로드..."
|
||||||
|
wget -q "https://ollama.com/download/ollama-linux-amd64" \
|
||||||
|
-O "$OFFLINE_DIR/common/ollama-linux-amd64" \
|
||||||
|
&& chmod +x "$OFFLINE_DIR/common/ollama-linux-amd64" \
|
||||||
|
&& ok "Ollama binary" \
|
||||||
|
|| warn "Ollama 다운로드 실패"
|
||||||
|
else
|
||||||
|
info "Ollama 바이너리 이미 존재"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Chart.js (오프라인 대시보드용)
|
||||||
|
if [[ ! -f "$OFFLINE_DIR/common/chart.umd.min.js" ]]; then
|
||||||
|
echo " Chart.js 다운로드..."
|
||||||
|
wget -q "https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" \
|
||||||
|
-O "$OFFLINE_DIR/common/chart.umd.min.js" \
|
||||||
|
&& ok "Chart.js 4.4.0" || warn "Chart.js 다운로드 실패"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NSSM (Windows 서비스 관리자)
|
||||||
|
if [[ ! -f "$OFFLINE_DIR/common/nssm-2.24.zip" ]]; then
|
||||||
|
echo " NSSM 다운로드..."
|
||||||
|
wget -q "https://nssm.cc/release/nssm-2.24.zip" \
|
||||||
|
-O "$OFFLINE_DIR/common/nssm-2.24.zip" \
|
||||||
|
&& ok "NSSM 2.24" || warn "NSSM 다운로드 실패"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Python 패키지 (pip wheel) ───────────────────────────
|
||||||
|
|
||||||
|
download_python_packages() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Python 패키지 다운로드 (pip wheel) ==="
|
||||||
|
|
||||||
|
if [[ ! -d "$OFFLINE_DIR/python/wheels" ]]; then
|
||||||
|
mkdir -p "$OFFLINE_DIR/python/wheels"
|
||||||
|
echo " requirements.txt 기반 wheel 다운로드..."
|
||||||
|
pip download \
|
||||||
|
-r "$GUARDIA_ROOT/itsm/requirements.txt" \
|
||||||
|
-d "$OFFLINE_DIR/python/wheels" \
|
||||||
|
--platform manylinux2014_x86_64 \
|
||||||
|
--python-version 311 \
|
||||||
|
--implementation cp \
|
||||||
|
--abi cp311 \
|
||||||
|
--only-binary=:all: \
|
||||||
|
2>/dev/null || true
|
||||||
|
|
||||||
|
# 일부 패키지는 소스 빌드 필요 — 아무 플랫폼 없이 재시도
|
||||||
|
pip download \
|
||||||
|
-r "$GUARDIA_ROOT/itsm/requirements.txt" \
|
||||||
|
-d "$OFFLINE_DIR/python/wheels" \
|
||||||
|
2>/dev/null || true
|
||||||
|
|
||||||
|
ok "Python 패키지 다운로드 완료 ($(ls "$OFFLINE_DIR/python/wheels" | wc -l)개)"
|
||||||
|
else
|
||||||
|
info "Python 패키지 이미 존재 ($(ls "$OFFLINE_DIR/python/wheels" | wc -l)개)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Ubuntu/Debian 패키지 ────────────────────────────────
|
||||||
|
|
||||||
|
download_ubuntu() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Ubuntu .deb 패키지 다운로드 ==="
|
||||||
|
|
||||||
|
if ! command -v apt-get &>/dev/null; then
|
||||||
|
warn "apt-get 없음 — Ubuntu/Debian 환경에서 실행하거나 Docker로 다운로드하세요."
|
||||||
|
warn " docker run --rm -v $OFFLINE_DIR/ubuntu:/downloads ubuntu:22.04 bash -c \\"
|
||||||
|
warn " 'apt-get update && apt-get install -y --download-only -o Dir::Cache::Archives=/downloads [패키지목록]'"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
UBUNTU_DIR="$OFFLINE_DIR/ubuntu"
|
||||||
|
mkdir -p "$UBUNTU_DIR"
|
||||||
|
|
||||||
|
apt-get update -qq
|
||||||
|
|
||||||
|
PACKAGES=(
|
||||||
|
python3.11 python3.11-venv python3.11-dev python3-pip
|
||||||
|
openjdk-17-jdk
|
||||||
|
openjdk-11-jdk
|
||||||
|
openjdk-8-jdk-headless
|
||||||
|
postgresql postgresql-contrib
|
||||||
|
redis-server
|
||||||
|
nginx
|
||||||
|
fail2ban
|
||||||
|
chrony
|
||||||
|
curl wget git
|
||||||
|
lsof unzip jq
|
||||||
|
libpq-dev gcc
|
||||||
|
)
|
||||||
|
|
||||||
|
echo " 패키지 다운로드 중 (${#PACKAGES[@]}개)..."
|
||||||
|
apt-get install -y --download-only \
|
||||||
|
-o Dir::Cache::Archives="$UBUNTU_DIR" \
|
||||||
|
"${PACKAGES[@]}" 2>/dev/null \
|
||||||
|
&& ok "Ubuntu .deb 다운로드 완료 ($(ls "$UBUNTU_DIR"/*.deb 2>/dev/null | wc -l)개)" \
|
||||||
|
|| warn "일부 패키지 다운로드 실패 — 이미 설치된 패키지는 아래로 수동 복사:"
|
||||||
|
|
||||||
|
# 이미 설치된 패키지의 캐시 복사
|
||||||
|
find /var/cache/apt/archives -name "*.deb" 2>/dev/null | while read -r f; do
|
||||||
|
cp -n "$f" "$UBUNTU_DIR/" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# 오프라인 설치 스크립트 생성
|
||||||
|
cat > "$UBUNTU_DIR/install_offline.sh" << 'UBSCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
# Ubuntu 오프라인 패키지 설치
|
||||||
|
set -e
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
echo "Ubuntu 패키지 오프라인 설치 중..."
|
||||||
|
dpkg -i "$SCRIPT_DIR"/*.deb 2>/dev/null || true
|
||||||
|
apt-get install -f -y 2>/dev/null || true
|
||||||
|
echo "완료"
|
||||||
|
UBSCRIPT
|
||||||
|
chmod +x "$UBUNTU_DIR/install_offline.sh"
|
||||||
|
ok "Ubuntu 오프라인 설치 스크립트 생성"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── CentOS/RHEL rpm 패키지 ──────────────────────────────
|
||||||
|
|
||||||
|
download_centos() {
|
||||||
|
echo ""
|
||||||
|
echo "=== CentOS/RHEL .rpm 패키지 다운로드 ==="
|
||||||
|
|
||||||
|
if ! command -v dnf &>/dev/null && ! command -v yum &>/dev/null; then
|
||||||
|
warn "dnf/yum 없음 — CentOS/RHEL 환경에서 실행하거나 Docker로 다운로드하세요."
|
||||||
|
warn " docker run --rm -v $OFFLINE_DIR/centos:/downloads centos:stream9 bash -c \\"
|
||||||
|
warn " 'dnf download --downloaddir=/downloads --resolve [패키지목록]'"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
CENTOS_DIR="$OFFLINE_DIR/centos"
|
||||||
|
mkdir -p "$CENTOS_DIR"
|
||||||
|
|
||||||
|
PACKAGES=(
|
||||||
|
python3.11 python3.11-devel python3-pip
|
||||||
|
java-17-openjdk java-17-openjdk-devel
|
||||||
|
java-11-openjdk
|
||||||
|
java-1.8.0-openjdk
|
||||||
|
postgresql-server postgresql-contrib
|
||||||
|
redis
|
||||||
|
nginx
|
||||||
|
fail2ban
|
||||||
|
chrony
|
||||||
|
curl wget git
|
||||||
|
lsof unzip jq
|
||||||
|
postgresql-devel gcc
|
||||||
|
)
|
||||||
|
|
||||||
|
PKG_MGR="dnf"
|
||||||
|
command -v dnf &>/dev/null || PKG_MGR="yum"
|
||||||
|
|
||||||
|
echo " 패키지 다운로드 중..."
|
||||||
|
$PKG_MGR install -y --downloadonly \
|
||||||
|
--downloaddir="$CENTOS_DIR" \
|
||||||
|
"${PACKAGES[@]}" 2>/dev/null \
|
||||||
|
&& ok "CentOS/RHEL .rpm 다운로드 완료 ($(ls "$CENTOS_DIR"/*.rpm 2>/dev/null | wc -l)개)" \
|
||||||
|
|| warn "일부 패키지 다운로드 실패"
|
||||||
|
|
||||||
|
cat > "$CENTOS_DIR/install_offline.sh" << 'RPMSCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
# CentOS/RHEL 오프라인 패키지 설치
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
echo "RPM 패키지 오프라인 설치 중..."
|
||||||
|
dnf install -y "$SCRIPT_DIR"/*.rpm --disablerepo='*' 2>/dev/null || \
|
||||||
|
yum install -y "$SCRIPT_DIR"/*.rpm 2>/dev/null || \
|
||||||
|
rpm -ivh --nodeps "$SCRIPT_DIR"/*.rpm 2>/dev/null || true
|
||||||
|
echo "완료"
|
||||||
|
RPMSCRIPT
|
||||||
|
chmod +x "$CENTOS_DIR/install_offline.sh"
|
||||||
|
ok "CentOS/RHEL 오프라인 설치 스크립트 생성"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Ollama 모델 다운로드 ─────────────────────────────────
|
||||||
|
|
||||||
|
download_ollama_models() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Ollama 모델 다운로드 ==="
|
||||||
|
|
||||||
|
MODELS_DIR="$OFFLINE_DIR/common/ollama-models"
|
||||||
|
mkdir -p "$MODELS_DIR"
|
||||||
|
|
||||||
|
MODELS=( "llama3.1:8b" "codellama:7b" )
|
||||||
|
|
||||||
|
if command -v ollama &>/dev/null; then
|
||||||
|
for model in "${MODELS[@]}"; do
|
||||||
|
echo " 모델 다운로드: $model"
|
||||||
|
ollama pull "$model" 2>&1 | tail -3 || warn "$model 다운로드 실패"
|
||||||
|
done
|
||||||
|
|
||||||
|
# 모델 파일 압축
|
||||||
|
OLLAMA_HOME="${OLLAMA_MODELS:-$HOME/.ollama}"
|
||||||
|
if [[ -d "$OLLAMA_HOME/models" ]]; then
|
||||||
|
echo " 모델 파일 압축 중..."
|
||||||
|
tar -czf "$MODELS_DIR/models.tar.gz" \
|
||||||
|
-C "$OLLAMA_HOME" models manifests 2>/dev/null \
|
||||||
|
&& ok "Ollama 모델 압축 완료 ($(du -sh "$MODELS_DIR/models.tar.gz" | cut -f1))" \
|
||||||
|
|| warn "모델 압축 실패"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Ollama 미설치 — 모델 다운로드 건너뜀"
|
||||||
|
info "설치 후 수동 실행: OLLAMA_MODELS_PATH=$MODELS_DIR ollama pull llama3.1:8b"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Docker 이미지 패키지 ─────────────────────────────────
|
||||||
|
|
||||||
|
download_docker_images() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Docker 이미지 패키지 ==="
|
||||||
|
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
bash "$SCRIPT_DIR/docker_package.sh" "$OFFLINE_DIR/docker"
|
||||||
|
ok "Docker 이미지 패키지 완료"
|
||||||
|
else
|
||||||
|
warn "Docker 미설치 — docker_package.sh를 Docker가 설치된 서버에서 별도 실행하세요."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 설치 인덱스 생성 ─────────────────────────────────────
|
||||||
|
|
||||||
|
create_index() {
|
||||||
|
echo ""
|
||||||
|
echo "=== 설치 인덱스 생성 ==="
|
||||||
|
|
||||||
|
cat > "$OFFLINE_DIR/README.md" << RDEOF
|
||||||
|
# GUARDiA 폐쇄망 설치 패키지
|
||||||
|
|
||||||
|
생성 일시: $(date)
|
||||||
|
|
||||||
|
## 디렉토리 구조
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
offline/
|
||||||
|
├── common/ # 공통 바이너리
|
||||||
|
│ ├── apache-tomcat-${TOMCAT_VER}.tar.gz
|
||||||
|
│ ├── ollama-linux-amd64
|
||||||
|
│ ├── chart.umd.min.js
|
||||||
|
│ └── ollama-models/models.tar.gz
|
||||||
|
├── ubuntu/ # Ubuntu .deb 패키지
|
||||||
|
│ └── install_offline.sh
|
||||||
|
├── centos/ # CentOS/RHEL .rpm 패키지
|
||||||
|
│ └── install_offline.sh
|
||||||
|
├── python/
|
||||||
|
│ └── wheels/ # pip wheel 파일
|
||||||
|
└── docker/ # Docker 이미지 tar (있는 경우)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 폐쇄망 설치 절차
|
||||||
|
|
||||||
|
### Ubuntu
|
||||||
|
\`\`\`bash
|
||||||
|
# 1. OS 패키지
|
||||||
|
bash offline/ubuntu/install_offline.sh
|
||||||
|
|
||||||
|
# 2. Python 패키지
|
||||||
|
pip install --no-index --find-links=offline/python/wheels -r itsm/requirements.txt
|
||||||
|
|
||||||
|
# 3. Tomcat
|
||||||
|
TOMCAT_MIRROR=file://\$(pwd)/offline/common bash setup/setup_ubuntu.sh
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### CentOS/RHEL
|
||||||
|
\`\`\`bash
|
||||||
|
bash offline/centos/install_offline.sh
|
||||||
|
pip install --no-index --find-links=offline/python/wheels -r itsm/requirements.txt
|
||||||
|
TOMCAT_MIRROR=file://\$(pwd)/offline/common bash setup/setup_centos.sh
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Docker (Docker 설치된 서버)
|
||||||
|
\`\`\`bash
|
||||||
|
bash offline/docker/guardia-docker-*/docker_load.sh --start
|
||||||
|
\`\`\`
|
||||||
|
RDEOF
|
||||||
|
|
||||||
|
ok "설치 인덱스 생성: $OFFLINE_DIR/README.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 메인 실행 ────────────────────────────────────────────
|
||||||
|
|
||||||
|
download_common
|
||||||
|
download_python_packages
|
||||||
|
|
||||||
|
case "$OS_TYPE" in
|
||||||
|
ubuntu)
|
||||||
|
download_ubuntu ;;
|
||||||
|
centos|rhel)
|
||||||
|
download_centos ;;
|
||||||
|
all)
|
||||||
|
download_ubuntu
|
||||||
|
download_centos ;;
|
||||||
|
docker)
|
||||||
|
download_docker_images ;;
|
||||||
|
*)
|
||||||
|
warn "알 수 없는 OS 유형: $OS_TYPE. ubuntu|centos|rhel|all|docker 중 선택하세요." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
download_ollama_models
|
||||||
|
create_index
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
ok "다운로드 완료!"
|
||||||
|
echo ""
|
||||||
|
info "저장 위치: $OFFLINE_DIR"
|
||||||
|
info "전체 크기: $(du -sh "$OFFLINE_DIR" | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
info "패키지 압축 (USB 복사용):"
|
||||||
|
info " tar -czf guardia-offline-$(date +%Y%m%d).tar.gz -C setup offline/"
|
||||||
|
echo "=================================================="
|
||||||
@ -5,9 +5,12 @@
|
|||||||
# 전제조건: 순수 CentOS OS (최소 설치)
|
# 전제조건: 순수 CentOS OS (최소 설치)
|
||||||
# 실행 방법: sudo bash setup_centos.sh
|
# 실행 방법: sudo bash setup_centos.sh
|
||||||
# 설치 테스트: bash setup_centos.sh --test
|
# 설치 테스트: bash setup_centos.sh --test
|
||||||
# 환경변수:
|
# 환경변수 (오프라인/폐쇄망):
|
||||||
# TOMCAT_VER=9.0.98 : Tomcat 버전
|
# TOMCAT_VER=9.0.98 : Tomcat 버전
|
||||||
# TOMCAT_MIRROR=http:// : 내부 미러 URL (오프라인 환경)
|
# TOMCAT_MIRROR=file://$(pwd)/setup/offline/common : 로컬 파일
|
||||||
|
# OFFLINE_PKG_DIR=./setup/offline/centos : .rpm 패키지 디렉토리
|
||||||
|
# OLLAMA_INSTALL=offline
|
||||||
|
# OLLAMA_BIN_PATH=./setup/offline/common/ollama-linux-amd64
|
||||||
# =============================================================
|
# =============================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@ -74,6 +77,15 @@ fi
|
|||||||
# ── 1. 시스템 패키지 ─────────────────────────────────────────
|
# ── 1. 시스템 패키지 ─────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
echo "[1/10] 시스템 패키지 설치..."
|
echo "[1/10] 시스템 패키지 설치..."
|
||||||
|
# 오프라인 RPM 패키지 디렉토리 사용 여부
|
||||||
|
OFFLINE_PKG_DIR="${OFFLINE_PKG_DIR:-}"
|
||||||
|
if [[ -n "$OFFLINE_PKG_DIR" && -d "$OFFLINE_PKG_DIR" ]]; then
|
||||||
|
info "오프라인 모드: $OFFLINE_PKG_DIR"
|
||||||
|
dnf install -y "$OFFLINE_PKG_DIR"/*.rpm --disablerepo='*' 2>/dev/null || \
|
||||||
|
rpm -ivh --nodeps "$OFFLINE_PKG_DIR"/*.rpm 2>/dev/null || true
|
||||||
|
ok "오프라인 RPM 설치 완료"
|
||||||
|
fi
|
||||||
|
|
||||||
yum install -y epel-release 2>/dev/null || dnf install -y epel-release 2>/dev/null || true
|
yum install -y epel-release 2>/dev/null || dnf install -y epel-release 2>/dev/null || true
|
||||||
|
|
||||||
if [[ "$OS_VER" -ge 8 ]]; then
|
if [[ "$OS_VER" -ge 8 ]]; then
|
||||||
|
|||||||
@ -5,9 +5,13 @@
|
|||||||
# 전제조건: 순수 Ubuntu OS (최소 설치)
|
# 전제조건: 순수 Ubuntu OS (최소 설치)
|
||||||
# 실행 방법: sudo bash setup_ubuntu.sh
|
# 실행 방법: sudo bash setup_ubuntu.sh
|
||||||
# 설치 테스트: bash setup_ubuntu.sh --test
|
# 설치 테스트: bash setup_ubuntu.sh --test
|
||||||
# 환경변수 (오프라인 환경):
|
# 환경변수 (오프라인/폐쇄망 환경):
|
||||||
# TOMCAT_VER=9.0.98 : Tomcat 버전 (기본 9.0.98)
|
# TOMCAT_VER=9.0.98 : Tomcat 버전 (기본 9.0.98)
|
||||||
# TOMCAT_MIRROR=http:// : 내부 미러 URL (기본 apache.org)
|
# TOMCAT_MIRROR=http://... : 내부 미러 URL (기본 apache.org)
|
||||||
|
# TOMCAT_MIRROR=file://$(pwd)/setup/offline/common : 로컬 파일 사용
|
||||||
|
# OLLAMA_INSTALL=offline : 오프라인 바이너리 설치
|
||||||
|
# OLLAMA_BIN_PATH=./setup/offline/common/ollama-linux-amd64
|
||||||
|
# OFFLINE_PKG_DIR=./setup/offline/ubuntu : .deb 패키지 디렉토리
|
||||||
# =============================================================
|
# =============================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@ -73,7 +77,18 @@ fi
|
|||||||
# ── 1. 시스템 패키지 업데이트 ────────────────────────────────
|
# ── 1. 시스템 패키지 업데이트 ────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
echo "[1/10] 시스템 패키지 업데이트..."
|
echo "[1/10] 시스템 패키지 업데이트..."
|
||||||
|
|
||||||
|
# 오프라인 패키지 디렉토리 사용 여부 확인
|
||||||
|
OFFLINE_PKG_DIR="${OFFLINE_PKG_DIR:-}"
|
||||||
|
if [[ -n "$OFFLINE_PKG_DIR" && -d "$OFFLINE_PKG_DIR" ]]; then
|
||||||
|
info "오프라인 모드: $OFFLINE_PKG_DIR"
|
||||||
|
dpkg -i "$OFFLINE_PKG_DIR"/*.deb 2>/dev/null || true
|
||||||
|
apt-get install -f -y -qq 2>/dev/null || true
|
||||||
|
ok "오프라인 패키지 설치 완료"
|
||||||
|
else
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
|
fi
|
||||||
|
|
||||||
apt-get install -y -qq \
|
apt-get install -y -qq \
|
||||||
curl wget git build-essential libssl-dev libffi-dev \
|
curl wget git build-essential libssl-dev libffi-dev \
|
||||||
python3.11 python3.11-venv python3.11-dev python3-pip \
|
python3.11 python3.11-venv python3.11-dev python3-pip \
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user