Files

61 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Read-only server discovery and conservative starter recommendations."""
import base64
import hashlib
import math
import socket
from .docker import DockerEngine, DockerError
def host_fingerprint(host: str, port: int) -> str:
import paramiko
transport = None
try:
with socket.create_connection((host, port), timeout=10) as connection:
transport = paramiko.Transport(connection)
transport.start_client(timeout=10)
key = transport.get_remote_server_key()
return "SHA256:" + base64.b64encode(hashlib.sha256(key.asbytes()).digest()).decode().rstrip("=")
except Exception as exc:
raise DockerError(f"读取服务器指纹失败({type(exc).__name__})") from None
finally:
if transport is not None:
transport.close()
def recommend(cpus, memory_mb, reserved_cpu=0, reserved_memory_mb=0, connection_budget=64):
# Reserve at least half the host for the main app, DB and other services.
cpu_budget = max(0, min(cpus / 2, cpus - reserved_cpu - 1))
memory_budget = max(0, min(memory_mb // 2, memory_mb - reserved_memory_mb - 2048))
cpu = 2 if cpu_budget >= 2 else 1
memory = 2048
count = max(0, min(4, math.floor(cpu_budget / cpu), memory_budget // memory, connection_budget // 12))
return {"desired": int(count), "cpu": cpu, "memory_mb": memory,
"total_cpu": max(cpu, count * cpu), "total_memory_mb": max(memory, count * memory),
"concurrency": 4, "database_pool_size": 4, "postgres_pool_size": 4}
def discover(settings, instance):
engine = DockerEngine(settings)
info = engine.request("GET", "/info")
containers = engine.request("GET", "/containers/json")
cpu_reserved = 0
memory_reserved = 0
templates = []
for item in containers:
if item.get("Labels", {}).get("deerflow.worker-pool") == instance:
continue
detail = engine.request("GET", f"/containers/{item['Id']}/json")
limits = detail.get("HostConfig", {})
cpu_reserved += limits.get("NanoCpus", 0) / 1_000_000_000
memory_reserved += limits.get("Memory", 0) // (1024 ** 2)
if item.get("Labels", {}).get("deerflow.worker-role") == "online":
continue
networks = [name for name in detail.get("NetworkSettings", {}).get("Networks", {}) if name not in {"host", "none", "bridge"}]
templates.append({"name": detail.get("Name", "").lstrip("/"), "networks": networks})
cpus = int(info.get("NCPU", 0))
memory_mb = int(info.get("MemTotal", 0)) // (1024 ** 2)
return {"cpus": cpus, "memory_mb": memory_mb, "containers": templates,
"recommendation": recommend(cpus, memory_mb, cpu_reserved, memory_reserved, settings.database_connection_budget),
"note": "按硬件总量和其他容器已配置的资源上限估算,至少为主服务、数据库及系统保留一半资源;不是实时空闲内存。建议先用最多 4 个节点,再按实际负载调整。"}