בניית פלטפורמת NaaS: אורקסטרציית צמתים, K8s, חיוב

כאשר מספר צמתי הבלוקצ'יין מגיע למאות, ניהול ידני הופך לכאוס והתשתית הופכת לצוואר בקבוק עסקי. אנחנו בונים פלטפורמות NaaS במפתח מלא: מאורכסטרציית צמתים על Kubernetes ועד חיוב ופרוקסי RPC. הצוות שלנו מטפל בכל המחזור—מביקורת ועד תמיכה—ומספק פתרון אמין שגדל עם המשימות שלכם.

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

בניית פלטפורמת NaaS: ניהול צמתים, K8s, חיוב

הפעלת צומת בלוקצ'יין ידנית היא משימה פשוטה עבור צומת בודד. כשיש לך מאה, זה הופך לפרויקט תשתית עם K8s, StatefulSet, אתחול מסנפשוט וחיוב. אנו מתמחים בפיתוח Node-as-a-Service סוהר ובנינו כמה פלטפורמות כאלה: מבחירת לקוחות (Ethereum, Solana, BNB) ועד API Gateway ברמת ייצור עם הגבלת קצב ויחידות מחשוב. בפועל: לקוח אחד מ-Fortune 500 החליף ניהול צמתים ידני ב-NaaS—עלויות התשתית ירדו ב-40%, חיסכון של $15,000 בחודש, ויותר מ-$180,000 בשנה. צור קשר—נעריך את הפרויקט שלך תוך יומיים.

איך פלטפורמת Node-as-a-Service עובדת?

פלטפורמת NaaS מספקת ללקוחות נקודת קצה RPC אחת המגובה באורקסטרציה של עשרות או מאות צמתים. כל צומת רץ ב-K8s כ-StatefulSet עם PersistentVolumeClaim משלו. לפלח התקציב, צמתים משותפים בין לקוחות (shared); ללקוחות תובעניים, הם מוקדשים לחלוטין (dedicated) או פרוסים באשכולות עם איזון עומסים (node clusters).

למה Kubernetes סטנדרטי לא מתאים לצמתי בלוקצ'יין?

Deployment רגיל ב-K8s לא מתחשב במאפייני צומת בלוקצ'יין, ולכן Kubernetes לתשתית בלוקצ'יין דורש StatefulSet. צמתים זקוקים לאחסון מצבי (מאות גיגה-בייט), פורטים P2P קבועים, והגנה מפני אתחולים ללא אובדן סנכרון. אנו משתמשים ב-StatefulSet עם PVC ושירות headless—זה מבטיח שבמקרה של תקלה ה-pod לא נוצר מחדש על צומת אחר, והנתונים נשארים קשורים לאחסון.

דוגמה לתצורת StatefulSet עבור צומת Ethereum:

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: ethereum-geth
spec:
  serviceName: "geth"
  replicas: 1
  selector:
    matchLabels:
      app: ethereum-geth
  template:
    spec:
      containers:
        - name: geth
          image: ethereum/client-go:v1.13.14
          args: ["--datadir=/data", "--http", "--http.addr=0.0.0.0", "--http.vhosts=*", "--http.api=eth,net,web3,txpool", "--ws", "--ws.addr=0.0.0.0", "--maxpeers=50", "--cache=4096"]
          ports:
            - containerPort: 8545
            - containerPort: 8546
            - containerPort: 30303
              protocol: TCP
            - containerPort: 30303
              protocol: UDP
          volumeMounts:
            - name: data
              mountPath: /data
          resources:
            requests:
              memory: "16Gi"
              cpu: "4"
            limits:
              memory: "32Gi"
              cpu: "8"
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "fast-nvme"
        resources:
          requests:
            storage: 3Ti
---

בעיות שאנו פותרים

אתחול מסנפשוט

סנכרון הרשת הראשית של Ethereum מאפס (snap sync) לוקח 12–24 שעות, וצומת ארכיון לוקח עד 5 שבועות. עבור NaaS, זה קריטי: לקוחות משלמים מהדקה הראשונה. אנו משתמשים בהפצת סנפשוט: אנו יוצרים עותק עדכני של מסד הנתונים כל 7 ימים, עם דיפרנציאלים מצטברים יומיים. אתחול צומת מסנפשוט לוקח 10–15 דקות.

השוואת מצבי סנכרון של צומת Ethereum:

מצב גודל נתונים זמן סנכרון זמינות RPC
Snap sync ~500 GB 12–24 שעות מלא
Full sync ~1.2 TB 3–5 ימים ארכיון
Archive ~15 TB 5–7 שבועות ארכיון + מעקב

בידוד לקוחות

פלטפורמה אחת מארחת סטארטאפים עם שכבה חינמית וארגונים עם ערבויות SLA. אנו מקצים משאבים דרך שלושה מודלים של ריבוי דיירים, כל אחד עם גישת חיוב תשתית בלוקצ'יין משלו.

