mirror of
https://github.com/singchia/frontier.git
synced 2026-04-22 16:07:04 +08:00
docs: README i18n (ja/ko/es/fr/de) + unified logging config (#100)
* feat: unify logging config for frontier and frontlas Add a shared Log config struct in pkg/config/log.go that drives both klog and armorigo through a single configuration layer. This fixes the asymmetry where frontier had --loglevel but frontlas did not, and enables file-based logging with rotation for non-container deployments. - New pkg/config/log.go: Log/LogFile structs, SetupLogging(), env overrides - Support four output modes: stdout, stderr, file, both (stdout+file) - Support env var overrides: LOG_LEVEL, LOG_OUTPUT, LOG_FORMAT, LOG_FILE - Add lumberjack dependency for file log rotation - Both binaries now accept: --loglevel, --log-output, --log-format, --log-file - Add log section to frontier_all.yaml and frontlas_all.yaml - Backward compatible: default behavior unchanged (stdout + info + text) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add README translations for ja, ko, es, fr, de Extend the README language switcher from English/简体中文 to seven languages and add full translations of the English README for Japanese, Korean, Spanish, French, and German. Code blocks, commands, configs, and URLs are preserved verbatim; only prose, headings, table cells, and captions are translated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
English | [简体中文](./README_zh.md)
|
||||
English | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [한국어](./README_ko.md) | [Español](./README_es.md) | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/frontier-logo.png" width="30%">
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/singchia/frontier/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/singchia/frontier)
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [한국어](./README_ko.md) | [Español](./README_es.md) | [Français](./README_fr.md) | Deutsch
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Frontier ist ein in Go geschriebenes, **vollduplexfähiges** Open-Source-Gateway für Langzeitverbindungen. Es ermöglicht Microservices, Edge-Knoten oder Clients direkt zu erreichen – und umgekehrt. Es bietet vollduplexfähige **bidirektionale RPCs**, **Messaging** und **Punkt-zu-Punkt-Streams**. Frontier folgt den Prinzipien einer **Cloud-Native**-Architektur, unterstützt eine schnelle Cluster-Bereitstellung über den Operator und ist für **Hochverfügbarkeit** und **elastische Skalierung** auf Millionen aktiver Edge-Knoten oder Clients ausgelegt.
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
- [Funktionen](#funktionen)
|
||||
- [Schnellstart](#schnellstart)
|
||||
- [Architektur](#architektur)
|
||||
- [Verwendung](#verwendung)
|
||||
- [Konfiguration](#konfiguration)
|
||||
- [Bereitstellung](#bereitstellung)
|
||||
- [Cluster](#cluster)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [Entwicklung](#entwicklung)
|
||||
- [Tests](#tests)
|
||||
- [Community](#community)
|
||||
- [Lizenz](#lizenz)
|
||||
|
||||
## Schnellstart
|
||||
|
||||
1. Eine einzelne Frontier-Instanz starten:
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
2. Beispiele bauen und ausführen:
|
||||
|
||||
```bash
|
||||
make examples
|
||||
```
|
||||
|
||||
Das Chatroom-Beispiel ausführen:
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
./bin/chatroom_service
|
||||
|
||||
# Terminal 2
|
||||
./bin/chatroom_agent
|
||||
```
|
||||
|
||||
Demovideo:
|
||||
|
||||
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
|
||||
|
||||
## Funktionen
|
||||
|
||||
- **Bidirektionale RPCs**: Services und Edges können sich gegenseitig aufrufen, inklusive Lastverteilung.
|
||||
- **Messaging**: Topic-basiertes Publish/Receive zwischen Services, Edges und externem MQ.
|
||||
- **Punkt-zu-Punkt-Streams**: direkte Streams für Proxying, Dateiübertragung und benutzerdefinierten Verkehr.
|
||||
- **Cloud-Native-Bereitstellung**: Ausführung über Docker, Compose, Helm oder Operator.
|
||||
- **Hochverfügbarkeit und Skalierung**: Unterstützung für Reconnect, Clustering und horizontale Skalierung mit Frontlas.
|
||||
- **Authentifizierung und Präsenz**: Edge-Authentifizierung sowie Online-/Offline-Benachrichtigungen.
|
||||
- **Control-Plane-APIs**: gRPC- und REST-APIs zum Abfragen und Verwalten aktiver Knoten.
|
||||
|
||||
|
||||
## Architektur
|
||||
|
||||
**Frontier-Komponente**
|
||||
|
||||
<img src="./docs/diagram/frontier.png" width="100%">
|
||||
|
||||
- _Service End_: Einstiegspunkt für Microservice-Funktionen, standardmäßig verbunden.
|
||||
- _Edge End_: Einstiegspunkt für Edge-Knoten- oder Client-Funktionen.
|
||||
- _Publish/Receive_: Nachrichten veröffentlichen und empfangen.
|
||||
- _Call/Register_: Funktionen aufrufen und registrieren.
|
||||
- _OpenStream/AcceptStream_: Öffnen und Annehmen von Punkt-zu-Punkt-Streams (Verbindungen).
|
||||
- _External MQ_: Frontier unterstützt gemäß Konfiguration die Weiterleitung von Nachrichten, die von Edge-Knoten veröffentlicht werden, an Topics eines externen MQ.
|
||||
|
||||
|
||||
Frontier verlangt, dass sich sowohl Microservices als auch Edge-Knoten aktiv zu Frontier verbinden. Beim Verbindungsaufbau können Metadaten von Service und Edge (Empfangs-Topics, RPC, Service-Namen etc.) mitgegeben werden. Die Standardverbindungsports sind:
|
||||
|
||||
- :30011: Microservices verbinden sich darüber und erhalten Service.
|
||||
- :30012: Edge-Knoten verbinden sich darüber und erhalten Edge.
|
||||
- :30010: für Betriebsmitarbeiter oder Programme zur Nutzung der Control Plane.
|
||||
|
||||
|
||||
### Funktionalität
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>Funktion</th>
|
||||
<th>Initiator</th>
|
||||
<th>Empfänger</th>
|
||||
<th>Methode</th>
|
||||
<th>Routing-Verfahren</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">Messager</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Publish</td>
|
||||
<td>EdgeID+Topic</td>
|
||||
<td>Muss an eine bestimmte EdgeID veröffentlicht werden, das Standard-Topic ist leer. Der Edge ruft Receive auf, um die Nachricht zu empfangen, und muss nach der Verarbeitung msg.Done() oder msg.Error(err) aufrufen, um die Nachrichtenkonsistenz sicherzustellen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service oder External MQ</td>
|
||||
<td>Publish</td>
|
||||
<td>Topic</td>
|
||||
<td>Muss an ein Topic veröffentlicht werden; Frontier wählt anhand des Topics einen konkreten Service oder MQ aus.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">RPCer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Call</td>
|
||||
<td>EdgeID+Method</td>
|
||||
<td>Muss eine bestimmte EdgeID aufrufen und dabei den Methodennamen mitführen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>Call</td>
|
||||
<td>Method</td>
|
||||
<td>Muss eine Methode aufrufen; Frontier wählt anhand des Methodennamens einen konkreten Service aus.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Multiplexer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>OpenStream</td>
|
||||
<td>EdgeID</td>
|
||||
<td>Muss einen Stream zu einer bestimmten EdgeID öffnen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>OpenStream</td>
|
||||
<td>ServiceName</td>
|
||||
<td>Muss einen Stream zu einem ServiceName öffnen, der bei der Service-Initialisierung über service.OptionServiceName angegeben wird.</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
**Zentrale Designprinzipien**:
|
||||
|
||||
1. Alle Nachrichten, RPCs und Streams sind Punkt-zu-Punkt-Übertragungen.
|
||||
- Von Microservices zu Edges muss die Edge-Knoten-ID angegeben werden.
|
||||
- Von Edges zu Microservices routet Frontier anhand von Topic und Method und wählt schließlich per Hashing einen Microservice oder externen MQ aus. Standardmäßig wird auf Basis von edgeid gehasht, wahlweise auch random oder srcip.
|
||||
2. Nachrichten erfordern eine explizite Bestätigung durch den Empfänger.
|
||||
- Zur Sicherstellung der Zustellsemantik muss der Empfänger msg.Done() oder msg.Error(err) aufrufen, um die Zustellkonsistenz zu gewährleisten.
|
||||
3. Vom Multiplexer geöffnete Streams repräsentieren logisch eine direkte Kommunikation zwischen Microservices und Edge-Knoten.
|
||||
- Sobald die Gegenseite den Stream empfängt, erreichen alle Funktionen auf diesem Stream die Gegenseite direkt und umgehen die Routing-Richtlinien von Frontier.
|
||||
|
||||
## Verwendung
|
||||
|
||||
Ausführliche Nutzungsanleitung: [docs/USAGE.md](./docs/USAGE.md)
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Ausführliche Konfigurationsanleitung: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
|
||||
|
||||
## Bereitstellung
|
||||
|
||||
Bei einer einzelnen Frontier-Instanz können Sie Ihre Instanz je nach Umgebung mit den folgenden Methoden bereitstellen.
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
### Docker-Compose
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/compose
|
||||
docker-compose up -d frontier
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
In einer Kubernetes-Umgebung können Sie mit Helm schnell eine Instanz bereitstellen.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/helm
|
||||
helm install frontier ./ -f values.yaml
|
||||
```
|
||||
|
||||
Ihr Microservice sollte sich mit ```service/frontier-servicebound-svc:30011``` verbinden, und Ihr Edge-Knoten kann sich mit dem NodePort verbinden, auf dem `:30012` liegt.
|
||||
|
||||
### Systemd
|
||||
|
||||
Nutzen Sie die separate Systemd-Dokumentation:
|
||||
|
||||
[dist/systemd/README.md](./dist/systemd/README.md)
|
||||
|
||||
### Operator
|
||||
|
||||
Siehe den Abschnitt zur Cluster-Bereitstellung weiter unten.
|
||||
|
||||
## Cluster
|
||||
|
||||
### Frontier + Frontlas-Architektur
|
||||
|
||||
<img src="./docs/diagram/frontlas.png" width="100%">
|
||||
|
||||
Die zusätzliche Frontlas-Komponente dient zum Aufbau des Clusters. Frontlas ist ebenfalls zustandslos und speichert keine weiteren Informationen im Speicher, weshalb eine zusätzliche Abhängigkeit zu Redis erforderlich ist. Sie müssen Frontlas Redis-Verbindungsdaten bereitstellen; unterstützt werden `redis`, `sentinel` und `redis-cluster`.
|
||||
|
||||
- _Frontier_: Kommunikationskomponente zwischen Microservices und der Edge-Datenebene.
|
||||
- _Frontlas_: kurz für Frontier Atlas, eine Cluster-Management-Komponente, die Metadaten und Aktivitätsinformationen von Microservices und Edges in Redis ablegt.
|
||||
|
||||
Frontier muss sich proaktiv mit Frontlas verbinden, um seinen eigenen Status sowie die Aktivität und den Status von Microservices und Edges zu melden. Die Standardports von Frontlas sind:
|
||||
|
||||
- `:40011` für Microservice-Verbindungen, ersetzt den Port 30011 einer einzelnen Frontier-Instanz.
|
||||
- `:40012` für die Verbindung von Frontier zur Statusmeldung.
|
||||
|
||||
Sie können beliebig viele Frontier-Instanzen bereitstellen; für Frontlas genügt es, zwei Instanzen getrennt bereitzustellen, um HA (Hochverfügbarkeit) zu gewährleisten, da es keinen Zustand speichert und keine Konsistenzprobleme aufweist.
|
||||
|
||||
### Konfiguration
|
||||
|
||||
In der `frontier.yaml` von **Frontier** muss folgende Konfiguration ergänzt werden:
|
||||
|
||||
```yaml
|
||||
frontlas:
|
||||
enable: true
|
||||
dial:
|
||||
network: tcp
|
||||
addr:
|
||||
- 127.0.0.1:40012
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
daemon:
|
||||
# Innerhalb des Frontier-Clusters eindeutige ID
|
||||
frontier_id: frontier01
|
||||
```
|
||||
|
||||
Frontier muss sich mit Frontlas verbinden, um seinen eigenen Status sowie den von Microservices und Edges zu melden.
|
||||
|
||||
Minimale Konfiguration der `frontlas.yaml` von **Frontlas**:
|
||||
|
||||
```yaml
|
||||
control_plane:
|
||||
listen:
|
||||
# Microservices verbinden sich mit dieser Adresse, um Edges im Cluster zu entdecken
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40011
|
||||
frontier_plane:
|
||||
# Frontier verbindet sich mit dieser Adresse
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40012
|
||||
expiration:
|
||||
# Ablaufzeit der Microservice-Metadaten in Redis
|
||||
service_meta: 30
|
||||
# Ablaufzeit der Edge-Metadaten in Redis
|
||||
edge_meta: 30
|
||||
redis:
|
||||
# Unterstützt Standalone-, Sentinel- und Cluster-Verbindungen
|
||||
mode: standalone
|
||||
standalone:
|
||||
network: tcp
|
||||
addr: redis:6379
|
||||
db: 0
|
||||
```
|
||||
|
||||
### Verwendung
|
||||
|
||||
Da Frontlas dazu dient, verfügbare Frontiers zu finden, müssen sich Microservices folgendermaßen anpassen:
|
||||
|
||||
**Microservice bezieht Service**
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/singchia/frontier/api/dataplane/v1/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// NewClusterService verwenden, um Service zu beziehen
|
||||
svc, err := service.NewClusterService("127.0.0.1:40011")
|
||||
// Service verwenden, alles andere bleibt unverändert
|
||||
}
|
||||
```
|
||||
|
||||
**Edge-Knoten erhält die Verbindungsadresse**
|
||||
|
||||
Edge-Knoten verbinden sich weiterhin mit Frontier, können aber verfügbare Frontier-Adressen von Frontlas erhalten. Frontlas stellt eine Schnittstelle zum Auflisten der Frontier-Instanzen bereit:
|
||||
|
||||
```bash
|
||||
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
|
||||
```
|
||||
|
||||
Sie können diese Schnittstelle kapseln, um den Edge-Knoten Lastverteilung oder Hochverfügbarkeit bereitzustellen, oder mTLS ergänzen, um sie den Edge-Knoten direkt zur Verfügung zu stellen (nicht empfohlen).
|
||||
|
||||
gRPC für die Control Plane siehe [Protobuf-Definition](./api/controlplane/frontlas/v1/cluster.proto).
|
||||
|
||||
Die Control Plane von Frontlas unterscheidet sich von der von Frontier, da sie cluster-orientiert ist und derzeit nur lesende Schnittstellen für den Cluster bereitstellt.
|
||||
|
||||
```protobuf
|
||||
service ClusterService {
|
||||
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
|
||||
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
|
||||
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
|
||||
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
|
||||
|
||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
||||
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
|
||||
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Operator
|
||||
|
||||
**CRD und Operator installieren**
|
||||
|
||||
Folgen Sie diesen Schritten, um den Operator in Ihrer .kubeconfig-Umgebung zu installieren und bereitzustellen:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/crd
|
||||
kubectl apply -f install.yaml
|
||||
```
|
||||
|
||||
CRD prüfen:
|
||||
|
||||
```bash
|
||||
kubectl get crd frontierclusters.frontier.singchia.io
|
||||
```
|
||||
|
||||
Operator prüfen:
|
||||
|
||||
```bash
|
||||
kubectl get all -n frontier-system
|
||||
```
|
||||
|
||||
**FrontierCluster**
|
||||
|
||||
```yaml
|
||||
apiVersion: frontier.singchia.io/v1alpha1
|
||||
kind: FrontierCluster
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontiercluster
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: frontiercluster
|
||||
spec:
|
||||
frontier:
|
||||
# Frontier als einzelne Instanz
|
||||
replicas: 2
|
||||
# Port auf der Microservice-Seite
|
||||
servicebound:
|
||||
port: 30011
|
||||
# Port auf der Edge-Knoten-Seite
|
||||
edgebound:
|
||||
port: 30012
|
||||
frontlas:
|
||||
# Frontlas als einzelne Instanz
|
||||
replicas: 1
|
||||
# Port der Control Plane
|
||||
controlplane:
|
||||
port: 40011
|
||||
redis:
|
||||
# Konfiguration des abhängigen Redis
|
||||
addrs:
|
||||
- rfs-redisfailover:26379
|
||||
password: your-password
|
||||
masterName: mymaster
|
||||
redisType: sentinel
|
||||
```
|
||||
|
||||
Als `frontiercluster.yaml` speichern und
|
||||
|
||||
```
|
||||
kubectl apply -f frontiercluster.yaml
|
||||
```
|
||||
|
||||
Innerhalb einer Minute steht Ihnen ein Cluster mit 2 Frontier-Instanzen + 1 Frontlas-Instanz zur Verfügung.
|
||||
|
||||
Den Bereitstellungsstatus der Ressourcen prüfen Sie mit:
|
||||
|
||||
```bash
|
||||
kubectl get all -l app=frontiercluster-frontier
|
||||
kubectl get all -l app=frontiercluster-frontlas
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
|
||||
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
|
||||
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
|
||||
```
|
||||
|
||||
Ihr Microservice sollte sich mit `service/frontiercluster-frontlas-svc:40011` verbinden, und Ihr Edge-Knoten kann sich mit dem NodePort verbinden, auf dem `:30012` liegt.
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Roadmap
|
||||
|
||||
Siehe [ROADMAP](./ROADMAP.md).
|
||||
|
||||
### Beiträge
|
||||
|
||||
Wenn Sie einen Fehler finden, öffnen Sie bitte ein Issue; die Projekt-Maintainer antworten zeitnah.
|
||||
|
||||
Wenn Sie Features einreichen oder Projektprobleme schneller lösen möchten, sind PRs unter diesen einfachen Bedingungen willkommen:
|
||||
|
||||
- Der Code-Stil bleibt konsistent
|
||||
- Jeder Commit enthält ein Feature
|
||||
- Der eingereichte Code enthält Unit-Tests
|
||||
|
||||
## Tests
|
||||
|
||||
### Stream-Funktion
|
||||
|
||||
<img src="./docs/diagram/stream.png" width="100%">
|
||||
|
||||
## Community
|
||||
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/wechat.JPG" width="30%">
|
||||
</p>
|
||||
|
||||
Treten Sie unserer WeChat-Gruppe bei, um zu diskutieren und Unterstützung zu erhalten.
|
||||
|
||||
## Lizenz
|
||||
|
||||
Veröffentlicht unter der [Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE).
|
||||
|
||||
---
|
||||
Ein Stern ⭐️ wäre sehr willkommen ♥️
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
English | [简体中文](./README_zh.md)
|
||||
English | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [한국어](./README_ko.md) | [Español](./README_es.md) | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/frontier-logo.png" width="30%">
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/singchia/frontier/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/singchia/frontier)
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [한국어](./README_ko.md) | Español | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Frontier es una pasarela de conexión persistente de **full-dúplex** y código abierto, escrita en Go. Permite que los microservicios lleguen directamente a los nodos de borde o clientes, y viceversa. Ofrece **RPC bidireccional** full-dúplex, **mensajería** y **flujos punto a punto**. Frontier sigue los principios de arquitectura **cloud-native**, admite un despliegue rápido de clústeres mediante Operator y está diseñado para **alta disponibilidad** y **escalado elástico** hasta millones de nodos de borde o clientes en línea.
|
||||
|
||||
## Tabla de contenidos
|
||||
|
||||
- [Características](#características)
|
||||
- [Inicio rápido](#inicio-rápido)
|
||||
- [Arquitectura](#arquitectura)
|
||||
- [Uso](#uso)
|
||||
- [Configuración](#configuración)
|
||||
- [Despliegue](#despliegue)
|
||||
- [Clúster](#clúster)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [Desarrollo](#desarrollo)
|
||||
- [Pruebas](#pruebas)
|
||||
- [Comunidad](#comunidad)
|
||||
- [Licencia](#licencia)
|
||||
|
||||
## Inicio rápido
|
||||
|
||||
1. Ejecutar una única instancia de Frontier:
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
2. Compilar y ejecutar los ejemplos:
|
||||
|
||||
```bash
|
||||
make examples
|
||||
```
|
||||
|
||||
Ejecutar el ejemplo de sala de chat:
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
./bin/chatroom_service
|
||||
|
||||
# Terminal 2
|
||||
./bin/chatroom_agent
|
||||
```
|
||||
|
||||
Vídeo de demostración:
|
||||
|
||||
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
|
||||
|
||||
## Características
|
||||
|
||||
- **RPC bidireccional**: servicios y bordes pueden invocarse entre sí con balanceo de carga.
|
||||
- **Mensajería**: publicación/recepción basada en temas entre servicios, bordes y MQ externo.
|
||||
- **Flujos punto a punto**: apertura de flujos directos para proxy, transferencia de archivos y tráfico personalizado.
|
||||
- **Despliegue cloud-native**: ejecución mediante Docker, Compose, Helm u Operator.
|
||||
- **Alta disponibilidad y escalado**: soporte de reconexión, clustering y escalado horizontal con Frontlas.
|
||||
- **Autenticación y presencia**: autenticación de bordes y notificaciones de conexión/desconexión.
|
||||
- **APIs del plano de control**: APIs gRPC y REST para consultar y gestionar los nodos en línea.
|
||||
|
||||
|
||||
## Arquitectura
|
||||
|
||||
**Componente Frontier**
|
||||
|
||||
<img src="./docs/diagram/frontier.png" width="100%">
|
||||
|
||||
- _Service End_: el punto de entrada para funciones de microservicios, que se conecta por defecto.
|
||||
- _Edge End_: el punto de entrada para funciones de nodos de borde o clientes.
|
||||
- _Publish/Receive_: publicación y recepción de mensajes.
|
||||
- _Call/Register_: invocación y registro de funciones.
|
||||
- _OpenStream/AcceptStream_: apertura y aceptación de flujos punto a punto (conexiones).
|
||||
- _External MQ_: Frontier admite el reenvío, según la configuración, de los mensajes publicados desde los nodos de borde hacia temas de MQ externos.
|
||||
|
||||
|
||||
Frontier requiere que tanto los microservicios como los nodos de borde se conecten activamente a Frontier. Durante la conexión se pueden transportar los metadatos de Service y Edge (temas de recepción, RPC, nombres de servicio, etc.). Los puertos de conexión por defecto son:
|
||||
|
||||
- :30011: para que los microservicios se conecten y obtengan Service.
|
||||
- :30012: para que los nodos de borde se conecten y obtengan Edge.
|
||||
- :30010: para que el personal de operaciones o los programas utilicen el plano de control.
|
||||
|
||||
|
||||
### Funcionalidad
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>Función</th>
|
||||
<th>Iniciador</th>
|
||||
<th>Receptor</th>
|
||||
<th>Método</th>
|
||||
<th>Método de enrutamiento</th>
|
||||
<th>Descripción</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">Messager</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Publish</td>
|
||||
<td>EdgeID+Topic</td>
|
||||
<td>Debe publicar a un EdgeID específico; el tema por defecto está vacío. El borde llama a Receive para recibir el mensaje y, tras procesarlo, debe llamar a msg.Done() o msg.Error(err) para garantizar la consistencia del mensaje.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service o External MQ</td>
|
||||
<td>Publish</td>
|
||||
<td>Topic</td>
|
||||
<td>Debe publicar a un tema; Frontier selecciona un Service o MQ concreto según el tema.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">RPCer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Call</td>
|
||||
<td>EdgeID+Method</td>
|
||||
<td>Debe invocar a un EdgeID específico, indicando el nombre del método.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>Call</td>
|
||||
<td>Method</td>
|
||||
<td>Debe invocar un método; Frontier selecciona un Service concreto según el nombre del método.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Multiplexer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>OpenStream</td>
|
||||
<td>EdgeID</td>
|
||||
<td>Debe abrir un flujo hacia un EdgeID específico.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>OpenStream</td>
|
||||
<td>ServiceName</td>
|
||||
<td>Debe abrir un flujo hacia un ServiceName, especificado mediante service.OptionServiceName durante la inicialización del Service.</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
**Principios clave de diseño**:
|
||||
|
||||
1. Todos los mensajes, RPCs y flujos son transmisiones punto a punto.
|
||||
- De los microservicios a los bordes, debe especificarse el ID del nodo de borde.
|
||||
- De los bordes a los microservicios, Frontier enruta según Topic y Method, y finalmente selecciona un microservicio o un MQ externo mediante hashing. Por defecto, el hashing se basa en edgeid, pero puede elegirse random o srcip.
|
||||
2. Los mensajes requieren un reconocimiento explícito por parte del receptor.
|
||||
- Para garantizar la semántica de entrega, el receptor debe llamar a msg.Done() o msg.Error(err) para asegurar la consistencia de entrega.
|
||||
3. Los flujos abiertos por el Multiplexer representan lógicamente una comunicación directa entre microservicios y nodos de borde.
|
||||
- Una vez que el otro lado recibe el flujo, toda la funcionalidad sobre este flujo llegará directamente al otro lado, omitiendo las políticas de enrutamiento de Frontier.
|
||||
|
||||
## Uso
|
||||
|
||||
Guía de uso detallada: [docs/USAGE.md](./docs/USAGE.md)
|
||||
|
||||
## Configuración
|
||||
|
||||
Guía de configuración detallada: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
|
||||
|
||||
## Despliegue
|
||||
|
||||
Para una única instancia de Frontier, puede elegir los siguientes métodos para desplegarla según su entorno.
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
### Docker-Compose
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/compose
|
||||
docker-compose up -d frontier
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
Si está en un entorno Kubernetes, puede usar Helm para desplegar rápidamente una instancia.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/helm
|
||||
helm install frontier ./ -f values.yaml
|
||||
```
|
||||
|
||||
Su microservicio debería conectarse a ```service/frontier-servicebound-svc:30011```, y su nodo de borde puede conectarse al NodePort donde se expone `:30012`.
|
||||
|
||||
### Systemd
|
||||
|
||||
Consulte la documentación dedicada a Systemd:
|
||||
|
||||
[dist/systemd/README.md](./dist/systemd/README.md)
|
||||
|
||||
### Operator
|
||||
|
||||
Véase la sección de despliegue en clúster más abajo.
|
||||
|
||||
## Clúster
|
||||
|
||||
### Arquitectura Frontier + Frontlas
|
||||
|
||||
<img src="./docs/diagram/frontlas.png" width="100%">
|
||||
|
||||
El componente adicional Frontlas se utiliza para construir el clúster. Frontlas también es un componente sin estado y no almacena otra información en memoria, por lo que requiere una dependencia adicional de Redis. Debe proporcionar información de conexión a Redis a Frontlas, con soporte para `redis`, `sentinel` y `redis-cluster`.
|
||||
|
||||
- _Frontier_: componente de comunicación entre los microservicios y el plano de datos de los bordes.
|
||||
- _Frontlas_: siglas de Frontier Atlas, un componente de gestión del clúster que registra en Redis los metadatos y la información activa de los microservicios y los bordes.
|
||||
|
||||
Frontier debe conectarse proactivamente a Frontlas para reportar su estado y el de los microservicios y los bordes. Los puertos por defecto de Frontlas son:
|
||||
|
||||
- `:40011` para la conexión de microservicios, reemplazando al puerto 30011 de una única instancia Frontier.
|
||||
- `:40012` para la conexión de Frontier para reportar estado.
|
||||
|
||||
Puede desplegar el número de instancias Frontier que necesite; en el caso de Frontlas, desplegar dos instancias por separado puede garantizar HA (alta disponibilidad), ya que no guarda estado ni presenta problemas de consistencia.
|
||||
|
||||
### Configuración
|
||||
|
||||
El `frontier.yaml` de **Frontier** necesita añadir la siguiente configuración:
|
||||
|
||||
```yaml
|
||||
frontlas:
|
||||
enable: true
|
||||
dial:
|
||||
network: tcp
|
||||
addr:
|
||||
- 127.0.0.1:40012
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
daemon:
|
||||
# ID único dentro del clúster de Frontier
|
||||
frontier_id: frontier01
|
||||
```
|
||||
|
||||
Frontier debe conectarse a Frontlas para reportar su estado y el de los microservicios y bordes.
|
||||
|
||||
Configuración mínima del `frontlas.yaml` de **Frontlas**:
|
||||
|
||||
```yaml
|
||||
control_plane:
|
||||
listen:
|
||||
# Los microservicios se conectan a esta dirección para descubrir bordes en el clúster
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40011
|
||||
frontier_plane:
|
||||
# Frontier se conecta a esta dirección
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40012
|
||||
expiration:
|
||||
# Tiempo de expiración de los metadatos de microservicio en Redis
|
||||
service_meta: 30
|
||||
# Tiempo de expiración de los metadatos de borde en Redis
|
||||
edge_meta: 30
|
||||
redis:
|
||||
# Soporte para conexiones standalone, sentinel y cluster
|
||||
mode: standalone
|
||||
standalone:
|
||||
network: tcp
|
||||
addr: redis:6379
|
||||
db: 0
|
||||
```
|
||||
|
||||
### Uso
|
||||
|
||||
Dado que Frontlas se utiliza para descubrir los Frontiers disponibles, los microservicios deben ajustarse de la siguiente manera:
|
||||
|
||||
**Obtención de Service desde un microservicio**
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/singchia/frontier/api/dataplane/v1/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Usar NewClusterService para obtener Service
|
||||
svc, err := service.NewClusterService("127.0.0.1:40011")
|
||||
// Empezar a usar service; todo lo demás permanece igual
|
||||
}
|
||||
```
|
||||
|
||||
**Obtención de la dirección de conexión por parte de los nodos de borde**
|
||||
|
||||
Los nodos de borde siguen conectándose a Frontier, pero pueden obtener las direcciones de los Frontier disponibles desde Frontlas. Frontlas proporciona una interfaz para listar las instancias de Frontier:
|
||||
|
||||
```bash
|
||||
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
|
||||
```
|
||||
|
||||
Puede envolver esta interfaz para proporcionar balanceo de carga o alta disponibilidad a los nodos de borde, o añadir mTLS para entregarla directamente a los nodos de borde (no recomendado).
|
||||
|
||||
Para el gRPC del plano de control, véase la [definición Protobuf](./api/controlplane/frontlas/v1/cluster.proto).
|
||||
|
||||
El plano de control de Frontlas difiere del de Frontier en que está orientado al clúster, y actualmente solo proporciona interfaces de lectura para el clúster.
|
||||
|
||||
```protobuf
|
||||
service ClusterService {
|
||||
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
|
||||
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
|
||||
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
|
||||
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
|
||||
|
||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
||||
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
|
||||
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Operator
|
||||
|
||||
**Instalar CRD y Operator**
|
||||
|
||||
Siga estos pasos para instalar y desplegar el Operator en su entorno .kubeconfig:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/crd
|
||||
kubectl apply -f install.yaml
|
||||
```
|
||||
|
||||
Comprobar CRD:
|
||||
|
||||
```bash
|
||||
kubectl get crd frontierclusters.frontier.singchia.io
|
||||
```
|
||||
|
||||
Comprobar Operator:
|
||||
|
||||
```bash
|
||||
kubectl get all -n frontier-system
|
||||
```
|
||||
|
||||
**FrontierCluster**
|
||||
|
||||
```yaml
|
||||
apiVersion: frontier.singchia.io/v1alpha1
|
||||
kind: FrontierCluster
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontiercluster
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: frontiercluster
|
||||
spec:
|
||||
frontier:
|
||||
# Frontier de instancia única
|
||||
replicas: 2
|
||||
# Puerto del lado de microservicio
|
||||
servicebound:
|
||||
port: 30011
|
||||
# Puerto del lado de nodo de borde
|
||||
edgebound:
|
||||
port: 30012
|
||||
frontlas:
|
||||
# Frontlas de instancia única
|
||||
replicas: 1
|
||||
# Puerto del plano de control
|
||||
controlplane:
|
||||
port: 40011
|
||||
redis:
|
||||
# Configuración de Redis del que se depende
|
||||
addrs:
|
||||
- rfs-redisfailover:26379
|
||||
password: your-password
|
||||
masterName: mymaster
|
||||
redisType: sentinel
|
||||
```
|
||||
|
||||
Guarde como `frontiercluster.yaml` y ejecute
|
||||
|
||||
```
|
||||
kubectl apply -f frontiercluster.yaml
|
||||
```
|
||||
|
||||
En 1 minuto tendrá un clúster con 2 instancias de Frontier + 1 de Frontlas.
|
||||
|
||||
Compruebe el estado del despliegue de los recursos con:
|
||||
|
||||
```bash
|
||||
kubectl get all -l app=frontiercluster-frontier
|
||||
kubectl get all -l app=frontiercluster-frontlas
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
|
||||
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
|
||||
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
|
||||
```
|
||||
|
||||
Su microservicio debería conectarse a `service/frontiercluster-frontlas-svc:40011`, y su nodo de borde puede conectarse al NodePort donde se expone `:30012`.
|
||||
|
||||
## Desarrollo
|
||||
|
||||
### Roadmap
|
||||
|
||||
Véase [ROADMAP](./ROADMAP.md).
|
||||
|
||||
### Contribuciones
|
||||
|
||||
Si encuentra algún error, abra una issue y los mantenedores responderán con prontitud.
|
||||
|
||||
Si desea enviar funcionalidades o resolver problemas del proyecto con mayor rapidez, los PR son bienvenidos bajo estas sencillas condiciones:
|
||||
|
||||
- Mantener un estilo de código coherente
|
||||
- Cada envío incluye una única funcionalidad
|
||||
- El código enviado incluye pruebas unitarias
|
||||
|
||||
## Pruebas
|
||||
|
||||
### Funcionalidad Stream
|
||||
|
||||
<img src="./docs/diagram/stream.png" width="100%">
|
||||
|
||||
## Comunidad
|
||||
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/wechat.JPG" width="30%">
|
||||
</p>
|
||||
|
||||
Únase a nuestro grupo de WeChat para debates y soporte.
|
||||
|
||||
## Licencia
|
||||
|
||||
Publicado bajo la [Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE).
|
||||
|
||||
---
|
||||
¡Una estrella ⭐️ sería muy apreciada ♥️!
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/frontier-logo.png" width="30%">
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/singchia/frontier/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/singchia/frontier)
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [한국어](./README_ko.md) | [Español](./README_es.md) | Français | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Frontier est une passerelle de connexion longue durée open source **full-duplex**, écrite en Go. Elle permet aux microservices d'atteindre directement les nœuds de périphérie ou les clients, et inversement. Elle fournit un **RPC bidirectionnel** full-duplex, de la **messagerie** et des **flux point à point**. Frontier suit les principes d'architecture **cloud-native**, prend en charge un déploiement rapide de clusters via Operator, et est conçue pour la **haute disponibilité** et la **mise à l'échelle élastique** jusqu'à des millions de nœuds de périphérie ou de clients en ligne.
|
||||
|
||||
## Table des matières
|
||||
|
||||
- [Fonctionnalités](#fonctionnalités)
|
||||
- [Démarrage rapide](#démarrage-rapide)
|
||||
- [Architecture](#architecture)
|
||||
- [Utilisation](#utilisation)
|
||||
- [Configuration](#configuration)
|
||||
- [Déploiement](#déploiement)
|
||||
- [Cluster](#cluster)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [Développement](#développement)
|
||||
- [Tests](#tests)
|
||||
- [Communauté](#communauté)
|
||||
- [Licence](#licence)
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
1. Lancer une instance unique de Frontier :
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
2. Compiler et exécuter les exemples :
|
||||
|
||||
```bash
|
||||
make examples
|
||||
```
|
||||
|
||||
Exécuter l'exemple de salon de discussion :
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
./bin/chatroom_service
|
||||
|
||||
# Terminal 2
|
||||
./bin/chatroom_agent
|
||||
```
|
||||
|
||||
Vidéo de démonstration :
|
||||
|
||||
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
- **RPC bidirectionnel** : les services et les bords peuvent s'appeler mutuellement avec équilibrage de charge.
|
||||
- **Messagerie** : publication/réception par sujet entre services, bords et MQ externe.
|
||||
- **Flux point à point** : ouverture de flux directs pour le proxy, le transfert de fichiers et le trafic personnalisé.
|
||||
- **Déploiement cloud-native** : exécution via Docker, Compose, Helm ou Operator.
|
||||
- **Haute disponibilité et mise à l'échelle** : prise en charge de la reconnexion, du clustering et de la mise à l'échelle horizontale avec Frontlas.
|
||||
- **Authentification et présence** : authentification des bords et notifications en ligne/hors ligne.
|
||||
- **APIs du plan de contrôle** : APIs gRPC et REST pour interroger et gérer les nœuds en ligne.
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||
**Composant Frontier**
|
||||
|
||||
<img src="./docs/diagram/frontier.png" width="100%">
|
||||
|
||||
- _Service End_ : point d'entrée pour les fonctions des microservices, se connecte par défaut.
|
||||
- _Edge End_ : point d'entrée pour les fonctions des nœuds de périphérie ou des clients.
|
||||
- _Publish/Receive_ : publication et réception de messages.
|
||||
- _Call/Register_ : appel et enregistrement de fonctions.
|
||||
- _OpenStream/AcceptStream_ : ouverture et acceptation de flux point à point (connexions).
|
||||
- _External MQ_ : Frontier prend en charge, selon la configuration, le transfert des messages publiés depuis les nœuds de périphérie vers des sujets MQ externes.
|
||||
|
||||
|
||||
Frontier exige que les microservices et les nœuds de périphérie se connectent activement à Frontier. Les métadonnées de Service et Edge (sujets de réception, RPC, noms de service, etc.) peuvent être transportées lors de la connexion. Les ports de connexion par défaut sont :
|
||||
|
||||
- :30011 : pour que les microservices se connectent et obtiennent Service.
|
||||
- :30012 : pour que les nœuds de périphérie se connectent et obtiennent Edge.
|
||||
- :30010 : pour les opérateurs ou programmes qui utilisent le plan de contrôle.
|
||||
|
||||
|
||||
### Fonctionnalités
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>Fonction</th>
|
||||
<th>Initiateur</th>
|
||||
<th>Destinataire</th>
|
||||
<th>Méthode</th>
|
||||
<th>Méthode de routage</th>
|
||||
<th>Description</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">Messager</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Publish</td>
|
||||
<td>EdgeID+Topic</td>
|
||||
<td>Doit publier vers un EdgeID spécifique, le sujet par défaut est vide. Le bord appelle Receive pour recevoir le message, puis après traitement, doit appeler msg.Done() ou msg.Error(err) pour garantir la cohérence du message.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service ou External MQ</td>
|
||||
<td>Publish</td>
|
||||
<td>Topic</td>
|
||||
<td>Doit publier vers un sujet ; Frontier sélectionne un Service ou un MQ spécifique selon le sujet.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">RPCer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Call</td>
|
||||
<td>EdgeID+Method</td>
|
||||
<td>Doit appeler un EdgeID spécifique, en transportant le nom de la méthode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>Call</td>
|
||||
<td>Method</td>
|
||||
<td>Doit appeler une méthode ; Frontier sélectionne un Service spécifique selon le nom de la méthode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Multiplexer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>OpenStream</td>
|
||||
<td>EdgeID</td>
|
||||
<td>Doit ouvrir un flux vers un EdgeID spécifique.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>OpenStream</td>
|
||||
<td>ServiceName</td>
|
||||
<td>Doit ouvrir un flux vers un ServiceName, spécifié via service.OptionServiceName lors de l'initialisation du Service.</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
**Principes de conception clés** :
|
||||
|
||||
1. Tous les messages, RPC et flux sont des transmissions point à point.
|
||||
- Des microservices vers les bords, l'identifiant du nœud de périphérie doit être spécifié.
|
||||
- Des bords vers les microservices, Frontier route selon Topic et Method, et sélectionne finalement un microservice ou un MQ externe par hachage. Par défaut, le hachage est basé sur edgeid, mais vous pouvez choisir random ou srcip.
|
||||
2. Les messages nécessitent un accusé de réception explicite du destinataire.
|
||||
- Afin de garantir la sémantique de livraison, le destinataire doit appeler msg.Done() ou msg.Error(err) pour garantir la cohérence de la livraison.
|
||||
3. Les flux ouverts par le Multiplexer représentent logiquement une communication directe entre microservices et nœuds de périphérie.
|
||||
- Une fois que l'autre partie reçoit le flux, toutes les fonctionnalités de ce flux atteignent directement l'autre partie, en contournant les politiques de routage de Frontier.
|
||||
|
||||
## Utilisation
|
||||
|
||||
Guide d'utilisation détaillé : [docs/USAGE.md](./docs/USAGE.md)
|
||||
|
||||
## Configuration
|
||||
|
||||
Guide de configuration détaillé : [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
|
||||
|
||||
## Déploiement
|
||||
|
||||
Pour une instance Frontier unique, vous pouvez choisir les méthodes suivantes pour déployer votre instance selon votre environnement.
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
### Docker-Compose
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/compose
|
||||
docker-compose up -d frontier
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
Dans un environnement Kubernetes, vous pouvez utiliser Helm pour déployer rapidement une instance.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/helm
|
||||
helm install frontier ./ -f values.yaml
|
||||
```
|
||||
|
||||
Votre microservice devrait se connecter à ```service/frontier-servicebound-svc:30011```, et votre nœud de périphérie peut se connecter au NodePort où se trouve `:30012`.
|
||||
|
||||
### Systemd
|
||||
|
||||
Utilisez la documentation Systemd dédiée :
|
||||
|
||||
[dist/systemd/README.md](./dist/systemd/README.md)
|
||||
|
||||
### Operator
|
||||
|
||||
Voir la section de déploiement en cluster ci-dessous.
|
||||
|
||||
## Cluster
|
||||
|
||||
### Architecture Frontier + Frontlas
|
||||
|
||||
<img src="./docs/diagram/frontlas.png" width="100%">
|
||||
|
||||
Le composant supplémentaire Frontlas sert à construire le cluster. Frontlas est également un composant sans état qui ne stocke pas d'autres informations en mémoire, il nécessite donc une dépendance supplémentaire à Redis. Vous devez fournir à Frontlas les informations de connexion Redis, en prenant en charge `redis`, `sentinel` et `redis-cluster`.
|
||||
|
||||
- _Frontier_ : composant de communication entre les microservices et le plan de données des bords.
|
||||
- _Frontlas_ : nommé Frontier Atlas, un composant de gestion de cluster qui enregistre dans Redis les métadonnées et les informations d'activité des microservices et des bords.
|
||||
|
||||
Frontier doit se connecter de manière proactive à Frontlas pour signaler son propre état ainsi que l'activité et l'état des microservices et des bords. Les ports par défaut de Frontlas sont :
|
||||
|
||||
- `:40011` pour la connexion des microservices, remplaçant le port 30011 d'une instance Frontier unique.
|
||||
- `:40012` pour la connexion de Frontier afin de rapporter l'état.
|
||||
|
||||
Vous pouvez déployer autant d'instances Frontier que nécessaire ; pour Frontlas, déployer deux instances séparément peut garantir la haute disponibilité (HA), car il ne stocke pas d'état et ne présente pas de problèmes de cohérence.
|
||||
|
||||
### Configuration
|
||||
|
||||
Le `frontier.yaml` de **Frontier** doit ajouter la configuration suivante :
|
||||
|
||||
```yaml
|
||||
frontlas:
|
||||
enable: true
|
||||
dial:
|
||||
network: tcp
|
||||
addr:
|
||||
- 127.0.0.1:40012
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
daemon:
|
||||
# Identifiant unique au sein du cluster Frontier
|
||||
frontier_id: frontier01
|
||||
```
|
||||
|
||||
Frontier doit se connecter à Frontlas pour signaler son propre état ainsi que celui des microservices et des bords.
|
||||
|
||||
Configuration minimale du `frontlas.yaml` de **Frontlas** :
|
||||
|
||||
```yaml
|
||||
control_plane:
|
||||
listen:
|
||||
# Les microservices se connectent à cette adresse pour découvrir les bords du cluster
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40011
|
||||
frontier_plane:
|
||||
# Frontier se connecte à cette adresse
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40012
|
||||
expiration:
|
||||
# Durée d'expiration des métadonnées des microservices dans Redis
|
||||
service_meta: 30
|
||||
# Durée d'expiration des métadonnées des bords dans Redis
|
||||
edge_meta: 30
|
||||
redis:
|
||||
# Prise en charge des connexions standalone, sentinel et cluster
|
||||
mode: standalone
|
||||
standalone:
|
||||
network: tcp
|
||||
addr: redis:6379
|
||||
db: 0
|
||||
```
|
||||
|
||||
### Utilisation
|
||||
|
||||
Comme Frontlas sert à découvrir les Frontiers disponibles, les microservices doivent s'adapter ainsi :
|
||||
|
||||
**Microservice obtenant Service**
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/singchia/frontier/api/dataplane/v1/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Utiliser NewClusterService pour obtenir Service
|
||||
svc, err := service.NewClusterService("127.0.0.1:40011")
|
||||
// Commencer à utiliser service, tout le reste reste inchangé
|
||||
}
|
||||
```
|
||||
|
||||
**Nœud de périphérie obtenant l'adresse de connexion**
|
||||
|
||||
Les nœuds de périphérie se connectent toujours à Frontier mais peuvent obtenir les adresses des Frontiers disponibles auprès de Frontlas. Frontlas fournit une interface permettant de lister les instances Frontier :
|
||||
|
||||
```bash
|
||||
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
|
||||
```
|
||||
|
||||
Vous pouvez encapsuler cette interface pour fournir l'équilibrage de charge ou la haute disponibilité aux nœuds de périphérie, ou ajouter du mTLS pour l'exposer directement aux nœuds (non recommandé).
|
||||
|
||||
Pour le gRPC du plan de contrôle, voir la [définition Protobuf](./api/controlplane/frontlas/v1/cluster.proto).
|
||||
|
||||
Le plan de contrôle de Frontlas diffère de celui de Frontier car il est orienté cluster, et ne fournit actuellement que des interfaces de lecture pour le cluster.
|
||||
|
||||
```protobuf
|
||||
service ClusterService {
|
||||
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
|
||||
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
|
||||
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
|
||||
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
|
||||
|
||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
||||
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
|
||||
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Operator
|
||||
|
||||
**Installer CRD et Operator**
|
||||
|
||||
Suivez ces étapes pour installer et déployer l'Operator dans votre environnement .kubeconfig :
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/crd
|
||||
kubectl apply -f install.yaml
|
||||
```
|
||||
|
||||
Vérifier la CRD :
|
||||
|
||||
```bash
|
||||
kubectl get crd frontierclusters.frontier.singchia.io
|
||||
```
|
||||
|
||||
Vérifier l'Operator :
|
||||
|
||||
```bash
|
||||
kubectl get all -n frontier-system
|
||||
```
|
||||
|
||||
**FrontierCluster**
|
||||
|
||||
```yaml
|
||||
apiVersion: frontier.singchia.io/v1alpha1
|
||||
kind: FrontierCluster
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontiercluster
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: frontiercluster
|
||||
spec:
|
||||
frontier:
|
||||
# Frontier en instance unique
|
||||
replicas: 2
|
||||
# Port côté microservice
|
||||
servicebound:
|
||||
port: 30011
|
||||
# Port côté nœud de périphérie
|
||||
edgebound:
|
||||
port: 30012
|
||||
frontlas:
|
||||
# Frontlas en instance unique
|
||||
replicas: 1
|
||||
# Port du plan de contrôle
|
||||
controlplane:
|
||||
port: 40011
|
||||
redis:
|
||||
# Configuration Redis dont dépend le système
|
||||
addrs:
|
||||
- rfs-redisfailover:26379
|
||||
password: your-password
|
||||
masterName: mymaster
|
||||
redisType: sentinel
|
||||
```
|
||||
|
||||
Enregistrez sous `frontiercluster.yaml`, puis
|
||||
|
||||
```
|
||||
kubectl apply -f frontiercluster.yaml
|
||||
```
|
||||
|
||||
En 1 minute, vous obtiendrez un cluster avec 2 instances Frontier + 1 instance Frontlas.
|
||||
|
||||
Vérifiez l'état du déploiement des ressources avec :
|
||||
|
||||
```bash
|
||||
kubectl get all -l app=frontiercluster-frontier
|
||||
kubectl get all -l app=frontiercluster-frontlas
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
|
||||
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
|
||||
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
|
||||
```
|
||||
|
||||
Votre microservice devrait se connecter à `service/frontiercluster-frontlas-svc:40011`, et votre nœud de périphérie peut se connecter au NodePort où se trouve `:30012`.
|
||||
|
||||
## Développement
|
||||
|
||||
### Feuille de route
|
||||
|
||||
Voir [ROADMAP](./ROADMAP.md).
|
||||
|
||||
### Contributions
|
||||
|
||||
Si vous trouvez un bug, ouvrez une issue et les mainteneurs du projet répondront rapidement.
|
||||
|
||||
Si vous souhaitez soumettre des fonctionnalités ou résoudre plus rapidement des problèmes du projet, les PR sont les bienvenues sous ces conditions simples :
|
||||
|
||||
- Le style de code reste cohérent
|
||||
- Chaque soumission inclut une seule fonctionnalité
|
||||
- Le code soumis inclut des tests unitaires
|
||||
|
||||
## Tests
|
||||
|
||||
### Fonction Stream
|
||||
|
||||
<img src="./docs/diagram/stream.png" width="100%">
|
||||
|
||||
## Communauté
|
||||
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/wechat.JPG" width="30%">
|
||||
</p>
|
||||
|
||||
Rejoignez notre groupe WeChat pour les discussions et le support.
|
||||
|
||||
## Licence
|
||||
|
||||
Publié sous la [licence Apache 2.0](https://github.com/singchia/geminio/blob/main/LICENSE).
|
||||
|
||||
---
|
||||
Une étoile ⭐️ serait très appréciée ♥️
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/frontier-logo.png" width="30%">
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/singchia/frontier/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/singchia/frontier)
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | 日本語 | [한국어](./README_ko.md) | [Español](./README_es.md) | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Frontier は Go で書かれた**全二重**のオープンソース長時間接続ゲートウェイです。マイクロサービスからエッジノードやクライアントへの直接通信、およびその逆方向の通信を可能にします。全二重の**双方向 RPC**、**メッセージング**、**ポイントツーポイントストリーム**を提供します。Frontier は**クラウドネイティブ**アーキテクチャの原則に従い、Operator による高速なクラスタデプロイをサポートし、**高可用性**と数百万のオンラインエッジノード/クライアントへの**弾性スケーリング**を実現します。
|
||||
|
||||
## 目次
|
||||
|
||||
- [特徴](#特徴)
|
||||
- [クイックスタート](#クイックスタート)
|
||||
- [アーキテクチャ](#アーキテクチャ)
|
||||
- [使い方](#使い方)
|
||||
- [設定](#設定)
|
||||
- [デプロイ](#デプロイ)
|
||||
- [クラスタ](#クラスタ)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [開発](#開発)
|
||||
- [テスト](#テスト)
|
||||
- [コミュニティ](#コミュニティ)
|
||||
- [ライセンス](#ライセンス)
|
||||
|
||||
## クイックスタート
|
||||
|
||||
1. 単一の Frontier インスタンスを起動:
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
2. サンプルをビルドして実行:
|
||||
|
||||
```bash
|
||||
make examples
|
||||
```
|
||||
|
||||
チャットルームのサンプルを実行:
|
||||
|
||||
```bash
|
||||
# ターミナル 1
|
||||
./bin/chatroom_service
|
||||
|
||||
# ターミナル 2
|
||||
./bin/chatroom_agent
|
||||
```
|
||||
|
||||
デモ動画:
|
||||
|
||||
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
|
||||
|
||||
## 特徴
|
||||
|
||||
- **双方向 RPC**: サービスとエッジが互いに呼び出し可能、ロードバランシング対応。
|
||||
- **メッセージング**: サービス、エッジ、外部 MQ 間でのトピックベースのパブリッシュ/受信。
|
||||
- **ポイントツーポイントストリーム**: プロキシ、ファイル転送、カスタムトラフィックのための直接ストリームを開始。
|
||||
- **クラウドネイティブデプロイ**: Docker、Compose、Helm、Operator での実行が可能。
|
||||
- **高可用性とスケーリング**: 再接続、クラスタリング、Frontlas による水平スケーリングをサポート。
|
||||
- **認証とプレゼンス**: エッジ認証およびオンライン/オフライン通知。
|
||||
- **コントロールプレーン API**: オンラインノードの問い合わせと管理のための gRPC および REST API。
|
||||
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
**Frontier コンポーネント**
|
||||
|
||||
<img src="./docs/diagram/frontier.png" width="100%">
|
||||
|
||||
- _Service End_: マイクロサービス機能のエントリポイント、デフォルトで接続します。
|
||||
- _Edge End_: エッジノードまたはクライアント機能のエントリポイント。
|
||||
- _Publish/Receive_: メッセージのパブリッシュと受信。
|
||||
- _Call/Register_: 関数の呼び出しと登録。
|
||||
- _OpenStream/AcceptStream_: ポイントツーポイントストリーム(接続)の開始と受付。
|
||||
- _External MQ_: Frontier は設定に基づき、エッジノードから発行されたメッセージを外部 MQ トピックへ転送できます。
|
||||
|
||||
|
||||
Frontier はマイクロサービスとエッジノードの双方が能動的に Frontier へ接続する必要があります。接続時に Service と Edge のメタデータ(受信トピック、RPC、サービス名など)を運ぶことができます。デフォルトの接続ポートは以下の通りです:
|
||||
|
||||
- :30011: マイクロサービスが Service を取得するために接続するポート。
|
||||
- :30012: エッジノードが Edge を取得するために接続するポート。
|
||||
- :30010: 運用担当者またはプログラムがコントロールプレーンを利用するポート。
|
||||
|
||||
|
||||
### 機能
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>機能</th>
|
||||
<th>発信側</th>
|
||||
<th>受信側</th>
|
||||
<th>メソッド</th>
|
||||
<th>ルーティング方式</th>
|
||||
<th>説明</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">Messager</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Publish</td>
|
||||
<td>EdgeID+Topic</td>
|
||||
<td>特定の EdgeID にパブリッシュする必要があり、デフォルトのトピックは空です。エッジは Receive を呼び出してメッセージを受信し、処理後に msg.Done() または msg.Error(err) を呼び出してメッセージの整合性を確保する必要があります。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service または External MQ</td>
|
||||
<td>Publish</td>
|
||||
<td>Topic</td>
|
||||
<td>トピックにパブリッシュする必要があり、Frontier はトピックに基づいて特定の Service または MQ を選択します。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">RPCer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Call</td>
|
||||
<td>EdgeID+Method</td>
|
||||
<td>特定の EdgeID を呼び出す必要があり、メソッド名を伴います。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>Call</td>
|
||||
<td>Method</td>
|
||||
<td>メソッドを呼び出す必要があり、Frontier はメソッド名に基づいて特定の Service を選択します。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Multiplexer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>OpenStream</td>
|
||||
<td>EdgeID</td>
|
||||
<td>特定の EdgeID に対してストリームを開く必要があります。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>OpenStream</td>
|
||||
<td>ServiceName</td>
|
||||
<td>ServiceName に対してストリームを開く必要があり、Service 初期化時に service.OptionServiceName で指定します。</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
**主要な設計原則**:
|
||||
|
||||
1. すべてのメッセージ、RPC、ストリームはポイントツーポイント伝送です。
|
||||
- マイクロサービスからエッジへは、エッジノード ID を指定する必要があります。
|
||||
- エッジからマイクロサービスへは、Frontier が Topic と Method に基づいてルーティングし、最終的にハッシュにより特定のマイクロサービスまたは外部 MQ を選択します。デフォルトは edgeid に基づくハッシュですが、random や srcip を選択できます。
|
||||
2. メッセージは受信側による明示的な確認が必要です。
|
||||
- メッセージ配信のセマンティクスを保証するため、受信側は msg.Done() または msg.Error(err) を呼び出して配信の整合性を確保する必要があります。
|
||||
3. Multiplexer によって開かれたストリームは、論理的にマイクロサービスとエッジノード間の直接通信を表します。
|
||||
- 相手側がストリームを受信すると、このストリーム上のすべての機能は Frontier のルーティングポリシーをバイパスして直接相手側に到達します。
|
||||
|
||||
## 使い方
|
||||
|
||||
詳細な使い方ガイド: [docs/USAGE.md](./docs/USAGE.md)
|
||||
|
||||
## 設定
|
||||
|
||||
詳細な設定ガイド: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
|
||||
|
||||
## デプロイ
|
||||
|
||||
単一の Frontier インスタンスでは、環境に応じて以下の方法から選んでインスタンスをデプロイできます。
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
### Docker-Compose
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/compose
|
||||
docker-compose up -d frontier
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
Kubernetes 環境の場合、Helm を使用して素早くインスタンスをデプロイできます。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/helm
|
||||
helm install frontier ./ -f values.yaml
|
||||
```
|
||||
|
||||
マイクロサービスは ```service/frontier-servicebound-svc:30011``` に接続し、エッジノードは `:30012` の NodePort に接続できます。
|
||||
|
||||
### Systemd
|
||||
|
||||
Systemd 専用ドキュメントを参照:
|
||||
|
||||
[dist/systemd/README.md](./dist/systemd/README.md)
|
||||
|
||||
### Operator
|
||||
|
||||
後述のクラスタデプロイセクションを参照してください。
|
||||
|
||||
## クラスタ
|
||||
|
||||
### Frontier + Frontlas アーキテクチャ
|
||||
|
||||
<img src="./docs/diagram/frontlas.png" width="100%">
|
||||
|
||||
追加の Frontlas コンポーネントはクラスタの構築に使用されます。Frontlas もステートレスなコンポーネントで、情報をメモリに保存しないため、Redis への追加依存が必要です。Frontlas に Redis 接続情報を提供する必要があり、`redis`、`sentinel`、`redis-cluster` をサポートします。
|
||||
|
||||
- _Frontier_: マイクロサービスとエッジデータプレーン間の通信コンポーネント。
|
||||
- _Frontlas_: Frontier Atlas の略で、マイクロサービスとエッジのメタデータおよびアクティブ情報を Redis に記録するクラスタ管理コンポーネント。
|
||||
|
||||
Frontier は能動的に Frontlas に接続し、自身、マイクロサービス、エッジのアクティブ状態を報告する必要があります。Frontlas のデフォルトポート:
|
||||
|
||||
- `:40011` マイクロサービス接続用、単一 Frontier インスタンスの 30011 ポートを置き換えます。
|
||||
- `:40012` Frontier がステータス報告のために接続するポート。
|
||||
|
||||
必要に応じて任意の数の Frontier インスタンスをデプロイでき、Frontlas は状態を保持せず一貫性の問題がないため、2 インスタンスを別々にデプロイすることで HA(高可用性)を保証できます。
|
||||
|
||||
### 設定
|
||||
|
||||
**Frontier** の `frontier.yaml` に以下の設定を追加する必要があります:
|
||||
|
||||
```yaml
|
||||
frontlas:
|
||||
enable: true
|
||||
dial:
|
||||
network: tcp
|
||||
addr:
|
||||
- 127.0.0.1:40012
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
daemon:
|
||||
# Frontier クラスタ内で一意の ID
|
||||
frontier_id: frontier01
|
||||
```
|
||||
|
||||
Frontier は Frontlas に接続して、自身、マイクロサービス、エッジのアクティブ状態を報告する必要があります。
|
||||
|
||||
**Frontlas** の `frontlas.yaml` の最小構成:
|
||||
|
||||
```yaml
|
||||
control_plane:
|
||||
listen:
|
||||
# マイクロサービスはこのアドレスに接続してクラスタ内のエッジを発見します
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40011
|
||||
frontier_plane:
|
||||
# Frontier はこのアドレスに接続します
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40012
|
||||
expiration:
|
||||
# Redis 内のマイクロサービスメタデータの有効期限
|
||||
service_meta: 30
|
||||
# Redis 内のエッジメタデータの有効期限
|
||||
edge_meta: 30
|
||||
redis:
|
||||
# standalone、sentinel、cluster 接続をサポート
|
||||
mode: standalone
|
||||
standalone:
|
||||
network: tcp
|
||||
addr: redis:6379
|
||||
db: 0
|
||||
```
|
||||
|
||||
### 使い方
|
||||
|
||||
Frontlas は利用可能な Frontier を発見するために使用されるため、マイクロサービスは次のように調整する必要があります:
|
||||
|
||||
**マイクロサービスが Service を取得**
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/singchia/frontier/api/dataplane/v1/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// NewClusterService を使用して Service を取得
|
||||
svc, err := service.NewClusterService("127.0.0.1:40011")
|
||||
// service の使用開始、その他はすべて変更なし
|
||||
}
|
||||
```
|
||||
|
||||
**エッジノードの接続アドレス取得**
|
||||
|
||||
エッジノードは引き続き Frontier に接続しますが、Frontlas から利用可能な Frontier のアドレスを取得できます。Frontlas は Frontier インスタンスを一覧取得するインターフェースを提供します:
|
||||
|
||||
```bash
|
||||
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
|
||||
```
|
||||
|
||||
このインターフェースをラップしてエッジノードにロードバランシングや高可用性を提供したり、mTLS を追加してエッジノードに直接提供したり(非推奨)できます。
|
||||
|
||||
コントロールプレーン gRPC は [Protobuf 定義](./api/controlplane/frontlas/v1/cluster.proto) を参照。
|
||||
|
||||
Frontlas のコントロールプレーンは Frontier のそれとは異なり、クラスタ指向のコントロールプレーンであり、現在はクラスタの読み取りインターフェースのみを提供します。
|
||||
|
||||
```protobuf
|
||||
service ClusterService {
|
||||
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
|
||||
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
|
||||
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
|
||||
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
|
||||
|
||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
||||
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
|
||||
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Operator
|
||||
|
||||
**CRD と Operator のインストール**
|
||||
|
||||
次の手順で Operator を .kubeconfig 環境にインストール/デプロイします:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/crd
|
||||
kubectl apply -f install.yaml
|
||||
```
|
||||
|
||||
CRD を確認:
|
||||
|
||||
```bash
|
||||
kubectl get crd frontierclusters.frontier.singchia.io
|
||||
```
|
||||
|
||||
Operator を確認:
|
||||
|
||||
```bash
|
||||
kubectl get all -n frontier-system
|
||||
```
|
||||
|
||||
**FrontierCluster**
|
||||
|
||||
```yaml
|
||||
apiVersion: frontier.singchia.io/v1alpha1
|
||||
kind: FrontierCluster
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontiercluster
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: frontiercluster
|
||||
spec:
|
||||
frontier:
|
||||
# 単一インスタンス Frontier
|
||||
replicas: 2
|
||||
# マイクロサービス側ポート
|
||||
servicebound:
|
||||
port: 30011
|
||||
# エッジノード側ポート
|
||||
edgebound:
|
||||
port: 30012
|
||||
frontlas:
|
||||
# 単一インスタンス Frontlas
|
||||
replicas: 1
|
||||
# コントロールプレーンポート
|
||||
controlplane:
|
||||
port: 40011
|
||||
redis:
|
||||
# 依存する Redis の構成
|
||||
addrs:
|
||||
- rfs-redisfailover:26379
|
||||
password: your-password
|
||||
masterName: mymaster
|
||||
redisType: sentinel
|
||||
```
|
||||
|
||||
`frontiercluster.yaml` として保存し、
|
||||
|
||||
```
|
||||
kubectl apply -f frontiercluster.yaml
|
||||
```
|
||||
|
||||
1 分以内に、2 インスタンスの Frontier + 1 インスタンスの Frontlas クラスタが得られます。
|
||||
|
||||
次のコマンドでリソースのデプロイ状況を確認:
|
||||
|
||||
```bash
|
||||
kubectl get all -l app=frontiercluster-frontier
|
||||
kubectl get all -l app=frontiercluster-frontlas
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
|
||||
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
|
||||
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
|
||||
```
|
||||
|
||||
マイクロサービスは `service/frontiercluster-frontlas-svc:40011` に接続し、エッジノードは `:30012` の NodePort に接続できます。
|
||||
|
||||
## 開発
|
||||
|
||||
### ロードマップ
|
||||
|
||||
[ROADMAP](./ROADMAP.md) を参照。
|
||||
|
||||
### コントリビューション
|
||||
|
||||
バグを発見した場合は issue を開いてください。プロジェクトメンテナが速やかに対応します。
|
||||
|
||||
機能の提出や、プロジェクト課題のより迅速な解決をご希望の場合は、次のシンプルな条件のもとで PR を提出いただけます:
|
||||
|
||||
- コードスタイルの一貫性を保つこと
|
||||
- 1 回の提出につき 1 つの機能とすること
|
||||
- 提出するコードにユニットテストを含めること
|
||||
|
||||
## テスト
|
||||
|
||||
### Stream 機能
|
||||
|
||||
<img src="./docs/diagram/stream.png" width="100%">
|
||||
|
||||
## コミュニティ
|
||||
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/wechat.JPG" width="30%">
|
||||
</p>
|
||||
|
||||
議論とサポートのために WeChat グループへご参加ください。
|
||||
|
||||
## ライセンス
|
||||
|
||||
[Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE) の下で公開されています。
|
||||
|
||||
---
|
||||
Star ⭐️ をいただけると大変嬉しいです ♥️
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/frontier-logo.png" width="30%">
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/singchia/frontier/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/singchia/frontier)
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | 한국어 | [Español](./README_es.md) | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Frontier는 Go로 작성된 **전이중(full-duplex)** 오픈소스 장기 연결 게이트웨이입니다. 마이크로서비스가 엣지 노드나 클라이언트에 직접 도달할 수 있도록 하고, 그 반대 방향도 지원합니다. 전이중 **양방향 RPC**, **메시징**, **점대점 스트림**을 제공합니다. Frontier는 **클라우드 네이티브** 아키텍처 원칙을 따르며, Operator를 통한 빠른 클러스터 배포를 지원하고, **고가용성**과 수백만 온라인 엣지 노드/클라이언트에 대한 **탄력적 확장**을 위해 설계되었습니다.
|
||||
|
||||
## 목차
|
||||
|
||||
- [기능](#기능)
|
||||
- [빠른 시작](#빠른-시작)
|
||||
- [아키텍처](#아키텍처)
|
||||
- [사용법](#사용법)
|
||||
- [구성](#구성)
|
||||
- [배포](#배포)
|
||||
- [클러스터](#클러스터)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [개발](#개발)
|
||||
- [테스트](#테스트)
|
||||
- [커뮤니티](#커뮤니티)
|
||||
- [라이선스](#라이선스)
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
1. 단일 Frontier 인스턴스 실행:
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
2. 예제 빌드 및 실행:
|
||||
|
||||
```bash
|
||||
make examples
|
||||
```
|
||||
|
||||
채팅방 예제 실행:
|
||||
|
||||
```bash
|
||||
# 터미널 1
|
||||
./bin/chatroom_service
|
||||
|
||||
# 터미널 2
|
||||
./bin/chatroom_agent
|
||||
```
|
||||
|
||||
데모 영상:
|
||||
|
||||
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
|
||||
|
||||
## 기능
|
||||
|
||||
- **양방향 RPC**: 서비스와 엣지가 로드 밸런싱과 함께 서로 호출할 수 있습니다.
|
||||
- **메시징**: 서비스, 엣지, 외부 MQ 간의 토픽 기반 게시/수신.
|
||||
- **점대점 스트림**: 프록시, 파일 전송, 사용자 정의 트래픽을 위한 직접 스트림 개설.
|
||||
- **클라우드 네이티브 배포**: Docker, Compose, Helm 또는 Operator를 통한 실행.
|
||||
- **고가용성 및 확장**: 재연결, 클러스터링, Frontlas를 통한 수평 확장 지원.
|
||||
- **인증 및 프레즌스**: 엣지 인증과 온라인/오프라인 알림.
|
||||
- **컨트롤 플레인 API**: 온라인 노드 조회 및 관리를 위한 gRPC 및 REST API.
|
||||
|
||||
|
||||
## 아키텍처
|
||||
|
||||
**Frontier 컴포넌트**
|
||||
|
||||
<img src="./docs/diagram/frontier.png" width="100%">
|
||||
|
||||
- _Service End_: 마이크로서비스 기능의 진입점, 기본적으로 연결됩니다.
|
||||
- _Edge End_: 엣지 노드 또는 클라이언트 기능의 진입점.
|
||||
- _Publish/Receive_: 메시지 게시 및 수신.
|
||||
- _Call/Register_: 함수 호출 및 등록.
|
||||
- _OpenStream/AcceptStream_: 점대점 스트림(연결)의 개설 및 수락.
|
||||
- _External MQ_: Frontier는 구성에 따라 엣지 노드에서 게시된 메시지를 외부 MQ 토픽으로 전달할 수 있습니다.
|
||||
|
||||
|
||||
Frontier는 마이크로서비스와 엣지 노드 모두 능동적으로 Frontier에 연결해야 합니다. 연결 시 Service와 Edge의 메타데이터(수신 토픽, RPC, 서비스 이름 등)를 실어 보낼 수 있습니다. 기본 연결 포트는 다음과 같습니다:
|
||||
|
||||
- :30011: 마이크로서비스가 Service를 얻기 위해 연결.
|
||||
- :30012: 엣지 노드가 Edge를 얻기 위해 연결.
|
||||
- :30010: 운영자 또는 프로그램이 컨트롤 플레인을 사용하기 위해 연결.
|
||||
|
||||
|
||||
### 기능 상세
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>기능</th>
|
||||
<th>개시자</th>
|
||||
<th>수신자</th>
|
||||
<th>메서드</th>
|
||||
<th>라우팅 방식</th>
|
||||
<th>설명</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">Messager</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Publish</td>
|
||||
<td>EdgeID+Topic</td>
|
||||
<td>특정 EdgeID로 게시해야 하며, 기본 토픽은 비어 있습니다. 엣지는 Receive를 호출해 메시지를 받고, 처리 후 메시지 일관성을 보장하기 위해 msg.Done() 또는 msg.Error(err)를 반드시 호출해야 합니다.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service 또는 External MQ</td>
|
||||
<td>Publish</td>
|
||||
<td>Topic</td>
|
||||
<td>토픽으로 게시해야 하며, Frontier가 토픽을 기반으로 특정 Service 또는 MQ를 선택합니다.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">RPCer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>Call</td>
|
||||
<td>EdgeID+Method</td>
|
||||
<td>특정 EdgeID를 메서드 이름과 함께 호출해야 합니다.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>Call</td>
|
||||
<td>Method</td>
|
||||
<td>메서드를 호출해야 하며, Frontier가 메서드 이름을 기반으로 특정 Service를 선택합니다.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Multiplexer</td>
|
||||
<td>Service</td>
|
||||
<td>Edge</td>
|
||||
<td>OpenStream</td>
|
||||
<td>EdgeID</td>
|
||||
<td>특정 EdgeID로 스트림을 개설해야 합니다.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
<td>Service</td>
|
||||
<td>OpenStream</td>
|
||||
<td>ServiceName</td>
|
||||
<td>ServiceName으로 스트림을 개설해야 하며, Service 초기화 시 service.OptionServiceName으로 지정합니다.</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
**주요 설계 원칙**:
|
||||
|
||||
1. 모든 메시지, RPC, 스트림은 점대점 전송입니다.
|
||||
- 마이크로서비스에서 엣지로 보낼 때는 엣지 노드 ID를 지정해야 합니다.
|
||||
- 엣지에서 마이크로서비스로는 Frontier가 Topic과 Method를 기반으로 라우팅하며, 최종적으로 해싱을 통해 마이크로서비스나 외부 MQ를 선택합니다. 기본값은 edgeid 기반 해싱이지만 random 또는 srcip를 선택할 수 있습니다.
|
||||
2. 메시지는 수신자의 명시적 확인이 필요합니다.
|
||||
- 메시지 전달 시맨틱을 보장하기 위해 수신자는 msg.Done() 또는 msg.Error(err)를 호출해 전달 일관성을 보장해야 합니다.
|
||||
3. Multiplexer가 개설한 스트림은 논리적으로 마이크로서비스와 엣지 노드 간의 직접 통신을 나타냅니다.
|
||||
- 상대방이 스트림을 받으면, 이 스트림의 모든 기능은 Frontier의 라우팅 정책을 우회하여 상대방에게 직접 도달합니다.
|
||||
|
||||
## 사용법
|
||||
|
||||
상세 사용 가이드: [docs/USAGE.md](./docs/USAGE.md)
|
||||
|
||||
## 구성
|
||||
|
||||
상세 구성 가이드: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
|
||||
|
||||
## 배포
|
||||
|
||||
단일 Frontier 인스턴스의 경우, 환경에 따라 다음 방법 중 하나로 Frontier 인스턴스를 배포할 수 있습니다.
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
|
||||
```
|
||||
|
||||
### Docker-Compose
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/compose
|
||||
docker-compose up -d frontier
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
Kubernetes 환경에서는 Helm으로 빠르게 인스턴스를 배포할 수 있습니다.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/helm
|
||||
helm install frontier ./ -f values.yaml
|
||||
```
|
||||
|
||||
마이크로서비스는 ```service/frontier-servicebound-svc:30011```에 연결해야 하고, 엣지 노드는 `:30012`가 위치한 NodePort에 연결할 수 있습니다.
|
||||
|
||||
### Systemd
|
||||
|
||||
전용 Systemd 문서 참고:
|
||||
|
||||
[dist/systemd/README.md](./dist/systemd/README.md)
|
||||
|
||||
### Operator
|
||||
|
||||
아래 클러스터 배포 섹션을 참조하세요.
|
||||
|
||||
## 클러스터
|
||||
|
||||
### Frontier + Frontlas 아키텍처
|
||||
|
||||
<img src="./docs/diagram/frontlas.png" width="100%">
|
||||
|
||||
추가 Frontlas 컴포넌트는 클러스터 구성에 사용됩니다. Frontlas도 스테이트리스 컴포넌트이며 메모리에 기타 정보를 저장하지 않기 때문에 Redis에 대한 추가 의존성이 필요합니다. Frontlas에 Redis 연결 정보를 제공해야 하며, `redis`, `sentinel`, `redis-cluster`를 지원합니다.
|
||||
|
||||
- _Frontier_: 마이크로서비스와 엣지 데이터 플레인 간 통신 컴포넌트.
|
||||
- _Frontlas_: Frontier Atlas의 약자로, 마이크로서비스와 엣지의 메타데이터 및 활성 정보를 Redis에 기록하는 클러스터 관리 컴포넌트.
|
||||
|
||||
Frontier는 Frontlas에 능동적으로 연결하여 자신, 마이크로서비스, 엣지의 활성 상태를 보고해야 합니다. Frontlas의 기본 포트:
|
||||
|
||||
- `:40011` 마이크로서비스 연결용, 단일 Frontier 인스턴스의 30011 포트를 대체합니다.
|
||||
- `:40012` Frontier가 상태 보고를 위해 연결.
|
||||
|
||||
필요한 만큼 Frontier 인스턴스를 배포할 수 있으며, Frontlas의 경우 상태를 저장하지 않고 일관성 문제가 없기 때문에 두 인스턴스를 별도로 배포하면 HA(고가용성)를 보장할 수 있습니다.
|
||||
|
||||
### 구성
|
||||
|
||||
**Frontier**의 `frontier.yaml`에 다음 구성을 추가해야 합니다:
|
||||
|
||||
```yaml
|
||||
frontlas:
|
||||
enable: true
|
||||
dial:
|
||||
network: tcp
|
||||
addr:
|
||||
- 127.0.0.1:40012
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
daemon:
|
||||
# Frontier 클러스터 내에서 고유한 ID
|
||||
frontier_id: frontier01
|
||||
```
|
||||
|
||||
Frontier는 Frontlas에 연결하여 자신, 마이크로서비스, 엣지의 활성 상태를 보고해야 합니다.
|
||||
|
||||
**Frontlas**의 `frontlas.yaml` 최소 구성:
|
||||
|
||||
```yaml
|
||||
control_plane:
|
||||
listen:
|
||||
# 마이크로서비스는 이 주소에 연결해 클러스터 내 엣지를 발견합니다
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40011
|
||||
frontier_plane:
|
||||
# Frontier가 이 주소에 연결합니다
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:40012
|
||||
expiration:
|
||||
# Redis 내 마이크로서비스 메타데이터 만료 시간
|
||||
service_meta: 30
|
||||
# Redis 내 엣지 메타데이터 만료 시간
|
||||
edge_meta: 30
|
||||
redis:
|
||||
# standalone, sentinel, cluster 연결 지원
|
||||
mode: standalone
|
||||
standalone:
|
||||
network: tcp
|
||||
addr: redis:6379
|
||||
db: 0
|
||||
```
|
||||
|
||||
### 사용법
|
||||
|
||||
Frontlas가 사용 가능한 Frontier를 발견하는 데 사용되므로, 마이크로서비스는 다음과 같이 조정해야 합니다:
|
||||
|
||||
**마이크로서비스가 Service 획득**
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/singchia/frontier/api/dataplane/v1/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// NewClusterService로 Service 획득
|
||||
svc, err := service.NewClusterService("127.0.0.1:40011")
|
||||
// service 사용 시작, 나머지는 그대로
|
||||
}
|
||||
```
|
||||
|
||||
**엣지 노드의 연결 주소 획득**
|
||||
|
||||
엣지 노드는 여전히 Frontier에 연결하지만, Frontlas에서 사용 가능한 Frontier 주소를 얻을 수 있습니다. Frontlas는 Frontier 인스턴스를 나열하는 인터페이스를 제공합니다:
|
||||
|
||||
```bash
|
||||
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
|
||||
```
|
||||
|
||||
이 인터페이스를 래핑해 엣지 노드에 로드 밸런싱 또는 고가용성을 제공하거나, mTLS를 추가해 엣지 노드에 직접 제공할 수 있습니다(권장하지 않음).
|
||||
|
||||
컨트롤 플레인 gRPC는 [Protobuf 정의](./api/controlplane/frontlas/v1/cluster.proto) 참조.
|
||||
|
||||
Frontlas 컨트롤 플레인은 Frontier와 다르게 클러스터 지향 컨트롤 플레인이며, 현재는 클러스터에 대한 읽기 인터페이스만 제공합니다.
|
||||
|
||||
```protobuf
|
||||
service ClusterService {
|
||||
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
|
||||
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
|
||||
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
|
||||
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
|
||||
|
||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
||||
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
|
||||
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Operator
|
||||
|
||||
**CRD 및 Operator 설치**
|
||||
|
||||
다음 단계에 따라 .kubeconfig 환경에 Operator를 설치 및 배포합니다:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/singchia/frontier.git
|
||||
cd dist/crd
|
||||
kubectl apply -f install.yaml
|
||||
```
|
||||
|
||||
CRD 확인:
|
||||
|
||||
```bash
|
||||
kubectl get crd frontierclusters.frontier.singchia.io
|
||||
```
|
||||
|
||||
Operator 확인:
|
||||
|
||||
```bash
|
||||
kubectl get all -n frontier-system
|
||||
```
|
||||
|
||||
**FrontierCluster**
|
||||
|
||||
```yaml
|
||||
apiVersion: frontier.singchia.io/v1alpha1
|
||||
kind: FrontierCluster
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontiercluster
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: frontiercluster
|
||||
spec:
|
||||
frontier:
|
||||
# 단일 인스턴스 Frontier
|
||||
replicas: 2
|
||||
# 마이크로서비스 측 포트
|
||||
servicebound:
|
||||
port: 30011
|
||||
# 엣지 노드 측 포트
|
||||
edgebound:
|
||||
port: 30012
|
||||
frontlas:
|
||||
# 단일 인스턴스 Frontlas
|
||||
replicas: 1
|
||||
# 컨트롤 플레인 포트
|
||||
controlplane:
|
||||
port: 40011
|
||||
redis:
|
||||
# 의존하는 Redis 구성
|
||||
addrs:
|
||||
- rfs-redisfailover:26379
|
||||
password: your-password
|
||||
masterName: mymaster
|
||||
redisType: sentinel
|
||||
```
|
||||
|
||||
`frontiercluster.yaml`로 저장하고,
|
||||
|
||||
```
|
||||
kubectl apply -f frontiercluster.yaml
|
||||
```
|
||||
|
||||
1분 이내에 2 인스턴스 Frontier + 1 인스턴스 Frontlas 클러스터가 준비됩니다.
|
||||
|
||||
다음 명령으로 리소스 배포 상태를 확인:
|
||||
|
||||
```bash
|
||||
kubectl get all -l app=frontiercluster-frontier
|
||||
kubectl get all -l app=frontiercluster-frontlas
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
|
||||
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
|
||||
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
|
||||
```
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
|
||||
```
|
||||
|
||||
마이크로서비스는 `service/frontiercluster-frontlas-svc:40011`에 연결하고, 엣지 노드는 `:30012`가 위치한 NodePort에 연결할 수 있습니다.
|
||||
|
||||
## 개발
|
||||
|
||||
### 로드맵
|
||||
|
||||
[ROADMAP](./ROADMAP.md) 참조.
|
||||
|
||||
### 기여
|
||||
|
||||
버그를 발견하면 issue를 열어주세요. 프로젝트 메인테이너가 신속히 응답합니다.
|
||||
|
||||
기능을 제출하거나 프로젝트 이슈를 더 빠르게 해결하고 싶다면, 다음의 간단한 조건에 따라 PR을 환영합니다:
|
||||
|
||||
- 코드 스타일 일관성 유지
|
||||
- 각 제출은 하나의 기능 포함
|
||||
- 제출한 코드에 단위 테스트 포함
|
||||
|
||||
## 테스트
|
||||
|
||||
### Stream 기능
|
||||
|
||||
<img src="./docs/diagram/stream.png" width="100%">
|
||||
|
||||
## 커뮤니티
|
||||
|
||||
<p align=center>
|
||||
<img src="./docs/diagram/wechat.JPG" width="30%">
|
||||
</p>
|
||||
|
||||
토론과 지원을 위해 WeChat 그룹에 참여하세요.
|
||||
|
||||
## 라이선스
|
||||
|
||||
[Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE)에 따라 배포됩니다.
|
||||
|
||||
---
|
||||
Star ⭐️ 부탁드립니다 ♥️
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
[](https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
[English](./README.md) | 简体中文
|
||||
[English](./README.md) | 简体中文 | [日本語](./README_ja.md) | [한국어](./README_ko.md) | [Español](./README_es.md) | [Français](./README_fr.md) | [Deutsch](./README_de.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+91
-141
@@ -1,3 +1,15 @@
|
||||
controlplane:
|
||||
enable: false
|
||||
listen:
|
||||
addr: 0.0.0.0:30010
|
||||
advertised_addr: ""
|
||||
network: tcp
|
||||
tls:
|
||||
ca_certs: null
|
||||
certs: null
|
||||
enable: false
|
||||
insecure_skip_verify: false
|
||||
mtls: false
|
||||
daemon:
|
||||
frontier_id: ""
|
||||
pprof:
|
||||
@@ -7,76 +19,47 @@ daemon:
|
||||
rlimit:
|
||||
enable: true
|
||||
nofile: 102400
|
||||
controlplane:
|
||||
enable: false
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:30010
|
||||
# advertised_addr: ""
|
||||
# tls:
|
||||
# ca_certs: null
|
||||
# certs: null
|
||||
# enable: false
|
||||
# insecure_skip_verify: false
|
||||
# mtls: false
|
||||
servicebound:
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:30011
|
||||
# advertised_addr: ""
|
||||
# tls:
|
||||
# ca_certs:
|
||||
# - ca1.cert
|
||||
# - ca2.cert
|
||||
# certs:
|
||||
# - cert: servicebound.cert
|
||||
# key: servicebound.key
|
||||
# enable: false
|
||||
# insecure_skip_verify: false
|
||||
# mtls: false
|
||||
edgebound:
|
||||
listen:
|
||||
network: tcp
|
||||
addr: 0.0.0.0:30012
|
||||
# advertised_addr: ""
|
||||
# tls:
|
||||
# ca_certs:
|
||||
# - ca1.cert
|
||||
# - ca2.cert
|
||||
# certs:
|
||||
# - cert: edgebound.cert
|
||||
# key: edgebound.key
|
||||
# enable: false
|
||||
# insecure_skip_verify: false
|
||||
# mtls: false
|
||||
edgeid_alloc_when_no_idservice_on: true
|
||||
# bypass:
|
||||
# addr: 192.168.1.10:8443
|
||||
# advertised_addr: ""
|
||||
# network: tcp
|
||||
# tls:
|
||||
# ca_certs:
|
||||
# - ca1.cert
|
||||
# certs:
|
||||
# - cert: frontier.cert
|
||||
# key: frontier.key
|
||||
# enable: true
|
||||
# insecure_skip_verify: false
|
||||
# mtls: true
|
||||
# bypass_enable: false
|
||||
dao:
|
||||
backend: buntdb
|
||||
debug: false
|
||||
edgebound:
|
||||
bypass:
|
||||
addrs:
|
||||
- 192.168.1.10:8443
|
||||
advertised_addr: ""
|
||||
network: tcp
|
||||
tls:
|
||||
ca_certs:
|
||||
- ca1.cert
|
||||
certs:
|
||||
- cert: frontier.cert
|
||||
key: frontier.key
|
||||
enable: true
|
||||
insecure_skip_verify: false
|
||||
mtls: true
|
||||
bypass_enable: false
|
||||
edgeid_alloc_when_no_idservice_on: true
|
||||
listen:
|
||||
addr: 0.0.0.0:30012
|
||||
advertised_addr: ""
|
||||
network: tcp
|
||||
tls:
|
||||
ca_certs:
|
||||
- ca1.cert
|
||||
- ca2.cert
|
||||
certs:
|
||||
- cert: edgebound.cert
|
||||
key: edgebound.key
|
||||
enable: false
|
||||
insecure_skip_verify: false
|
||||
mtls: false
|
||||
exchange:
|
||||
hashby: edgeid
|
||||
hashby: ""
|
||||
frontlas:
|
||||
enable: false
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
dial:
|
||||
addrs:
|
||||
- 127.0.0.1:40012
|
||||
addrs:
|
||||
- 127.0.0.1:40012
|
||||
advertised_addr: ""
|
||||
network: tcp
|
||||
tls:
|
||||
ca_certs: null
|
||||
@@ -84,134 +67,101 @@ frontlas:
|
||||
enable: false
|
||||
insecure_skip_verify: false
|
||||
mtls: false
|
||||
enable: false
|
||||
metrics:
|
||||
enable: false
|
||||
interval: 0
|
||||
log:
|
||||
file:
|
||||
compress: false
|
||||
max_age: 30
|
||||
max_backups: 5
|
||||
max_size: 100
|
||||
path: /var/log/frontier/frontier.log
|
||||
format: text
|
||||
level: info
|
||||
output: stdout
|
||||
mqm:
|
||||
amqp:
|
||||
enable: false
|
||||
addrs: null
|
||||
# 0 max channels means 2^16 - 1
|
||||
channel_max: 0
|
||||
# exchange to declare
|
||||
enable: false
|
||||
exchanges: null
|
||||
# 0 max bytes means unlimited
|
||||
frame_size: 0
|
||||
# less than 1s uses the server's interval
|
||||
heartbeat: 0
|
||||
# Connection locale that we expect to always be en_US
|
||||
# Even though servers must return it as per the AMQP 0-9-1 spec,
|
||||
# we are not aware of it being used other than to satisfy the spec requirements
|
||||
locale: ""
|
||||
producer:
|
||||
# exchange name to produce
|
||||
exchange: ""
|
||||
routing_keys: null
|
||||
# creating application id
|
||||
app_id: ""
|
||||
# MIME content encoding
|
||||
content_encoding: ""
|
||||
# MIME content type
|
||||
content_type: ""
|
||||
# Transient (0 or 1) or Persistent (2)
|
||||
delivery_mode: 0
|
||||
exchange: ""
|
||||
expiration: ""
|
||||
# message related headers
|
||||
headers: null
|
||||
immediate: false
|
||||
mandatory: false
|
||||
# 0 to 9
|
||||
priority: 0
|
||||
# address to to reply to (ex: RPC)
|
||||
reply_to: ""
|
||||
# message type name
|
||||
routing_keys: null
|
||||
type: ""
|
||||
# creating user id - ex: "guest"
|
||||
user_id: ""
|
||||
queueBindings: null
|
||||
queues: null
|
||||
# Vhost specifies the namespace of permissions, exchanges, queues and
|
||||
# bindings on the server. Dial sets this to the path parsed from the URL.
|
||||
vhost: ""
|
||||
kafka:
|
||||
enable: false
|
||||
addrs: null
|
||||
enable: false
|
||||
producer:
|
||||
# topics to notify frontier which topics to allow to publish
|
||||
topics: null
|
||||
# The type of compression to use on messages (defaults to no compression).
|
||||
# Similar to `compression.codec` setting of the JVM producer.
|
||||
async: false
|
||||
compression: none
|
||||
# The level of compression to use on messages. The meaning depends
|
||||
# on the actual compression type used and defaults to default compression
|
||||
# level for the codec.
|
||||
compression_level: 0
|
||||
# If enabled, the producer will ensure that exactly one copy of each message is
|
||||
# written.
|
||||
idempotent: false
|
||||
# The maximum permitted size of a message (defaults to 1000000). Should be
|
||||
# set equal to or smaller than the broker's `message.max.bytes`.
|
||||
max_message_bytes: 0
|
||||
# The level of acknowledgement reliability needed from the broker (defaults
|
||||
# to WaitForLocal). Equivalent to the `request.required.acks` setting of the
|
||||
# JVM producer.
|
||||
required_acks: 0
|
||||
# The maximum duration the broker will wait the receipt of the number of
|
||||
# RequiredAcks (defaults to 10 seconds). This is only relevant when
|
||||
# RequiredAcks is set to WaitForAll or a number > 1. Only supports
|
||||
# millisecond resolution, nanoseconds will be truncated. Equivalent to
|
||||
# the JVM producer's `request.timeout.ms` setting.
|
||||
timeout: 0
|
||||
# The following config options control how often messages are batched up and
|
||||
# sent to the broker. By default, messages are sent as fast as possible, and
|
||||
# all messages received while the current batch is in-flight are placed
|
||||
# into the subsequent batch.
|
||||
flush:
|
||||
# The best-effort number of bytes needed to trigger a flush. Use the
|
||||
# global sarama.MaxRequestSize to set a hard upper limit.
|
||||
bytes: 0
|
||||
# The best-effort frequency of flushes. Equivalent to
|
||||
# `queue.buffering.max.ms` setting of JVM producer.
|
||||
frequency: 0
|
||||
# The maximum number of messages the producer will send in a single
|
||||
# broker request. Defaults to 0 for unlimited. Similar to
|
||||
# `queue.buffering.max.messages` in the JVM producer.
|
||||
max_messages: 0
|
||||
# The best-effort number of messages needed to trigger a flush. Use
|
||||
# `MaxMessages` to set a hard upper limit.
|
||||
messages: 0
|
||||
idempotent: false
|
||||
max_message_bytes: 0
|
||||
required_acks: 0
|
||||
retry:
|
||||
#How long to wait for the cluster to settle between retries
|
||||
# (default 100ms). Similar to the `retry.backoff.ms` setting of the
|
||||
# JVM producer.
|
||||
backoff: 0
|
||||
# The total number of times to retry sending a message (default 3).
|
||||
# Similar to the `message.send.max.retries` setting of the JVM producer.
|
||||
max: 0
|
||||
|
||||
timeout: 0
|
||||
topics: null
|
||||
nats:
|
||||
enable: false
|
||||
addrs: null
|
||||
producer:
|
||||
# topics to specific
|
||||
subjects: null
|
||||
# jetstream will replace upper producer.
|
||||
enable: false
|
||||
jetstream:
|
||||
enable: false
|
||||
# jetstream name to publish
|
||||
name: ""
|
||||
# jetstream producer
|
||||
producer:
|
||||
# topics to specific
|
||||
subjects: null
|
||||
producer:
|
||||
subjects: null
|
||||
nsq:
|
||||
enable: false
|
||||
addrs: null
|
||||
enable: false
|
||||
producer:
|
||||
topics: null
|
||||
redis:
|
||||
enable: false
|
||||
addrs: null
|
||||
db: 0
|
||||
enable: false
|
||||
password: ""
|
||||
producer:
|
||||
# topics to specific
|
||||
channels: null
|
||||
|
||||
servicebound:
|
||||
listen:
|
||||
addr: 0.0.0.0:30011
|
||||
advertised_addr: ""
|
||||
network: tcp
|
||||
tls:
|
||||
ca_certs:
|
||||
- ca1.cert
|
||||
- ca2.cert
|
||||
certs:
|
||||
- cert: servicebound.cert
|
||||
key: servicebound.key
|
||||
enable: false
|
||||
insecure_skip_verify: false
|
||||
mtls: false
|
||||
|
||||
@@ -31,6 +31,16 @@ frontier_manager:
|
||||
enable: false
|
||||
insecure_skip_verify: false
|
||||
mtls: false
|
||||
log:
|
||||
file:
|
||||
compress: false
|
||||
max_age: 30
|
||||
max_backups: 5
|
||||
max_size: 100
|
||||
path: /var/log/frontier/frontlas.log
|
||||
format: text
|
||||
level: info
|
||||
output: stdout
|
||||
redis:
|
||||
client_name: ""
|
||||
cluster:
|
||||
|
||||
@@ -24,6 +24,7 @@ require (
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
|
||||
@@ -271,6 +271,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jumboframes/armorigo/log"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// Log holds unified logging configuration for both frontier and frontlas.
|
||||
type Log struct {
|
||||
// Level controls log verbosity for both klog and armorigo.
|
||||
// Options: "debug", "info", "warn", "error". Default: "info".
|
||||
Level string `yaml:"level,omitempty" json:"level"`
|
||||
|
||||
// Output controls where logs are written.
|
||||
// Options: "stdout", "stderr", "file", "both" (stdout+file). Default: "stdout".
|
||||
Output string `yaml:"output,omitempty" json:"output"`
|
||||
|
||||
// Format controls the log output format.
|
||||
// Options: "text", "json". Default: "text".
|
||||
Format string `yaml:"format,omitempty" json:"format"`
|
||||
|
||||
// File holds file-based logging config, used when Output is "file" or "both".
|
||||
File LogFile `yaml:"file,omitempty" json:"file"`
|
||||
}
|
||||
|
||||
// LogFile configures file-based log output with rotation via lumberjack.
|
||||
type LogFile struct {
|
||||
// Path is the log file path. Default: "/var/log/frontier/<component>.log".
|
||||
Path string `yaml:"path,omitempty" json:"path"`
|
||||
|
||||
// MaxSize is the max size in MB before rotation. Default: 100.
|
||||
MaxSize int `yaml:"max_size,omitempty" json:"max_size"`
|
||||
|
||||
// MaxBackups is the max number of old log files to keep. Default: 5.
|
||||
MaxBackups int `yaml:"max_backups,omitempty" json:"max_backups"`
|
||||
|
||||
// MaxAge is the max days to retain old log files. 0 means no age limit. Default: 30.
|
||||
MaxAge int `yaml:"max_age,omitempty" json:"max_age"`
|
||||
|
||||
// Compress enables gzip compression for rotated log files. Default: false.
|
||||
Compress bool `yaml:"compress,omitempty" json:"compress"`
|
||||
}
|
||||
|
||||
// level mapping: user-facing level -> klog verbosity
|
||||
var levelToKlogVerbosity = map[string]int{
|
||||
"debug": 4,
|
||||
"info": 2,
|
||||
"warn": 0,
|
||||
"error": 0,
|
||||
}
|
||||
|
||||
// level mapping: user-facing level -> armorigo level
|
||||
var levelToArmorigo = map[string]log.Level{
|
||||
"debug": log.LevelDebug,
|
||||
"info": log.LevelInfo,
|
||||
"warn": log.LevelWarn,
|
||||
"error": log.LevelError,
|
||||
}
|
||||
|
||||
// SetupLogging initializes both klog and armorigo logging based on the unified
|
||||
// Log config. The component parameter ("frontier" or "frontlas") is used for
|
||||
// the default log file path.
|
||||
func SetupLogging(cfg *Log, component string) error {
|
||||
applyDefaults(cfg, component)
|
||||
|
||||
if err := validateConfig(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build the writer(s) for the chosen output mode
|
||||
writer, err := buildWriter(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// configure klog
|
||||
setupKlog(cfg, writer)
|
||||
|
||||
// configure armorigo
|
||||
setupArmorigo(cfg, writer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyDefaults(cfg *Log, component string) {
|
||||
if cfg.Level == "" {
|
||||
cfg.Level = "info"
|
||||
}
|
||||
cfg.Level = strings.ToLower(cfg.Level)
|
||||
|
||||
if cfg.Output == "" {
|
||||
cfg.Output = "stdout"
|
||||
}
|
||||
cfg.Output = strings.ToLower(cfg.Output)
|
||||
|
||||
if cfg.Format == "" {
|
||||
cfg.Format = "text"
|
||||
}
|
||||
cfg.Format = strings.ToLower(cfg.Format)
|
||||
|
||||
if cfg.File.Path == "" {
|
||||
cfg.File.Path = "/var/log/frontier/" + component + ".log"
|
||||
}
|
||||
if cfg.File.MaxSize <= 0 {
|
||||
cfg.File.MaxSize = 100
|
||||
}
|
||||
if cfg.File.MaxBackups <= 0 {
|
||||
cfg.File.MaxBackups = 5
|
||||
}
|
||||
if cfg.File.MaxAge <= 0 {
|
||||
cfg.File.MaxAge = 30
|
||||
}
|
||||
}
|
||||
|
||||
func validateConfig(cfg *Log) error {
|
||||
if _, ok := levelToKlogVerbosity[cfg.Level]; !ok {
|
||||
return fmt.Errorf("unsupported log level %q, options: debug, info, warn, error", cfg.Level)
|
||||
}
|
||||
switch cfg.Output {
|
||||
case "stdout", "stderr", "file", "both":
|
||||
default:
|
||||
return fmt.Errorf("unsupported log output %q, options: stdout, stderr, file, both", cfg.Output)
|
||||
}
|
||||
switch cfg.Format {
|
||||
case "text", "json":
|
||||
default:
|
||||
return fmt.Errorf("unsupported log format %q, options: text, json", cfg.Format)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildWriter(cfg *Log) (io.Writer, error) {
|
||||
var fileWriter io.Writer
|
||||
if cfg.Output == "file" || cfg.Output == "both" {
|
||||
fileWriter = &lumberjack.Logger{
|
||||
Filename: cfg.File.Path,
|
||||
MaxSize: cfg.File.MaxSize,
|
||||
MaxBackups: cfg.File.MaxBackups,
|
||||
MaxAge: cfg.File.MaxAge,
|
||||
Compress: cfg.File.Compress,
|
||||
}
|
||||
}
|
||||
|
||||
switch cfg.Output {
|
||||
case "stdout":
|
||||
return os.Stdout, nil
|
||||
case "stderr":
|
||||
return os.Stderr, nil
|
||||
case "file":
|
||||
return fileWriter, nil
|
||||
case "both":
|
||||
return io.MultiWriter(os.Stdout, fileWriter), nil
|
||||
}
|
||||
return os.Stdout, nil
|
||||
}
|
||||
|
||||
func setupKlog(cfg *Log, writer io.Writer) {
|
||||
// ensure klog flags are initialized
|
||||
fs := flag.NewFlagSet("klog", flag.ContinueOnError)
|
||||
klog.InitFlags(fs)
|
||||
|
||||
// set verbosity
|
||||
verbosity := levelToKlogVerbosity[cfg.Level]
|
||||
fs.Set("v", fmt.Sprintf("%d", verbosity))
|
||||
|
||||
// for "error" level, raise the stderr threshold so only warnings+ go through
|
||||
if cfg.Level == "error" {
|
||||
fs.Set("stderrthreshold", "WARNING")
|
||||
}
|
||||
|
||||
// direct all klog output to our unified writer
|
||||
// disable klog's own stderr/file logic — we handle it
|
||||
fs.Set("logtostderr", "false")
|
||||
fs.Set("alsologtostderr", "false")
|
||||
klog.SetOutput(writer)
|
||||
}
|
||||
|
||||
func setupArmorigo(cfg *Log, writer io.Writer) {
|
||||
log.SetLevel(levelToArmorigo[cfg.Level])
|
||||
log.SetOutput(writer)
|
||||
}
|
||||
|
||||
// ApplyLogEnvOverrides applies environment variable overrides to the Log config.
|
||||
// Priority: env > yaml (caller should call this after loading yaml but before SetupLogging).
|
||||
func ApplyLogEnvOverrides(cfg *Log) {
|
||||
if v := os.Getenv("LOG_LEVEL"); v != "" {
|
||||
cfg.Level = v
|
||||
}
|
||||
if v := os.Getenv("LOG_OUTPUT"); v != "" {
|
||||
cfg.Output = v
|
||||
}
|
||||
if v := os.Getenv("LOG_FORMAT"); v != "" {
|
||||
cfg.Format = v
|
||||
}
|
||||
if v := os.Getenv("LOG_FILE"); v != "" {
|
||||
cfg.File.Path = v
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,15 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
armio "github.com/jumboframes/armorigo/io"
|
||||
"github.com/jumboframes/armorigo/log"
|
||||
"github.com/singchia/frontier/pkg/config"
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// daemon related
|
||||
@@ -252,6 +248,8 @@ type Frontlas struct {
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Log config.Log `yaml:"log,omitempty" json:"log"`
|
||||
|
||||
Daemon Daemon `yaml:"daemon,omitempty" json:"daemon"`
|
||||
|
||||
Edgebound Edgebound `yaml:"edgebound" json:"edgebound"`
|
||||
@@ -273,95 +271,104 @@ type Configuration struct {
|
||||
func Parse() (*Configuration, error) {
|
||||
var (
|
||||
argConfigFile = pflag.String("config", "", "config file, default not configured")
|
||||
argArmorigoLogLevel = pflag.String("loglevel", "info", "log level for armorigo log")
|
||||
argLogLevel = pflag.String("loglevel", "", "log level: debug, info, warn, error")
|
||||
argLogOutput = pflag.String("log-output", "", "log output: stdout, stderr, file, both")
|
||||
argLogFormat = pflag.String("log-format", "", "log format: text, json")
|
||||
argLogFile = pflag.String("log-file", "", "log file path, used when output is file or both")
|
||||
argDaemonRLimitNofile = pflag.Int("daemon-rlimit-nofile", -1, "SetRLimit for number of file of this daemon, default: -1 means ignore")
|
||||
// TODO more command-line args
|
||||
|
||||
config *Configuration
|
||||
conf *Configuration
|
||||
)
|
||||
pflag.Lookup("daemon-rlimit-nofile").NoOptDefVal = "1048576"
|
||||
|
||||
// set klog
|
||||
klogFlags := flag.NewFlagSet("klog", flag.ExitOnError)
|
||||
klog.InitFlags(klogFlags)
|
||||
|
||||
// sync the glog and klog flags.
|
||||
pflag.CommandLine.VisitAll(func(f1 *pflag.Flag) {
|
||||
f2 := klogFlags.Lookup(f1.Name)
|
||||
if f2 != nil {
|
||||
value := f1.Value.String()
|
||||
if err := f2.Value.Set(value); err != nil {
|
||||
klog.Fatal(err, "failed to set flag")
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pflag.CommandLine.AddGoFlagSet(klogFlags)
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
pflag.Parse()
|
||||
|
||||
// armorigo log
|
||||
level, err := log.ParseLevel(*argArmorigoLogLevel)
|
||||
if err != nil {
|
||||
fmt.Println("parse log level err:", err)
|
||||
return nil, err
|
||||
}
|
||||
log.SetLevel(level)
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
// config file
|
||||
if *argConfigFile != "" {
|
||||
// TODO the command-line is prior to config file
|
||||
data, err := os.ReadFile(*argConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config = &Configuration{}
|
||||
if err = yaml.Unmarshal(data, config); err != nil {
|
||||
conf = &Configuration{}
|
||||
if err = yaml.Unmarshal(data, conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = &Configuration{}
|
||||
if conf == nil {
|
||||
conf = &Configuration{}
|
||||
}
|
||||
|
||||
// env overrides for log (priority: flag > env > yaml > default)
|
||||
config.ApplyLogEnvOverrides(&conf.Log)
|
||||
|
||||
// flag overrides for log (highest priority)
|
||||
if *argLogLevel != "" {
|
||||
conf.Log.Level = *argLogLevel
|
||||
}
|
||||
if *argLogOutput != "" {
|
||||
conf.Log.Output = *argLogOutput
|
||||
}
|
||||
if *argLogFormat != "" {
|
||||
conf.Log.Format = *argLogFormat
|
||||
}
|
||||
if *argLogFile != "" {
|
||||
conf.Log.File.Path = *argLogFile
|
||||
}
|
||||
|
||||
// setup unified logging
|
||||
if err := config.SetupLogging(&conf.Log, "frontier"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// daemon
|
||||
config.Daemon.RLimit.NumFile = *argDaemonRLimitNofile
|
||||
if config.Daemon.PProf.CPUProfileRate == 0 {
|
||||
config.Daemon.PProf.CPUProfileRate = 10000
|
||||
conf.Daemon.RLimit.NumFile = *argDaemonRLimitNofile
|
||||
if conf.Daemon.PProf.CPUProfileRate == 0 {
|
||||
conf.Daemon.PProf.CPUProfileRate = 10000
|
||||
}
|
||||
// env
|
||||
// env overrides for network
|
||||
sbPort := os.Getenv("FRONTIER_SERVICEBOUND_PORT")
|
||||
if sbPort != "" {
|
||||
host, _, err := net.SplitHostPort(config.Servicebound.Listen.Addr)
|
||||
host, _, err := net.SplitHostPort(conf.Servicebound.Listen.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Servicebound.Listen.Addr = net.JoinHostPort(host, sbPort)
|
||||
conf.Servicebound.Listen.Addr = net.JoinHostPort(host, sbPort)
|
||||
}
|
||||
ebPort := os.Getenv("FRONTIER_EDGEBOUND_PORT")
|
||||
if ebPort != "" {
|
||||
host, _, err := net.SplitHostPort(config.Edgebound.Listen.Addr)
|
||||
host, _, err := net.SplitHostPort(conf.Edgebound.Listen.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Edgebound.Listen.Addr = net.JoinHostPort(host, ebPort)
|
||||
conf.Edgebound.Listen.Addr = net.JoinHostPort(host, ebPort)
|
||||
}
|
||||
nodeName := os.Getenv("NODE_NAME")
|
||||
if nodeName != "" {
|
||||
config.Daemon.FrontierID = "frontier-" + nodeName
|
||||
conf.Daemon.FrontierID = "frontier-" + nodeName
|
||||
}
|
||||
frontlasAddr := os.Getenv("FRONTLAS_ADDR")
|
||||
if frontlasAddr != "" {
|
||||
config.Frontlas.Enable = true
|
||||
config.Frontlas.Dial.Addrs = []string{frontlasAddr}
|
||||
conf.Frontlas.Enable = true
|
||||
conf.Frontlas.Dial.Addrs = []string{frontlasAddr}
|
||||
}
|
||||
return config, nil
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func genAllConfig(writer io.Writer) error {
|
||||
conf := &Configuration{
|
||||
Log: config.Log{
|
||||
Level: "info",
|
||||
Output: "stdout",
|
||||
Format: "text",
|
||||
File: config.LogFile{
|
||||
Path: "/var/log/frontier/frontier.log",
|
||||
MaxSize: 100,
|
||||
MaxBackups: 5,
|
||||
MaxAge: 30,
|
||||
Compress: false,
|
||||
},
|
||||
},
|
||||
Daemon: Daemon{
|
||||
RLimit: RLimit{
|
||||
Enable: true,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/singchia/frontier/pkg/config"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -17,10 +18,24 @@ func TestParseFlags(t *testing.T) {
|
||||
{
|
||||
name: "tryrun",
|
||||
want: Configuration{
|
||||
Log: config.Log{
|
||||
Level: "info",
|
||||
Output: "stdout",
|
||||
Format: "text",
|
||||
File: config.LogFile{
|
||||
Path: "/var/log/frontier/frontier.log",
|
||||
MaxSize: 100,
|
||||
MaxBackups: 5,
|
||||
MaxAge: 30,
|
||||
},
|
||||
},
|
||||
Daemon: Daemon{
|
||||
RLimit: RLimit{
|
||||
NumFile: -1,
|
||||
},
|
||||
PProf: PProf{
|
||||
CPUProfileRate: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
|
||||
@@ -2,7 +2,6 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"github.com/singchia/frontier/pkg/config"
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// daemon related
|
||||
@@ -133,6 +131,8 @@ type FrontierManager struct {
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Log config.Log `yaml:"log,omitempty" json:"log"`
|
||||
|
||||
Daemon Daemon `yaml:"daemon" json:"daemon"`
|
||||
|
||||
ControlPlane ControlPlane `yaml:"control_plane" json:"control_plane"`
|
||||
@@ -145,62 +145,69 @@ type Configuration struct {
|
||||
func Parse() (*Configuration, error) {
|
||||
var (
|
||||
argConfigFile = pflag.String("config", "", "config file, default not configured")
|
||||
argLogLevel = pflag.String("loglevel", "", "log level: debug, info, warn, error")
|
||||
argLogOutput = pflag.String("log-output", "", "log output: stdout, stderr, file, both")
|
||||
argLogFormat = pflag.String("log-format", "", "log format: text, json")
|
||||
argLogFile = pflag.String("log-file", "", "log file path, used when output is file or both")
|
||||
argDaemonRLimitNofile = pflag.Int("daemon-rlimit-nofile", -1, "SetRLimit for number of file of this daemon, default: -1 means ignore")
|
||||
// TODO more command-line args
|
||||
|
||||
config *Configuration
|
||||
conf *Configuration
|
||||
)
|
||||
pflag.Lookup("daemon-rlimit-nofile").NoOptDefVal = "1048576"
|
||||
|
||||
// set klog
|
||||
klogFlags := flag.NewFlagSet("klog", flag.ExitOnError)
|
||||
klog.InitFlags(klogFlags)
|
||||
|
||||
// sync the glog and klog flags.
|
||||
pflag.CommandLine.VisitAll(func(f1 *pflag.Flag) {
|
||||
f2 := klogFlags.Lookup(f1.Name)
|
||||
if f2 != nil {
|
||||
value := f1.Value.String()
|
||||
if err := f2.Value.Set(value); err != nil {
|
||||
klog.Fatal(err, "failed to set flag")
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pflag.CommandLine.AddGoFlagSet(klogFlags)
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
pflag.Parse()
|
||||
|
||||
// config file
|
||||
if *argConfigFile != "" {
|
||||
// TODO the command-line is prior to config file
|
||||
data, err := os.ReadFile(*argConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config = &Configuration{}
|
||||
if err = yaml.Unmarshal(data, config); err != nil {
|
||||
conf = &Configuration{}
|
||||
if err = yaml.Unmarshal(data, conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = &Configuration{}
|
||||
if conf == nil {
|
||||
conf = &Configuration{}
|
||||
}
|
||||
|
||||
// env overrides for log (priority: flag > env > yaml > default)
|
||||
config.ApplyLogEnvOverrides(&conf.Log)
|
||||
|
||||
// flag overrides for log (highest priority)
|
||||
if *argLogLevel != "" {
|
||||
conf.Log.Level = *argLogLevel
|
||||
}
|
||||
if *argLogOutput != "" {
|
||||
conf.Log.Output = *argLogOutput
|
||||
}
|
||||
if *argLogFormat != "" {
|
||||
conf.Log.Format = *argLogFormat
|
||||
}
|
||||
if *argLogFile != "" {
|
||||
conf.Log.File.Path = *argLogFile
|
||||
}
|
||||
|
||||
// setup unified logging
|
||||
if err := config.SetupLogging(&conf.Log, "frontlas"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// daemon
|
||||
config.Daemon.RLimit.NumFile = *argDaemonRLimitNofile
|
||||
if config.Daemon.PProf.CPUProfileRate == 0 {
|
||||
config.Daemon.PProf.CPUProfileRate = 10000
|
||||
conf.Daemon.RLimit.NumFile = *argDaemonRLimitNofile
|
||||
if conf.Daemon.PProf.CPUProfileRate == 0 {
|
||||
conf.Daemon.PProf.CPUProfileRate = 10000
|
||||
}
|
||||
// env, set only exists
|
||||
// env overrides for network
|
||||
cpPort := os.Getenv("FRONTLAS_CONTROLPLANE_PORT")
|
||||
if cpPort != "" {
|
||||
host, _, err := net.SplitHostPort(config.ControlPlane.Listen.Addr)
|
||||
host, _, err := net.SplitHostPort(conf.ControlPlane.Listen.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.ControlPlane.Listen.Addr = net.JoinHostPort(host, cpPort)
|
||||
conf.ControlPlane.Listen.Addr = net.JoinHostPort(host, cpPort)
|
||||
}
|
||||
redisType := os.Getenv("REDIS_TYPE")
|
||||
redisAddrs := os.Getenv("REDIS_ADDRS")
|
||||
@@ -215,30 +222,42 @@ func Parse() (*Configuration, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Redis.Standalone.DB = db
|
||||
config.Redis.Standalone.Addr = addrs[0]
|
||||
config.Redis.Username = redisUser
|
||||
config.Redis.Password = redisPassword
|
||||
config.Redis.Mode = redisType
|
||||
conf.Redis.Standalone.DB = db
|
||||
conf.Redis.Standalone.Addr = addrs[0]
|
||||
conf.Redis.Username = redisUser
|
||||
conf.Redis.Password = redisPassword
|
||||
conf.Redis.Mode = redisType
|
||||
case "sentinel":
|
||||
addrs := strings.Split(redisAddrs, ",")
|
||||
config.Redis.Sentinel.Addrs = addrs
|
||||
config.Redis.Sentinel.MasterName = redisMasterName
|
||||
config.Redis.Username = redisUser
|
||||
config.Redis.Password = redisPassword
|
||||
config.Redis.Mode = redisType
|
||||
conf.Redis.Sentinel.Addrs = addrs
|
||||
conf.Redis.Sentinel.MasterName = redisMasterName
|
||||
conf.Redis.Username = redisUser
|
||||
conf.Redis.Password = redisPassword
|
||||
conf.Redis.Mode = redisType
|
||||
case "cluster":
|
||||
addrs := strings.Split(redisAddrs, ",")
|
||||
config.Redis.Cluster.Addrs = addrs
|
||||
config.Redis.Username = redisUser
|
||||
config.Redis.Password = redisPassword
|
||||
config.Redis.Mode = redisType
|
||||
conf.Redis.Cluster.Addrs = addrs
|
||||
conf.Redis.Username = redisUser
|
||||
conf.Redis.Password = redisPassword
|
||||
conf.Redis.Mode = redisType
|
||||
}
|
||||
return config, nil
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func genAllConfig(writer io.Writer) error {
|
||||
conf := &Configuration{
|
||||
Log: config.Log{
|
||||
Level: "info",
|
||||
Output: "stdout",
|
||||
Format: "text",
|
||||
File: config.LogFile{
|
||||
Path: "/var/log/frontier/frontlas.log",
|
||||
MaxSize: 100,
|
||||
MaxBackups: 5,
|
||||
MaxAge: 30,
|
||||
Compress: false,
|
||||
},
|
||||
},
|
||||
Daemon: Daemon{
|
||||
RLimit: RLimit{
|
||||
NumFile: 1024,
|
||||
|
||||
Reference in New Issue
Block a user