446 lines
11 KiB
Markdown
446 lines
11 KiB
Markdown
|
|
# Demo Website trên Google Colab với FRP
|
|||
|
|
|
|||
|
|
Hướng dẫn này giúp bạn chạy toàn bộ hệ thống **remote-sensing** trên Google Colab
|
|||
|
|
và expose ra internet qua **FRP (Fast Reverse Proxy)** — không cần ngrok, URL cố định, không giới hạn session.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Yêu cầu
|
|||
|
|
|
|||
|
|
| Thành phần | Mô tả |
|
|||
|
|
|---|---|
|
|||
|
|
| **Google Colab** | Tài khoản Google thông thường (free tier là đủ) |
|
|||
|
|
| **Google Drive** | Dùng để lưu project và models |
|
|||
|
|
| **1 VPS có IP public** | Chạy `frps` server — VPS $3–5/tháng là đủ |
|
|||
|
|
| **Port mở trên VPS** | 7000 (FRP control) + 8080 (web traffic) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 1 — Chuẩn bị VPS (chạy 1 lần, giữ mãi)
|
|||
|
|
|
|||
|
|
### Bước 1.1 — Download FRP lên VPS
|
|||
|
|
|
|||
|
|
SSH vào VPS rồi chạy:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz
|
|||
|
|
tar -xzf frp_0.61.1_linux_amd64.tar.gz
|
|||
|
|
cd frp_0.61.1_linux_amd64
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Bước 1.2 — Tạo file cấu hình `frps.toml`
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cat > frps.toml << 'EOF'
|
|||
|
|
bindPort = 7000
|
|||
|
|
auth.token = "your_secret_token_here"
|
|||
|
|
|
|||
|
|
# Dashboard để theo dõi kết nối (tuỳ chọn)
|
|||
|
|
webServer.port = 7500
|
|||
|
|
webServer.user = "admin"
|
|||
|
|
webServer.password = "admin123"
|
|||
|
|
EOF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> ⚠️ Đặt `auth.token` thành chuỗi bí mật của bạn, ví dụ: `"rs_demo_2026_abc123"`. Phải giống với phía Colab.
|
|||
|
|
|
|||
|
|
### Bước 1.3 — Chạy frps
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Test chạy foreground (Ctrl+C để dừng)
|
|||
|
|
./frps -c frps.toml
|
|||
|
|
|
|||
|
|
# Chạy nền (production)
|
|||
|
|
nohup ./frps -c frps.toml > frps.log 2>&1 &
|
|||
|
|
|
|||
|
|
# Kiểm tra đang chạy
|
|||
|
|
ps aux | grep frps
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Bước 1.4 — Mở firewall VPS
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Ubuntu/Debian
|
|||
|
|
ufw allow 7000 # FRP control port
|
|||
|
|
ufw allow 8080 # Web traffic port
|
|||
|
|
ufw allow 7500 # Dashboard (tuỳ chọn)
|
|||
|
|
ufw reload
|
|||
|
|
|
|||
|
|
# CentOS/RHEL
|
|||
|
|
firewall-cmd --permanent --add-port=7000/tcp
|
|||
|
|
firewall-cmd --permanent --add-port=8080/tcp
|
|||
|
|
firewall-cmd --reload
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Bước 1.5 — Kiểm tra frps hoạt động
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Xem log
|
|||
|
|
tail -f frps.log
|
|||
|
|
|
|||
|
|
# Kết quả kỳ vọng:
|
|||
|
|
# [frps] frp service started, listen on 0.0.0.0:7000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 2 — Chuẩn bị Google Drive
|
|||
|
|
|
|||
|
|
### Bước 2.1 — Upload project lên Drive
|
|||
|
|
|
|||
|
|
Cấu trúc thư mục trên Google Drive:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
My Drive/
|
|||
|
|
└── remote-sensing/
|
|||
|
|
├── api_server.py
|
|||
|
|
├── train_module.py
|
|||
|
|
├── feature_extractor.py
|
|||
|
|
├── model_manager.py
|
|||
|
|
├── cloud_removal.py
|
|||
|
|
├── report_generator.py
|
|||
|
|
├── generate_previews.py
|
|||
|
|
├── vietnam_provinces.py
|
|||
|
|
├── vietnam_provinces_merged.py
|
|||
|
|
├── utils.py
|
|||
|
|
├── model_train/ ← copy toàn bộ models đã train
|
|||
|
|
│ ├── *.joblib
|
|||
|
|
│ └── *.json
|
|||
|
|
├── cloud_removal_model/ ← copy nếu dùng cloud removal DL
|
|||
|
|
│ └── *.pth
|
|||
|
|
├── predictions/ ← để trống, Colab sẽ tạo output vào đây
|
|||
|
|
├── reports/ ← để trống
|
|||
|
|
└── training_interface.html ← và tất cả *.html
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 💡 Upload nhanh nhất: zip toàn bộ folder `remote-sensing`, upload 1 file zip lên Drive, rồi giải nén bằng Colab.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 3 — Notebook Google Colab
|
|||
|
|
|
|||
|
|
Tạo notebook mới tại [colab.google.com](https://colab.google.com) và paste từng cell sau.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 1 — Mount Drive và di chuyển vào project
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from google.colab import drive
|
|||
|
|
drive.mount('/content/drive')
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
PROJECT_PATH = '/content/drive/MyDrive/remote-sensing'
|
|||
|
|
os.chdir(PROJECT_PATH)
|
|||
|
|
|
|||
|
|
print(f"Working directory: {os.getcwd()}")
|
|||
|
|
print("Files:", os.listdir()[:10])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 2 — Giải nén nếu upload dạng zip (tuỳ chọn)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Chỉ chạy nếu bạn upload file zip
|
|||
|
|
import zipfile
|
|||
|
|
|
|||
|
|
ZIP_PATH = '/content/drive/MyDrive/remote-sensing.zip'
|
|||
|
|
EXTRACT_TO = '/content/drive/MyDrive/'
|
|||
|
|
|
|||
|
|
if os.path.exists(ZIP_PATH):
|
|||
|
|
with zipfile.ZipFile(ZIP_PATH, 'r') as z:
|
|||
|
|
z.extractall(EXTRACT_TO)
|
|||
|
|
print("✅ Extracted successfully")
|
|||
|
|
else:
|
|||
|
|
print("⏭️ No zip found, skipping")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 3 — Cài dependencies (chạy lần đầu, ~8–12 phút)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
print("Installing core API dependencies...")
|
|||
|
|
!pip install -q fastapi uvicorn pydantic
|
|||
|
|
|
|||
|
|
print("Installing geospatial + ML dependencies...")
|
|||
|
|
!pip install -q \
|
|||
|
|
numpy pandas xarray rasterio rioxarray geopandas shapely \
|
|||
|
|
scikit-learn xgboost joblib \
|
|||
|
|
matplotlib pillow markdown
|
|||
|
|
|
|||
|
|
print("Installing Planetary Computer dependencies...")
|
|||
|
|
!pip install -q pystac-client planetary-computer odc-stac
|
|||
|
|
|
|||
|
|
print("Installing PyTorch (GPU)...")
|
|||
|
|
!pip install -q torch torchvision \
|
|||
|
|
--extra-index-url https://download.pytorch.org/whl/cu118
|
|||
|
|
|
|||
|
|
print("✅ All dependencies installed")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 4 — Kiểm tra GPU và môi trường
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import torch
|
|||
|
|
|
|||
|
|
print(f"PyTorch version : {torch.__version__}")
|
|||
|
|
print(f"GPU available : {torch.cuda.is_available()}")
|
|||
|
|
if torch.cuda.is_available():
|
|||
|
|
print(f"GPU name : {torch.cuda.get_device_name(0)}")
|
|||
|
|
|
|||
|
|
import rasterio, xarray, geopandas
|
|||
|
|
print(f"rasterio : {rasterio.__version__}")
|
|||
|
|
print(f"xarray : {xarray.__version__}")
|
|||
|
|
print(f"geopandas : {geopandas.__version__}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 5 — Download và cấu hình frpc
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import subprocess, os
|
|||
|
|
|
|||
|
|
# Download frpc
|
|||
|
|
!wget -q https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz \
|
|||
|
|
-O /tmp/frp.tar.gz
|
|||
|
|
!tar -xzf /tmp/frp.tar.gz -C /tmp/
|
|||
|
|
!chmod +x /tmp/frp_0.61.1_linux_amd64/frpc
|
|||
|
|
|
|||
|
|
# ============================================================
|
|||
|
|
# ⚠️ SỬA 2 DÒNG NÀY TRƯỚC KHI CHẠY
|
|||
|
|
VPS_IP = "123.456.789.000" # IP public của VPS bạn
|
|||
|
|
FRP_TOKEN = "your_secret_token_here" # Phải giống frps.toml trên VPS
|
|||
|
|
# ============================================================
|
|||
|
|
|
|||
|
|
frpc_config = f"""
|
|||
|
|
serverAddr = "{VPS_IP}"
|
|||
|
|
serverPort = 7000
|
|||
|
|
auth.token = "{FRP_TOKEN}"
|
|||
|
|
|
|||
|
|
[[proxies]]
|
|||
|
|
name = "remote-sensing-web"
|
|||
|
|
type = "tcp"
|
|||
|
|
localIP = "127.0.0.1"
|
|||
|
|
localPort = 8000
|
|||
|
|
remotePort = 8080
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
with open('/tmp/frpc.toml', 'w') as f:
|
|||
|
|
f.write(frpc_config)
|
|||
|
|
|
|||
|
|
print(f"✅ frpc configured → VPS: {VPS_IP}:8080")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 6 — Khởi động FastAPI server
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import subprocess, time, os
|
|||
|
|
|
|||
|
|
os.chdir('/content/drive/MyDrive/remote-sensing')
|
|||
|
|
|
|||
|
|
# Khởi động FastAPI
|
|||
|
|
server = subprocess.Popen(
|
|||
|
|
["uvicorn", "api_server:app",
|
|||
|
|
"--host", "127.0.0.1",
|
|||
|
|
"--port", "8000",
|
|||
|
|
"--log-level", "warning"],
|
|||
|
|
stdout=subprocess.PIPE,
|
|||
|
|
stderr=subprocess.PIPE
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
time.sleep(4)
|
|||
|
|
|
|||
|
|
# Kiểm tra server đã lên chưa
|
|||
|
|
if server.poll() is None:
|
|||
|
|
print("✅ FastAPI server is running on port 8000")
|
|||
|
|
else:
|
|||
|
|
out, err = server.communicate()
|
|||
|
|
print("❌ Server failed to start:")
|
|||
|
|
print(err.decode()[:1000])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 7 — Khởi động frpc tunnel
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import subprocess, time
|
|||
|
|
|
|||
|
|
frpc = subprocess.Popen(
|
|||
|
|
["/tmp/frp_0.61.1_linux_amd64/frpc", "-c", "/tmp/frpc.toml"],
|
|||
|
|
stdout=subprocess.PIPE,
|
|||
|
|
stderr=subprocess.PIPE
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
time.sleep(3)
|
|||
|
|
|
|||
|
|
if frpc.poll() is None:
|
|||
|
|
print("✅ FRP tunnel is active")
|
|||
|
|
print("=" * 55)
|
|||
|
|
print(f"🌐 Main site : http://{VPS_IP}:8080")
|
|||
|
|
print(f"📊 Dashboard : http://{VPS_IP}:8080/dashboard")
|
|||
|
|
print(f"🏋️ Training : http://{VPS_IP}:8080/training")
|
|||
|
|
print(f"🔮 Prediction : http://{VPS_IP}:8080/prediction")
|
|||
|
|
print(f"📦 Batch : http://{VPS_IP}:8080/batch")
|
|||
|
|
print(f"🌿 NDVI : http://{VPS_IP}:8080/ndvi")
|
|||
|
|
print(f"📈 Reports : http://{VPS_IP}:8080/reports")
|
|||
|
|
print("=" * 55)
|
|||
|
|
else:
|
|||
|
|
out, err = frpc.communicate()
|
|||
|
|
print("❌ FRP failed:")
|
|||
|
|
print(err.decode()[:500])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 8 — Kiểm tra toàn bộ hệ thống
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import urllib.request, json
|
|||
|
|
|
|||
|
|
BASE = "http://127.0.0.1:8000"
|
|||
|
|
checks = [
|
|||
|
|
("/api/network/check", "Network connectivity"),
|
|||
|
|
("/api/models/list", "Model list"),
|
|||
|
|
("/api/provinces/list", "Province list"),
|
|||
|
|
("/api/cloud-removal/methods", "Cloud removal methods"),
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
for path, label in checks:
|
|||
|
|
try:
|
|||
|
|
r = urllib.request.urlopen(BASE + path, timeout=5)
|
|||
|
|
data = json.loads(r.read())
|
|||
|
|
status = "✅"
|
|||
|
|
except Exception as e:
|
|||
|
|
data = str(e)
|
|||
|
|
status = "❌"
|
|||
|
|
print(f"{status} {label:<30} → {str(data)[:80]}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 9 — Xem log nếu có lỗi (tuỳ chọn)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Xem stderr của FastAPI
|
|||
|
|
import select, sys
|
|||
|
|
|
|||
|
|
if server.poll() is not None:
|
|||
|
|
_, err = server.communicate()
|
|||
|
|
print("FastAPI stderr:")
|
|||
|
|
print(err.decode())
|
|||
|
|
else:
|
|||
|
|
# Đọc log không block
|
|||
|
|
import os
|
|||
|
|
flags = os.O_RDONLY | os.O_NONBLOCK
|
|||
|
|
try:
|
|||
|
|
fd = server.stderr.fileno()
|
|||
|
|
os.set_blocking(fd, False)
|
|||
|
|
print(server.stderr.read(2000).decode())
|
|||
|
|
except:
|
|||
|
|
print("Server is running (no errors captured)")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Cell 10 — Dừng server khi xong demo
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import subprocess
|
|||
|
|
|
|||
|
|
server.terminate()
|
|||
|
|
frpc.terminate()
|
|||
|
|
|
|||
|
|
print("✅ FastAPI stopped")
|
|||
|
|
print("✅ FRP tunnel closed")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 4 — Dùng domain thay vì IP (nâng cao)
|
|||
|
|
|
|||
|
|
Nếu VPS có domain riêng, bạn có thể truy cập bằng URL đẹp hơn.
|
|||
|
|
|
|||
|
|
### Sửa `frps.toml` trên VPS
|
|||
|
|
|
|||
|
|
```toml
|
|||
|
|
bindPort = 7000
|
|||
|
|
auth.token = "your_secret_token_here"
|
|||
|
|
vhostHTTPPort = 80
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Sửa Cell 5 (`frpc.toml`) trên Colab
|
|||
|
|
|
|||
|
|
```toml
|
|||
|
|
serverAddr = "your-vps.com"
|
|||
|
|
serverPort = 7000
|
|||
|
|
auth.token = "your_secret_token_here"
|
|||
|
|
|
|||
|
|
[[proxies]]
|
|||
|
|
name = "remote-sensing-web"
|
|||
|
|
type = "http"
|
|||
|
|
localPort = 8000
|
|||
|
|
customDomains = ["demo.your-vps.com"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DNS — trỏ subdomain về VPS
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
demo.your-vps.com → A record → 123.456.789.000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
→ Truy cập: `http://demo.your-vps.com` (không cần `:8080`)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 5 — Giữ session Colab sống lâu hơn
|
|||
|
|
|
|||
|
|
Colab tự disconnect sau ~90 phút idle. Để tránh:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Paste vào console trình duyệt (F12 → Console)
|
|||
|
|
function keepAlive() {
|
|||
|
|
document.querySelector('#connect button')?.click();
|
|||
|
|
console.log('keep-alive ping:', new Date().toLocaleTimeString());
|
|||
|
|
}
|
|||
|
|
setInterval(keepAlive, 60000);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Hoặc dùng **Colab Pro** ($10/tháng) để không bị giới hạn session.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Phần 6 — Tóm tắt so sánh FRP vs ngrok
|
|||
|
|
|
|||
|
|
| Tiêu chí | FRP (self-host) | ngrok (free) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| URL cố định | ✅ Có | ❌ Đổi mỗi session |
|
|||
|
|
| Session timeout | ✅ Không giới hạn | ⚠️ 2 giờ |
|
|||
|
|
| Chi phí | Free (cần VPS) | Free tier có giới hạn |
|
|||
|
|
| Dữ liệu qua server bên thứ 3 | ❌ Không | ✅ Qua server ngrok |
|
|||
|
|
| Cần setup | ⚠️ Cần cài frps trên VPS | ✅ Chạy ngay |
|
|||
|
|
| Phù hợp | Demo dài hạn, production | Demo nhanh 1 lần |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Checklist trước khi demo
|
|||
|
|
|
|||
|
|
- [ ] VPS đang chạy `frps` và port 7000, 8080 đã mở
|
|||
|
|
- [ ] Project đã upload đầy đủ lên Google Drive (kể cả `model_train/`)
|
|||
|
|
- [ ] Đã điền đúng `VPS_IP` và `FRP_TOKEN` trong Cell 5
|
|||
|
|
- [ ] Cell 3 (cài deps) đã chạy thành công
|
|||
|
|
- [ ] Cell 8 (health check) cho thấy tất cả ✅
|
|||
|
|
- [ ] Truy cập `http://VPS_IP:8080/dashboard` từ trình duyệt → hiện trang
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*Tạo ngày: 03/04/2026 — dự án remote-sensing Vietnam Land Classification*
|