Browse Source
Nostr-Signature: d089915a2d9a9d46ba25d2d3c1cb4608a2b658ecc4260f17e73efa4ccc63a28d 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 3d447f05a55704d45ed843b7cc5fa16e49f3da0e452b1523392aefbb7a2ae3e79400a763df5705db8e38abc89e9a89480ab2c529890b531b171c4e980520d9b8main
15 changed files with 834 additions and 386 deletions
@ -0,0 +1,221 @@ |
|||||||
|
# Enterprise Mode Setup Guide |
||||||
|
|
||||||
|
Enterprise mode provides complete isolation between tenants using Kubernetes. Each tenant (user/npub) gets their own container, namespace, and persistent volume. |
||||||
|
|
||||||
|
## Quick Start |
||||||
|
|
||||||
|
### Prerequisites |
||||||
|
|
||||||
|
1. **Kubernetes cluster** (minikube, kind, or production cluster) |
||||||
|
2. **kubectl** configured to access your cluster |
||||||
|
3. **envsubst** (usually comes with `gettext` package) |
||||||
|
4. **Ingress controller** (nginx-ingress recommended) |
||||||
|
|
||||||
|
### Enable Enterprise Mode |
||||||
|
|
||||||
|
Set the `ENTERPRISE_MODE` environment variable to `true`: |
||||||
|
|
||||||
|
```bash |
||||||
|
export ENTERPRISE_MODE=true |
||||||
|
``` |
||||||
|
|
||||||
|
**Default**: `false` (lightweight mode - single container) |
||||||
|
|
||||||
|
### Deploy a Tenant |
||||||
|
|
||||||
|
```bash |
||||||
|
cd k8s |
||||||
|
./deploy-tenant.sh npub1abc123... \ |
||||||
|
--domain git.example.com \ |
||||||
|
--storage-class fast-ssd \ |
||||||
|
--storage-size 50Gi |
||||||
|
``` |
||||||
|
|
||||||
|
### Check Status |
||||||
|
|
||||||
|
```bash |
||||||
|
# List all tenant namespaces |
||||||
|
kubectl get namespaces | grep gitrepublic-tenant |
||||||
|
|
||||||
|
# Check tenant resources |
||||||
|
kubectl get all -n gitrepublic-tenant-<npub> |
||||||
|
|
||||||
|
# View logs |
||||||
|
kubectl logs -n gitrepublic-tenant-<npub> -l app=gitrepublic -f |
||||||
|
``` |
||||||
|
|
||||||
|
### Delete a Tenant |
||||||
|
|
||||||
|
```bash |
||||||
|
./delete-tenant.sh npub1abc123... |
||||||
|
``` |
||||||
|
|
||||||
|
## Architecture |
||||||
|
|
||||||
|
In Enterprise Mode: |
||||||
|
|
||||||
|
``` |
||||||
|
Kubernetes Cluster |
||||||
|
├── Namespace: gitrepublic-tenant-npub1 |
||||||
|
│ ├── Deployment (1 pod) |
||||||
|
│ │ └── Container: gitrepublic-web |
||||||
|
│ ├── Service (ClusterIP) |
||||||
|
│ ├── PersistentVolumeClaim (100Gi) |
||||||
|
│ ├── ResourceQuota |
||||||
|
│ ├── NetworkPolicy |
||||||
|
│ └── Ingress |
||||||
|
│ |
||||||
|
├── Namespace: gitrepublic-tenant-npub2 |
||||||
|
│ └── (same structure) |
||||||
|
│ |
||||||
|
└── Namespace: gitrepublic-tenant-npub3 |
||||||
|
└── (same structure) |
||||||
|
``` |
||||||
|
|
||||||
|
## Configuration |
||||||
|
|
||||||
|
### Environment Variables |
||||||
|
|
||||||
|
| Variable | Description | Default | |
||||||
|
|----------|-------------|---------| |
||||||
|
| `ENTERPRISE_MODE` | Enable enterprise mode | `false` | |
||||||
|
| `GIT_DOMAIN` | Domain for git repositories | `localhost:6543` | |
||||||
|
| `NOSTR_RELAYS` | Comma-separated Nostr relays | `wss://theforest.nostr1.com,...` | |
||||||
|
| `STORAGE_CLASS` | Kubernetes storage class | `standard` | |
||||||
|
| `STORAGE_SIZE` | Volume size per tenant | `100Gi` | |
||||||
|
|
||||||
|
### Resource Limits (per tenant) |
||||||
|
|
||||||
|
Default limits in `resource-quota.yaml`: |
||||||
|
- **CPU**: 2 requests, 4 limits |
||||||
|
- **Memory**: 2Gi requests, 4Gi limits |
||||||
|
- **Storage**: 100Gi requests, 200Gi limits |
||||||
|
- **Pods**: 2 max |
||||||
|
|
||||||
|
Adjust in `k8s/base/resource-quota.yaml` as needed. |
||||||
|
|
||||||
|
## Ingress Configuration |
||||||
|
|
||||||
|
Each tenant can have: |
||||||
|
- **Subdomain**: `user1.git.example.com` |
||||||
|
- **Path-based**: `git.example.com/npub1abc123...` |
||||||
|
|
||||||
|
Update `k8s/base/ingress.yaml` for your routing strategy. |
||||||
|
|
||||||
|
### SSL/TLS |
||||||
|
|
||||||
|
To enable HTTPS, uncomment the TLS section in `ingress.yaml` and configure cert-manager: |
||||||
|
|
||||||
|
```yaml |
||||||
|
tls: |
||||||
|
- hosts: |
||||||
|
- ${TENANT_SUBDOMAIN}.${GIT_DOMAIN} |
||||||
|
secretName: gitrepublic-tls |
||||||
|
``` |
||||||
|
|
||||||
|
## Network Isolation |
||||||
|
|
||||||
|
Network policies prevent: |
||||||
|
- Inter-tenant communication |
||||||
|
- Unauthorized ingress |
||||||
|
- Unnecessary egress |
||||||
|
|
||||||
|
Only allows: |
||||||
|
- Ingress from ingress controller |
||||||
|
- Egress to Nostr relays (WSS on port 443) |
||||||
|
- DNS queries |
||||||
|
|
||||||
|
## Storage |
||||||
|
|
||||||
|
Each tenant gets: |
||||||
|
- **Own PersistentVolume**: Complete isolation |
||||||
|
- **Size limits**: Configurable per tenant |
||||||
|
- **Storage class**: Can use fast SSD for performance |
||||||
|
|
||||||
|
### Backup |
||||||
|
|
||||||
|
Use Kubernetes VolumeSnapshots: |
||||||
|
|
||||||
|
```bash |
||||||
|
kubectl create volumesnapshot gitrepublic-snapshot-$(date +%Y%m%d) \ |
||||||
|
--namespace gitrepublic-tenant-<npub> \ |
||||||
|
--source-pvc gitrepublic-repos |
||||||
|
``` |
||||||
|
|
||||||
|
## Monitoring |
||||||
|
|
||||||
|
### View Resource Usage |
||||||
|
|
||||||
|
```bash |
||||||
|
# CPU and memory usage |
||||||
|
kubectl top pods -n gitrepublic-tenant-<npub> |
||||||
|
|
||||||
|
# Storage usage |
||||||
|
kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant-<npub> |
||||||
|
``` |
||||||
|
|
||||||
|
### Logs |
||||||
|
|
||||||
|
```bash |
||||||
|
# Application logs |
||||||
|
kubectl logs -n gitrepublic-tenant-<npub> -l app=gitrepublic |
||||||
|
|
||||||
|
# All resources in namespace |
||||||
|
kubectl get all -n gitrepublic-tenant-<npub> |
||||||
|
``` |
||||||
|
|
||||||
|
## Troubleshooting |
||||||
|
|
||||||
|
### Pod Not Starting |
||||||
|
|
||||||
|
```bash |
||||||
|
# Check pod status |
||||||
|
kubectl describe pod -n gitrepublic-tenant-<npub> -l app=gitrepublic |
||||||
|
|
||||||
|
# Check events |
||||||
|
kubectl get events -n gitrepublic-tenant-<npub> --sort-by='.lastTimestamp' |
||||||
|
``` |
||||||
|
|
||||||
|
### Volume Issues |
||||||
|
|
||||||
|
```bash |
||||||
|
# Check PVC status |
||||||
|
kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant-<npub> |
||||||
|
|
||||||
|
# Check storage class |
||||||
|
kubectl get storageclass |
||||||
|
``` |
||||||
|
|
||||||
|
### Network Issues |
||||||
|
|
||||||
|
```bash |
||||||
|
# Test connectivity from pod |
||||||
|
kubectl exec -n gitrepublic-tenant-<npub> -l app=gitrepublic -- curl -I https://theforest.nostr1.com |
||||||
|
``` |
||||||
|
|
||||||
|
## Migration from Lightweight Mode |
||||||
|
|
||||||
|
1. **Backup repositories** from lightweight mode |
||||||
|
2. **Deploy tenant** in enterprise mode |
||||||
|
3. **Restore data** to new volume |
||||||
|
4. **Update DNS** to point to new ingress |
||||||
|
5. **Verify** all operations work |
||||||
|
6. **Decommission** old lightweight container |
||||||
|
|
||||||
|
## Cost Considerations |
||||||
|
|
||||||
|
Enterprise mode uses more resources: |
||||||
|
- **Per-tenant overhead**: ~500MB RAM, 0.5 CPU per tenant |
||||||
|
- **Storage**: Separate volumes (can't share unused space) |
||||||
|
- **Network**: More complex routing |
||||||
|
|
||||||
|
**Recommendation**: Use enterprise mode for: |
||||||
|
- High-value tenants |
||||||
|
- Security-sensitive deployments |
||||||
|
- Compliance requirements |
||||||
|
- Large-scale deployments |
||||||
|
|
||||||
|
Use lightweight mode for: |
||||||
|
- Development/testing |
||||||
|
- Small deployments |
||||||
|
- Cost-sensitive scenarios |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
# Ingress for gitrepublic tenant |
||||||
|
# Routes external traffic to the tenant's service |
||||||
|
# Each tenant can have their own subdomain or path-based routing |
||||||
|
|
||||||
|
apiVersion: networking.k8s.io/v1 |
||||||
|
kind: Ingress |
||||||
|
metadata: |
||||||
|
name: gitrepublic-ingress |
||||||
|
namespace: gitrepublic-tenant-${TENANT_ID} |
||||||
|
annotations: |
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: / |
||||||
|
# Optional: SSL/TLS configuration |
||||||
|
# cert-manager.io/cluster-issuer: "letsencrypt-prod" |
||||||
|
spec: |
||||||
|
ingressClassName: nginx |
||||||
|
rules: |
||||||
|
- host: ${TENANT_SUBDOMAIN}.${GIT_DOMAIN} |
||||||
|
http: |
||||||
|
paths: |
||||||
|
- path: / |
||||||
|
pathType: Prefix |
||||||
|
backend: |
||||||
|
service: |
||||||
|
name: gitrepublic |
||||||
|
port: |
||||||
|
number: 80 |
||||||
|
# Optional: TLS configuration |
||||||
|
# tls: |
||||||
|
# - hosts: |
||||||
|
# - ${TENANT_SUBDOMAIN}.${GIT_DOMAIN} |
||||||
|
# secretName: gitrepublic-tls |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# Delete a tenant from Kubernetes |
||||||
|
# Usage: ./delete-tenant.sh <tenant-npub> |
||||||
|
|
||||||
|
set -e |
||||||
|
|
||||||
|
# Colors for output |
||||||
|
RED='\033[0;31m' |
||||||
|
GREEN='\033[0;32m' |
||||||
|
YELLOW='\033[1;33m' |
||||||
|
NC='\033[0m' # No Color |
||||||
|
|
||||||
|
# Check if tenant ID is provided |
||||||
|
if [ -z "$1" ]; then |
||||||
|
echo -e "${RED}Error: Tenant ID (npub) is required${NC}" |
||||||
|
echo "Usage: $0 <tenant-npub>" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
TENANT_ID="$1" |
||||||
|
|
||||||
|
# Validate tenant ID format (basic check) |
||||||
|
if [[ ! "$TENANT_ID" =~ ^npub1[a-z0-9]+$ ]]; then |
||||||
|
echo -e "${YELLOW}Warning: Tenant ID doesn't look like a valid npub format${NC}" |
||||||
|
read -p "Continue anyway? (y/N) " -n 1 -r |
||||||
|
echo |
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
NAMESPACE="gitrepublic-tenant-${TENANT_ID}" |
||||||
|
|
||||||
|
# Check if namespace exists |
||||||
|
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then |
||||||
|
echo -e "${RED}Error: Namespace ${NAMESPACE} does not exist${NC}" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
echo -e "${YELLOW}WARNING: This will delete the entire tenant namespace and all resources!${NC}" |
||||||
|
echo " Namespace: ${NAMESPACE}" |
||||||
|
echo " This includes:" |
||||||
|
echo " - All pods and containers" |
||||||
|
echo " - Persistent volumes (data will be lost unless backed up)" |
||||||
|
echo " - All configuration" |
||||||
|
echo "" |
||||||
|
read -p "Are you sure you want to delete this tenant? (yes/NO) " -r |
||||||
|
echo |
||||||
|
|
||||||
|
if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then |
||||||
|
echo "Cancelled." |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
echo -e "${GREEN}Deleting tenant namespace: ${NAMESPACE}${NC}" |
||||||
|
kubectl delete namespace "$NAMESPACE" --wait=true |
||||||
|
|
||||||
|
echo "" |
||||||
|
echo -e "${GREEN}✓ Tenant deleted successfully!${NC}" |
||||||
@ -0,0 +1,138 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# Deploy a tenant to Kubernetes in enterprise mode |
||||||
|
# Usage: ./deploy-tenant.sh <tenant-npub> [options] |
||||||
|
|
||||||
|
set -e |
||||||
|
|
||||||
|
# Colors for output |
||||||
|
RED='\033[0;31m' |
||||||
|
GREEN='\033[0;32m' |
||||||
|
YELLOW='\033[1;33m' |
||||||
|
NC='\033[0m' # No Color |
||||||
|
|
||||||
|
# Check if tenant ID is provided |
||||||
|
if [ -z "$1" ]; then |
||||||
|
echo -e "${RED}Error: Tenant ID (npub) is required${NC}" |
||||||
|
echo "Usage: $0 <tenant-npub> [--domain <domain>] [--storage-class <class>] [--storage-size <size>] [--subdomain <subdomain>]" |
||||||
|
echo "" |
||||||
|
echo "Example:" |
||||||
|
echo " $0 npub1abc123... --domain git.example.com --storage-class fast-ssd --storage-size 50Gi" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
TENANT_ID="$1" |
||||||
|
shift |
||||||
|
|
||||||
|
# Default values |
||||||
|
GIT_DOMAIN="${GIT_DOMAIN:-git.example.com}" |
||||||
|
STORAGE_CLASS="${STORAGE_CLASS:-standard}" |
||||||
|
STORAGE_SIZE="${STORAGE_SIZE:-100Gi}" |
||||||
|
TENANT_SUBDOMAIN="${TENANT_SUBDOMAIN:-${TENANT_ID:0:16}}" |
||||||
|
NOSTR_RELAYS="${NOSTR_RELAYS:-wss://theforest.nostr1.com,wss://nostr.land}" |
||||||
|
|
||||||
|
# Parse arguments |
||||||
|
while [[ $# -gt 0 ]]; do |
||||||
|
case $1 in |
||||||
|
--domain) |
||||||
|
GIT_DOMAIN="$2" |
||||||
|
shift 2 |
||||||
|
;; |
||||||
|
--storage-class) |
||||||
|
STORAGE_CLASS="$2" |
||||||
|
shift 2 |
||||||
|
;; |
||||||
|
--storage-size) |
||||||
|
STORAGE_SIZE="$2" |
||||||
|
shift 2 |
||||||
|
;; |
||||||
|
--subdomain) |
||||||
|
TENANT_SUBDOMAIN="$2" |
||||||
|
shift 2 |
||||||
|
;; |
||||||
|
--relays) |
||||||
|
NOSTR_RELAYS="$2" |
||||||
|
shift 2 |
||||||
|
;; |
||||||
|
*) |
||||||
|
echo -e "${RED}Unknown option: $1${NC}" |
||||||
|
exit 1 |
||||||
|
;; |
||||||
|
esac |
||||||
|
done |
||||||
|
|
||||||
|
# Validate tenant ID format (basic check) |
||||||
|
if [[ ! "$TENANT_ID" =~ ^npub1[a-z0-9]+$ ]]; then |
||||||
|
echo -e "${YELLOW}Warning: Tenant ID doesn't look like a valid npub format${NC}" |
||||||
|
read -p "Continue anyway? (y/N) " -n 1 -r |
||||||
|
echo |
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
echo -e "${GREEN}Deploying tenant: ${TENANT_ID}${NC}" |
||||||
|
echo " Domain: ${GIT_DOMAIN}" |
||||||
|
echo " Subdomain: ${TENANT_SUBDOMAIN}" |
||||||
|
echo " Storage Class: ${STORAGE_CLASS}" |
||||||
|
echo " Storage Size: ${STORAGE_SIZE}" |
||||||
|
echo "" |
||||||
|
|
||||||
|
# Export variables for envsubst |
||||||
|
export TENANT_ID |
||||||
|
export GIT_DOMAIN |
||||||
|
export STORAGE_CLASS |
||||||
|
export STORAGE_SIZE |
||||||
|
export TENANT_SUBDOMAIN |
||||||
|
export NOSTR_RELAYS |
||||||
|
|
||||||
|
# Check if kubectl is available |
||||||
|
if ! command -v kubectl &> /dev/null; then |
||||||
|
echo -e "${RED}Error: kubectl is not installed or not in PATH${NC}" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
# Check if we can connect to cluster |
||||||
|
if ! kubectl cluster-info &> /dev/null; then |
||||||
|
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
# Get the directory where this script is located |
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
||||||
|
BASE_DIR="${SCRIPT_DIR}/base" |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating namespace...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/namespace.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating resource quota...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/resource-quota.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating limit range...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/limit-range.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating persistent volume claim...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/pvc.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating deployment...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/deployment.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating service...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/service.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating network policy...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/network-policy.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo -e "${GREEN}Creating ingress...${NC}" |
||||||
|
envsubst < "${BASE_DIR}/ingress.yaml" | kubectl apply -f - |
||||||
|
|
||||||
|
echo "" |
||||||
|
echo -e "${GREEN}✓ Tenant deployed successfully!${NC}" |
||||||
|
echo "" |
||||||
|
echo "To check status:" |
||||||
|
echo " kubectl get all -n gitrepublic-tenant-${TENANT_ID}" |
||||||
|
echo "" |
||||||
|
echo "To view logs:" |
||||||
|
echo " kubectl logs -n gitrepublic-tenant-${TENANT_ID} -l app=gitrepublic" |
||||||
|
echo "" |
||||||
|
echo "To delete tenant:" |
||||||
|
echo " kubectl delete namespace gitrepublic-tenant-${TENANT_ID}" |
||||||
Loading…
Reference in new issue