feat: add production deployment documentation and service management commands

- Introduced a new section in the README for Production Deployment, detailing prerequisites, installation steps, and service management commands.
- Added commands for starting, stopping, and restarting production services in the CLI, enhancing user control over service management.
- Updated IPFS initialization to configure API, Gateway, and Swarm addresses to avoid port conflicts, improving deployment reliability.
- Enhanced error handling and logging for service management operations, ensuring better feedback during execution.
This commit is contained in:
anonpenguin23 2025-11-11 09:00:45 +02:00
parent 52b3a99bb9
commit b0ac58af3e
No known key found for this signature in database
GPG Key ID: 1CBB1FE35AFBEE30
6 changed files with 465 additions and 3 deletions

View File

@ -13,6 +13,22 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant
### Deprecated
### Fixed
## [0.68.0] - 2025-11-11
### Added
- Added comprehensive documentation for production deployment, including installation, upgrade, service management, and troubleshooting.
- Added new CLI commands (`dbn prod start`, `dbn prod stop`, `dbn prod restart`) for convenient management of production systemd services.
### Changed
- Updated IPFS configuration during production installation to use port 4501 for the API (to avoid conflicts with RQLite on port 5001) and port 8080 for the Gateway.
### Deprecated
### Removed
### Fixed
- Ensured that IPFS configuration automatically disables AutoConf when a private swarm key is present during installation and upgrade, preventing startup errors.
## [0.67.7] - 2025-11-11
### Added

View File

@ -19,7 +19,7 @@ test-e2e:
.PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill
VERSION := 0.67.7
VERSION := 0.68.0
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'

281
README.md
View File

