Bedrock migrated its video delivery to the cloud. Very quickly, we needed to add a cache layer to absorb the load. AWS load balancers do not allow for advanced load balancing algorithms, such as consistent hashing, so we decided to use HAProxy. Thanks to the HAProxy Runtime API, we developed a tool in Python that allows us to synchronize the AWS AutoScalingGroup with HAProxy. In the end, we scale our load balancers as well as our applications, while optimizing the cache. We also improved our resilience, using among other things HAProxy's retry and redispatch to bypass the network limitations of AWS EC2 instances. During this presentation, I will describe the different versions deployed since the beginning of this cloud migration and their evolution until today. I will detail the significance of each element of our configuration, which allows Bedrock to provide videos to more than 45 million people, by explaining the gains in performance, or resilience.
AWS Community DAY Albertini-Ellan Cloud Security (1).pptx
HAProxyConf22: Scaling Bedrock Video Delivery to 50 Million Users with HAProxy
1. Scaling Bedrock video
delivery to 50 million
users with HAProxy
HAProxyConf 2022 Paris, France
Vincent GALLISSOT
Bedrock Streaming
Lead Cloud Architect
@vgallissot
29. HSDO client: Using the runtime API
def sendHaproxyCommand(self, command):
command = command + "n"
haproxySock = socket.socket(socket.AF_UNIX,
socket.SOCK_STREAM)
try:
haproxySock.connect(self.socketPath)
haproxySock.send(command.encode("utf-8"))
https://github.com/BedrockStreaming/hsdo
30. HSDO client: Using the runtime API
https://github.com/BedrockStreaming/hsdo
commands.append(
"set server %s/%s addr %s port %s"
% (
backendName,
serverName,
server.IPAddress,
self.backendServerPort
)
)
31. HSDO client: Using the runtime API
https://github.com/BedrockStreaming/hsdo
commands.append(
"set server %s/%s weight %s"
% (
backendName,
serverName,
server.weight
)
)
32. HSDO client: Makes sure the running configuration
respects the desired state
https://github.com/BedrockStreaming/hsdo
def checkBackendConf(self):
stat = self.sendHaproxyCommand("show stat")
check = self.backendConfReady(stat)
61. Running a Load Balancer on EC2 Spot instances
● Use at least 12 different instance types
● Mix instance families (C, M, R, etc.)
● Prefer many small servers rather than a few large ones
● Take care to the “UP to” vs “baseline” bandwidths
about Kubernetes and cloud,
Load balancing algorithms & streaming
tech company
basically
B2B2C
50 million
Advertised, Subscribed VOD or Hybrid
We face several challenges
Sometimes we help
existing platform
Video Cache layers will be empty
Customer want to publish lot of contents all at once
Remove technical barriers
Respecting our customers’ partnerships
Improve user experience
Static file
CDN cache
Packaging gives control
First download a manifest
6s duration
Manifest follow playlist
Adapt video quality to the user’s bandwidth
Store entire file
manifest alongside the video
Compute when needed
CDN to cache
Advantage of both the cache and doing things dynamically
Worked well for 10 years
Follow Bedrock business model
Customer A customer B
COVID
Consumed lot of streaming
Stability issues
Use managed services as much as possible
TLS endpoint
Scaling CPU bandwidth
Skeleton
Load tests before migration
Do 20 times more than on-prem
S3 bucket scales after some time
handle the load while it scales
7mn
Player asks for chunk
metadata shared for the same video
Cache metadata locally
Between USP and S3 bucket
TCP Keepalive
Nginx 1 request, then queue
Increase performance
Increase cache efficiency
advanced load balancing
Round Robin
Least Outstanding Requests
no cache specification
Video request: cache1, cache2, cache1, cache2
each server is a duplicate of the cache
Each cache server is less likely to have the answer in its cache and will proxy the request to S3 instead
Ineffective cache means bad performances, higher costs and maybe, S3 rate-limiting
we pay more for a worse service
the traffic of a video is always sent to the same cache
Metadata of a video is specific to a single cacher server
The more servers there are, the more specific and optimized the cache is
Scaling becomes more efficient
NLB = single TLS
Abstract HAProxy scaling
Health checks
NLB = single TLS
Servers in the same order
Populate backend server list
No file management
Server handle all backend servers info: ID’s, weights, etc.
Runtime API connect through Unix socket
Last line we send a command
Modify servers
Enable disable
weight
Client Keeps track of the desired state from DynamoDB
Compares to running config
HSDO controls backend configuration
server-template
as many backend servers
No create/delete server, only modify
disabled by default: no traffic
good CHR: 60% fewer calls
Good perfs, effective costs
Scale-up = sudden cache drop
-> time needed for a new server to populate its cache
We limit the impact of Cache Hit Ratio drops
HSDO server to ensure consistency
Increase weight every X secs
Keep good performances when we scale-up
Scaling by design
Haproxy slowstart
19mn
not all contents are equally popular
few loaded servers
Most servers doing nothing
Averages means no scaling if only 1 server is loaded
Andrew from vimeo
Gave us the idea to do the same
Too many requests
Overflow traffic
harmonize the load
overflow
We still optimize the cache even in case of overflow
40% more connections than the average
Load is smoothed on all servers
we can scale up
& use consistent hashing
22 mn
Network limits on AWS
Bandwidth is throttled
Limit on packets per second
Queued or dropped
very short duration
increases latency
Reduces overall bandwidth
Impacts performances
New TCP connection needs < 1ms
Server in bad shape, fail quickly
S3 or USP can answer badly
No retry on 500
Redispatch respects consistent hashing
Like bounded loads → By design
At a very low rate
No impact on performances
25 mn
Most expensive cost is inter-AZ
EC2-Other : Inter-AZ traffic
HAProxy uses all AZ by design
Splitting traffic forced us to duplicate EC2 servers → to keep resiliency
easy to do
Duplicate all AutoScalingGroups
Dedicate each of them to a single AZ
HSDO Client sticks
keep cache and good performances
Deployed V3 in november
Still migrating onprem to cloud traffic
All bars are increasing
EC2-other drops to almost 0
We kept a fallback backend, multi-AZ, that we can use when we don’t have enough ready servers in the primary backend.
It’s populated with HSDO, so this secondary backend is the exact same for all HAProxy servers on every AZs, to try to optimize as much as possible, even in a degraded state.
any available server in the fallback-backend
pay more to provide the service at any cost
multi-AZ fallback
Compute = 15% of costs
Spot instances
Reclaim notification = launch new server
NLB more expensive than the whole compute
Avoid mass reclaims
The “Up to” traffic in the overall doc
Burst is not guaranteed, not even for 1s
Baseline capacity is guaranteed
24 times less
33mn
single TLS endpoint for dozens of haproxy
cost optimization
-> won’t increase performances or resiliency
no rush for V4
Easy to run HAProxy on AWS -> APIs
Provide VOD to 50 million users