מודל בידוד מקרה שימוש טיפוסי דוגמת תמחור
Shared נמוך (תהליך יחיד) שכבה חינמית, פרויקטי בדיקה תשלום לפי CU
Dedicated גבוה (צומת ייעודי) ייצור, RPC יציב תעריף קבוע
Node cluster מקסימלי (עותקים + LB) ארגוני, HA הצעת מחיר מותאמת

לחיוב תשתית בלוקצ'יין, אנו משתמשים ביחידות מחשוב—לכל שיטת RPC יש משקל ב-CU: apiVersion: apps/v1 kind: StatefulSet metadata: name: ethereum-geth spec: serviceName: "geth" replicas: 1 selector: matchLabels: app: ethereum-geth template: spec: containers: - name: geth image: ethereum/client-go:v1.13.14 args: ["--datadir=/data", "--http", "--http.addr=0.0.0.0", "--http.vhosts=*", "--http.api=eth,net,web3,txpool", "--ws", "--ws.addr=0.0.0.0", "--maxpeers=50", "--cache=4096"] ports: - containerPort: 8545 - containerPort: 8546 - containerPort: 30303 protocol: TCP - containerPort: 30303 protocol: UDP volumeMounts: - name: data mountPath: /data resources: requests: memory: "16Gi" cpu: "4" limits: memory: "32Gi" cpu: "8" volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] storageClassName: "fast-nvme" resources: requests: storage: 3Ti — 10 CU, eth_blockNumber — 26 CU, eth_call — 75 CU.

איך אנחנו עושים את זה: מחסנית ומקרי בוחן

RPC Proxy עם ניתוב חכם

פרוקסי Go מותאם אישית מסנן שיטות מסוכנות (למשל, trace_replayTransaction רק לפרימיום), מפיץ בקשות בין צמתי ארכיון וצמתים מלאים, ומטמון תשובות (TTL — שנייה אחת עבור debug_*). הגבלת קצב מיושמת דרך חלון הזזה של Redis—זה מדויק יותר מ-token bucket עבור עומסי RPC.

// Пример RPC прокси с routing logic
package proxy

type RPCRouter struct {
    archivePool NodePool
    fullNodePool NodePool
    cacheClient *redis.Client
}

var archiveMethods = map[string]bool{
    "eth_getBalance": true,
    "eth_call": true,
    "eth_getStorageAt": true,
    "trace_call": true,
    "trace_replayTransaction": true,
}

func (r *RPCRouter) Route(req *RPCRequest) NodePool {
    if archiveMethods[req.Method] {
        if req.RequiresHistoricalBlock() {
            return r.archivePool
        }
    }
    return r.fullNodePool
}

func (r *RPCRouter) Handle(w http.ResponseWriter, req *RPCRequest, apiKey string) {
    cacheKey := req.CacheKey()
    if cached, err := r.cacheClient.Get(ctx, cacheKey).Bytes(); err == nil {
        w.Write(cached)
        return
    }
    pool := r.Route(req)
    node := pool.GetHealthyNode()
    resp := node.Forward(req)
    if req.IsCacheable() {
        r.cacheClient.Set(ctx, cacheKey, resp, req.CacheTTL())
    }
    r.billing.RecordRequest(apiKey, req.Method, resp.ComputeUnits())
    w.Write(resp)
}

בדיקת בריאות עם מודעות למצב צומת

Ping לא מבטיח שהצומת מעבד בקשות. אנו משתמשים בבדיקות התקדמות סנכרון: אם eth_blockNumber אינו nil או שהבלוק ישן יותר משתי דקות, הצומת מוחרג מהמאגר. בדיקות בריאות רצות כל 15 שניות.

type NodeHealthChecker struct {
    client *ethclient.Client
}

func (h *NodeHealthChecker) IsHealthy(ctx context.Context) (bool, error) {
    syncing, err := h.client.SyncProgress(ctx)
    if err != nil {
        return false, err
    }
    if syncing != nil {
        return false, fmt.Errorf("node is syncing: %d/%d", syncing.CurrentBlock, syncing.HighestBlock)
    }
    header, err := h.client.HeaderByNumber(ctx, nil)
    if err != nil {
        return false, err
    }
    blockAge := time.Since(time.Unix(int64(header.Time), 0))
    if blockAge > 2*time.Minute {
        return false, fmt.Errorf("block too old: %v", blockAge)
    }
    return true, nil
}

הגבלת קצב על Redis

func (rl *RateLimiter) Allow(ctx context.Context, apiKey string, rps int) (bool, error) {
	now := time.Now().UnixMilli()
	window := int64(1000)
	pipe := rl.redis.Pipeline()
	pipe.ZRemRangeByScore(ctx, apiKey, "0", strconv.FormatInt(now-window, 10))
	pipe.ZCard(ctx, apiKey)
	pipe.ZAdd(ctx, apiKey, redis.Z{Score: float64(now), Member: now})
	pipe.Expire(ctx, apiKey, 2*time.Second)
	results, err := pipe.Exec(ctx)
	count := results[1].(*redis.IntCmd).Val()
	return count < int64(rps), nil
}

