Когда приложение выжато на уровне кода, остаётся оптимизация на уровне ОС и железа. На наших серверах с двумя NUMA-нодами (2x Intel Xeon, 32 ядра на ноду) неправильное размещение процессов стоило 15-20% производительности:
# Проверяем NUMA-топологию сервера
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0-31
# node 0 size: 64 GB
# node 1 cpus: 32-63
# node 1 size: 64 GB
# node distances:
# node 0 1
# 0: 10 21
# 1: 21 10
# Расстояние между нодами 21 vs 10 — cross-NUMA доступ в 2.1 раза медленнее
# CPU pinning — привязываем Go-процесс к конкретным ядрам одной NUMA-ноды
# Вместо разброса по 64 ядрам — 7 ядер на одной ноде
taskset -c 0-6 ./adserver
# Или через cgroup в Kubernetes:
# resources:
# limits:
# cpu: "7"
# requests:
# cpu: "7"
# С kubelet параметром --cpu-manager-policy=static
# Kubernetes гарантирует exclusive CPU cores
# NUMA-aware запуск — память и CPU на одной ноде
numactl --cpunodebind=0 --membind=0 ./adserver
# Проверяем, что процесс действительно на одной NUMA-ноде
numastat -p $(pgrep adserver)
# Per-node process memory usage (in MBs)
# PID Node 0 Node 1
# 12345 6144.2 12.1 ← почти вся память на Node 0
# Kernel-уровень оптимизации — sysctl
# /etc/sysctl.d/99-adplatform.conf
# Увеличиваем буферы сети
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# TCP Fast Open
net.ipv4.tcp_fastopen = 3
# Переиспользование TIME_WAIT соединений
net.ipv4.tcp_tw_reuse = 1
# Размер буферов
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# Применяем
sysctl -p /etc/sysctl.d/99-adplatform.conf
Для самых горячих путей мы рассматривали DPDK (Data Plane Development Kit) — обход ядра для сетевых пакетов. Однако DPDK требует выделения NIC из ядра и не совместим с Kubernetes-сетевой моделью. Вместо этого применили io_uring для асинхронного I/O и SO_REUSEPORT для балансировки по ядрам:
// SO_REUSEPORT — каждое ядро обрабатывает свою очередь соединений
listener, err := net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET,
unix.SO_REUSEPORT, 1)
})
},
}.Listen(ctx, "tcp", ":8080")
Оставить комментарий