Files
260526_s1/index.html

574 lines
23 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>s1.byunc.com 서버 구성 보고서</title>
<style>
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--success: #16a34a;
--warning: #d97706;
--danger: #dc2626;
--bg: #f8fafc;
--card: #ffffff;
--border: #e2e8f0;
--text: #1e293b;
--text-muted: #64748b;
--code-bg: #1e293b;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
header {
background: linear-gradient(135deg, var(--primary-dark), var(--primary));
color: white;
padding: 40px 40px 30px;
}
header h1 { font-size: 2rem; font-weight: 700; }
header p { margin-top: 6px; opacity: 0.85; font-size: 0.95rem; }
.badge {
display: inline-block;
padding: 2px 10px;
border-radius: 99px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-green { background: #dcfce7; color: #16a34a; }
.badge-blue { background: #dbeafe; color: #1d4ed8; }
.badge-orange{ background: #fef3c7; color: #d97706; }
.badge-red { background: #fee2e2; color: #dc2626; }
main { max-width: 1100px; margin: 0 auto; padding: 30px 24px; }
section { margin-bottom: 40px; }
h2 {
font-size: 1.3rem; font-weight: 700;
border-left: 4px solid var(--primary);
padding-left: 12px; margin-bottom: 16px;
color: var(--primary-dark);
}
h3 { font-size: 1.05rem; font-weight: 600; margin: 16px 0 8px; }
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 20px 24px;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,.05);
}
.card-header {
display: flex; align-items: center; gap: 10px;
margin-bottom: 12px;
}
.card-title { font-size: 1.1rem; font-weight: 700; }
.card-sub { font-size: 0.85rem; color: var(--text-muted); margin-top: 2px; }
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 8px;
margin: 10px 0;
}
.info-item { background: var(--bg); border-radius: 6px; padding: 8px 12px; }
.info-label { font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
.info-value { font-size: 0.9rem; font-weight: 600; }
code, .code-inline {
font-family: 'Consolas', 'Courier New', monospace;
background: #f1f5f9;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.85rem;
color: #0f172a;
}
pre {
background: var(--code-bg);
color: #e2e8f0;
border-radius: 8px;
padding: 16px 18px;
overflow-x: auto;
font-size: 0.85rem;
line-height: 1.7;
margin: 10px 0;
}
pre .comment { color: #94a3b8; }
pre .cmd { color: #7dd3fc; }
pre .path { color: #86efac; }
.step {
display: flex; gap: 14px;
margin-bottom: 10px;
}
.step-num {
flex-shrink: 0;
width: 28px; height: 28px;
background: var(--primary);
color: white;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 0.8rem; font-weight: 700;
}
.step-content { padding-top: 3px; }
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
th {
background: #f1f5f9;
padding: 9px 12px;
text-align: left;
font-weight: 600;
border-bottom: 2px solid var(--border);
}
td { padding: 9px 12px; border-bottom: 1px solid var(--border); }
tr:hover td { background: #f8fafc; }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }
.url-chip {
display: inline-flex; align-items: center; gap: 5px;
background: #eff6ff; border: 1px solid #bfdbfe;
color: var(--primary); border-radius: 6px;
padding: 3px 10px; font-size: 0.85rem; font-weight: 500;
}
.alert {
border-radius: 8px; padding: 12px 16px;
margin: 12px 0; font-size: 0.88rem;
display: flex; gap: 10px; align-items: flex-start;
}
.alert-blue { background: #eff6ff; border-left: 4px solid var(--primary); }
.alert-yellow{ background: #fffbeb; border-left: 4px solid var(--warning); }
.alert-green { background: #f0fdf4; border-left: 4px solid var(--success); }
footer {
text-align: center; padding: 24px;
font-size: 0.8rem; color: var(--text-muted);
border-top: 1px solid var(--border);
margin-top: 20px;
}
</style>
</head>
<body>
<header>
<h1>🖥️ s1.byunc.com 서버 구성 보고서</h1>
<p>서버 IP: 192.168.0.126 &nbsp;|&nbsp; 도메인: s1.byunc.com &nbsp;|&nbsp; 최종 업데이트: 2026-05-26</p>
</header>
<main>
<!-- ───────────── 1. 개요 ───────────── -->
<section>
<h2>1. 서버 개요</h2>
<div class="card">
<div class="info-grid">
<div class="info-item">
<div class="info-label">서버 주소</div>
<div class="info-value">192.168.0.126</div>
</div>
<div class="info-item">
<div class="info-label">도메인</div>
<div class="info-value">s1.byunc.com</div>
</div>
<div class="info-item">
<div class="info-label">웹서버</div>
<div class="info-value">nginx 1.22.1 (HTTPS)</div>
</div>
<div class="info-item">
<div class="info-label">SSL</div>
<div class="info-value">Let's Encrypt (Certbot)</div>
</div>
<div class="info-item">
<div class="info-label">컨테이너 엔진</div>
<div class="info-value">Docker + Docker Compose</div>
</div>
<div class="info-item">
<div class="info-label">주요 서비스</div>
<div class="info-value">SteVe OCPP / EV Dashboard</div>
</div>
</div>
<div class="alert alert-blue">
<span></span>
<div>HTTP(80)로 접속하면 자동으로 HTTPS(443)로 리다이렉트됩니다. 루트(/)는 <code>/steve/</code>로 리다이렉트됩니다.</div>
</div>
</div>
</section>
<!-- ───────────── 2. URL 라우팅 ───────────── -->
<section>
<h2>2. nginx URL 라우팅 구조</h2>
<div class="card">
<p style="margin-bottom:12px; color:var(--text-muted); font-size:.88rem;">설정 파일: <code>/etc/nginx/conf.d/s1.byunc.com.conf</code></p>
<table>
<thead>
<tr><th>경로</th><th>프록시 대상</th><th>설명</th></tr>
</thead>
<tbody>
<tr>
<td><span class="url-chip">https://s1.byunc.com/</span></td>
<td><code>/steve/</code> (302 리다이렉트)</td>
<td>루트 접속 시 자동 이동</td>
</tr>
<tr>
<td><span class="url-chip">https://s1.byunc.com/steve/</span></td>
<td><code>192.168.0.126:8180</code></td>
<td>SteVe OCPP 관리 시스템</td>
</tr>
<tr>
<td><span class="url-chip">https://s1.byunc.com/steve/websocket</span></td>
<td><code>192.168.0.126:8180</code> (WS)</td>
<td>OCPP 충전기 WebSocket 연결 전용</td>
</tr>
<tr>
<td><span class="url-chip">https://s1.byunc.com/dashboard</span></td>
<td><code>192.168.0.126:8000</code></td>
<td>EV 충전 대시보드 (FastAPI)</td>
</tr>
<tr>
<td><span class="url-chip">https://s1.byunc.com/api/</span></td>
<td><code>192.168.0.126:8000</code></td>
<td>EV 충전 REST API</td>
</tr>
<tr>
<td><span class="url-chip">https://s1.byunc.com/health</span></td>
<td><code>192.168.0.126:8000</code></td>
<td>헬스체크 엔드포인트</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- ───────────── 3. SteVe ───────────── -->
<section>
<h2>3. SteVe — OCPP 중앙 관리 시스템</h2>
<div class="card">
<div class="card-header">
<span style="font-size:1.6rem"></span>
<div>
<div class="card-title">SteVe (SteckdosenVerwaltung)</div>
<div class="card-sub">오픈소스 OCPP 1.2 / 1.5 / 1.6 중앙 시스템 &nbsp;<span class="badge badge-green">운영중</span></div>
</div>
</div>
<h3>접속 정보</h3>
<div class="info-grid">
<div class="info-item">
<div class="info-label">접속 URL</div>
<div class="info-value"><a href="https://s1.byunc.com/steve/" target="_blank">s1.byunc.com/steve/</a></div>
</div>
<div class="info-item">
<div class="info-label">관리자 아이디</div>
<div class="info-value">admin</div>
</div>
<div class="info-item">
<div class="info-label">관리자 비밀번호</div>
<div class="info-value">1234</div>
</div>
<div class="info-item">
<div class="info-label">내부 포트</div>
<div class="info-value">8180 (HTTP), 8443 (HTTPS)</div>
</div>
<div class="info-item">
<div class="info-label">컨텍스트 경로</div>
<div class="info-value">/steve</div>
</div>
<div class="info-item">
<div class="info-label">Jetty 버전</div>
<div class="info-value">12.1.7</div>
</div>
</div>
<h3>주요 기능</h3>
<ul style="margin-left:18px; line-height:2; font-size:.9rem;">
<li><b>충전기 관리</b> — ChargeBox 등록, 상태 조회, 원격 제어 (시작/중지)</li>
<li><b>사용자 관리</b> — OCPP 태그(RFID) 등록/관리, 사용 이력</li>
<li><b>거래 내역</b> — 충전 트랜잭션 로그, 미터값 기록</li>
<li><b>OCPP 운영</b> — 1.2/1.5/1.6 SOAP · JSON-over-WebSocket 지원</li>
<li><b>예약</b> — 충전기 예약 관리</li>
<li><b>Swagger UI</b><code>/steve/manager/swagger-ui/index.html</code></li>
</ul>
<h3>컨테이너 구성</h3>
<table>
<thead><tr><th>컨테이너</th><th>이미지</th><th>포트</th><th>역할</th></tr></thead>
<tbody>
<tr><td><code>steve-app-1</code></td><td>steve-app (빌드)</td><td>8180, 8445</td><td>Spring Boot + Jetty 앱</td></tr>
<tr><td><code>steve-db-1</code></td><td>mariadb:10.11.16</td><td>3307→3306</td><td>MariaDB 데이터베이스</td></tr>
</tbody>
</table>
<h3>소스 경로</h3>
<pre><span class="path">/root/steve/steve/</span> <span class="comment"># 프로젝트 루트</span>
├── src/main/java/...
│ └── config/JettyConfiguration.java <span class="comment"># HTTP compliance 설정 (중요!)</span>
├── src/main/resources/
│ └── application-docker.properties <span class="comment"># DB/포트/인증 설정</span>
└── docker-compose.yml</pre>
<div class="alert alert-yellow">
<span>⚠️</span>
<div><b>중요:</b> 컨테이너 시작 시 Maven으로 소스를 직접 빌드합니다. 첫 시작에 <b>3~5분</b> 소요됩니다.</div>
</div>
</div>
</section>
<!-- ───────────── 4. EV Dashboard ───────────── -->
<section>
<h2>4. EV Dashboard — 충전 모니터링 대시보드</h2>
<div class="card">
<div class="card-header">
<span style="font-size:1.6rem">📊</span>
<div>
<div class="card-title">EV Charging Dashboard (FastAPI)</div>
<div class="card-sub">EV 충전기 실시간 모니터링 및 REST API &nbsp;<span class="badge badge-green">운영중</span></div>
</div>
</div>
<h3>접속 정보</h3>
<div class="info-grid">
<div class="info-item">
<div class="info-label">대시보드 URL</div>
<div class="info-value"><a href="https://s1.byunc.com/dashboard" target="_blank">s1.byunc.com/dashboard</a></div>
</div>
<div class="info-item">
<div class="info-label">API URL</div>
<div class="info-value">s1.byunc.com/api/</div>
</div>
<div class="info-item">
<div class="info-label">내부 포트</div>
<div class="info-value">8000 (FastAPI/uvicorn)</div>
</div>
<div class="info-item">
<div class="info-label">DB</div>
<div class="info-value">PostgreSQL 16 (ev-postgres)</div>
</div>
<div class="info-item">
<div class="info-label">Cache</div>
<div class="info-value">Redis (ev-redis:6375)</div>
</div>
<div class="info-item">
<div class="info-label">OCPP 프록시</div>
<div class="info-value">ev-proxy (9002/9003)</div>
</div>
</div>
<h3>주요 기능</h3>
<ul style="margin-left:18px; line-height:2; font-size:.9rem;">
<li><b>실시간 충전 현황</b> — 충전기 상태, 충전량, 전력 모니터링</li>
<li><b>충전 이력 조회</b> — 날짜/충전기별 상세 로그</li>
<li><b>REST API</b><code>/api/</code> 경로로 외부 연동 가능</li>
<li><b>OCPP 프록시</b> — ev-proxy 컨테이너로 충전기 통신 중계</li>
<li><b>시뮬레이터</b><code>/simulator.html</code> (내부 접근)</li>
</ul>
<h3>컨테이너 구성</h3>
<table>
<thead><tr><th>컨테이너</th><th>이미지</th><th>포트</th><th>역할</th></tr></thead>
<tbody>
<tr><td><code>ev-api</code></td><td>ev-charging-backend-api</td><td>8000</td><td>FastAPI 메인 앱</td></tr>
<tr><td><code>ev-proxy</code></td><td>ev-charging-backend-proxy</td><td>9002, 9003</td><td>OCPP 프록시 서버</td></tr>
<tr><td><code>ev-postgres</code></td><td>postgres:16-alpine</td><td>5432</td><td>PostgreSQL DB</td></tr>
<tr><td><code>ev-redis</code></td><td>redis:7-alpine</td><td>6375→6379</td><td>Redis 캐시</td></tr>
</tbody>
</table>
<h3>소스 경로</h3>
<pre><span class="path">/root/steve/ev-charging-backend/</span>
├── app/ <span class="comment"># FastAPI 소스</span>
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.proxy
└── .env <span class="comment"># DB 접속정보 (비공개)</span></pre>
<div class="alert alert-yellow">
<span>⚠️</span>
<div><b>주의:</b> 서버 재부팅 후 <code>ev-api</code>가 DB 준비 전에 시작되면 uvicorn이 종료될 수 있습니다. 아래 재시작 방법 참고.</div>
</div>
</div>
</section>
<!-- ───────────── 5. 재시작 방법 ───────────── -->
<section>
<h2>5. 서비스 재시작 방법</h2>
<div class="card">
<div class="card-title" style="margin-bottom:12px">⚡ SteVe 재시작</div>
<div class="step">
<div class="step-num">1</div>
<div class="step-content">
디렉토리 이동
<pre><span class="cmd">cd /root/steve/steve</span></pre>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-content">
컨테이너 재시작 <span class="badge badge-orange">빌드 포함 3~5분 소요</span>
<pre><span class="cmd">docker compose restart app</span>
<span class="comment"># 또는 완전 재빌드:</span>
<span class="cmd">docker compose up -d --build app</span></pre>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-content">
시작 확인
<pre><span class="cmd">docker logs steve-app-1 -f</span>
<span class="comment"># "Started SteveApplication" 로그 확인 시 정상</span></pre>
</div>
</div>
</div>
<div class="card">
<div class="card-title" style="margin-bottom:12px">📊 EV Dashboard 재시작</div>
<div class="step">
<div class="step-num">1</div>
<div class="step-content">
디렉토리 이동
<pre><span class="cmd">cd /root/steve/ev-charging-backend</span></pre>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-content">
API 서버 재시작
<pre><span class="cmd">docker compose restart api</span></pre>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-content">
시작 확인
<pre><span class="cmd">docker logs ev-api --tail 20</span>
<span class="comment"># "Application startup complete." 확인</span></pre>
</div>
</div>
</div>
<div class="card">
<div class="card-title" style="margin-bottom:12px">🔧 nginx 재시작</div>
<pre><span class="comment"># 설정 문법 검사 후 리로드</span>
<span class="cmd">nginx -t && systemctl reload nginx</span>
<span class="comment"># 완전 재시작 (필요 시)</span>
<span class="cmd">systemctl restart nginx</span>
<span class="comment"># 설정 파일 위치</span>
<span class="path">/etc/nginx/conf.d/s1.byunc.com.conf</span></pre>
</div>
<div class="card">
<div class="card-title" style="margin-bottom:12px">🩺 상태 확인 명령어</div>
<pre><span class="comment"># 전체 컨테이너 상태</span>
<span class="cmd">docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"</span>
<span class="comment"># nginx 상태</span>
<span class="cmd">systemctl status nginx</span>
<span class="comment"># SteVe 로그 실시간</span>
<span class="cmd">docker logs steve-app-1 -f --tail 50</span>
<span class="comment"># EV API 로그</span>
<span class="cmd">docker logs ev-api --tail 50</span>
<span class="comment"># 접속 테스트</span>
<span class="cmd">curl -s -o /dev/null -w "%{http_code}" https://s1.byunc.com/steve/</span>
<span class="cmd">curl -s -o /dev/null -w "%{http_code}" https://s1.byunc.com/dashboard</span></pre>
</div>
</section>
<!-- ───────────── 6. 트러블슈팅 ───────────── -->
<section>
<h2>6. 트러블슈팅</h2>
<div class="card">
<h3>🚨 /steve/ 접속 시 HTTP 400 Bad Request</h3>
<p style="font-size:.9rem; margin:8px 0;">Jetty 12의 strict HTTP compliance가 중복 Host 헤더를 거부하는 현상</p>
<div class="alert alert-green">
<span></span>
<div>
<b>해결됨 (2026-05-26):</b> <code>JettyConfiguration.java</code> 추가로 <code>HttpCompliance.RFC7230_LEGACY</code> 적용 완료.
파일 위치: <code>/root/steve/steve/src/main/java/de/rwth/idsg/steve/config/JettyConfiguration.java</code>
</div>
</div>
<pre><span class="comment"># 재발 시 steve 컨테이너 재빌드</span>
<span class="cmd">cd /root/steve/steve && docker compose up -d --build app</span></pre>
</div>
<div class="card">
<h3>🚨 /dashboard 접속 불가 (ev-api 중단)</h3>
<p style="font-size:.9rem; margin:8px 0;">서버 재부팅 후 PostgreSQL 준비 전에 ev-api가 시작되면 uvicorn 프로세스가 종료됨</p>
<pre><span class="comment"># 확인</span>
<span class="cmd">docker logs ev-api --tail 20</span>
<span class="comment"># "CannotConnectNowError" 또는 "Application startup failed" 시</span>
<span class="comment"># 해결</span>
<span class="cmd">cd /root/steve/ev-charging-backend && docker compose restart api</span></pre>
</div>
<div class="card">
<h3>🚨 nginx SSL 인증서 갱신</h3>
<pre><span class="comment"># 인증서 갱신 (Certbot 자동 갱신)</span>
<span class="cmd">certbot renew --dry-run</span>
<span class="cmd">certbot renew</span>
<span class="cmd">systemctl reload nginx</span>
<span class="comment"># 인증서 만료일 확인</span>
<span class="cmd">certbot certificates</span></pre>
</div>
</section>
<!-- ───────────── 7. 전체 컨테이너 목록 ───────────── -->
<section>
<h2>7. 서버 전체 컨테이너 목록</h2>
<div class="card">
<table>
<thead><tr><th>컨테이너명</th><th>용도</th><th>외부 포트</th></tr></thead>
<tbody>
<tr><td><code>steve-app-1</code></td><td>⚡ SteVe OCPP 관리 시스템</td><td>8180, 8445</td></tr>
<tr><td><code>steve-db-1</code></td><td>SteVe MariaDB</td><td>3307</td></tr>
<tr><td><code>ev-api</code></td><td>📊 EV 충전 대시보드 API</td><td>8000</td></tr>
<tr><td><code>ev-proxy</code></td><td>OCPP 프록시</td><td>9002, 9003</td></tr>
<tr><td><code>ev-postgres</code></td><td>EV API PostgreSQL</td><td>5432</td></tr>
<tr><td><code>ev-redis</code></td><td>EV API Redis</td><td>6375</td></tr>
<tr><td><code>ev-dashboard</code></td><td>EV 대시보드 (별도)</td><td>8001</td></tr>
<tr><td><code>gitea</code></td><td>🐙 Git 서버 (gitea.byunc.com)</td><td>8832, 2224</td></tr>
<tr><td><code>codium</code></td><td>💻 VS Code Server</td><td>8443</td></tr>
<tr><td><code>nextcloud</code></td><td>☁️ 파일 클라우드</td><td>8081</td></tr>
<tr><td><code>open-webui</code></td><td>🤖 OpenWebUI (AI)</td><td>3233</td></tr>
<tr><td><code>n8n_n8n_1</code></td><td>⚙️ n8n 자동화</td><td>5678</td></tr>
<tr><td><code>immich_server</code></td><td>📷 사진 관리</td><td>2283</td></tr>
<tr><td><code>jupyter-lab</code></td><td>📓 Jupyter Lab</td><td>8888</td></tr>
<tr><td><code>metabase</code></td><td>📈 Metabase BI</td><td>3000</td></tr>
<tr><td><code>vaultwarden</code></td><td>🔑 비밀번호 관리</td><td>8055</td></tr>
<tr><td><code>homarr</code></td><td>🏠 홈 대시보드</td><td>7575</td></tr>
<tr><td><code>portainer</code></td><td>🐳 Docker 관리 UI</td><td>9000, 9443</td></tr>
<tr><td><code>drawio</code></td><td>📐 다이어그램 툴</td><td>8044, 8444</td></tr>
<tr><td><code>stirling-pdf</code></td><td>📄 PDF 도구</td><td>8084</td></tr>
<tr><td><code>penpot-*</code></td><td>🎨 UI 디자인 도구</td><td>9001</td></tr>
<tr><td><code>trilium-notes</code></td><td>📝 노트 앱</td><td>8066</td></tr>
<tr><td><code>flowise_*</code></td><td>🔀 AI 플로우 빌더</td><td>3001</td></tr>
<tr><td><code>comfyui-cpu</code></td><td>🎨 ComfyUI (이미지 AI)</td><td>8188</td></tr>
<tr><td><code>whisper_*</code></td><td>🎙️ 음성인식 (Whisper)</td><td>8800</td></tr>
<tr><td><code>perplexica_*</code></td><td>🔍 AI 검색</td><td>3011</td></tr>
</tbody>
</table>
</div>
</section>
</main>
<footer>
s1.byunc.com 서버 구성 보고서 &nbsp;|&nbsp; 생성일: 2026-05-26 &nbsp;|&nbsp; /root/steve/steve · /root/steve/ev-charging-backend
</footer>
</body>
</html>