חיוב מבוסס יחידות מחשוב

CREATE TABLE api_keys (
    id UUID PRIMARY KEY,
    customer_id UUID NOT NULL,
    key_hash BYTEA NOT NULL,
    tier VARCHAR(20) NOT NULL,
    rate_limit_rps INTEGER NOT NULL,
    monthly_cu_limit BIGINT,
    node_type VARCHAR(20) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE usage_records (
    id BIGSERIAL PRIMARY KEY,
    api_key_id UUID NOT NULL REFERENCES api_keys(id),
    method VARCHAR(100) NOT NULL,
    chain_id INTEGER NOT NULL,
    compute_units INTEGER NOT NULL,
    response_time_ms INTEGER,
    recorded_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_usage_billing ON usage_records (api_key_id, recorded_at);

איך אנחנו בונים פלטפורמת NaaS: שלבים ולוח זמנים

  1. ניתוח וביקורת (1–2 שבועות): הגדרת בלוקצ'יין יעד, מודל ריבוי דיירים, דרישות SLA ואזור.
  2. עיצוב ארכיטקטורה (1–2 שבועות): בחירת לקוחות (Geth, Reth, Erigon, Solana Agave), הכנת סכמות K8s, API Gateway, חיוב.
  3. יישום תשתית ליבה (4–6 שבועות): תבניות StatefulSet, צינור אתחול מסנפשוט, בודק בריאות.
  4. פיתוח API Gateway וחיוב (6–8 שבועות): RPC proxy, הגבלת קצב, יחידות מחשוב, אינטגרציית Stripe.
  5. נראות ופורטל שירות עצמי (6–9 שבועות): Prometheus + Grafana, התראות, ממשק אינטרנט לניהול מפתחות וצפייה במדדים.
  6. בדיקות ופריסה (2–3 שבועות): בדיקות עומס, ביקורת אבטחה, השקה לייצור.

סה"כ: 16 עד 23 שבועות לפלטפורמה מוכנה לייצור. אם תרצה להאיץ, צור קשר עם המהנדסים שלנו—נציע קצב מתאים.

מה כלול

  • תיעוד: דיאגרמות ארכיטקטורה, הוראות הוספת שרשרת, runbook לתמיכה.
  • גישה: מאגר תבניות, צינור CI/CD, ניטור (לוחות Grafana).
  • הדרכה: 2–3 מפגשים לצוות שלך (DevOps ו-backend).
  • תמיכה: 3 חודשים לאחר ההשקה (תיקוני באגים, ייעוץ).

טעויות נפוצות בפיתוח NaaS

  • שימוש ב-hostNetwork עבור פורטי P2P—מאבד בידוד. עדיף להשתמש ב-NodePort או LoadBalancer עם פורט קבוע לכל צומת.
  • חוסר מטמון עבור שיטות RPC תכופות (// Пример RPC прокси с routing logic package proxy type RPCRouter struct { archivePool NodePool fullNodePool NodePool cacheClient *redis.Client } var archiveMethods = map[string]bool{ "eth_getBalance": true, "eth_call": true, "eth_getStorageAt": true, "trace_call": true, "trace_replayTransaction": true, } func (r *RPCRouter) Route(req *RPCRequest) NodePool { if archiveMethods[req.Method] { if req.RequiresHistoricalBlock() { return r.archivePool } } return r.fullNodePool } func (r *RPCRouter) Handle(w http.ResponseWriter, req *RPCRequest, apiKey string) { cacheKey := req.CacheKey() if cached, err := r.cacheClient.Get(ctx, cacheKey).Bytes(); err == nil { w.Write(cached) return } pool := r.Route(req) node := pool.GetHealthyNode() resp := node.Forward(req) if req.IsCacheable() { r.cacheClient.Set(ctx, cacheKey, resp, req.CacheTTL()) } r.billing.RecordRequest(apiKey, req.Method, resp.ComputeUnits()) w.Write(resp) } , SyncProgress)—מגדיל עומס על הצומת וחיוב.
  • בדיקת בריאות רק דרך TCP—הצומת עשוי להיות חי אך מאות בלוקים מאחורי הרשת.

אם נתקלת בבעיות אלה או רוצה להימנע מהן, הזמן פיתוח פלטפורמת NaaS מאיתנו. למידע נוסף על StatefulSet. יש לנו ניסיון של 10+ שנים בתשתית בלוקצ'יין והשלמנו 50+ פרויקטים, כולל פלטפורמות עבור Fortune 500. צור קשר—נעריך את המשימה שלך ונציע את הפתרון האופטימלי.