285 lines
7.7 KiB
Markdown
285 lines
7.7 KiB
Markdown
# Bare metal: Solana RPC (non‑voting) + Geyser/“Yellowstone” gRPC (Ubuntu 24.04)
|
||
|
||
Cel: postawić **jedną maszynę** jako **źródło danych on‑chain**:
|
||
- Solana `validator` w trybie **non‑voting** z **RPC + WS** (tylko prywatnie),
|
||
- **Geyser gRPC** (“Yellowstone”) jako stabilny, skalowalny feed account/tx/slot,
|
||
- serwisy tradingowe (DLOB/boty/DB/UI) działają **osobno** na VPS/k3s.
|
||
|
||
Ten dokument jest runbookiem. Nie zawiera sekretów.
|
||
|
||
---
|
||
|
||
## Powiązane dokumenty (DLOB + metryki + koszty)
|
||
|
||
Żeby spiąć “RPC/Geyser → dane → metryki → UI”, zobacz też:
|
||
|
||
- Mapa dokumentów o RPC/DLOB/metrykach: `doc/solana-rpc.md`
|
||
- DLOB (co działa w k3s, jakie tabele, skąd dane): `doc/dlob-services.md`
|
||
- DLOB basics (L1/L2/L3, pojęcia): `doc/dlob-basics.md`
|
||
- Ingest ticków / candles / źródła danych: `doc/workflow-api-ingest.md`
|
||
- Readiness do live tradingu (w tym plan pod własny RPC + streaming): `doc/trading-readiness.md`
|
||
- Czy da się bez własnego RPC / bez RPC w ogóle (mapowanie źródeł danych): `doc/drift-data-bez-solana-rpc.md`
|
||
- Kanoniczna architektura “własny RPC + własny DLOB” (co skąd bierzemy): `doc/rpc-dlob-kanoniczna-architektura.md`
|
||
- Koszty kontraktu: API compute/monitor (backend liczy, UI tylko rysuje): `doc/contract-cost-api.md`
|
||
- Kanoniczny payload eventów bota pod koszty/PnL (żeby agregacje działały): `doc/bot-events-cost-payload.md`
|
||
|
||
## 0) Założenia
|
||
|
||
- OS: **Ubuntu 24.04**
|
||
- Sprzęt: **Ryzen 9 9950X, 192GB RAM, 2× Gen5 NVMe, 1Gbps**
|
||
- Rola: **RPC node bez voting** (brak vote account)
|
||
- Prywatny dostęp: **WireGuard** między bare metal a k3s/VPS
|
||
|
||
---
|
||
|
||
## 1) Dlaczego RPC+WS i gRPC jednocześnie
|
||
|
||
- **RPC/WS** (HTTP + WebSocket) zostaje jako:
|
||
- wysyłka transakcji (place/cancel/close),
|
||
- odczyty ad‑hoc i fallback.
|
||
- **Geyser/Yellowstone gRPC** jest preferowany jako:
|
||
- stabilny stream updates (account/slot/tx) dla DLOB/indexerów,
|
||
- mniejsze “rwanie” niż WS przy większej skali.
|
||
|
||
W praktyce: data plane = gRPC, execution plane = RPC.
|
||
|
||
---
|
||
|
||
## 2) Podział dysków (must‑have)
|
||
|
||
Rekomendacja (żeby uniknąć I/O contention):
|
||
- NVMe #1: ledger / accounts
|
||
- mount: `/solana/ledger`
|
||
- NVMe #2: snapshots / logs / plugin state
|
||
- mount: `/solana/snapshots`
|
||
- ewentualnie: `/var/lib/yellowstone`
|
||
|
||
---
|
||
|
||
## 3) Porty (proponowane)
|
||
|
||
Publicznie:
|
||
- `22/tcp` (SSH) – tylko z Twoich IP (allowlist)
|
||
|
||
Tylko po WireGuard (private):
|
||
- `8899/tcp` RPC HTTP
|
||
- `8900/tcp` RPC WS
|
||
- `10000/tcp` Geyser gRPC (Yellowstone)
|
||
|
||
Uwaga: dokładne porty Solany (gossip/TPU) są inne i zależą od flag; one zwykle muszą być publicznie osiągalne do sieci Solany, ale **RPC ma być private**.
|
||
|
||
---
|
||
|
||
## 4) WireGuard (skeleton)
|
||
|
||
Założenie: bare metal = `wg0 = 10.8.0.1`, k3s/VPS = `wg0 = 10.8.0.2`.
|
||
|
||
### 4.1 Bare metal: `/etc/wireguard/wg0.conf`
|
||
|
||
```ini
|
||
[Interface]
|
||
Address = 10.8.0.1/24
|
||
ListenPort = 51820
|
||
PrivateKey = <BARE_METAL_PRIVATE_KEY>
|
||
|
||
# opcjonalnie: NAT dla ruchu wychodzącego
|
||
# PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||
# PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||
|
||
[Peer]
|
||
PublicKey = <K3S_PUBLIC_KEY>
|
||
AllowedIPs = 10.8.0.2/32
|
||
```
|
||
|
||
### 4.2 VPS/k3s: `/etc/wireguard/wg0.conf`
|
||
|
||
```ini
|
||
[Interface]
|
||
Address = 10.8.0.2/24
|
||
PrivateKey = <K3S_PRIVATE_KEY>
|
||
|
||
[Peer]
|
||
PublicKey = <BARE_METAL_PUBLIC_KEY>
|
||
Endpoint = <BARE_METAL_PUBLIC_IP>:51820
|
||
AllowedIPs = 10.8.0.1/32
|
||
PersistentKeepalive = 25
|
||
```
|
||
|
||
Start:
|
||
|
||
```bash
|
||
sudo systemctl enable --now wg-quick@wg0
|
||
```
|
||
|
||
Test:
|
||
|
||
```bash
|
||
ping -c 2 10.8.0.1
|
||
```
|
||
|
||
---
|
||
|
||
## 5) Firewall: zasada “RPC i gRPC tylko private”
|
||
|
||
Wariant z `ufw` (przykład, dopasuj do swojego środowiska):
|
||
|
||
```bash
|
||
sudo ufw default deny incoming
|
||
sudo ufw default allow outgoing
|
||
|
||
# SSH – najlepiej allowlist Twoje IP
|
||
sudo ufw allow 22/tcp
|
||
|
||
# WireGuard
|
||
sudo ufw allow 51820/udp
|
||
|
||
# RPC/WS/gRPC tylko na interfejsie wg0 (ufw ma ograniczone wsparcie; alternatywnie nftables)
|
||
# Minimalnie: nie otwieraj 8899/8900/10000 na publicznym NIC.
|
||
|
||
sudo ufw enable
|
||
sudo ufw status verbose
|
||
```
|
||
|
||
---
|
||
|
||
## 6) Instalacja i uruchomienie Solany (non‑voting)
|
||
|
||
### 6.1 Zasada bezpieczeństwa
|
||
- identity key i konfiguracja tylko na serwerze,
|
||
- nie commituj tego do gita,
|
||
- RPC ma być “private RPC”.
|
||
|
||
### 6.2 Flagi mogą się zmieniać
|
||
Solana bywa zmienna w detalach CLI. Zawsze weryfikuj:
|
||
|
||
```bash
|
||
solana-validator --help | less
|
||
```
|
||
|
||
### 6.3 Minimalny szkic uruchomienia (do uzupełnienia)
|
||
|
||
Poniżej jest “kształt” – nie traktuj jako jedyny prawdziwy set flag:
|
||
|
||
```bash
|
||
solana-validator \
|
||
--identity /etc/solana/identity.json \
|
||
--ledger /solana/ledger \
|
||
--snapshots /solana/snapshots \
|
||
--rpc-port 8899 \
|
||
--rpc-bind-address 10.8.0.1 \
|
||
--private-rpc \
|
||
--ws-port 8900 \
|
||
--dynamic-port-range 8000-8020 \
|
||
--no-voting \
|
||
--entrypoint <ENTRYPOINT_1> \
|
||
--entrypoint <ENTRYPOINT_2> \
|
||
--entrypoint <ENTRYPOINT_3> \
|
||
--expected-genesis-hash <GENESIS_HASH> \
|
||
--wal-recovery-mode skip_any_corrupted_record
|
||
```
|
||
|
||
Uwagi:
|
||
- `--rpc-bind-address` ustaw na IP WireGuard (private).
|
||
- `--no-voting` = non‑voting.
|
||
- `--dynamic-port-range` i reszta portów zależą od Twojej polityki sieciowej.
|
||
|
||
### 6.4 systemd (skeleton)
|
||
|
||
Plik: `/etc/systemd/system/solana-validator.service`
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Solana Validator (non-voting, private RPC)
|
||
After=network-online.target wg-quick@wg0.service
|
||
Wants=network-online.target
|
||
|
||
[Service]
|
||
User=solana
|
||
Group=solana
|
||
LimitNOFILE=1048576
|
||
ExecStart=/usr/local/bin/solana-validator <ARGS...>
|
||
Restart=always
|
||
RestartSec=3
|
||
TimeoutStopSec=120
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
---
|
||
|
||
## 7) Geyser / “Yellowstone” gRPC
|
||
|
||
### 7.1 Co to jest
|
||
Geyser to plugin, który “wypycha” stream danych z runtime’u walidatora.
|
||
Yellowstone gRPC to popularny stack, który wystawia ten stream przez gRPC.
|
||
|
||
### 7.2 Model wdrożenia (recommended)
|
||
- plugin jest skonfigurowany przy starcie `solana-validator` (`--geyser-plugin-config <path>`),
|
||
- gRPC endpoint nasłuchuje na `10.8.0.1:10000` (private),
|
||
- klienci (k3s) subskrybują gRPC.
|
||
|
||
### 7.3 Konfiguracja pluginu (placeholder)
|
||
Dokładny format config zależy od wybranego pluginu/wersji.
|
||
Trzymaj config jako plik na serwerze, np. `/etc/solana/geyser-grpc.json`.
|
||
|
||
Wymagania, które chcemy mieć niezależnie od implementacji:
|
||
- bind na interfejsie WireGuard (`10.8.0.1`),
|
||
- opcjonalny auth/token dla klientów,
|
||
- limit/allowlist klientów,
|
||
- logi do journald + limitowanie.
|
||
|
||
Test (z VPS/k3s po WireGuard):
|
||
- port open: `nc -vz 10.8.0.1 10000` (albo `grpcurl` jeśli masz),
|
||
- stream slotów/health (zależy od klienta).
|
||
|
||
---
|
||
|
||
## 8) Integracja z naszym stackiem `trade`
|
||
|
||
### 8.1 Co zmieniamy w k3s
|
||
|
||
Aktualizujemy secreta z endpointami RPC/WS dla `dlob-publisher` i executora:
|
||
- `ENDPOINT=http://10.8.0.1:8899`
|
||
- `WS_ENDPOINT=ws://10.8.0.1:8900`
|
||
|
||
Docelowo dodamy również:
|
||
- `GEYSER_GRPC_URL=http://10.8.0.1:10000` (lub `grpc://...`) do collectorów.
|
||
|
||
### 8.2 Fallback
|
||
Zostawiamy fallback RPC endpoint (np. publiczny provider) dla:
|
||
- awarii bare-metala,
|
||
- bootstrapu,
|
||
- sanity check.
|
||
|
||
Executor ma zawsze mieć tryb degradacji:
|
||
- Vast down → `observe/off`,
|
||
- feed down → `panic` lub `off` zależnie od ryzyka.
|
||
|
||
---
|
||
|
||
## 9) Operacje i monitoring (must‑have)
|
||
|
||
Mierz/alertuj:
|
||
- slot lag / “behind”,
|
||
- iowait + saturacja NVMe,
|
||
- disk fill (`ledger` rośnie),
|
||
- restart loop serwisów,
|
||
- liczba klientów gRPC / błędy streamu,
|
||
- RPC latencja / error rate.
|
||
|
||
Minimalne narzędzia:
|
||
- `node_exporter` + Prometheus/Grafana,
|
||
- logrotate/journald limity,
|
||
- `smartctl`/`nvme smart-log` dla NVMe.
|
||
|
||
---
|
||
|
||
## 10) Gotowość do startu (checklista)
|
||
|
||
- [ ] WireGuard działa (ping wg IP).
|
||
- [ ] RPC/WS/gRPC są dostępne tylko po WG.
|
||
- [ ] `solana-validator` trzyma sync, nie robi OOM, I/O stabilne.
|
||
- [ ] Geyser gRPC stream stabilny (brak częstych reconnect).
|
||
- [ ] `dlob-publisher` działa na nowych endpointach bez “No ws data … resubscribing”.
|