Deep Dive into a Self‑Built SIP‑WebRTC Video Gateway Architecture on K8s
An in‑depth analysis of a custom SIP‑WebRTC video gateway built on Kubernetes, detailing dual load‑balancing via K8s and Redis RPC, a seven‑layer architecture, media mixing and aggregation pipelines, and graceful shutdown mechanisms that enable reliable SIP call integration with modern WebRTC conferences.
Introduction
Connecting traditional telephone networks with modern real‑time audio/video platforms poses challenges beyond protocol translation; it requires coordinated call ingress, node scheduling, media bridging, and multi‑party conference experience.
Core Problems
Where inbound requests enter the system and which SIP node handles them.
How outbound requests select the optimal SIP pod.
How multi‑party audio is reliably sent to SIP endpoints that accept a single audio stream.
How multiple video and screen‑share tracks are aggregated and bridged to a SIP video link.
Overall Architecture: K8s Orchestration + Multi‑Replica SIP Nodes + Redis RPC Bus
The system follows a cloud‑native design, using Kubernetes for container orchestration and elastic scaling. Four main data flows compose the gateway:
Inbound flow: external SIP request → K8s service/LB → a SIP pod.
Outbound flow: internal service → Redis RPC bus → selected SIP pod → outbound call.
Audio flow: room‑level multi‑audio mixing → single audio stream back to SIP.
Video flow: multi‑camera and screen‑share tracks → unified aggregation → SIP video link.
Two Load‑Balancing Mechanisms
2.1 Inbound – K8s Service/LB
External SIP terminals send INVITE to the exposed SIP service address. K8s performs the first‑hop distribution, assigning the request to a pod. After the pod receives the request it handles authentication, scheduling, media negotiation, and room entry.
Media streams are stateful; once negotiation finishes, the media must stay on the same pod, so ports are fixed rather than statelessly forwarded.
2.2 Outbound – Redis + Internal RPC
Outbound calls originate from an internal business request that creates a SIP participant. The request travels through the Redis‑backed internal RPC bus, which selects one SIP pod among many to execute the call. This selection is the true load‑balancing for outbound traffic.
2.3 Why Separate Mechanisms?
Inbound traffic arrives from outside the cluster and is naturally handled by the K8s entry point, while outbound traffic is generated inside the cluster and must be dispatched by the internal RPC layer.
Seven‑Layer Structure Inside Each SIP Node
3.1 CLI Entry Layer (main.go)
Bootstraps the application, parses configuration, initializes logging, monitoring, tracing, RPC clients, Redis, database connections, and starts the service. It also handles graceful shutdown signals.
3.2 Service Layer (service/)
Manages the pod’s lifecycle, registers with the control center, reports CPU/memory/bandwidth/room metrics, receives RPC commands, creates/destroys rooms, and oversees the whole pod.
3.3 SIP Service Layer (sip/service.go)
Handles SIP protocol specifics: REGISTER, INVITE, ACK, BYE, CANCEL, OPTIONS, REFER, INFO, SUBSCRIBE/NOTIFY, etc.
3.4 Room (Conference Core)
Acts as the director of a conference, deciding participant join/leave, visibility, layout, and whether to start mixing.
3.5 Media Port (Media Abstraction)
Provides a uniform interface for RTP/RTCP handling, jitter buffering, and clock synchronization.
3.6 Pipeline (Audio/Video Processing)
Performs video decoding/encoding, audio‑video mixing, resolution scaling, frame‑rate conversion, and RTP packaging.
3.7 Infrastructure Layer
Supplies shared components: configuration, logger, Redis client, RPC client, Prometheus metrics, OpenTelemetry tracing, worker pool, timers, UUID generator, retry logic, ring buffer, etc.
Startup Process
Load configuration and initialize logging/monitoring.
Connect to Redis cluster and establish internal RPC bus.
Initialize SIP service and start inbound/outbound handling.
Register internal RPC endpoints.
Await termination signal for graceful shutdown.
Two critical techniques ensure production robustness:
Both inbound server and outbound client share the same sipgo.UserAgent so that BYE, re‑INVITE, and other session signals return to the same physical port, avoiding NAT mapping failures.
Top‑level service registers on the internal RPC bus via RegisterCreateSIPParticipantTopic, making the node discoverable for outbound dispatch.
Inbound Flow
Steps: external SIP terminal initiates call → K8s LB forwards to a SIP pod → pod authenticates and schedules → optional PIN verification → media capability negotiation → media ports and conference room are created → terminal joins the conference for bidirectional audio/video.
Outbound Flow
Steps: business service requests an outbound call → request enters Redis RPC bus → RPC selects a SIP pod → pod creates outbound task and joins the conference → SIP INVITE sent to external terminal → media negotiation → call established and result returned.
Outbound load‑balancing operates on two levels: the Redis‑backed PSRPC bus distributes commands, and the selection strategy prefers the pod with the fewest active calls (both inbound and outbound combined).
Mixing (Conferencing) Challenges
Audio Mixing
Audio mixing is performed in the Room layer, not in MediaPort. Two key objects are created:
out := msdk.NewSwitchWriter(RoomSampleRate) mix := mixer.NewMixer(r.out, rtp.DefFrameDur, 1, mixer.WithStats(&st.Mixer), mixer.WithOutputChannel())Incoming room audio tracks are fed into mixer.NewInput(), mixed into a single PCM stream, and then handed to the SIP encoding chain.
Video Aggregation
Video handling differs from audio: multiple camera and screen‑share tracks are first classified, then selectively subscribed. Camera tracks are collected as UnmutedCameraTracks and processed separately from screen‑share tracks. The Room decides which tracks are worth bridging.
After selection, the Room calls
SetSource(orchestrator.cameraRtcPad.RtcSrc, orchestrator.screenshareRtcPad.RtcSrc)to bind the tracks to the media pipeline.
The VideoManager holds RTP/RTCP connections, SDP parameters, and a pipeline.Controller. The controller builds two GStreamer pipelines: downstream (WebRTC → SIP) and upstream (SIP → WebRTC). The downstream pipeline performs depayload, parsing, decoding, conversion, compositing, encoding, and RTP output to the SIP endpoint.
Graceful Shutdown & Rolling Upgrade
Node deregisters its PSRPC topic on shutdown, preventing new outbound dispatches to it.
K8s gradually redirects new inbound connections to healthy pods.
Every 5 seconds the node checks its active call count; existing calls are allowed to finish naturally.
The process exits only after all media ports and room resources are fully released and active call count reaches zero, ensuring a seamless, zero‑impact upgrade.
Conclusion
The gateway’s value lies in its dual‑layer load‑balancing: K8s handles external SIP ingress, while Redis + internal RPC manages internal outbound selection. This design bridges traditional SIP networks with WebRTC conferences reliably and scales horizontally in production environments.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
360 Zhihui Cloud Developer
360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