@ -6,6 +6,7 @@ DeBros Network is a decentralized peer-to-peer data platform built in Go. It com
- [At a Glance](#at-a-glance)
- [Quick Start](#quick-start)
- [Production Deployment](#production-deployment)
- [Components & Ports](#components--ports)
- [Configuration Cheatsheet](#configuration-cheatsheet)
- [CLI Highlights](#cli-highlights)
@ -54,6 +55,286 @@ DeBros Network is a decentralized peer-to-peer data platform built in Go. It com
./bin/dbn pubsub subscribe notifications 10s
```
## Production Deployment
DeBros Network can be deployed as production systemd services on Linux servers. The production installer handles all dependencies, configuration, and service management automatically.
### Prerequisites
- **OS**: Ubuntu 20.04+, Debian 11+, or compatible Linux distribution
- **Architecture**: `amd64` (x86_64) or `arm64` (aarch64)
- **Permissions**: Root access (use `sudo`)
- **Resources**: Minimum 2GB RAM, 10GB disk space, 2 CPU cores
### Installation
#### Quick Install
Install the CLI tool first:
```bash
curl -fsSL https://install.debros.network | sudo bash
```
Or download manually from [GitHub Releases](https://github.com/DeBrosOfficial/network/releases).
#### Bootstrap Node (First Node)
Install the first node in your cluster:
```bash
# Main branch (stable releases)
sudo dbn prod install --bootstrap
# Nightly branch (latest development)
sudo dbn prod install --bootstrap --branch nightly
```
The bootstrap node initializes the cluster and serves as the primary peer for other nodes to join.
#### Secondary Node (Join Existing Cluster)
Join an existing cluster by providing the bootstrap node's IP and peer multiaddr:
```bash
sudo dbn prod install \
--vps-ip <your_public_ip> \
--peers /ip4/<bootstrap_ip>/tcp/4001/p2p/<peer_id> \
--branch nightly
```
**Required flags for secondary nodes:**
- `--vps-ip`: Your server's public IP address
- `--peers`: Comma-separated list of bootstrap peer multiaddrs
**Optional flags:**
- `--branch`: Git branch to use (`main` or `nightly`, default: `main`)
- `--domain`: Domain name for HTTPS (enables ACME/Let's Encrypt)
- `--bootstrap-join`: Raft join address for secondary bootstrap nodes
#### Secondary Bootstrap Node
Create a secondary bootstrap node that joins an existing Raft cluster:
```bash
sudo dbn prod install \
--bootstrap \
--vps-ip <your_public_ip> \
--bootstrap-join <primary_bootstrap_ip>:7001 \
--branch nightly
```
### Branch Selection
DeBros Network supports two branches:
- **`main`**: Stable releases (default). Recommended for production.
- **`nightly`**: Latest development builds. Use for testing new features.
**Branch preference is saved automatically** during installation. Future upgrades will use the same branch unless you override it with `--branch`.
**Examples:**
```bash
# Install with nightly branch
sudo dbn prod install --bootstrap --branch nightly
# Upgrade using saved branch preference
sudo dbn prod upgrade --restart
# Upgrade and switch to main branch
sudo dbn prod upgrade --restart --branch main
```
### Upgrade
Upgrade an existing installation to the latest version:
```bash
# Upgrade using saved branch preference
sudo dbn prod upgrade --restart
# Upgrade and switch branches
sudo dbn prod upgrade --restart --branch nightly
# Upgrade without restarting services
sudo dbn prod upgrade
```
The upgrade process:
1. ✅ Checks prerequisites
2. ✅ Updates binaries (fetches latest from selected branch)
3. ✅ Preserves existing configurations and data
4. ✅ Updates configurations to latest format
5. ✅ Updates systemd service files
6. ✅ Optionally restarts services (`--restart` flag)
**Note**: The upgrade automatically detects your node type (bootstrap vs. regular node) and preserves all secrets, data, and configurations.
### Service Management
All services run as systemd units under the `debros` user.
#### Check Status
```bash
# View status of all services
dbn prod status
# Or use systemctl directly
systemctl status debros-node-bootstrap
systemctl status debros-ipfs-bootstrap
systemctl status debros-gateway
```
#### View Logs
```bash
# View recent logs
dbn prod logs node
# Follow logs in real-time
dbn prod logs node --follow
# View specific service logs
dbn prod logs ipfs --follow
dbn prod logs gateway --follow
```
Available log targets: `node`, `ipfs`, `ipfs-cluster`, `rqlite`, `olric`, `gateway`
#### Service Control Commands
Use `dbn prod` commands for convenient service management:
```bash
# Start all services
sudo dbn prod start
# Stop all services
sudo dbn prod stop
# Restart all services
sudo dbn prod restart
```
Or use `systemctl` directly for more control:
```bash
# Restart all services
sudo systemctl restart debros-*
# Restart specific service
sudo systemctl restart debros-node-bootstrap
# Stop services
sudo systemctl stop debros-*
# Start services
sudo systemctl start debros-*
# Enable services (start on boot)
sudo systemctl enable debros-*
```
### Directory Structure
Production installations use `/home/debros/.debros/`:
```
/home/debros/.debros/
├── configs/ # Configuration files
│ ├── bootstrap.yaml # Bootstrap node config
│ ├── node.yaml # Regular node config
│ ├── gateway.yaml # Gateway config
│ └── olric/ # Olric cache config
├── data/ # Runtime data
│ ├── bootstrap/ # Bootstrap node data
│ │ ├── ipfs/ # IPFS repository
│ │ ├── ipfs-cluster/ # IPFS Cluster data
│ │ └── rqlite/ # RQLite database
│ └── node/ # Regular node data
├── secrets/ # Secrets and keys
│ ├── cluster-secret # IPFS Cluster secret
│ └── swarm.key # IPFS swarm key
├── logs/ # Service logs
│ ├── node-bootstrap.log
│ ├── ipfs-bootstrap.log
│ └── gateway.log
└── .branch # Saved branch preference
```
### Uninstall
Remove all production services (preserves data and configs):
```bash
sudo dbn prod uninstall
```
This stops and removes all systemd services but keeps `/home/debros/.debros/` intact. To completely remove:
```bash
sudo dbn prod uninstall
sudo rm -rf /home/debros/.debros
```
### Production Troubleshooting
#### Services Not Starting
```bash
# Check service status
systemctl status debros-node-bootstrap
# View detailed logs
journalctl -u debros-node-bootstrap -n 100
# Check log files
tail -f /home/debros/.debros/logs/node-bootstrap.log
```
#### Configuration Issues
```bash
# Verify configs exist
ls -la /home/debros/.debros/configs/
# Regenerate configs (preserves secrets)
sudo dbn prod upgrade --restart
```
#### IPFS AutoConf Errors
If you see "AutoConf.Enabled=false but 'auto' placeholder is used" errors, the upgrade process should fix this automatically. If not:
```bash
# Re-run upgrade to fix IPFS config
sudo dbn prod upgrade --restart
```
#### Port Conflicts
```bash
# Check what's using ports
sudo lsof -i :4001 # P2P port
sudo lsof -i :5001 # RQLite HTTP
sudo lsof -i :6001 # Gateway
```
#### Reset Installation
To start fresh (⚠️ **destroys all data**):
```bash
sudo dbn prod uninstall
sudo rm -rf /home/debros/.debros
sudo dbn prod install --bootstrap --branch nightly
```
## Components & Ports
- **Bootstrap node**: P2P `4001`, RQLite HTTP `5001`, Raft `7001`

View File

@ -28,6 +28,12 @@ func HandleProdCommand(args []string) {
handleProdUpgrade(subargs)
case "status":
handleProdStatus()
case "start":
handleProdStart()
case "stop":
handleProdStop()
case "restart":
handleProdRestart()
case "logs":
handleProdLogs(subargs)
case "uninstall":
@ -59,6 +65,9 @@ func showProdHelp() {
fmt.Printf(" --restart - Automatically restart services after upgrade\n")
fmt.Printf(" --branch BRANCH - Git branch to use (main or nightly, uses saved preference if not specified)\n")
fmt.Printf(" status - Show status of production services\n")
fmt.Printf(" start - Start all production services (requires root/sudo)\n")
fmt.Printf(" stop - Stop all production services (requires root/sudo)\n")
fmt.Printf(" restart - Restart all production services (requires root/sudo)\n")
fmt.Printf(" logs <service> - View production service logs\n")
fmt.Printf(" Options:\n")
fmt.Printf(" --follow - Follow logs in real-time\n")
@ -76,6 +85,10 @@ func showProdHelp() {
fmt.Printf(" sudo dbn prod upgrade --restart\n\n")
fmt.Printf(" # Upgrade and switch to nightly branch\n")
fmt.Printf(" sudo dbn prod upgrade --restart --branch nightly\n\n")
fmt.Printf(" # Service management\n")
fmt.Printf(" sudo dbn prod start\n")
fmt.Printf(" sudo dbn prod stop\n")
fmt.Printf(" sudo dbn prod restart\n\n")
fmt.Printf(" dbn prod status\n")
fmt.Printf(" dbn prod logs node --follow\n")
}
@ -452,6 +465,112 @@ func handleProdLogs(args []string) {
}
}
// getProductionServices returns a list of all DeBros production service names that exist
func getProductionServices() []string {
// All possible service names (both bootstrap and node variants)
allServices := []string{
"debros-gateway",
"debros-node-node",
"debros-node-bootstrap",
"debros-olric",
"debros-rqlite-bootstrap",
"debros-rqlite-node",
"debros-ipfs-cluster-bootstrap",
"debros-ipfs-cluster-node",
"debros-ipfs-bootstrap",
"debros-ipfs-node",
}
// Filter to only existing services by checking if unit file exists
var existing []string
for _, svc := range allServices {
unitPath := filepath.Join("/etc/systemd/system", svc+".service")
if _, err := os.Stat(unitPath); err == nil {
existing = append(existing, svc)
}
}
return existing
}
func handleProdStart() {
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "❌ Production commands must be run as root (use sudo)\n")
os.Exit(1)
}
fmt.Printf("Starting all DeBros production services...\n")
services := getProductionServices()
if len(services) == 0 {
fmt.Printf(" ⚠️ No DeBros services found\n")
return
}
for _, svc := range services {
cmd := exec.Command("systemctl", "start", svc)
if err := cmd.Run(); err != nil {
fmt.Printf(" ⚠️ Failed to start %s: %v\n", svc, err)
} else {
fmt.Printf(" ✓ Started %s\n", svc)
}
}
fmt.Printf("\n✅ All services started\n")
}
func handleProdStop() {
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "❌ Production commands must be run as root (use sudo)\n")
os.Exit(1)
}
fmt.Printf("Stopping all DeBros production services...\n")
services := getProductionServices()
if len(services) == 0 {
fmt.Printf(" ⚠️ No DeBros services found\n")
return
}
for _, svc := range services {
cmd := exec.Command("systemctl", "stop", svc)
if err := cmd.Run(); err != nil {
fmt.Printf(" ⚠️ Failed to stop %s: %v\n", svc, err)
} else {
fmt.Printf(" ✓ Stopped %s\n", svc)
}
}
fmt.Printf("\n✅ All services stopped\n")
}
func handleProdRestart() {
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "❌ Production commands must be run as root (use sudo)\n")
os.Exit(1)
}
fmt.Printf("Restarting all DeBros production services...\n")
services := getProductionServices()
if len(services) == 0 {
fmt.Printf(" ⚠️ No DeBros services found\n")
return
}
for _, svc := range services {
cmd := exec.Command("systemctl", "restart", svc)
if err := cmd.Run(); err != nil {
fmt.Printf(" ⚠️ Failed to restart %s: %v\n", svc, err)
} else {
fmt.Printf(" ✓ Restarted %s\n", svc)
}
}
fmt.Printf("\n✅ All services restarted\n")
}
func handleProdUninstall() {
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "❌ Production uninstall must be run as root (use sudo)\n")

View File

@ -355,7 +355,7 @@ func (bi *BinaryInstaller) InstallSystemDependencies() error {
}
// InitializeIPFSRepo initializes an IPFS repository for a node
func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swarmKeyPath string) error {
func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swarmKeyPath string, apiPort, gatewayPort, swarmPort int) error {
configPath := filepath.Join(ipfsRepoPath, "config")
repoExists := false
if _, err := os.Stat(configPath); err == nil {
@ -393,6 +393,13 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
swarmKeyExists = true
}
// Configure IPFS addresses (API, Gateway, Swarm) by modifying the config file directly
// This ensures the ports are set correctly and avoids conflicts with RQLite on port 5001
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Configuring IPFS addresses (API: %d, Gateway: %d, Swarm: %d)...\n", apiPort, gatewayPort, swarmPort)
if err := bi.configureIPFSAddresses(ipfsRepoPath, apiPort, gatewayPort, swarmPort); err != nil {
return fmt.Errorf("failed to configure IPFS addresses: %w", err)
}
// Always disable AutoConf for private swarm when swarm.key is present
// This is critical - IPFS will fail to start if AutoConf is enabled on a private network
// We do this even for existing repos to fix repos initialized before this fix was applied
@ -437,6 +444,44 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
return nil
}
// configureIPFSAddresses configures the IPFS API, Gateway, and Swarm addresses in the config file
func (bi *BinaryInstaller) configureIPFSAddresses(ipfsRepoPath string, apiPort, gatewayPort, swarmPort int) error {
configPath := filepath.Join(ipfsRepoPath, "config")
// Read existing config
data, err := os.ReadFile(configPath)
if err != nil {
return fmt.Errorf("failed to read IPFS config: %w", err)
}
var config map[string]interface{}
if err := json.Unmarshal(data, &config); err != nil {
return fmt.Errorf("failed to parse IPFS config: %w", err)
}
// Set Addresses
config["Addresses"] = map[string]interface{}{
"API": []string{fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", apiPort)},
"Gateway": []string{fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", gatewayPort)},
"Swarm": []string{
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", swarmPort),
fmt.Sprintf("/ip6/::/tcp/%d", swarmPort),
},
}
// Write config back
updatedData, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal IPFS config: %w", err)
}
if err := os.WriteFile(configPath, updatedData, 0600); err != nil {
return fmt.Errorf("failed to write IPFS config: %w", err)
}
return nil
}
// InitializeIPFSClusterConfig initializes IPFS Cluster configuration
// This runs `ipfs-cluster-service init` to create the service.json configuration file.
// For existing installations, it ensures the cluster secret is up to date.

View File

@ -263,8 +263,9 @@ func (ps *ProductionSetup) Phase2cInitializeServices(nodeType string) error {
dataDir := filepath.Join(ps.debrosDir, "data", nodeType)
// Initialize IPFS repo with correct path structure
// Use port 4501 for API (to avoid conflict with RQLite on 5001), 8080 for gateway (standard), 4001 for swarm
ipfsRepoPath := filepath.Join(dataDir, "ipfs", "repo")
if err := ps.binaryInstaller.InitializeIPFSRepo(nodeType, ipfsRepoPath, filepath.Join(ps.debrosDir, "secrets", "swarm.key")); err != nil {
if err := ps.binaryInstaller.InitializeIPFSRepo(nodeType, ipfsRepoPath, filepath.Join(ps.debrosDir, "secrets", "swarm.key"), 4501, 8080, 4001); err != nil {
return fmt.Errorf("failed to initialize IPFS repo: %w", err)
}