
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Fri, 03 Apr 2026 23:30:45 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Shedding old code with ecdysis: graceful restarts for Rust services at Cloudflare]]></title>
            <link>https://blog.cloudflare.com/ecdysis-rust-graceful-restarts/</link>
            <pubDate>Fri, 13 Feb 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[ ecdysis is a Rust library enabling zero-downtime upgrades for network services. After five years protecting millions of connections at Cloudflare, it’s now open source. ]]></description>
            <content:encoded><![CDATA[ <blockquote><p>ecdysis | <i>ˈekdəsəs</i> |</p><p>noun</p><p>    the process of shedding the old skin (in reptiles) or casting off the outer 
    cuticle (in insects and other arthropods).  </p></blockquote><p>How do you upgrade a network service, handling millions of requests per second around the globe, without disrupting even a single connection?</p><p>One of our solutions at Cloudflare to this massive challenge has long been <a href="https://github.com/cloudflare/ecdysis"><b><u>ecdysis</u></b></a>, a Rust library that implements graceful process restarts where no live connections are dropped, and no new connections are refused. </p><p>Last month, <b>we open-sourced ecdysis</b>, so now anyone can use it. After five years of production use at Cloudflare, ecdysis has proven itself by enabling zero-downtime upgrades across our critical Rust infrastructure, saving millions of requests with every restart across Cloudflare’s <a href="https://www.cloudflare.com/network/"><u>global network</u></a>.</p><p>It’s hard to overstate the importance of getting these upgrades right, especially at the scale of Cloudflare’s network. Many of our services perform critical tasks such as traffic routing, <a href="https://www.cloudflare.com/application-services/solutions/certificate-lifecycle-management/"><u>TLS lifecycle management</u></a>, or firewall rules enforcement, and must operate continuously. If one of these services goes down, even for an instant, the cascading impact can be catastrophic. Dropped connections and failed requests quickly lead to degraded customer performance and business impact.</p><p>When these services need updates, security patches can’t wait. Bug fixes need deployment and new features must roll out. </p><p>The naive approach involves waiting for the old process to be stopped before spinning up the new one, but this creates a window of time where connections are refused and requests are dropped. For a service handling thousands of requests per second in a single location, multiply that across hundreds of data centers, and a brief restart becomes millions of failed requests globally.</p><p>Let’s dig into the problem, and how ecdysis has been the solution for us — and maybe will be for you. </p><p><b>Links</b>: <a href="https://github.com/cloudflare/ecdysis">GitHub</a> <b>|</b> <a href="https://crates.io/crates/ecdysis">crates.io</a> <b>|</b> <a href="https://docs.rs/ecdysis">docs.rs</a></p>
    <div>
      <h3>Why graceful restarts are hard</h3>
      <a href="#why-graceful-restarts-are-hard">
        
      </a>
    </div>
    <p>The naive approach to restarting a service, as we mentioned, is to stop the old process and start a new one. This works acceptably for simple services that don’t handle real-time requests, but for network services processing live connections, this approach has critical limitations.</p><p>First, the naive approach creates a window during which no process is listening for incoming connections. When the old process stops, it closes its listening sockets, which causes the OS to immediately refuse new connections with <code>ECONNREFUSED</code>. Even if the new process starts immediately, there will always be a gap where nothing is accepting connections, whether milliseconds or seconds. For a service handling thousands of requests per second, even a gap of 100ms means hundreds of dropped connections.</p><p>Second, stopping the old process kills all already-established connections. A client uploading a large file or streaming video gets abruptly disconnected. Long-lived connections like WebSockets or gRPC streams are terminated mid-operation. From the client’s perspective, the service simply vanishes.</p><p>Binding the new process before shutting down the old one appears to solve this, but also introduces additional issues. The kernel normally allows only one process to bind to an address:port combination, but <a href="https://man7.org/linux/man-pages/man7/socket.7.html"><u>the SO_REUSEPORT socket option</u></a> permits multiple binds. However, this creates a problem during process transitions that makes it unsuitable for graceful restarts.</p><p>When <code>SO_REUSEPORT</code> is used, the kernel creates separate listening sockets for each process and <a href="https://lwn.net/Articles/542629/"><u>load balances new connections across these sockets</u></a>. When the initial <code>SYN</code> packet for a connection is received, the kernel will assign it to one of the listening processes. Once the initial handshake is completed, the connection then sits in the <code>accept()</code> queue of the process until the process accepts it. If the process then exits before accepting this connection, it becomes orphaned and is terminated by the kernel. GitHub’s engineering team documented this issue extensively when <a href="https://github.blog/2020-10-07-glb-director-zero-downtime-load-balancer-updates/"><u>building their GLB Director load balancer</u></a>.</p>
    <div>
      <h3>How ecdysis works</h3>
      <a href="#how-ecdysis-works">
        
      </a>
    </div>
    <p>When we set out to design and build ecdysis, we identified four key goals for the library:</p><ol><li><p><b>Old code can be completely shut down</b> post-upgrade.</p></li><li><p><b>The new process has a grace period</b> for initialization.</p></li><li><p><b>New code crashing during initialization is acceptable</b> and shouldn’t affect the running service.</p></li><li><p><b>Only a single upgrade runs in parallel</b> to avoid cascading failures.</p></li></ol><p>ecdysis satisfies these requirements following an approach pioneered by NGINX, which has supported graceful upgrades since its early days. The approach is straightforward: </p><ol><li><p>The parent process <code>fork()</code>s a new child process.</p></li><li><p>The child process replaces itself with a new version of the code with <code>execve()</code>.</p></li><li><p>The child process inherits the socket file descriptors via a named pipe shared with the parent.</p></li><li><p>The parent process waits for the child process to signal readiness before shutting down.</p></li></ol>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4QK8GY1s30C8RUovBQnqbD/525094478911eda96c7877a10753159f/image3.png" />
          </figure><p>Crucially, the socket remains open throughout the transition. The child process inherits the listening socket from the parent as a file descriptor shared via a named pipe. During the child's initialization, both processes share the same underlying kernel data structure, allowing the parent to continue accepting and processing new and existing connections. Once the child completes initialization, it notifies the parent and begins accepting connections. Upon receiving this ready notification, the parent immediately closes its copy of the listening socket and continues handling only existing connections. </p><p>This process eliminates coverage gaps while providing the child a safe initialization window. There is a brief window of time when both the parent and child may accept connections concurrently. This is intentional; any connections accepted by the parent are simply handled until completion as part of the draining process.</p><p>This model also provides the required crash safety. If the child process fails during initialization (e.g., due to a configuration error), it simply exits. Since the parent never stopped listening, no connections are dropped, and the upgrade can be retried once the problem is fixed.</p><p>ecdysis implements the forking model with first-class support for asynchronous programming through<a href="https://tokio.rs"> <u>Tokio</u></a> and s<code>ystemd</code> integration:</p><ul><li><p><b>Tokio integration</b>: Native async stream wrappers for Tokio. Inherited sockets become listeners without additional glue code. For synchronous services, ecdysis supports operation without async runtime requirements.</p></li><li><p><b>systemd-notify support</b>: When the <code>systemd_notify</code> feature is enabled, ecdysis automatically integrates with systemd’s process lifecycle notifications. Setting <code>Type=notify-reload</code> in your service unit file allows systemd to track upgrades correctly.</p></li><li><p><b>systemd named sockets</b>: The <code>systemd_sockets</code> feature enables ecdysis to manage systemd-activated sockets. Your service can be socket-activated and support graceful restarts simultaneously.</p></li></ul><p>Platform note: ecdysis relies on Unix-specific syscalls for socket inheritance and process management. It does not work on Windows. This is a fundamental limitation of the forking approach.</p>
    <div>
      <h3>Security considerations</h3>
      <a href="#security-considerations">
        
      </a>
    </div>
    <p>Graceful restarts introduce security considerations. The forking model creates a brief window where two process generations coexist, both with access to the same listening sockets and potentially sensitive file descriptors.</p><p>ecdysis addresses these concerns through its design:</p><p><b>Fork-then-exec</b>: ecdysis follows the traditional Unix pattern of <code>fork()</code> followed immediately by <code>execve()</code>. This ensures the child process starts with a clean slate: new address space, fresh code, and no inherited memory. Only explicitly-passed file descriptors cross the boundary.</p><p><b>Explicit inheritance</b>: Only listening sockets and communication pipes are inherited. Other file descriptors are closed via <code>CLOEXEC</code> flags. This prevents accidental leakage of sensitive handles.</p><p><b>seccomp compatibility</b>: Services using seccomp filters must allow <code>fork()</code> and <code>execve()</code>. This is a tradeoff: graceful restarts require these syscalls, so they cannot be blocked.</p><p>For most network services, these tradeoffs are acceptable. The security of the fork-exec model is well understood and has been battle-tested for decades in software like NGINX and Apache.</p>
    <div>
      <h3>Code example</h3>
      <a href="#code-example">
        
      </a>
    </div>
    <p>Let’s look at a practical example. Here’s a simplified TCP echo server that supports graceful restarts:</p>
            <pre><code>use ecdysis::tokio_ecdysis::{SignalKind, StopOnShutdown, TokioEcdysisBuilder};
use tokio::{net::TcpStream, task::JoinSet};
use futures::StreamExt;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // Create the ecdysis builder
    let mut ecdysis_builder = TokioEcdysisBuilder::new(
        SignalKind::hangup()  // Trigger upgrade/reload on SIGHUP
    ).unwrap();

    // Trigger stop on SIGUSR1
    ecdysis_builder
        .stop_on_signal(SignalKind::user_defined1())
        .unwrap();

    // Create listening socket - will be inherited by children
    let addr: SocketAddr = "0.0.0.0:8080".parse().unwrap();
    let stream = ecdysis_builder
        .build_listen_tcp(StopOnShutdown::Yes, addr, |builder, addr| {
            builder.set_reuse_address(true)?;
            builder.bind(&amp;addr.into())?;
            builder.listen(128)?;
            Ok(builder.into())
        })
        .unwrap();

    // Spawn task to handle connections
    let server_handle = tokio::spawn(async move {
        let mut stream = stream;
        let mut set = JoinSet::new();
        while let Some(Ok(socket)) = stream.next().await {
            set.spawn(handle_connection(socket));
        }
        set.join_all().await;
    });

    // Signal readiness and wait for shutdown
    let (_ecdysis, shutdown_fut) = ecdysis_builder.ready().unwrap();
    let shutdown_reason = shutdown_fut.await;

    log::info!("Shutting down: {:?}", shutdown_reason);

    // Gracefully drain connections
    server_handle.await.unwrap();
}

async fn handle_connection(mut socket: TcpStream) {
    // Echo connection logic here
}</code></pre>
            <p>The key points:</p><ol><li><p><code><b>build_listen_tcp</b></code> creates a listener that will be inherited by child processes.</p></li><li><p><code><b>ready()</b></code> signals to the parent process that initialization is complete and that it can safely exit.</p></li><li><p><code><b>shutdown_fut.await</b></code> blocks until an upgrade or stop is requested. This future only yields once the process should be shut down, either because an upgrade/reload was executed successfully or because a shutdown signal was received.</p></li></ol><p>When you send <code>SIGHUP</code> to this process, here’s what ecdysis does…</p><p><i>…on the parent process:</i></p><ul><li><p>Forks and execs a new instance of your binary.</p></li><li><p>Passes the listening socket to the child.</p></li><li><p>Waits for the child to call <code>ready()</code>.</p></li><li><p>Drains existing connections, then exits.</p></li></ul><p><i>…on the child process:</i></p><ul><li><p>Initializes itself following the same execution flow as the parent, except any sockets owned by ecdysis are inherited and not bound by the child.</p></li><li><p>Signals readiness to the parent by calling <code>ready()</code>.</p></li><li><p>Blocks waiting for a shutdown or upgrade signal.</p></li></ul>
    <div>
      <h3>Production at scale</h3>
      <a href="#production-at-scale">
        
      </a>
    </div>
    <p>ecdysis has been running in production at Cloudflare since 2021. It powers critical Rust infrastructure services deployed across 330+ data centers in 120+ countries. These services handle billions of requests per day and require frequent updates for security patches, feature releases, and configuration changes.</p><p>Every restart using ecdysis saves hundreds of thousands of requests that would otherwise be dropped during a naive stop/start cycle. Across our global footprint, this translates to millions of preserved connections and improved reliability for customers.</p>
    <div>
      <h3>ecdysis vs alternatives</h3>
      <a href="#ecdysis-vs-alternatives">
        
      </a>
    </div>
    <p>Graceful restart libraries exist for several ecosystems. Understanding when to use ecdysis versus alternatives is critical to choosing the right tool.</p><p><a href="https://github.com/cloudflare/tableflip"><b><u>tableflip</u></b></a> is our Go library that inspired ecdysis. It implements the same fork-and-inherit model for Go services. If you need Go, tableflip is a great option!</p><p><a href="https://github.com/cloudflare/shellflip"><b><u>shellflip</u></b></a> is Cloudflare’s other Rust graceful restart library, designed specifically for Oxy, our Rust-based proxy. shellflip is more opinionated: it assumes systemd and Tokio, and focuses on transferring arbitrary application state between parent and child. This makes it excellent for complex stateful services, or services that want to apply such aggressive sandboxing that they can’t even open their own sockets, but adds overhead for simpler cases.</p>
    <div>
      <h3>Start building</h3>
      <a href="#start-building">
        
      </a>
    </div>
    <p>ecdysis brings five years of production-hardened graceful restart capabilities to the Rust ecosystem. It’s the same technology protecting millions of connections across Cloudflare’s global network, now open-sourced and available for anyone!</p><p>Full documentation is available at <a href="https://docs.rs/ecdysis"><u>docs.rs/ecdysis</u></a>, including API reference, examples for common use cases, and steps for integrating with <code>systemd</code>.</p><p>The <a href="https://github.com/cloudflare/ecdysis/tree/main/examples"><u>examples directory</u></a> in the repository contains working code demonstrating TCP listeners, Unix socket listeners, and systemd integration.</p><p>The library is actively maintained by the Argo Smart Routing &amp; Orpheus team, with contributions from teams across Cloudflare. We welcome contributions, bug reports, and feature requests on <a href="https://github.com/cloudflare/ecdysis"><u>GitHub</u></a>.</p><p>Whether you’re building a high-performance proxy, a long-lived API server, or any network service where uptime matters, ecdysis can provide a foundation for zero-downtime operations.</p><p>Start building:<a href="https://github.com/cloudflare/ecdysis"> <u>github.com/cloudflare/ecdysis</u></a></p> ]]></content:encoded>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Infrastructure]]></category>
            <category><![CDATA[Engineering]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Application Services]]></category>
            <category><![CDATA[Rust]]></category>
            <guid isPermaLink="false">GMarF75NkFuiwVuyFJk77</guid>
            <dc:creator>Manuel Olguín Muñoz</dc:creator>
        </item>
        <item>
            <title><![CDATA[QUIC action: patching a broadcast address amplification vulnerability]]></title>
            <link>https://blog.cloudflare.com/mitigating-broadcast-address-attack/</link>
            <pubDate>Mon, 10 Feb 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare was recently contacted by researchers who discovered a broadcast amplification vulnerability through their QUIC Internet measurement research. We've implemented a mitigation. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare was recently contacted by a group of anonymous security researchers who discovered a broadcast amplification vulnerability through their <a href="https://blog.cloudflare.com/tag/quic"><u>QUIC</u></a> Internet measurement research. Our team collaborated with these researchers through our Public Bug Bounty program, and worked to fully patch a dangerous vulnerability that affected our infrastructure.</p><p>Since being notified about the vulnerability, we've implemented a mitigation to help secure our infrastructure. According to our analysis, we have fully patched this vulnerability and the amplification vector no longer exists. </p>
    <div>
      <h3>Summary of the amplification attack</h3>
      <a href="#summary-of-the-amplification-attack">
        
      </a>
    </div>
    <p>QUIC is an Internet transport protocol that is encrypted by default. It offers equivalent features to <a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/"><u>TCP</u></a> (Transmission Control Protocol) and <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"><u>TLS</u></a> (Transport Layer Security), while using a shorter handshake sequence that helps reduce connection establishment times. QUIC runs over <a href="https://www.cloudflare.com/en-gb/learning/ddos/glossary/user-datagram-protocol-udp/"><u>UDP</u></a> (User Datagram Protocol).</p><p>The researchers found that a single client QUIC <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2"><u>Initial packet</u></a> targeting a broadcast IP destination address could trigger a large response of initial packets. This manifested as both a server CPU amplification attack and a reflection amplification attack.</p>
    <div>
      <h3>Transport and security handshakes</h3>
      <a href="#transport-and-security-handshakes">
        
      </a>
    </div>
    <p>When using TCP and TLS there are two handshake interactions. First, is the TCP 3-way transport handshake. A client sends a SYN packet to a server, it responds with a SYN-ACK to the client, and the client responds with an ACK. This process validates the client IP address. Second, is the TLS security handshake. A client sends a ClientHello to a server, it carries out some cryptographic operations and responds with a ServerHello containing a server certificate. The client verifies the certificate, confirms the handshake and sends application traffic such as an HTTP request.</p><p><a href="https://datatracker.ietf.org/doc/html/rfc9000#section-7"><u>QUIC</u></a> follows a similar process, however the sequence is shorter because the transport and security handshake is combined. A client sends an Initial packet containing a ClientHello to a server, it carries out some cryptographic operations and responds with an Initial packet containing a ServerHello with a server certificate. The client verifies the certificate and then sends application data.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7wsMcFwy8xMRYwQyFNm6oC/5d131543e7704794776dfc3ed89c1693/image2.png" />
          </figure><p>The QUIC handshake does not require client IP address validation before starting the security handshake. This means there is a risk that an attacker could spoof a client IP and cause a server to do cryptographic work and send data to a target victim IP (aka a <a href="https://blog.cloudflare.com/reflections-on-reflections/"><u>reflection attack</u></a>). <a href="https://datatracker.ietf.org/doc/html/rfc9000"><u>RFC 9000</u></a> is careful to describe the risks this poses and provides mechanisms to reduce them (for example, see Sections <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-8"><u>8</u></a> and <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-9.3.1"><u>9.3.1</u></a>). Until a client address is verified, a server employs an anti-amplification limit, sending a maximum of 3x as many bytes as it has received. Furthermore, a server can initiate address validation before engaging in the cryptographic handshake by responding with a <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-8.1.2"><u>Retry packet</u></a>. The retry mechanism, however, adds an additional round-trip to the QUIC handshake sequence, negating some of its benefits compared to TCP. Real-world QUIC deployments use a range of strategies and heuristics to detect traffic loads and enable different mitigations.</p><p>In order to understand how the researchers triggered an amplification attack despite these QUIC guardrails, we first need to dive into how IP broadcast works.</p>
    <div>
      <h3>Broadcast addresses</h3>
      <a href="#broadcast-addresses">
        
      </a>
    </div>
    <p>In Internet Protocol version 4 (IPv4) addressing, the final address in any given <a href="https://www.cloudflare.com/learning/network-layer/what-is-a-subnet/"><u>subnet</u></a> is a special broadcast IP address used to send packets to every node within the IP address range. Every node that is within the same subnet receives any packet that is sent to the broadcast address, enabling one sender to send a message that can be “heard” by potentially hundreds of adjacent nodes. This behavior is enabled by default in most network-connected systems and is critical for discovery of devices within the same IPv4 network.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/49zGjFbeIv7RxZMM6W2i5V/9e9e5f2f3bd8401467887d488930f476/image3.png" />
          </figure><p>The broadcast address by nature poses a risk of DDoS amplification; for every one packet sent, hundreds of nodes have to process the traffic. </p>
    <div>
      <h3>Dealing with the expected broadcast</h3>
      <a href="#dealing-with-the-expected-broadcast">
        
      </a>
    </div>
    <p>To combat the risk posed by broadcast addresses, by default most routers reject packets originating from outside their IP subnet which are targeted at the broadcast address of networks for which they are locally connected. Broadcast packets are only allowed to be forwarded within the same IP subnet, preventing attacks from the Internet from targeting servers across the world.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5TU3GO26KOJgzLHcS9Uxiu/6cd334afc3925b1713b7e706decc7269/image1.png" />
          </figure><p>The same techniques are not generally applied when a given router is not directly connected to a given subnet. So long as an address is not locally treated as a broadcast address, <a href="https://www.cloudflare.com/learning/security/glossary/what-is-bgp/"><u>Border Gateway Protocol</u></a> (BGP) or other routing protocols will continue to route traffic from external IPs toward the last IPv4 address in a subnet. Essentially, this means a “broadcast address” is only relevant within a local scope of routers and hosts connected together via Ethernet. To routers and hosts across the Internet, a broadcast IP address is routed in the same way as any other IP.</p>
    <div>
      <h3>Binding IP address ranges to hosts</h3>
      <a href="#binding-ip-address-ranges-to-hosts">
        
      </a>
    </div>
    <p>Each Cloudflare server is expected to be capable of serving content from every website on the Cloudflare network. Because our network utilizes <a href="https://www.cloudflare.com/learning/cdn/glossary/anycast-network/"><u>Anycast</u></a> routing, each server necessarily needs to be listening on (and capable of returning traffic from) every Anycast IP address in use on our network.</p><p>To do so, we take advantage of the loopback interface on each server. Unlike a physical network interface, all IP addresses within a given IP address range are made available to the host (and will be processed locally by the kernel) when bound to the loopback interface.</p><p>The mechanism by which this works is straightforward. In a traditional routing environment, <a href="https://en.wikipedia.org/wiki/Longest_prefix_match"><u>longest prefix matching</u></a> is employed to select a route. Under longest prefix matching, routes towards more specific blocks of IP addresses (such as 192.0.2.96/29, a range of 8 addresses) will be selected over routes to less specific blocks of IP addresses (such as 192.0.2.0/24, a range of 256 addresses).</p><p>While Linux utilizes longest prefix matching, it consults an additional step — the Routing Policy Database (RPDB) — before immediately searching for a match. The RPDB contains a list of routing tables which can contain routing information and their individual priorities. The default RPDB looks like this:</p>
            <pre><code>$ ip rule show
0:	from all lookup local
32766:	from all lookup main
32767:	from all lookup default</code></pre>
            <p>Linux will consult each routing table in ascending numerical order to try and find a matching route. Once one is found, the search is terminated and the route immediately used.</p><p>If you’ve previously worked with routing rules on Linux, you are likely familiar with the contents of the main table. Contrary to the existence of the table named “default”, “main” generally functions as the default lookup table. It is also the one which contains what we traditionally associate with route table information:</p>
            <pre><code>$ ip route show table main
default via 192.0.2.1 dev eth0 onlink
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.2</code></pre>
            <p>This is, however, not the first routing table that will be consulted for a given lookup. Instead, that task falls to the local table:</p>
            <pre><code>$ ip route show table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 192.0.2.2 dev eth0 proto kernel scope host src 192.0.2.2
broadcast 192.0.2.255 dev eth0 proto kernel scope link src 192.0.2.2</code></pre>
            <p>Looking at the table, we see two new types of routes — local and broadcast. As their names would suggest, these routes dictate two distinctly different functions: routes that are handled locally and routes that will result in a packet being broadcast. Local routes provide the desired functionality — any prefix with a local route will have all IP addresses in the range processed by the kernel. Broadcast routes will result in a packet being broadcast to all IP addresses within the given range. Both types of routes are added automatically when an IP address is bound to an interface (and, when a range is bound to the loopback (lo) interface, the range itself will be added as a local route).</p>
    <div>
      <h3>Vulnerability discovery</h3>
      <a href="#vulnerability-discovery">
        
      </a>
    </div>
    <p>Deployments of QUIC are highly dependent on the load-balancing and packet forwarding infrastructure that they sit on top of. Although QUIC’s RFCs describe risks and mitigations, there can still be attack vectors depending on the nature of server deployments. The reporting researchers studied QUIC deployments across the Internet and discovered that sending a QUIC Initial packet to one of Cloudflare’s broadcast addresses triggered a flood of responses. The aggregate amount of response data exceeded the RFC's 3x amplification limit.</p><p>Taking a look at the local routing table of an example Cloudflare system, we see a potential culprit:</p>
            <pre><code>$ ip route show table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 192.0.2.2 dev eth0 proto kernel scope host src 192.0.2.2
broadcast 192.0.2.255 dev eth0 proto kernel scope link src 192.0.2.2
local 203.0.113.0 dev lo proto kernel scope host src 203.0.113.0
local 203.0.113.0/24 dev lo proto kernel scope host src 203.0.113.0
broadcast 203.0.113.255 dev lo proto kernel scope link src 203.0.113.0</code></pre>
            <p>On this example system, the anycast prefix 203.0.113.0/24 has been bound to the loopback interface (lo) through the use of standard tooling. Acting dutifully under the standards of IPv4, the tooling has assigned both special types of routes — a local one for the IP range itself and a broadcast one for the final address in the range — to the interface.</p><p>While traffic to the broadcast address of our router’s directly connected subnet is filtered as expected, broadcast traffic targeting our routed anycast prefixes still arrives at our servers themselves. Normally, broadcast traffic arriving at the loopback interface does little to cause problems. Services bound to a specific port across an entire range will receive data sent to the broadcast address and continue as normal. Unfortunately, this relatively simple trait breaks down when normal expectations are broken.</p><p>Cloudflare’s frontend consists of several worker processes, each of which independently binds to the entire anycast range on UDP port 443. In order to enable multiple processes to bind to the same port, we use the SO_REUSEPORT socket option. While SO_REUSEPORT <a href="https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/"><u>has additional benefits</u></a>, it also causes traffic sent to the broadcast address to be copied to every listener.</p><p>Each individual QUIC server worker operates in isolation. Each one reacted to the same client Initial, duplicating the work on the server side and generating response traffic to the client's IP address. Thus, a single packet could trigger a significant amplification. While specifics will vary by implementation, a typical one-listener-per-core stack (which sends retries in response to presumed timeouts) on a 128-core system could result in 384 replies being generated and sent for each packet sent to the broadcast address.</p><p>Although the researchers demonstrated this attack on QUIC, the underlying vulnerability can affect other UDP request/response protocols that use sockets in the same way.</p>
    <div>
      <h3>Mitigation</h3>
      <a href="#mitigation">
        
      </a>
    </div>
    <p>As a communication methodology, broadcast is not generally desirable for anycast prefixes. Thus, the easiest method to mitigate the issue was simply to disable broadcast functionality for the final address in each range.</p><p>Ideally, this would be done by modifying our tooling to only add the local routes in the local routing table, skipping the inclusion of the broadcast ones altogether. Unfortunately, the only practical mechanism to do so would involve patching and maintaining our own internal fork of the iproute2 suite, a rather heavy-handed solution for the problem at hand.</p><p>Instead, we decided to focus on removing the route itself. Similar to any other route, it can be removed using standard tooling:</p>
            <pre><code>$ sudo ip route del 203.0.113.255 table local</code></pre>
            <p>To do so at scale, we made a relatively minor change to our deployment system:</p>
            <pre><code>  {%- for lo_route in lo_routes %}
    {%- if lo_route.type == "broadcast" %}
        # All broadcast addresses are implicitly ipv4
        {%- do remove_route({
        "dev": "lo",
        "dst": lo_route.dst,
        "type": "broadcast",
        "src": lo_route.src,
        }) %}
    {%- endif %}
  {%- endfor %}</code></pre>
            <p>In doing so, we effectively ensure that all broadcast routes attached to the loopback interface are removed, mitigating the risk by ensuring that the specification-defined broadcast address is treated no differently than any other address in the range.</p>
    <div>
      <h3>Next steps </h3>
      <a href="#next-steps">
        
      </a>
    </div>
    <p>While the vulnerability specifically affected broadcast addresses within our anycast range, it likely expands past our infrastructure. Anyone with infrastructure that meets the relatively narrow criteria (a multi-worker, multi-listener UDP-based service that is bound to all IP addresses on a machine with routable IP prefixes attached in such a way as to expose the broadcast address) will be affected unless mitigations are in place. We encourage network administrators and security professionals to assess their systems for configurations that may present a local amplification attack vector.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[HTTP3]]></category>
            <category><![CDATA[Bug Bounty]]></category>
            <guid isPermaLink="false">6ZaxgQxDACeIF6MZAquLPV</guid>
            <dc:creator>Josephine Chow</dc:creator>
            <dc:creator>June Slater</dc:creator>
            <dc:creator>Bryton Herdes</dc:creator>
            <dc:creator>Lucas Pardue</dc:creator>
        </item>
        <item>
            <title><![CDATA[Improving platform resilience at Cloudflare through automation]]></title>
            <link>https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare/</link>
            <pubDate>Wed, 09 Oct 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ We realized that we need a way to automatically heal our platform from an operations perspective, and designed and built a workflow orchestration platform to provide these self-healing capabilities   ]]></description>
            <content:encoded><![CDATA[ <p>Failure is an expected state in production systems, and no predictable failure of either software or hardware components should result in a negative experience for users. The exact failure mode may vary, but certain remediation steps must be taken after detection. A common example is when an error occurs on a server, rendering it unfit for production workloads, and requiring action to recover.</p><p>When operating at Cloudflare’s scale, it is important to ensure that our platform is able to recover from faults seamlessly. It can be tempting to rely on the expertise of world-class engineers to remediate these faults, but this would be manual, repetitive, unlikely to produce enduring value, and not scaling. In one word: toil; not a viable solution at our scale and rate of growth.</p><p>In this post we discuss how we built the foundations to enable a more scalable future, and what problems it has immediately allowed us to solve.</p>
    <div>
      <h2>Growing pains</h2>
      <a href="#growing-pains">
        
      </a>
    </div>
    <p>The Cloudflare <a href="https://en.wikipedia.org/wiki/Site_reliability_engineering"><u>Site Reliability Engineering (SRE)</u></a> team builds and manages the platform that helps product teams deliver our extensive suite of offerings to customers. One important component of this platform is the collection of servers that power critical products such as Durable Objects, Workers, and DDoS mitigation. We also build and maintain foundational software services that power our product offerings, such as configuration management, provisioning, and IP address allocation systems.</p><p>As part of tactical operations work, we are often required to respond to failures in any of these components to minimize impact to users. Impact can vary from lack of access to a specific product feature, to total unavailability. The level of response required is determined by the priority, which is usually a reflection of the severity of impact on users. Lower-priority failures are more common — a server may run too hot, or experience an unrecoverable hardware error. Higher-priority failures are rare and are typically resolved via a well-defined incident response process, requiring collaboration with multiple other teams.</p><p>The commonality of lower-priority failures makes it obvious when the response required, as defined in runbooks, is “toilsome”. To reduce this toil, we had previously implemented a plethora of solutions to automate runbook actions such as manually-invoked shell scripts, cron jobs, and ad-hoc software services. These had grown organically over time and provided solutions on a case-by-case basis, which led to duplication of work, tight coupling, and lack of context awareness across the solutions.</p><p>We also care about how long it takes to resolve any potential impact on users. A resolution process which involves the manual invocation of a script relies on human action, increasing the Mean-Time-To-Resolve (MTTR) and leaving room for human error. This risks increasing the amount of errors we serve to users and degrading trust.</p><p>These problems proved that we needed a way to automatically heal these platform components. This especially applies to our servers, for which failure can cause impact across multiple product offerings. While we have <a href="https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer"><u>mechanisms to automatically steer traffic away</u></a> from these degraded servers, in some rare cases the breakage is sudden enough to be visible.</p>
    <div>
      <h2>Solving the problem</h2>
      <a href="#solving-the-problem">
        
      </a>
    </div>
    <p>To provide a more reliable platform, we needed a new component that provides a common ground for remediation efforts. This would remove duplication of work, provide unified context-awareness and increase development speed, which ultimately saves hours of engineering time and effort.</p><p>A good solution would not allow only the SRE team to auto-remediate, it would empower the entire company. The key to adding self-healing capability was a generic interface for all teams to self-service and quickly remediate failures at various levels: machine, service, network, or dependencies.</p><p>A good way to think about auto-remediation is in terms of workflows. A workflow is a sequence of steps to get to a desired outcome. This is not dissimilar to a manual shell script which executes what a human would otherwise do via runbook instructions. Because of this logical fit with workflows and durable execution, we decided to adopt an open-source platform called <a href="https://github.com/temporalio/temporal"><u>Temporal</u></a>. </p><p>The concept of durable execution is useful to gracefully manage infrastructure failures such as network outages and transient failures in external service endpoints. This capability meant we only needed to build a way to schedule “workflow” tasks and have the code provide reliability guarantees by default, using Temporal. This allowed us to focus on building out the orchestration system to support the control and flow of workflow execution in our data centers. </p><p><a href="https://learn.temporal.io/getting_started/go/first_program_in_go/"><u>Temporal’s documentation</u></a> provides a good introduction to writing Temporal workflows.</p>
    <div>
      <h2>Building an Automatic Remediation System</h2>
      <a href="#building-an-automatic-remediation-system">
        
      </a>
    </div>
    <p>Below, we describe how our automatic remediation system works. It is essentially a way to schedule tasks across our global network with built-in reliability guarantees. With this system, teams can serve their customers more reliably. An unexpected failure mode can be recognized and immediately mitigated, while the root cause can be determined later via a more detailed analysis.</p>
    <div>
      <h3>Step one: we need a coordinator</h3>
      <a href="#step-one-we-need-a-coordinator">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/FOpkEE13QcgwHJ9vhcIZj/5b0c5328ee5326794329a4a07c5db065/Building_on_Temporal_process.png" />
          </figure><p>After our initial testing of Temporal, it was now possible to write workflows. But we needed a way to schedule workflow tasks from other internal services. The coordinator was built to serve this purpose, and became the primary mechanism for the authorisation and scheduling of workflows. </p><p>The most important roles of the coordinator are authorisation, workflow task routing, and safety constraints enforcement. Each consumer is authorized via <a href="https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/"><u>mTLS authentication</u></a>, and the coordinator uses an ACL to determine whether to permit the execution of a workflow. An ACL configuration looks like the following example.</p>
            <pre><code>server_config {
    enable_tls = true
    [...]
    route_rule {
      name  = "global_get"
      method = "GET"
      route_patterns = ["/*"]
      uris = ["spiffe://example.com/worker-admin"]
    }
    route_rule {
      name = "global_post"
      method = "POST"
      route_patterns = ["/*"]
      uris = ["spiffe://example.com/worker-admin"]
      allow_public = true
    }
    route_rule {
      name = "public_access"
      method = "GET"
      route_patterns = ["/metrics"]
      uris = []
      allow_public = true
      skip_log_match = true
    }
}
</code></pre>
            <p>Each workflow specifies two key characteristics: where to run the tasks and the safety constraints, using an <a href="https://github.com/hashicorp/hcl"><u>HCL</u></a> configuration file. Example constraints could be whether to run on only a specific node type (such as a database), or if multiple parallel executions are allowed: if a task has been triggered too many times, that is a sign of a wider problem that might require human intervention. The coordinator uses the Temporal <a href="https://docs.temporal.io/visibility"><u>Visibility API</u></a> to determine the current state of the executions in the Temporal cluster.</p><p>An example of a configuration file is shown below:</p>
            <pre><code>task_queue_target = "&lt;target&gt;"

# The following entries will ensure that
# 1. This workflow is not run at the same time in a 15m window.
# 2. This workflow will not run more than once an hour.
# 3. This workflow will not run more than 3 times in one day.
#
constraint {
    kind = "concurency"
    value = "1"
    period = "15m"
}

constraint {
    kind = "maxExecution"
    value = "1"
    period = "1h"
}

constraint {
    kind = "maxExecution"
    value = "3"
    period = "24h"
    is_global = true
}
</code></pre>
            
    <div>
      <h3>Step two: Task Routing is amazing</h3>
      <a href="#step-two-task-routing-is-amazing">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4wOIwKfkKzp7k46Z6tPuhW/f35667a65872cf7a90fc9c03f38a48d5/Task_Routing_is_amazing_process.png" />
          </figure><p>An unforeseen benefit of using a central Temporal cluster was the discovery of Task Routing. This feature allows us to schedule a Workflow/Activity on any server that has a running Temporal Worker, and further segment by the type of server, its location, etc. For this reason, we have three primary task queues — the general queue in which tasks can be executed by any worker in the datacenter, the node type queue in which tasks can only be executed by a specific node type in the datacenter, and the individual node queue where we target a specific node for task execution.</p><p>We rely on this heavily to ensure the speed and efficiency of automated remediation. Certain tasks can be run in datacenters with known low latency to an external resource, or a node type with better performance than others (due to differences in the underlying hardware). This reduces the amount of failure and latency we see overall in task executions. Sometimes we are also constrained by certain types of tasks that can only run on a certain node type, such as a database.</p><p>Task Routing also means that we can configure certain task queues to have a higher priority for execution, although this is not a feature we have needed so far. A drawback of task routing is that every Workflow/Activity needs to be registered to the target task queue, which is a common gotcha. Thankfully, it is possible to catch this failure condition with proper testing.</p>
    <div>
      <h3>Step three: when/how to self-heal?</h3>
      <a href="#step-three-when-how-to-self-heal">
        
      </a>
    </div>
    <p>None of this would be relevant if we didn’t put it to good use. A primary design goal for the platform was to ensure we had easy, quick ways to trigger workflows on the most important failure conditions. The next step was to determine what the best sources to trigger the actions were. The answer to this was simple: we could trigger workflows from anywhere as long as they are properly authorized and detect the failure conditions accurately.</p><p>Example triggers are an alerting system, a log tailer, a health check daemon, or an authorized engineer via a chatbot. Such flexibility allows a high level of reuse, and permits to invest more in workflow quality and reliability.</p><p>As part of the solution, we built a daemon that is able to poll a signal source for any unwanted condition and trigger a configured workflow. We have initially found <a href="https://blog.cloudflare.com/how-cloudflare-runs-prometheus-at-scale"><u>Prometheus</u></a> useful as a source because it contains both service-level and hardware/system-level metrics. We are also exploring more event-based trigger mechanisms, which could eliminate the need to use precious system resources to poll for metrics.</p><p>We already had internal services that are able to detect widespread failure conditions for our customers, but were only able to page a human. With the adoption of auto-remediation, these systems are now able to react automatically. This ability to create an automatic feedback loop with our customers is the cornerstone of these self-healing capabilities, and we continue to work on stronger signals, faster reaction times, and better prevention of future occurrences.</p><p>The most exciting part, however, is the future possibility. Every customer cares about any negative impact from Cloudflare. With this platform we can onboard several services (especially those that are foundational for the critical path) and ensure we react quickly to any failure conditions, even before there is any visible impact.</p>
    <div>
      <h3>Step four: packaging and deployment</h3>
      <a href="#step-four-packaging-and-deployment">
        
      </a>
    </div>
    <p>The whole system is written in <a href="https://go.dev/"><u>golang</u></a>, and a single binary can implement each role. We distribute it as an apt package or a container for maximum ease of deployment.</p><p>We deploy a Temporal-based worker to every server we intend to run tasks on, and a daemon in datacenters where we intend to automatically trigger workflows based on the local conditions. The coordinator is more nuanced since we rely on task routing and can trigger from a central coordinator, but we have also found value in running coordinators locally in the datacenters. This is especially useful in datacenters with less capacity or degraded performance, removing the need for a round-trip to schedule the workflows.</p>
    <div>
      <h3>Step five: test, test, test</h3>
      <a href="#step-five-test-test-test">
        
      </a>
    </div>
    <p>Temporal provides native mechanisms to test an entire workflow, via a <a href="https://docs.temporal.io/develop/go/testing-suite"><u>comprehensive test suite</u></a> that supports end-to-end, integration, and unit testing, which we used extensively to prevent regressions while developing. We also ensured proper test coverage for all the critical platform components, especially the coordinator.</p><p>Despite the ease of written tests, we quickly discovered that they were not enough. After writing workflows, engineers need an environment as close as possible to the target conditions. This is why we configured our staging environments to support quick and efficient testing. These environments receive the latest changes and point to a different (staging) Temporal cluster, which enables experimentation and easy validation of changes.</p><p>After a workflow is validated in the staging environment, we can then do a full release to production. It seems obvious, but catching simple configuration errors before releasing has saved us many hours in development/change-related-task time.</p>
    <div>
      <h2>Deploying to production</h2>
      <a href="#deploying-to-production">
        
      </a>
    </div>
    <p>As you can guess from the title of this post, we put this in production to automatically react to server-specific errors and unrecoverable failures. To this end, we have a set of services that are able to detect single-server failure conditions based on analyzed traffic data. After deployment, we have successfully mitigated potential impact by taking any errant single sources of failure out of production.</p><p>We have also created a set of workflows to reduce internal toil and improve efficiency. These workflows can automatically test pull requests on target machines, wipe and reset servers after experiments are concluded, and take away manual processes that cost many hours in toil.</p><p>Building a system that is maintained by several SRE teams has allowed us to iterate faster, and rapidly tackle long-standing problems. We have set ambitious goals regarding toil elimination and are on course to achieve them, which will allow us to scale faster by eliminating the human bottleneck.</p>
    <div>
      <h2>Looking to the future</h2>
      <a href="#looking-to-the-future">
        
      </a>
    </div>
    <p>Our immediate plans are to leverage this system to provide a more reliable platform for our customers and drastically reduce operational toil, freeing up engineering resources to tackle larger-scale problems. We also intend to leverage more Temporal features such as <a href="https://docs.temporal.io/develop/go/versioning"><u>Workflow Versioning</u></a>, which will simplify the process of making changes to workflows by ensuring that triggered workflows run expected versions. </p><p> We are also interested in how others are solving problems using durable execution platforms such as Temporal, and general strategies to eliminate toil. If you would like to discuss this further, feel free to reach out on the <a href="https://community.cloudflare.com"><u>Cloudflare Community</u></a> and start a conversation!</p><p>If you’re interested in contributing to projects that help build a better Internet, <a href="https://www.cloudflare.com/en-gb/careers/jobs/?department=Engineering&amp;location=default"><u>our engineering teams are hiring</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Engineering]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">2i9tHkPAfy7GxYAioGM100</guid>
            <dc:creator>Opeyemi Onikute</dc:creator>
        </item>
        <item>
            <title><![CDATA[Thermal design supporting Gen 12 hardware: cool, efficient and reliable]]></title>
            <link>https://blog.cloudflare.com/thermal-design-supporting-gen-12-hardware-cool-efficient-and-reliable/</link>
            <pubDate>Mon, 07 Oct 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ Great thermal solutions play a crucial role in hardware reliability and performance. Gen 12 servers have implemented an exhaustive thermal analysis to ensure optimal operations within a wide variety of temperature conditions and use cases. By implementing new design and control features for improved power efficiency on the compute nodes we also enabled the support of powerful accelerators to serve our customers. ]]></description>
            <content:encoded><![CDATA[ <p>In the dynamic evolution of AI and cloud computing, the deployment of efficient and reliable hardware is critical. As we roll out our <a href="https://blog.cloudflare.com/cloudflare-gen-12-server-bigger-better-cooler-in-a-2u1n-form-factor/"><u>Gen 12 hardware</u></a> across hundreds of cities worldwide, the challenge of maintaining optimal thermal performance becomes essential. This blog post provides a deep dive into the robust thermal design that supports our newest Gen 12 server hardware, ensuring it remains reliable, efficient, and cool (pun very much intended).</p>
    <div>
      <h2>The importance of thermal design for hardware electronics</h2>
      <a href="#the-importance-of-thermal-design-for-hardware-electronics">
        
      </a>
    </div>
    <p>Generally speaking, a server has five core resources: CPU (computing power), RAM (short term memory), SSD (long term storage), NIC (Network Interface Controller, connectivity beyond the server), and GPU (for AI/ML computations). Each of these components can withstand different temperature limits based on their design, materials, location within the server, and most importantly, the power they are designed to work at. This final criteria is known as thermal design power (TDP).</p><p>The reason why TDP is so important is closely related to the <a href="https://chem.libretexts.org/Bookshelves/Physical_and_Theoretical_Chemistry_Textbook_Maps/Supplemental_Modules_(Physical_and_Theoretical_Chemistry)/Thermodynamics/The_Four_Laws_of_Thermodynamics/First_Law_of_Thermodynamics"><u>first law of thermodynamics</u></a>, which states that energy cannot be created or destroyed, only transformed. In semiconductors, electrical energy is converted into heat, and TDP measures the maximum heat output that needs to be managed to ensure proper functioning.</p><p>Back in December 2023, we <a href="https://blog.cloudflare.com/cloudflare-gen-12-server-bigger-better-cooler-in-a-2u1n-form-factor"><u>talked</u></a> about our decision to move to a 2U form factor, doubling the height of the server chassis to optimize rack density and increase cooling capacity. In this post, we want to share more details on how this additional space is being used to improve performance and reliability supporting up to three times more total system power.</p>
    <div>
      <h2>Standardization</h2>
      <a href="#standardization">
        
      </a>
    </div>
    <p>In order to support our multi-vendor strategy that mitigates supply chain risks ensuring continuity for our infrastructure, we introduced our own thermal specification to standardize thermal design and system performance. At Cloudflare, we find significant value in building customized hardware optimized for our unique workloads and applications, and we are very fortunate to partner with great hardware vendors who understand and support this vision. However, partnering with multiple vendors can introduce design variables that Cloudflare then controls for consistency within a hardware generation. Some of the most relevant requirements we include in our thermal specification are:</p><ul><li><p><b>Ambient conditions:</b> Given our globally distributed footprint with presence <a href="https://www.cloudflare.com/network/"><u>in over 330 cities</u></a>, environmental conditions can vary significantly.  Hence, servers in our fleet can experience a wide range of temperatures, typically ranging between 28 to 35°C. Therefore, our systems are designed and validated to operate with no issue over temperature ranges from 5 to 40°C (following the <a href="https://xp20.ashrae.org/datacom1_4th/ReferenceCard.pdf"><u>ASHRAE A3</u></a> definition).</p></li><li><p><b>Thermal margins:</b> Cloudflare designs with clear requirements for temperature limits on different operating conditions, simulating peak stress, average workloads, and idle conditions. This allows Cloudflare to validate that the system won’t experience thermal throttling, which is a power management control mechanism used to protect electronics from high temperatures.</p></li><li><p><b>Fan failure support to increase system reliability:</b> This new generation of servers is 100% air cooled. As such, the algorithm that controls fan speed based on critical component temperature needs to be optimized to support continuous operation over the server life cycle. Even though fans are designed with a high (up to seven years) mean time between failure (MTBF), we know fans can and do fail. Losing a server's worth of capacity due to thermal risks caused by a single fan failure is expensive. Cloudflare requires the server to continue to operate with no issue even in the event of one fan failure. Each Gen 12 server contains four axial fans providing the extra cooling capacity to prevent failures.</p></li><li><p><b>Maximum power used to cool the system:</b> Because our goal is to serve more Internet traffic using less power, we aim to ensure the hardware we deploy is using power efficiently. Great thermal management must consider the overall cost of cooling relative to the total system power input. It is inefficient to burn power consumption on cooling instead of compute. Thermal solutions should look at the hardware architecture holistically and implement mechanical modifications to the system design in order to optimize airflow and cooling capacity before considering increasing fan speed, as fan power consumption proportionally scales to the cube of its rotational speed. (For example, running the fans at twice (2x) the rotational speed would consume 8x more power,)</p></li></ul>
    <div>
      <h2>System layout</h2>
      <a href="#system-layout">
        
      </a>
    </div>
    <p>Placing each component strategically within the server will also influence the thermal performance of the system. For this generation of servers, we made several internal layout decisions, where the final component placement takes into consideration optimal airflow patterns, preventing pre-heated air from affecting equipment in the rear end of the chassis. </p><p>Bigger and more powerful fans were selected in order to take advantage of the additional volume available in a 2U form factor. Growing from 40 to 80 millimeters, a single fan can provide up to four times more airflow. Hence, bigger fans can run at slower speeds to provide the required airflow to cool down the same components, significantly improving power efficiency. </p><p>The Extended Volume Air Cooled (EVAC) heatsink was optimized for Gen 12 hardware, and is designed with increased surface area to maximize heat transfer. It uses heatpipes to move the heat effectively away from the CPU to the extended fin region that sits immediately in front of the fans as shown in the picture below.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5jC8iqP4QeyZ6EfJsQo9Fi/c5ff4981a3f5bb70dfa2576f864df5e1/BLOG-2444_2.png" />
          </figure><p><sub><i>EVAC heatsink installed in one of our Gen 12 servers. The extended fin region sits right in front of the axial fans. (Photo courtesy of vendor.)</i></sub></p><p>The combination of optimized heatsink design and selection of high-performing fans is expected to significantly reduce the power used for cooling the system. These savings will vary depending on ambient conditions and system stress, but under a typical stress scenario at 25°C ambient temperature, power savings could be as much as 50%.</p><p>Additionally, we ensured that the critical components in the rear section of the system, such as the NIC and <a href="https://blog.cloudflare.com/introducing-the-project-argus-datacenter-ready-secure-control-module-design-specification/"><u>DC-SCM</u></a>, were positioned away from the heatsink to promote the use of cooler available air within the system. Learning from past experience, the NIC temperature is monitored by the Baseboard Management Controller (BMC), which provides remote access to the server for administrative tasks and monitoring health metrics. Because the NIC has a built-in feature to protect itself from overheating by going into standby mode when the chip temperature reaches critical limits, it is important to provide air at the lowest possible temperature. As a reference, the temperature of the air right behind the CPU heatsink can reach 70°C or higher, whereas behind the memory banks, it would reach about 55°C under the same circumstances. The image below shows the internal placement of the most relevant components considered while building the thermal solution.  </p><p>Using air as cold as possible to cool down any component will increase overall system reliability, preventing potential thermal issues and unplanned system shutdowns. That’s why our fan algorithm uses every thermal sensor available to ensure thermal health while using the minimum possible amount of energy.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4O9x7Xe3PxR1iU1gNCEHIg/86a8e4a89d069f3f8d41c5c0a83ea00b/BLOG-2444_3.png" />
          </figure><p><sub><i>Components inside the compute server from one of our vendors, viewed from the rear of the server. (Illustration courtesy of vendor.)</i></sub></p><table><tr><td><p>1️. Host Processor Module (HPM)</p></td><td><p>8. Power Distribution Board (PDB)</p></td></tr><tr><td><p>2️. DIMMs (x12) </p></td><td><p>9. GPUs (up to 2)</p></td></tr><tr><td><p>3️. CPU (under CPU heatsink)</p></td><td><p>10. GPU riser card</p></td></tr><tr><td><p>4. CPU heatsink</p></td><td><p>11. GPU riser cage</p></td></tr><tr><td><p>5. System fans (x4: 80mm, dual rotor)</p></td><td><p>12. Power Supply Units, PSUs (x2)</p></td></tr><tr><td><p>6. Bracket with power button and intrusion switch</p></td><td><p>13. DC-SCM 2.0 module</p></td></tr><tr><td><p>7. E1.S SSD </p></td><td><p>14. OCP 3.0 module</p></td></tr></table>
    <div>
      <h2>Making hardware flexible</h2>
      <a href="#making-hardware-flexible">
        
      </a>
    </div>
    <p>With the same thought process of optimizing system layout, we decided to use a PCIe riser above the Power Supply Units (PSUs), enabling the support of up to 2x single wide GPU add-in cards. Once again, the combination of high-performing fans with strategic system architecture gave us the capability to add up to 400W to the original power envelope and incorporate accelerators used in our new and recently announced AI and ML features. </p><p>Hardware lead times are typically long, certainly when compared to software development. Therefore, a reliable strategy for hardware flexibility is imperative in this rapidly changing environment for specialized computing. When we started evaluating Gen 12 hardware architecture and early concept design, we didn’t know for sure we would be needing to implement GPUs for this generation, let alone how many or which type. However, highly efficient design and intentional due diligence analyzing hypothetical use cases help ensure flexibility and scalability of our thermal solution, supporting new requirements from our product teams, and ultimately providing the best solutions to our customers.</p>
    <div>
      <h2>Rack-integrated solutions</h2>
      <a href="#rack-integrated-solutions">
        
      </a>
    </div>
    <p>We are also increasing the volume of integrated racks shipped to our global colocation facilities. Due to the expected increase in rack shipments, it is now more important that we also increase the corresponding mechanical and thermal test coverage from system level (L10) to rack level (L11).</p><p>Since our servers don’t use the full depth of a standard rack in order to leave room for cable management and Power Distribution Units (PDUs), there is another fluid mechanics factor that we need to consider to improve our holistic solution. </p><p>We design our hardware based on one of the most typical data center architectures, which have alternating cold and hot aisles. Fans at the front of the server pull in cold air from the corresponding aisle, the air then flows through the server, cooling down the internal components and the hot air is exhausted into the adjacent aisle, as illustrated in the diagram below.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3rxT3iQ1d2ObzYRzH5tMVc/78e372f64e79e0c1c773b9d108514d01/BLOG-2444_4.png" />
          </figure><p><sub><i>A conventional air-flow diagram of a standard server where the cold air enters from the front of the server and hot air leaves through the rear side of the system. </i></sub></p><p>In fluid dynamics, the minimum effort principle will drive fluids (air in this case) to move where there is less resistance — i.e. wherever it takes less energy to get from point A to point B. With the help of fans forcing air to flow inside the server and pushing it through the rear, the more crowded systems will naturally get less air than those with more space where the air can move around. Since we need more airflow to pass through the systems with higher power demands, we’ve also ensured that the rack configuration keeps these systems in the bottom of the rack where air tends to be at a lower temperature. Remember that heat rises, so even within the cold aisle, there can be a small but important temperature difference between the bottom and the top section of the rack. It is our duty as hardware engineers to use thermodynamics in our favor. </p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Our new generation of hardware is live in our data centers and it represents a significant leap forward in our efficiency, reliability, and sustainability commitments. Combining optimal heat sink design, thoughtful fan selection, and meticulous system layout and hardware architecture, we are confident that these new servers will operate smoothly in our global network with diverse environmental conditions, maintaining optimal performance of our Connectivity Cloud. </p><p><a href="https://www.cloudflare.com/careers/jobs/"><u>Come join us</u></a> at Cloudflare to help deliver a better Internet!</p> ]]></content:encoded>
            <category><![CDATA[Hardware]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Cloudflare Network]]></category>
            <guid isPermaLink="false">5GRHH8385Hxg3UHjINuHz8</guid>
            <dc:creator>Leslye Paniagua</dc:creator>
        </item>
        <item>
            <title><![CDATA[Removing uncertainty through "what-if" capacity planning]]></title>
            <link>https://blog.cloudflare.com/scenario-planner/</link>
            <pubDate>Fri, 20 Sep 2024 14:01:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare’s Capacity Planning team discusses planning for “what-if” type scenarios, and how they’ve introduced a new “Scenario Planner” system to quickly model hypothetical future changes ]]></description>
            <content:encoded><![CDATA[ <p>Infrastructure planning for a network serving more than 81 million requests at peak and which is globally distributed across <a href="https://www.cloudflare.com/network/"><u>more than 330 cities in 120+ countries</u></a> is complex. The capacity planning team at Cloudflare ensures there is enough capacity in place all over the world so that our customers have one less thing to worry about - our infrastructure, which should just work. Through our processes, the team puts careful consideration into “what-ifs”. What if something unexpected happens and <a href="https://blog.cloudflare.com/post-mortem-on-cloudflare-control-plane-and-analytics-outage/"><u>one of our data centers fails</u></a>? What if one of our largest customers triples, or quadruples their request count?  Across a gamut of scenarios like these, the team works to understand where traffic will be served from and how the Cloudflare customer experience may change.</p><p>This blog post gives a look behind the curtain of how these scenarios are modeled at Cloudflare, and why it's so critical for our customers.</p>
    <div>
      <h2>Scenario planning and our customers</h2>
      <a href="#scenario-planning-and-our-customers">
        
      </a>
    </div>
    <p>Cloudflare customers rely on the data centers that Cloudflare has deployed all over the world, placing us within 50 ms of approximately 95% of the Internet-connected population globally. But round-trip time to our end users means little if those data centers don’t have the capacity to serve requests. Cloudflare has invested deeply into systems that are working around the clock to optimize the requests flowing through our network because we know that failures happen all the time: the Internet can be a volatile place. See <a href="https://blog.cloudflare.com/backbone2024"><u>our blog post from August 2024</u></a> on how we handle this volatility in real time on our backbone, and our <a href="https://blog.cloudflare.com/meet-traffic-manager"><u>blog post from late 2023</u></a> about how another system, Traffic Manager, actively works in and between data centers, moving traffic to optimize the customer experience around constraints in our data centers. Both of these systems do a fantastic job in real time, but there is still a gap — what about over the long term?  </p><p>Most of the volatility that the above systems are built to manage is resolved within shorter time scales than which we build plans for. (There are, of course, some failures that are <a href="https://itweb.africa/content/O2rQGMAEyPGMd1ea"><u>exceptions</u></a>.) Most scenarios we model still need to take into account the state of our data centers in the future, as well as what actions systems like Traffic Manager will take during those periods.  But before getting into those constraints, it’s important to note how capacity planning measures things: in units of CPU Time, defined as the time that each request takes in the CPU.  This is done for the same reasons that <a href="https://blog.cloudflare.com/meet-traffic-manager"><u>Traffic Manager</u></a> uses CPU Time, in that it enables the team to 1) use a common unit across different types of customer workloads and 2) speak a common language with other teams and systems (like Traffic Manager). The same reasoning the Traffic Manager team cited <a href="https://blog.cloudflare.com/meet-traffic-manager"><u>in their own blog post</u></a> is equally applicable for capacity planning: </p><blockquote><p><i>…using requests per second as a metric isn’t accurate enough when actually moving traffic. The reason for this is that different customers have different resource costs to our service; a website served mainly from cache with the WAF deactivated is much cheaper CPU wise than a site with all WAF rules enabled and caching disabled. So we record the time that each request takes in the CPU. We can then aggregate the CPU time across each plan to find the CPU time usage per plan. We record the CPU time in ms, and take a per second value, resulting in a unit of milliseconds per second.</i></p></blockquote><p>This is important for customers for the same reason that the Traffic Manager team cited in their blog post as well: we can correlate CPU time to performance, specifically latency.</p><p>Now that we know our unit of measurement is CPU time, we need to set up our models with the new constraints associated with the change that we’re trying to model.  Specifically, there are a subset of constraints that we are particularly interested in because we know that they have the ability to impact our customers by impacting the availability of CPU in a data center.  These are split into two main inputs in our models: Supply and Demand.  We can think of these as “what-if” questions, such as the following examples:</p>
    <div>
      <h3>Demand what-ifs</h3>
      <a href="#demand-what-ifs">
        
      </a>
    </div>
    <ul><li><p>What if a new customer onboards to Cloudflare with a significant volume of requests and/or bytes?  </p></li><li><p>What if an existing customer increased its volume of requests and/or bytes by some multiplier (i.e. 2x, 3x, nx), at peak, for the next three months?</p></li><li><p>What if the growth rate, in number of requests and bytes, of all of our data centers worldwide increased from X to Y two months from now, indefinitely?</p></li><li><p>What if the growth rate, in number of requests and bytes, of data center facility A increased from X to Y one month from now?</p></li><li><p>What if traffic egressing from Cloudflare to a last-mile network shifted from one location (such as Boston) to another (such as New York City) next week?</p></li></ul>
    <div>
      <h3>Supply what-ifs</h3>
      <a href="#supply-what-ifs">
        
      </a>
    </div>
    <ul><li><p>What if data center facility A lost some or all of its available servers two months from now?</p></li><li><p>What if we added X servers to data center facility A today?</p></li><li><p>What if some or all of our connectivity to other ASNs (<a href="https://www.cloudflare.com/network/"><u>12,500 Networks/nearly 300 Tbps</u></a>) failed now?</p></li></ul>
    <div>
      <h3>Output</h3>
      <a href="#output">
        
      </a>
    </div>
    <p>For any one of these, or a combination of them, in our model’s output, we aim to provide answers to the following: </p><ul><li><p>What will the overall capacity picture look like over time? </p></li><li><p>Where will the traffic go? </p></li><li><p>How will this impact our costs?</p></li><li><p>Will we need to deploy additional servers to handle the increased load?</p></li></ul><p>Given these sets of questions and outputs, manually creating a model to answer each of these questions, or a combination of these questions, quickly becomes an operational burden for any team.  This is what led us to launch “Scenario Planner”.</p>
    <div>
      <h2>Scenario Planner</h2>
      <a href="#scenario-planner">
        
      </a>
    </div>
    <p>In August 2024, the infrastructure team finished building “Scenario Planner”, a system that enables anyone at Cloudflare to simulate “what-ifs”. This provides our team the opportunity to quickly model hypothetical changes to our demand and supply metrics across time and in any of Cloudflare’s data centers. The core functionality of the system has to do with the same questions we need to answer in the manual models discussed above.  After we enter the changes we want to model, Scenario Planner converts from units that are commonly associated with each question to our common unit of measurement: CPU Time. These inputs are then used to model the updated capacity across all of our data centers, including how demand may be distributed in cases where capacity constraints may start impacting performance in a particular location.  As we know, if that happens then it triggers Traffic Manager to serve some portion of those requests from a nearby location to minimize impact on customers and user experience.</p>
    <div>
      <h3>Updated demand questions with inputs</h3>
      <a href="#updated-demand-questions-with-inputs">
        
      </a>
    </div>
    <ul><li><p><b>Question:</b> What if a new customer onboards to Cloudflare with a significant volume of requests?  </p></li><li><p><b>Input:</b> The new customer’s expected volume, geographic distribution, and timeframe of requests, converted to a count of virtual CPUs</p></li><li><p><b>Calculation(s)</b>: Scenario Planner converts from server count to CPU Time, and distributes the new demand across the regions selected according to the aggregate distribution of all customer usage.  </p></li></ul><p>
<br />
</p><ul><li><p><b>Question:</b> What if an existing customer increased its volume of requests and/or bytes by some multiplier (i.e. 2x, 3x, nx), at peak, for the next three months?</p></li><li><p><b>Input</b>: Select the customer name, the multiplier, and the timeframe</p></li><li><p><b>Calculation(s)</b>: Scenario Planner already has how the selected customer’s traffic is distributed across all data centers globally, so this involves simply multiplying that value by the multiplier selected by the user</p></li></ul><p>
<br />
</p><ul><li><p><b>Question:</b> What if the growth rate, in number of requests and bytes, of all of our data centers worldwide increased from X to Y two months from now, indefinitely?</p></li><li><p><b>Input:</b> Enter a new global growth rate and timeframe</p></li><li><p><b>Calculation(s)</b>: Scenario Planner distributes this growth across all data centers globally according to their current growth rate.  In other words, the global growth is an aggregation of all individual data center’s growth rates, and to apply a new “Global” growth rate, the system scales up each of the individual data center’s growth rates commensurate with the current distribution of growth.</p></li></ul><p>
<br />
</p><ul><li><p><b>Question:</b> What if the growth rate, in number of requests and bytes, of data center facility A increased from X to Y one month from now?</p></li><li><p><b>Input:</b> Select a data center facility, enter a new growth rate for that data center and the timeframe to apply that change across.</p></li><li><p><b>Calculation(s)</b>: Scenario Planner passes the new growth rate for the data center to the backend simulator, across the timeline specified by the user</p></li></ul><p>
<br />
</p>
    <div>
      <h3>Updated supply questions with inputs</h3>
      <a href="#updated-supply-questions-with-inputs">
        
      </a>
    </div>
    <ul><li><p><b>Question:</b> What if data center facility A lost some or all of its available servers two months from now?</p></li><li><p><b>Input</b>: Select a data center, and enter the number of servers to remove, or select to remove all servers in that location, as well as the timeframe for when those servers will not be available</p></li><li><p><b>Calculation(s)</b>: Scenario Planner converts the server count entered (including all servers in a given location) to CPU Time before passing to the backend</p></li></ul><p>
<br />
</p><ul><li><p><b>Question:</b> What if we added X servers to data center facility A today?</p></li><li><p><b>Input</b>: Select a data center, and enter the number of servers to add, as well as the timeline for when those servers will first go live</p></li><li><p><b>Calculation(s)</b>: Scenario Planner converts the server count entered (including all servers in a given location) to CPU Time before passing to the backend</p></li></ul><p>
<br />
</p><p>We made it simple for internal users to understand the impact of those changes because Scenario Planner outputs the same views that everyone who has seen our heatmaps and visual representations of our capacity status is familiar with. There are two main outputs the system provides: a heatmap and an “Expected Failovers” view. Below, we explore what these are, with some examples.</p>
    <div>
      <h3>Heatmap</h3>
      <a href="#heatmap">
        
      </a>
    </div>
    <p>Capacity planning evaluates its success on its ability to predict demand: we generally produce a weekly, monthly, and quarterly forecast of 12 months to three years worth of demand, and nearly all of our infrastructure decisions are based on the output of this forecast.  Scenario Planner provides a view of the results of those forecasts that are implemented via a heatmap: it shows our current state, as well as future planned server additions that are scheduled based on the forecast.  </p><p>Here is an example of our heatmap, showing some of our largest data centers in Eastern North America (ENAM). Ashburn is showing as yellow, briefly, because our capacity planning threshold for adding more server capacity to our data centers is 65% utilization (based on CPU time supply and demand): this gives the Cloudflare teams time to procure additional servers, ship them, install them, and bring them live <i>before</i> customers will be impacted and systems like Traffic Manager would begin triggering.  The little cloud icons indicate planned upgrades of varying sizes to get ahead of forecasted future demand well ahead of time to avoid customer performance degradation.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/PXIr1mpsrWPLmWpP9RCPg/5062269f1432a41c8177a7850243ab8c/BLOG-2554_2.png" />
          </figure><p><b></b></p><p>The question Scenario Planner answers then is how this view changes with a hypothetical scenario: What if our Ashburn, Miami, and Atlanta facilities shut down completely?  This is unlikely to happen, but we would expect to see enormous impact on the remaining largest facilities in ENAM. We’ll simulate all three of these failing at the same time, taking them offline indefinitely:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3apNpQAHPs9uY1QTAxOQ6d/6230e0950d3f70b50e17bfb10bca999c/BLOG-2554_3.png" />
          </figure>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6vfWrww4uT8VWtLa0vcDAM/dacf2bf102c8c38672ff08c48bc42542/BLOG-2554_4.png" />
          </figure>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7amWSND6PZ95oVJA5oO0Qc/588c7d7efcccfedca7cec240f830fa01/BLOG-2554_5.png" />
          </figure><p><b></b></p><p>This results in a view of our capacity through the rest of the year in the remaining large data centers in ENAM — capacity is clearly constrained: Traffic Manager will be working hard to mitigate any impact to customer performance if this were to happen. Our capacity view in the heatmap is capped at 75%: this is because Traffic Manager typically engages around this level of CPU utilization. Beyond 75%, Cloudflare customers may begin to experience increased latency, though this is dependent on the product and workload, and is in reality much more dynamic. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Tse9FexuemDHzExPYnY0i/0cdba516c0b33888b7079d435ec02fd6/BLOG-2554_6.png" />
          </figure><p><b></b></p><p>This outcome in the heatmap is not unexpected.  But now we typically get a follow-up question: clearly this traffic won’t fit in just Newark, Chicago, and Toronto, so where do all these requests get served from?  Enter the failover simulator: Capacity Planning has been simulating how Traffic Manager may work in the long term for quite a while, and for Scenario Planner, it was simple to extend this functionality to answer exactly this question.</p><p>There is currently no traffic being moved by Traffic Manager from these data centers, but our simulation shows a significant portion of the Atlanta CPU time being served from our DFW/Dallas data center as well as Newark (bottom pink), and Chicago (orange) through the rest of the year, during this hypothetical failure. With Scenario Planner, Capacity Planning can take this information and simulate multiple failures all over the world to understand the impact to customers, taking action to ensure that customers trusting Cloudflare with their web properties can expect high performance even in instances of major data center failures. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1iv8nxYdIXOJ626UHpUadI/8581d88d740b6369ba5fc8500d9c7d97/Screenshot_2024-09-18_at_10.27.50_PM.png" />
          </figure><p><b></b></p>
    <div>
      <h2>Planning with uncertainty</h2>
      <a href="#planning-with-uncertainty">
        
      </a>
    </div>
    <p>Capacity planning a large global network comes with plenty of uncertainties. Scenario Planner is one example of the work the Capacity Planning team is doing to ensure that the millions of web properties our customers entrust to Cloudflare can expect consistent, top tier performance all over the world.</p><p>The Capacity Planning team is hiring — check out the <a href="https://www.cloudflare.com/careers/"><u>Cloudflare careers page</u></a> and <a href="https://www.cloudflare.com/careers/jobs/?title=capacity"><u>search for open roles on the Capacity Planning team</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Data Center]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Cloudflare Network]]></category>
            <category><![CDATA[Network Services]]></category>
            <guid isPermaLink="false">13ueCTf2YFQfu3c1KeOlWj</guid>
            <dc:creator>Curt Robords</dc:creator>
        </item>
        <item>
            <title><![CDATA[Oxy: Fish/Bumblebee/Splicer subsystems to improve reliability]]></title>
            <link>https://blog.cloudflare.com/oxy-fish-bumblebee-splicer-subsystems-to-improve-reliability/</link>
            <pubDate>Thu, 20 Apr 2023 13:00:00 GMT</pubDate>
            <description><![CDATA[ We split a proxy application into multiple services to improve development agility and reliability. This blog also shares some common patterns we are leveraging to design a system supporting zero-downtime restart ]]></description>
            <content:encoded><![CDATA[ <p></p><p>At Cloudflare, we are building proxy applications on top of <a href="/introducing-oxy/">Oxy</a> that must be able to handle a <i>huge</i> amount of traffic. Besides high performance requirements, the applications must also be resilient against crashes or reloads. As the framework evolves, the complexity also increases. While migrating WARP to support soft-unicast (<a href="/cloudflare-servers-dont-own-ips-anymore/">Cloudflare servers don't own IPs anymore</a>), we needed to add different functionalities to our proxy framework. Those additions increased not only the code size but also resource usage and states required to be <a href="/oxy-the-journey-of-graceful-restarts/">preserved between process upgrades</a>.</p><p>To address those issues, we opted to split a big proxy process into smaller, specialized services. Following the Unix philosophy, each service should have a single responsibility, and it must do it well. In this blog post, we will talk about how our proxy interacts with three different services - Splicer (which pipes data between sockets), Bumblebee (which upgrades an IP flow to a TCP socket), and Fish (which handles layer 3 egress using soft-unicast IPs). Those three services help us to improve system reliability and efficiency as we migrated WARP to support soft-unicast.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7BnvngSFKAe8Lo7PrjUbIQ/770f180a0fd67ad8ad7c5914046614f8/image2-8.png" />
            
            </figure>
    <div>
      <h3>Splicer</h3>
      <a href="#splicer">
        
      </a>
    </div>
    <p>Most transmission tunnels in our proxy forward <a href="https://www.cloudflare.com/learning/network-layer/what-is-a-packet/">packets</a> without making any modifications. In other words, given two sockets, the proxy just relays the data between them: read from one socket and write to the other. This is a common pattern within Cloudflare, and we reimplement very similar functionality in separate projects. These projects often have their own tweaks for buffering, flushing, and terminating connections, but they also have to coordinate long-running proxy tasks with their process restart or upgrade handling, too.</p><p>Turning this into a service allows other applications to send a long-running proxying task to Splicer. The applications pass the two sockets to Splicer and they will not need to worry about keeping the connection alive when restart. After finishing the task, Splicer will return the two original sockets and the original metadata attached to the request, so the original application can inspect the final state of the sockets - <a href="/when-tcp-sockets-refuse-to-die/">for example using TCP_INFO</a> - and finalize audit logging if required.</p>
    <div>
      <h3>Bumblebee</h3>
      <a href="#bumblebee">
        
      </a>
    </div>
    <p>Many of Cloudflare’s on-ramps are IP-based (layer 3) but most of our services operate on <a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/">TCP</a> or <a href="https://www.cloudflare.com/learning/ddos/glossary/user-datagram-protocol-udp/">UDP</a> sockets (layer 4). To handle TCP termination, we want to create a <i>kernel</i> TCP socket from IP packets received from the client (and we can later forward this socket and an upstream socket to Splicer to proxy data between the eyeball and origin). Bumblebee performs the upgrades by spawning a thread in an anonymous network namespace with <a href="https://man7.org/linux/man-pages/man2/unshare.2.html">unshare</a> syscall, NAT-ing the IP packets, and using a tun device there to perform TCP three-way handshakes to a listener. You can find a more detailed write-up on how we upgrade an IP flows to a TCP stream <a href="/from-ip-packets-to-http-the-many-faces-of-our-oxy-framework/">here</a>.</p><p>In short, other services just need to pass a socket carrying the IP flow, and Bumblebee will upgrade it to a TCP socket, no user-space TCP stack involved! After the socket is created, Bumblebee will return the socket to the application requesting the upgrade. Again, the proxy can restart without breaking the connection as Bumblebee pipes the IP socket while Splicer handles the TCP ones.</p>
    <div>
      <h3>Fish</h3>
      <a href="#fish">
        
      </a>
    </div>
    <p>Fish forwards IP packets using <a href="/cloudflare-servers-dont-own-ips-anymore/">soft-unicast</a> IP space without upgrading them to layer 4 sockets. We previously implemented packet forwarding on shared IP space using iptables and conntrack. However, IP/port mapping management is not simple when you have many possible IPs to egress from and variable port assignments. Conntrack is highly configurable, but applying configuration through iptables rules requires careful coordination and debugging iptables execution can be challenging. Plus, relying on configuration when sending a packet through the network stack results in arcane failure modes when conntrack is unable to rewrite a packet to the exact IP or port range specified.</p><p>Fish attempts to overcome this problem by rewriting the packets and configuring conntrack using the netlink protocol. Put differently, a proxy application sends a socket containing IP packets from the client, together with the desired soft-unicast IP and port range, to Fish. Then, Fish will ensure to forward those packets to their destination. The client’s choice of IP address does not matter; Fish ensures that egressed IP packets have a unique five-tuple within the root network namespace and performs the necessary packet rewriting to maintain this isolation. Fish’s internal state is also survived across restarts.</p>
    <div>
      <h3>The Unix philosophy, manifest</h3>
      <a href="#the-unix-philosophy-manifest">
        
      </a>
    </div>
    <p>To sum up what we are having so far: instead of adding the functionalities directly to the proxy application, we create smaller and reusable services. It becomes possible to understand the failure cases present in a smaller system and design it to exhibit reliable behavior. Then if we can remove the subsystems of a larger system, we can apply this logic to those subsystems. By focusing on making the smaller service work correctly, we improve the whole system's reliability and development agility.</p><p>Although those three services’ business logics are different, you can notice what they do in common: receive sockets, or file descriptors, from other applications to allow them to restart. Those services can be restarted without dropping the connection too. Let’s take a look at how graceful restart and file descriptor passing work in our cases.</p>
    <div>
      <h3>File descriptor passing</h3>
      <a href="#file-descriptor-passing">
        
      </a>
    </div>
    <p>We use Unix Domain Sockets for interprocess communication. This is a common pattern for inter-process communication. Besides sending raw data, unix sockets also allow passing file descriptors between different processes. This is essential for our architecture as well as graceful restarts.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/43M4QXoTMDFZLlb9idMPbs/5f9ea51f3c055e0b7ee8723c97c9188a/image4-6.png" />
            
            </figure><p>There are two main ways to transfer a file descriptor: using pid_getfd syscall or <a href="/know-your-scm_rights/">SCM_RIGHTS</a>. The latter is the better choice for us here as the use cases gear toward the proxy application “giving” the sockets instead of the microservices “taking” them. Moreover, the first method would require special permission and a way for the proxy to signal which file descriptor to take.</p><p>Currently we have our own internal library named hot-potato to pass the file descriptors around as we use stable Rust in production. If you are fine with using nightly Rust, you may want to consider the <a href="https://doc.rust-lang.org/std/os/unix/net/struct.SocketAncillary.html">unix_socket_ancillary_data</a> feature. The linked blog post above about SCM_RIGHTS also explains how that can be implemented. Still, we also want to add some “interesting” details you may want to know before using your SCM_RIGHTS in production:</p><ul><li><p>There is a maximum number of file descriptors you can pass per messageThe limit is defined by the constant SCM_MAX_FD in the kernel. This is set to 253 since kernel version 2.6.38</p></li><li><p>Getting the peer credentials of a socket may be quite useful for observability in multi-tenant settings</p></li><li><p>A SCM_RIGHTS ancillary data forms a message boundary.</p></li><li><p>It is possible to send any file descriptors, not only socketsWe use this trick together with memfd_create to get around the maximum buffer size without implementing something like length-encoded frames. This also makes zero-copy message passing possible.</p></li></ul>
    <div>
      <h3>Graceful restart</h3>
      <a href="#graceful-restart">
        
      </a>
    </div>
    <p>We explored the general strategy for graceful restart in “<a href="/oxy-the-journey-of-graceful-restarts/">Oxy: the journey of graceful restarts</a>” blog. Let’s dive into how we leverage tokio and file descriptor passing to migrate all important states in the old process to the new one. We can terminate the old process almost instantly without leaving any connection behind.</p>
    <div>
      <h3>Passing states and file descriptors</h3>
      <a href="#passing-states-and-file-descriptors">
        
      </a>
    </div>
    <p>Applications like NGINX can be reloaded with no downtime. However, if there are pending requests then there will be lingering processes that handle those connections before they terminate. This is not ideal for observability. It can also cause performance degradation when the old processes start building up after consecutive restarts.</p><p>In three micro-services in this blog post, we use the state-passing concept, where the pending requests will be paused and transferred to the new process. The new process will pick up both new requests and the old ones immediately on start. This method indeed requires a higher complexity than keeping the old process running. At a high level, we have the following extra steps when the application receives an upgrade request (usually SIGHUP): pause all tasks, wait until all tasks (in groups) are paused, and send them to the new process.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4JKw9eZUPQspxBsmCKcwm6/5a83e36bcc5fb03531a89cd75da73158/Graceful-restart.png" />
            
            </figure>
    <div>
      <h3>WaitGroup using JoinSet</h3>
      <a href="#waitgroup-using-joinset">
        
      </a>
    </div>
    <p>Problem statement: we dynamically spawn different concurrent tasks, and each task can spawn new child tasks. We must wait for some of them to complete before continuing.</p><p>In other words, tasks can be managed as groups. In Go, waiting for a collection of tasks to complete is a solved problem with WaitGroup. We discussed a way to implement WaitGroup in Rust using channels in a <a href="/oxy-the-journey-of-graceful-restarts/">previous blog</a>. There also exist crates like waitgroup that simply use AtomicWaker. Another approach is using JoinSet, which may make the code more readable. Considering the below example, we group the requests using a JoinSet.</p>
            <pre><code>    let mut task_group = JoinSet::new();

    loop {
        // Receive the request from a listener
        let Some(request) = listener.recv().await else {
            println!("There is no more request");
            break;
        };
        // Spawn a task that will process request.
        // This returns immediately
        task_group.spawn(process_request(request));
    }

    // Wait for all requests to be completed before continue
    while task_group.join_next().await.is_some() {}</code></pre>
            <p>However, an obvious problem with this is if we receive a lot of requests then the JoinSet will need to keep the results for all of them. Let’s change the code to clean up the JoinSet as the application processes new requests, so we have lower memory pressure</p>
            <pre><code>    loop {
        tokio::select! {
            biased; // This is optional

            // Clean up the JoinSet as we go
            // Note: checking for is_empty is important ?
            _task_result = task_group.join_next(), if !task_group.is_empty() =&gt; {}

            req = listener.recv() =&gt; {
                let Some(request) = req else {
                    println!("There is no more request");
                    break;
                };
                task_group.spawn(process_request(request));
            }
        }
    }

    while task_group.join_next().await.is_some() {}</code></pre>
            
    <div>
      <h3>Cancellation</h3>
      <a href="#cancellation">
        
      </a>
    </div>
    <p>We want to pass the pending requests to the new process as soon as possible once the upgrade signal is received. This requires us to pause all requests we are processing. In other terms, to be able to implement graceful restart, we need to implement graceful shutdown. The <a href="https://tokio.rs/tokio/topics/shutdown">official tokio tutorial</a> already covered how this can be achieved by using channels. Of course, we must guarantee the tasks we are pausing are cancellation-safe. The paused results will be collected into the JoinSet, and we just need to pass them to the new process using file descriptor passing.</p><p>For example, in Bumblebee, a paused state will include the environment’s file descriptors, client socket, and the socket proxying IP flow. We also need to transfer the current NAT table to the new process, which could be larger than the socket buffer. So the NAT table state is encoded into an anonymous file descriptor, and we just need to pass the file descriptor to the new process.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>We considered how a complex proxy app can be divided into smaller components. Those components can run as new processes, allowing different life-times. Still, this type of architecture does incur additional costs: distributed tracing and inter-process communication. However, the costs are acceptable nonetheless considering the performance, maintainability, and reliability improvements. In the upcoming blog posts, we will talk about different debug tricks we learned when working with a large codebase with complex service interactions using tools like strace and eBPF.</p> ]]></content:encoded>
            <category><![CDATA[Proxying]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[Oxy]]></category>
            <guid isPermaLink="false">3xNFwkSFuO8BXQtaddgoVq</guid>
            <dc:creator>Quang Luong</dc:creator>
            <dc:creator>Chris Branch</dc:creator>
        </item>
        <item>
            <title><![CDATA[Oxy: the journey of graceful restarts]]></title>
            <link>https://blog.cloudflare.com/oxy-the-journey-of-graceful-restarts/</link>
            <pubDate>Tue, 04 Apr 2023 13:00:00 GMT</pubDate>
            <description><![CDATA[ Deploying new versions of long-lived server software while maintaining a reliable experience is challenging. For oxy, we established several development and operational patterns to increase reliability and reduce friction in deployments ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Any software under continuous development and improvement will eventually need a new version deployed to the systems running it. This can happen in several ways, depending on how much you care about things like reliability, availability, and correctness. When I started out in web development, I didn’t think about any of these qualities; I simply blasted my new code over FTP directly to my <code>/cgi-bin/</code> directory, which was the style at the time. For those of us producing desktop software, often you sidestep this entirely by having the user save their work, close the program and install an update – but they usually get to decide when this happens.</p><p>At Cloudflare we have to take this seriously. Our software is in constant use and cannot simply be stopped abruptly. A dropped HTTP request can cause an entire webpage to load incorrectly, and a broken connection can kick you out of a video call. Taking away reliability creates a vacuum filled only by user frustration.</p>
    <div>
      <h3>The limitations of the typical upgrade process</h3>
      <a href="#the-limitations-of-the-typical-upgrade-process">
        
      </a>
    </div>
    <p>There is no one right way to upgrade software reliably. <a href="https://www.erlang.org">Some programming languages</a> and environments make it easier than others, but in a Turing-complete language <a href="https://en.wikipedia.org/wiki/Halting_problem">few things are impossible</a>.</p><p>One popular and generally applicable approach is to start a new version of the software, make it responsible for a small number of tasks at first, and then gradually increase its workload until the new version is responsible for everything and the old version responsible for nothing. At that point, you can stop the old version.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3AkYGHsqbNozEYjlnqYAPA/4abd834e1a5117244cb5f79967e88937/image2-1.png" />
            
            </figure><p>Most of Cloudflare’s proxies follow a similar pattern: they receive connections or requests from many clients over the Internet, communicate with other internal services to decide how to serve the request, and fetch content over the Internet if we cannot serve it locally. In general, all of this work happens within the lifetime of a client’s connection. If we aren’t serving any clients, we aren’t doing any work.</p><p>The safest time to restart, therefore, is when there is nobody to interrupt. But does such a time really exist? The Internet operates 24 hours a day and many users rely on long-running connections for things like backups, real-time updates or remote shell sessions. Even if you defer restarts to a “quiet” period, the next-best strategy of “interrupt the fewest number of people possible” will fail when you have a critical security fix that needs to be deployed immediately.</p><p>Despite this challenge, we have to start somewhere. You rarely arrive at the perfect solution in your first try.</p>
    <div>
      <h3><a href="https://knowyourmeme.com/memes/flipping-tables-%E2%95%AF%E2%96%A1%E2%95%AF%EF%B8%B5-%E2%94%BB%E2%94%81%E2%94%BB">(╯°□°）╯︵ ┻━┻</a></h3>
      <a href="#">
        
      </a>
    </div>
    <p>We have previously blogged about <a href="/graceful-upgrades-in-go/">implementing graceful restarts in Cloudflare’s Go projects</a>, using a library called <a href="https://github.com/cloudflare/tableflip">tableflip</a>. This starts a new version of your program and allows the new version to signal to the old version that it started successfully, then lets the old version clear its workload. For a proxy like any Oxy application, that means the old version stops accepting new connections once the new version starts accepting connections, then drives its remaining connections to completion.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2yUYfctQn4Vpmwc3xiBEPi/4c5717abe754fd365aea93ea9bc2c933/image6-1.png" />
            
            </figure><p>This is the simplest case of the migration strategy previously described: the new version immediately takes all new connections, instead of a gradual rollout. But in aggregate across Cloudflare’s server fleet the upgrade process is spread across several hours and the result is as gradual as a deployment orchestrated by Kubernetes or similar.</p><p>tableflip also allows your program to bind to sockets, or to reuse the sockets opened by a previous instance. This enables the new instance to accept new connections on the same socket and let the old instance release that responsibility.</p><p>Oxy is a Rust project, so we can’t reuse tableflip. We rewrote the spawning/signaling section in Rust, but not the socket code. For that we had an alternative approach.</p>
    <div>
      <h3>Socket management with systemd</h3>
      <a href="#socket-management-with-systemd">
        
      </a>
    </div>
    <p>systemd is a widely used suite of programs for starting and managing all of the system software needed to run a useful Linux system. It is responsible for running software in the correct order – for example ensuring the network is ready before starting a program that needs network access – or running it only if it is needed by another program.</p><p>Socket management falls in this latter category, under the term ‘socket activation’. Its <a href="https://mgdm.net/weblog/systemd-socket-activation/">intended and original use is interesting</a> but ultimately irrelevant here; for our purposes, systemd is a mere socket manager. Many Cloudflare services configure their sockets using systemd .socket files, and when their service is started the socket is brought into the process with it. This is how we deploy most Oxy-based services, and Oxy has first-class support for sockets opened by systemd.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/374DW87mqCYpK3NDSWWWQe/2395b888ef72a70713da9b7cadb361cc/image4-1.png" />
            
            </figure><p>Using systemd decouples the lifetime of sockets from the lifetime of the Oxy application. When Oxy creates its sockets on startup, if you restart or temporarily stop the Oxy application the sockets are closed. When clients attempt to connect to the proxy during this time, they will get a very unfriendly “connection refused” error. If, however, systemd manages the socket, that socket remains open even while the Oxy application is stopped. Clients can still connect to the socket and those connections will be served as soon as the Oxy application starts up successfully.</p>
    <div>
      <h3>Channeling your inner WaitGroup</h3>
      <a href="#channeling-your-inner-waitgroup">
        
      </a>
    </div>
    <p>A useful piece of library code our Go projects use is <a href="https://gobyexample.com/waitgroups">WaitGroups</a>. These are essential in Go, where goroutines - asynchronously-running code blocks - are pervasive. Waiting for goroutines to complete before continuing another task is a common requirement. Even the example for tableflip uses them, to demonstrate how to wait for tasks to shut down cleanly before quitting your process.</p><p>There is not an out-of-the-box equivalent in <a href="https://tokio.rs">tokio</a> – the async Rust runtime Oxy uses – or async/await generally, so we had to create one ourselves. Fortunately, most of the building blocks to roll your own exist already. Tokio has <a href="https://docs.rs/tokio/latest/tokio/sync/mpsc/index.html">multi-producer, single consumer (MPSC) channels</a>, generally used by multiple tasks to push the results of work onto a queue for a single task to process, but we can exploit the fact that it signals to that single receiver when all the sender channels have been closed and no new messages are expected.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/696myOP10jhM6nQdMPOerD/59613bd231ef16df30683b277f5cb86f/image5-1.png" />
            
            </figure><p>To start, we create an MPSC channel. Each task takes a clone of the producer end of the channel, and when that task completes it closes its instance of the producer. When we want to wait for all of the tasks to complete, we await a result on the consumer end of the MPSC channel. When every instance of the producer channel is closed - i.e. all tasks have completed - the consumer receives a notification that all of the channels are closed. Closing the channel when a task completes is an automatic consequence of Rust’s <a href="https://doc.rust-lang.org/rust-by-example/scope/raii.html">RAII</a> rules. Because the language enforces this rule it is harder to write incorrect code, though in fact we need to write very little code at all.</p>
    <div>
      <h3>Getting feedback on failure</h3>
      <a href="#getting-feedback-on-failure">
        
      </a>
    </div>
    <p>Many programs that implement a graceful reload/restart mechanism use Unix signals to trigger the process to perform an action. Signals are an ancient technique introduced in early versions of Unix to <a href="https://lwn.net/Articles/414618/">solve a specific problem while creating dozens more</a>. A common pattern is to change a program’s configuration on disk, then send it a signal (often SIGHUP) which the program handles by reloading those configuration files.</p><p>The limitations of this technique are obvious as soon as you make a mistake in the configuration, or when an important file referenced in the configuration is deleted. You reload the program and wonder why it isn’t behaving as you expect. If an error is raised, you have to look in the program’s log output to find out.</p><p>This problem compounds when you use <a href="/future-proofing-saltstack/">an automated configuration management tool</a>. It is not useful if that tool makes a configuration change and reports that it successfully reloaded your program, when in fact the program failed to read the change. The only thing that was successful was sending the reload signal!</p><p>We solved this in Oxy by creating a Unix socket specifically for coordinating restarts, and adding a new mode to Oxy that triggers a restart. In this mode:</p><ol><li><p>The restarter process validates the configuration file.</p></li><li><p>It connects to the restart coordination socket defined in that file.</p></li><li><p>It sends a “restart requested” message.</p></li><li><p>The current proxy instance receives this message.</p></li><li><p>A new instance is started, inheriting a pipe it will use to notify its parent instance.</p></li><li><p>The current instance waits for the new instance to report success or fail.</p></li><li><p>The current instance sends a “restart response” message back to the restarter process, containing the result.</p></li><li><p>The restarter process reports this result back to the user, using exit codes for automated systems to detect failure.</p></li></ol>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24v87tIgWVdtxWE80HINrJ/4e64fff6fdb5591c8f8f36d2deb0ae7b/image3-1.png" />
            
            </figure><p>Now when we make a change to any of our Oxy applications, we can be confident that failures are detected using nothing more than our SREs’ existing tooling. This lets us discover failures earlier, narrow down root causes sooner, and avoid our systems getting into an inconsistent state.</p><p>This technique is described more generally in a coworker’s blog, <a href="https://blog.adamchalmers.com/signals-vs-servers/#a-better-way-control-servers">using an internal HTTP endpoint instead</a>. Yet HTTP is missing one important property of Unix sockets for the purpose of replacing signals. A user may only send a signal to a process if the process belongs to them - i.e. they started it - or if the user is root. This prevents another user logged into the same machine from you from terminating all of your processes. As Unix sockets are files, they also follow the Unix permission model. <a href="https://man7.org/linux/man-pages/man7/unix.7.html">Write permissions are required to connect to a socket</a>. Thus we can trivially reproduce the signals security model by making the restart coordination socket user writable only. (Root, as always, bypasses all permission checks.)</p>
    <div>
      <h3>Leave no connection behind</h3>
      <a href="#leave-no-connection-behind">
        
      </a>
    </div>
    <p>We have put a lot of effort into making restarts as graceful as possible, but there are still certain limitations. After restarting, eventually the old process has to terminate, to prevent a build-up of old processes after successive restarts consuming excessive memory and reducing the performance of other running services. There is an upper bound to how long we’ll let the old process run for; when this is reached, any connections remaining are forcibly broken.</p><p>The configuration changes that can be applied using graceful restart is limited by the design of systemd. While some configuration like resource limits can now be applied without restarting the service it applies to, others cannot; most significantly, new sockets. This is a problem inherent to the fork-and-inherit model.</p><p>For UDP-based protocols like HTTP/3, there is not even a concept of listener socket. The new process may open UDP sockets, but by default incoming packets are balanced between all open unconnected UDP sockets for a given address. How does the old process drain existing sessions without receiving packets intended for the new process and vice versa?</p><p>Is there a way to carry existing state to a new process to avoid some of these limitations? This is a hard problem to solve generally, and even in languages designed to support hot code upgrades there is some degree of running old tasks with old versions of code. Yet there are some common useful tasks that can be carried between processes so we can “interrupt the fewest number of people possible”.</p><p>Let’s not forget the unplanned outages: segfaults, oomkiller and other crashes. Thankfully rare in Rust code, but not impossible.</p><p>You can find the source for our Rust implementation of graceful restarts, named shellflip, in <a href="https://github.com/cloudflare/shellflip">its GitHub repository</a>. However, restarting correctly is just the first step of many needed to achieve our ultimate reliability goals. In a follow-up blog post we’ll talk about some creative solutions to these limitations.</p> ]]></content:encoded>
            <category><![CDATA[Oxy]]></category>
            <category><![CDATA[Proxying]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Edge]]></category>
            <guid isPermaLink="false">nebmQQHCFE8esMLxchEw9</guid>
            <dc:creator>Chris Branch</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Rollbacks for Workers Deployments]]></title>
            <link>https://blog.cloudflare.com/introducing-rollbacks-for-workers-deployments/</link>
            <pubDate>Mon, 03 Apr 2023 13:00:00 GMT</pubDate>
            <description><![CDATA[ Deployment rollbacks provide users the ability to quickly visualize and deploy past versions of their Workers, providing even more confidence in the deployment pipeline ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In November, 2022, we introduced <a href="/deployments-for-workers/">deployments for Workers</a>. Deployments are created as you make changes to a Worker. Each one is unique. These let you track changes to your Workers over time, seeing who made the changes, and where they came from.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kYW6tf3Hrwpv3PKkmMOa3/0be12df468ecd4a8a67c08f51104b314/image5.png" />
            
            </figure><p>When we made the announcement, we also said our intention was to build more functionality on top of deployments.</p><p>Today, we’re proud to release rollbacks for deployments.</p>
    <div>
      <h2>Rollbacks</h2>
      <a href="#rollbacks">
        
      </a>
    </div>
    <p>As nice as it would be to know that every deployment is perfect, it’s not always possible - for various reasons. Rollbacks provide a quick way to deploy past versions of a Worker - providing another layer of confidence when developing and deploying with Workers.</p>
    <div>
      <h3>Via the dashboard</h3>
      <a href="#via-the-dashboard">
        
      </a>
    </div>
    <p>In the dashboard, you can navigate to the <b>Deployments</b> tab. For each deployment that’s not the most recent, you should see a new icon on the far right of the deployment. Hovering over that icon will display the option to rollback to the specified deployment.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5U6DDnyubvT7UXnR7sRUDk/cf343fc7b07b51962c90647c62c557d2/image3.png" />
            
            </figure><p>Clicking on that will bring up a confirmation dialog, where you can enter a reason for rollback. This provides another mechanism of record-keeping and helps give more context for why the rollback was necessary.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4A9laQMfOq2KcbFvnMHeey/5a5dae8225559b93da09d0be590a241d/image2.png" />
            
            </figure><p>Once you enter a reason and confirm, a new rollback deployment will be created. This deployment has its own ID, but is a duplicate of the one you rolled back to. A message appears with the new deployment ID, as well as an icon showing the rollback message you entered above.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1tXWsG9Yhh00HF8D2P0EJY/fe9ff085d320216613bf3d9d215612cd/image6.png" />
            
            </figure>
    <div>
      <h3>Via Wrangler</h3>
      <a href="#via-wrangler">
        
      </a>
    </div>
    <p>With Wrangler version 2.13, rolling back deployments via Wrangler can be done via a new command - <code>wrangler rollback</code>. This command takes an optional ID to rollback to a specific deployment, but can also be run without an ID to rollback to the previous deployment. This provides an even faster way to rollback in a situation where you know that the previous deployment is the one that you want.</p><p>Just like the dashboard, when you initiate a rollback you will be prompted to add a rollback reason and to confirm the action.</p><p>In addition to <code>wrangler rollback</code> we’ve done some refactoring to the <code>wrangler deployments</code> command. Now you can run <code>wrangler deployments list</code> to view up to the last 10 deployments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZdZipCnby1lHSs3wuPMSy/6aa6aee246c7238bf9e3173aa5c0f098/image7-1.png" />
            
            </figure><p>Here, you can see two new annotations: <b>rollback from</b> and <b>message</b>. These match the dashboard experience, and provide more visibility into your deployment history.</p><p>To view an individual deployment, you can run wrangler deployments view. This will display the last deployment made, which is the active deployment. If you would like to see a specific deployment, you can run <code>wrangler deployments view [ID]</code>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6lNq1vG8GBcmNpC634bmvB/2e0f0f7544f2572a0a17d93f7c802249/image4.png" />
            
            </figure><p>We’ve updated this command to display more data like: compatibility date, usage model, and bindings. This additional data will help you to quickly visualize changes to Worker or to see more about a specific Worker deployment without having to open your editor and go through source code.</p>
    <div>
      <h2>Keep deploying!</h2>
      <a href="#keep-deploying">
        
      </a>
    </div>
    <p>We hope this feature provides even more confidence in deploying Workers, and encourages you to try it out! If you leverage the Cloudflare dashboard to manage deployments, you should have access immediately. Wrangler users will need to update to version 2.13 to see the new functionality.</p><p>Make sure to check out our updated <a href="https://developers.cloudflare.com/workers/platform/deployments/">deployments docs</a> for more information, as well as information on limitations to rollbacks. If you have any feedback, please let us know via <a href="https://docs.google.com/forms/d/e/1FAIpQLSfVRtmYOlzp6hJG50-8OfqpZameR2fd_5ySlmTlSeW5SSAzZw/viewform">this form</a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">5q5y2KunPqLJ3nnjxrIqRH</guid>
            <dc:creator>Adam Murray</dc:creator>
        </item>
        <item>
            <title><![CDATA[Oxy is Cloudflare's Rust-based next generation proxy framework]]></title>
            <link>https://blog.cloudflare.com/introducing-oxy/</link>
            <pubDate>Thu, 02 Mar 2023 15:05:00 GMT</pubDate>
            <description><![CDATA[ In this blog post, we are proud to introduce Oxy - our modern proxy framework, developed using the Rust programming language ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In this blog post, we are proud to introduce Oxy - our modern proxy framework, developed using the Rust programming language. Oxy is a foundation of several Cloudflare projects, including the <a href="https://www.cloudflare.com/products/zero-trust/gateway/">Zero Trust Gateway</a>, the iCloud Private Relay <a href="/icloud-private-relay/">second hop proxy</a>, and the internal <a href="/cloudflare-servers-dont-own-ips-anymore/">egress routing service</a>.</p><p>Oxy leverages our years of experience building high-load proxies to implement the latest communication protocols, enabling us to effortlessly build sophisticated services that can accommodate massive amounts of daily traffic.</p><p>We will be exploring Oxy in greater detail in upcoming technical blog posts, providing a comprehensive and in-depth look at its capabilities and potential applications. For now, let us embark on this journey and discover what Oxy is and how we built it.</p>
    <div>
      <h2>What Oxy does</h2>
      <a href="#what-oxy-does">
        
      </a>
    </div>
    <p>We refer to Oxy as our "next-generation proxy framework". But what do we really mean by “proxy framework”? Picture a server (like NGINX, that reader might be familiar with) that can proxy traffic with an array of protocols, including various predefined common traffic flow scenarios that enable you to route traffic to specific destinations or even egress with a different protocol than the one used for ingress. This server can be configured in many ways for specific flows and boasts tight integration with the surrounding infrastructure, whether telemetry consumers or networking services.</p><p>Now, take all of that and add in the ability to programmatically control every aspect of the proxying: protocol decapsulation, traffic analysis, routing, tunneling logic, DNS resolution, and so much more. And this is what Oxy proxy framework is: a feature-rich proxy server tightly integrated with our internal infrastructure that's customizable to meet application requirements, allowing engineers to tweak every component.</p><p>This design is in line with our belief in an iterative approach to development, where a basic solution is built first and then gradually improved over time. With Oxy, you can start with a basic solution that can be deployed to our servers and then add additional features as needed, taking advantage of the many extensibility points offered by Oxy. In fact, you can avoid writing any code, besides a few lines of bootstrap boilerplate and get a production-ready server with a wide variety of startup configuration options and traffic flow scenarios.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5nk7Ri6viC85BdWoRSiB9v/f40a9971fdad71cb07ee0b3aebf99fd9/image3-2.png" />
            
            </figure><p><i>High-level Oxy architecture</i></p><p>For example, suppose you'd like to implement an HTTP firewall. With Oxy, you can proxy HTTP(S) requests right out of the box, eliminating the need to write any code related to production services, such as request metrics and logs. You simply need to implement an Oxy hook handler for HTTP requests and responses. If you've used <a href="https://developers.cloudflare.com/workers/examples/respond-with-another-site/">Cloudflare Workers</a> before, then you should be familiar with this extensibility model.</p><p>Similarly, you can implement a <a href="https://en.wikipedia.org/wiki/OSI_model">layer 4</a> firewall by providing application hooks that handle ingress and egress connections. This goes beyond a simple block/accept scenario, as you can build authentication functionality or a traffic router that sends traffic to different destinations based on the geographical information of the ingress connection. The capabilities are incredibly rich, and we've made the extensibility model as ergonomic and flexible as possible. As an example, if information obtained from layer 4 is insufficient to make an informed firewall decision, the app can simply ask Oxy to decapsulate the traffic and process it with HTTP firewall.</p><p>The aforementioned scenarios are prevalent in many products we build at Cloudflare, so having a foundation that incorporates ready solutions is incredibly useful. This foundation has absorbed lots of experience we've gained over the years, taking care of many sharp and dark corners of high-load service programming. As a result, application implementers can stay focused on the business logic of their application with Oxy taking care of the rest. In fact, we've been able to create a few privacy proxy applications using Oxy that now serve massive amounts of traffic in production with less than a couple of hundred lines of code. This is something that would have taken multiple orders of magnitude more time and lines of code before.</p><p>As previously mentioned, we'll dive deeper into the technical aspects in future blog posts. However, for now, we'd like to provide a brief overview of Oxy's capabilities. This will give you a glimpse of the many ways in which Oxy can be customized and used.</p>
    <div>
      <h3>On-ramps</h3>
      <a href="#on-ramps">
        
      </a>
    </div>
    <p>On-ramp defines a combination of transport layer socket type and protocols that server listeners can use for ingress traffic.</p><p>Oxy supports a wide variety of traffic on-ramps:</p><ul><li><p>HTTP 1/2/3 (including various CONNECT protocols for layer 3 and 4 traffic)</p></li><li><p>TCP and UDP traffic over Proxy Protocol</p></li><li><p>general purpose IP traffic, including ICMP</p></li></ul><p>With Oxy, you have the ability to analyze and manipulate traffic at multiple layers of the OSI model - from layer 3 to layer 7. This allows for a wide range of possibilities in terms of how you handle incoming traffic.</p><p>One of the most notable and powerful features of Oxy is the ability for applications to force decapsulation. This means that an application can analyze traffic at a higher level, even if it originally arrived at a lower level. For example, if an application receives IP traffic, it can choose to analyze the UDP traffic encapsulated within the IP packets. With just a few lines of code, the application can tell Oxy to upgrade the IP flow to a UDP tunnel, effectively allowing the same code to be used for different on-ramps.</p><p>The application can even go further and ask Oxy to sniff UDP packets and check if they contain <a href="https://www.cloudflare.com/learning/performance/what-is-http3/">HTTP/3 traffic</a>. In this case, Oxy can upgrade the UDP traffic to HTTP and handle HTTP/3 requests that were originally received as raw IP packets. This allows for the simultaneous processing of traffic at all three layers (L3, L4, L7), enabling applications to analyze, filter, and manipulate the traffic flow from multiple perspectives. This provides a robust toolset for developing advanced traffic processing applications.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4tVlLbQeNVeN2lYN9ovJNH/d87cc5adb53ff0fc441530520540f781/image1-1.png" />
            
            </figure><p><i>Multi-layer traffic processing in Oxy applications</i></p>
    <div>
      <h3>Off-ramps</h3>
      <a href="#off-ramps">
        
      </a>
    </div>
    <p>Off-ramp defines a combination of transport layer socket type and protocols that proxy server connectors can use for egress traffic.</p><p>Oxy offers versatility in its egress methods, supporting a range of protocols including HTTP 1 and 2, UDP, TCP, and IP. It is equipped with internal DNS resolution and caching, as well as customizable resolvers, with automatic fallback options for maximum system reliability. Oxy implements <a href="https://www.rfc-editor.org/rfc/rfc8305">happy eyeballs</a> for TCP, advanced tunnel timeout logic and has the ability to route traffic to internal services with accompanying metadata.</p><p>Additionally, through collaboration with one of our internal services (which is an Oxy application itself!) <a href="/geoexit-improving-warp-user-experience-larger-network/">Oxy is able to offer geographical egress</a> — allowing applications to route traffic to the public Internet from various locations in our extensive network covering numerous cities worldwide. This complex and powerful feature can be easily utilized by Oxy application developers at no extra cost, simply by adjusting configuration settings.</p>
    <div>
      <h3>Tunneling and request handling</h3>
      <a href="#tunneling-and-request-handling">
        
      </a>
    </div>
    <p>We've discussed Oxy's communication capabilities with the outside world through on-ramps and off-ramps. In the middle, Oxy handles efficient stateful tunneling of various traffic types including TCP, UDP, QUIC, and IP, while giving applications full control over traffic blocking and redirection.</p><p>Additionally, Oxy effectively handles HTTP traffic, providing full control over requests and responses, and allowing it to serve as a direct HTTP or API service. With built-in tools for streaming analysis of HTTP bodies, Oxy makes it easy to extract and process data, such as form data from uploads and downloads.</p><p>In addition to its multi-layer traffic processing capabilities, Oxy also supports advanced HTTP tunneling methods, such as <a href="https://datatracker.ietf.org/doc/html/rfc9298">CONNECT-UDP</a> and <a href="https://datatracker.ietf.org/doc/draft-ietf-masque-connect-ip/">CONNECT-IP</a>, using the latest extensions to HTTP 3 and 2 protocols. It can even process HTTP CONNECT request payloads on layer 4 and recursively process the payload as HTTP if the encapsulated traffic is HTTP.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4a80AwmzUmUyxx7q8j2hcK/c2bcd1903e037852e57186510f6bac58/image2-2.png" />
            
            </figure><p><i>Recursive processing of HTTP CONNECT body payload in HTTP pipeline</i></p>
    <div>
      <h3>TLS</h3>
      <a href="#tls">
        
      </a>
    </div>
    <p>The modern Internet is unimaginable without traffic encryption, and Oxy, of course, provides this essential aspect. Oxy's cryptography and TLS are based on BoringSSL, providing both a FIPS-compliant version with a limited set of certified features and the latest version that supports all the currently available TLS features. Oxy also allows applications to switch between the two versions in real-time, on a per-request or per-connection basis.</p><p>Oxy's TLS client is designed to make HTTPS requests to <a href="https://en.wikipedia.org/wiki/Upstream_server">upstream servers</a>, with the functionality and security of a browser-grade client. This includes the reconstruction of certificate chains, certificate revocation checks, and more. In addition, Oxy applications can be secured with TLS v1.3, and optionally mTLS, allowing for the extraction of client authentication information from x509 certificates.</p><p>Oxy has the ability to inspect and filter HTTPS traffic, including HTTP/3, and provides the means for dynamically generating certificates, serving as a foundation for implementing data loss prevention (DLP) products. Additionally, Oxy's internal fork of BoringSSL, which is not FIPS-compliant, supports the use of <a href="https://datatracker.ietf.org/doc/html/rfc7250">raw public keys</a> as an alternative to WebPKI, making it ideal for internal service communication. This allows for all the benefits of TLS without the hassle of managing root certificates.</p>
    <div>
      <h3>Gluing everything together</h3>
      <a href="#gluing-everything-together">
        
      </a>
    </div>
    <p>Oxy is more than just a set of building blocks for network applications. It acts as a cohesive glue, handling the bootstrapping of the entire proxy application with ease, including parsing and applying configurations, setting up an asynchronous runtime, applying seccomp hardening and providing automated graceful restarts functionality.</p><p>With built-in support for panic reporting to Sentry, Prometheus metrics with a Rust-macro based API, Kibana logging, distributed tracing, memory and runtime profiling, Oxy offers comprehensive <a href="https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/">monitoring</a> and analysis capabilities. It can also generate detailed audit logs for layer 4 traffic, useful for billing and network analysis.</p><p>To top it off, Oxy includes an integration testing framework, allowing for easy testing of application interactions using TypeScript-based tests.</p>
    <div>
      <h3>Extensibility model</h3>
      <a href="#extensibility-model">
        
      </a>
    </div>
    <p>To take full advantage of Oxy's capabilities, one must understand how to extend and configure its features. Oxy applications are configured using YAML configuration files, offering numerous options for each feature. Additionally, application developers can extend these options by leveraging the convenient macros provided by the framework, making customization a breeze.</p><p>Suppose the Oxy application uses a key-value database to retrieve user information. In that case, it would be beneficial to expose a YAML configuration settings section for this purpose. With Oxy, defining a structure and annotating it with the <code>#[oxy_app_settings]</code> attribute is all it takes to accomplish this:</p>
            <pre><code>///Application’s key-value (KV) database settings
#[oxy_app_settings]
pub struct MyAppKVSettings {
    /// Key prefix.
    pub prefix: Option&lt;String&gt;,
    /// Path to the UNIX domain socket for the appropriate KV 
    /// server instance.
    pub socket: Option&lt;String&gt;,
}</code></pre>
            <p>Oxy can then generate a default YAML configuration file listing available options and their default values, including those extended by the application. The configuration options are automatically documented in the generated file from the Rust doc comments, following best Rust practices.</p><p>Moreover, Oxy supports multi-tenancy, allowing a single application instance to expose multiple on-ramp endpoints, each with a unique configuration. But, sometimes even a YAML configuration file is not enough to build a desired application, this is where Oxy's comprehensive set of hooks comes in handy. These hooks can be used to extend the application with Rust code and cover almost all aspects of the traffic processing.</p><p>To give you an idea of how easy it is to write an Oxy application, here is an example of basic Oxy code:</p>
            <pre><code>struct MyApp;

// Defines types for various application extensions to Oxy's
// data types. Contexts provide information and control knobs for
// the different parts of the traffic flow and applications can extend // all of them with their custom data. As was mentioned before,
// applications could also define their custom configuration.
// It’s just a matter of defining a configuration object with
// `#[oxy_app_settings]` attribute and providing the object type here.
impl OxyExt for MyApp {
    type AppSettings = MyAppKVSettings;
    type EndpointAppSettings = ();
    type EndpointContext = ();
    type IngressConnectionContext = MyAppIngressConnectionContext;
    type RequestContext = ();
    type IpTunnelContext = ();
    type DnsCacheItem = ();

}
   
#[async_trait]
impl OxyApp for MyApp {
    fn name() -&gt; &amp;'static str {
        "My app"
    }

    fn version() -&gt; &amp;'static str {
        env!("CARGO_PKG_VERSION")
    }

    fn description() -&gt; &amp;'static str {
        "This is an example of Oxy application"
    }

    async fn start(
        settings: ServerSettings&lt;MyAppSettings, ()&gt;
    ) -&gt; anyhow::Result&lt;Hooks&lt;Self&gt;&gt; {
        // Here the application initializes various hooks, with each
        // hook being a trait implementation containing multiple
        // optional callbacks invoked during the lifecycle of the
        // traffic processing.
        let ingress_hook = create_ingress_hook(&amp;settings);
        let egress_hook = create_egress_hook(&amp;settings);
        let tunnel_hook = create_tunnel_hook(&amp;settings);
        let http_request_hook = create_http_request_hook(&amp;settings);
        let ip_flow_hook = create_ip_flow_hook(&amp;settings);

        Ok(Hooks {
            ingress: Some(ingress_hook),
            egress: Some(egress_hook),
            tunnel: Some(tunnel_hook),
            http_request: Some(http_request_hook),
            ip_flow: Some(ip_flow_hook),
            ..Default::default()
        })
    }
}

// The entry point of the application
fn main() -&gt; OxyResult&lt;()&gt; {
    oxy::bootstrap::&lt;MyApp&gt;()
}</code></pre>
            
    <div>
      <h2>Technology choice</h2>
      <a href="#technology-choice">
        
      </a>
    </div>
    <p>Oxy leverages the safety and performance benefits of Rust as its implementation language. At Cloudflare, Rust has emerged as a popular choice for new product development, and there are ongoing efforts to migrate some of the existing products to the language as well.</p><p>Rust offers memory and concurrency safety through its ownership and borrowing system, preventing issues like null pointers and data races. This safety is achieved without sacrificing performance, as Rust provides low-level control and the ability to write code with minimal runtime overhead. Rust's balance of safety and performance has made it popular for building safe performance-critical applications, like proxies.</p><p>We intentionally tried to stand on the shoulders of the giants with this project and avoid reinventing the wheel. Oxy heavily relies on open-source dependencies, with <a href="https://github.com/hyperium/hyper">hyper</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a> being the backbone of the framework. Our philosophy is that we should pull from existing solutions as much as we can, allowing for faster iteration, but also use widely battle-tested code. If something doesn't work for us, we try to collaborate with maintainers and contribute back our fixes and improvements. In fact, we now have two team members who are core team members of tokio and hyper projects.</p><p>Even though Oxy is a proprietary project, we try to give back some love to the open-source community without which the project wouldn’t be possible by open-sourcing some of the building blocks such as <a href="https://github.com/cloudflare/boring">https://github.com/cloudflare/boring</a> and <a href="https://github.com/cloudflare/quiche">https://github.com/cloudflare/quiche</a>.</p>
    <div>
      <h2>The road to implementation</h2>
      <a href="#the-road-to-implementation">
        
      </a>
    </div>
    <p>At the beginning of our journey, we set out to implement a proof-of-concept  for an HTTP firewall using Rust for what would eventually become Zero Trust Gateway product. This project was originally part of the <a href="/1111-warp-better-vpn/">WARP</a> service repository. However, as the PoC rapidly advanced, it became clear that it needed to be separated into its own Gateway proxy for both technical and operational reasons.</p><p>Later on, when tasked with implementing a relay proxy for iCloud Private Relay, we saw the opportunity to reuse much of the code from the Gateway proxy. The Gateway project could also benefit from the HTTP/3 support that was being added for the Private Relay project. In fact, early iterations of the relay service were forks of the Gateway server.</p><p>It was then that we realized we could extract common elements from both projects to create a new framework, Oxy. The history of Oxy can be traced back to its origins in the commit history of the Gateway and Private Relay projects, up until its separation as a standalone framework.</p><p>Since our inception, we have leveraged the power of Oxy to efficiently roll out multiple projects that would have required a significant amount of time and effort without it. Our iterative development approach has been a strength of the project, as we have been able to identify common, reusable components through hands-on testing and implementation.</p><p>Our small core team is supplemented by internal contributors from across the company, ensuring that the best subject-matter experts are working on the relevant parts of the project. This contribution model also allows us to shape the framework's API to meet the functional and ergonomic needs of its users, while the core team ensures that the project stays on track.</p>
    <div>
      <h2>Relation to <a href="/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/">Pingora</a></h2>
      <a href="#relation-to">
        
      </a>
    </div>
    <p>Although Pingora, another proxy server developed by us in Rust, shares some similarities with Oxy, it was intentionally designed as a separate proxy server with a different objective. Pingora was created to serve traffic from millions of our client’s upstream servers, including those with ancient and unusual configurations. Non-UTF 8 URLs or TLS settings that are not supported by most TLS libraries being just a few such quirks among many others. This focus on handling technically challenging unusual configurations sets Pingora apart from other proxy servers.</p><p>The concept of Pingora came about during the same period when we were beginning to develop Oxy, and we initially considered merging the two projects. However, we quickly realized that their objectives were too different to do that. Pingora is specifically designed to establish Cloudflare’s HTTP connectivity with the Internet, even in its most technically obscure corners. On the other hand, Oxy is a multipurpose platform that supports a wide variety of communication protocols and aims to provide a simple way to develop high-performance proxy applications with business logic.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Oxy is a proxy framework that we have developed to meet the demanding needs of modern services. It has been designed  to provide a flexible and scalable solution that can be adapted to meet the unique requirements of each project and by leveraging the power of Rust, we made it both safe and fast.</p><p>Looking forward, Oxy is poised to play one of the critical roles in our company's larger effort to modernize and improve our architecture. It provides a solid block in foundation on which we can keep building the better Internet.</p><p>As the framework continues to evolve and grow, we remain committed to our iterative approach to development, constantly seeking out new opportunities to reuse existing solutions and improve our codebase. This collaborative, community-driven approach has already yielded impressive results, and we are confident that it will continue to drive the future success of Oxy.</p><p>Stay tuned for more tech savvy blog posts on the subject!</p> ]]></content:encoded>
            <category><![CDATA[Proxying]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[iCloud Private Relay]]></category>
            <category><![CDATA[Cloudflare Gateway]]></category>
            <category><![CDATA[Oxy]]></category>
            <guid isPermaLink="false">1HAnoThlPiFQ4Bgpn04CM0</guid>
            <dc:creator>Ivan Nikulin</dc:creator>
        </item>
        <item>
            <title><![CDATA[Twilio Segment Edge SDK powered by Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/twilio-segment-sdk-powered-by-cloudflare-workers/</link>
            <pubDate>Fri, 18 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ With Segment Edge SDK, built on Cloudflare Workers, developers can collect high-quality first-party data and use Segment Edge SDK to access real-time user profiles and state, to deliver personalized  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The Cloudflare team was so excited to hear how Twilio Segment solved problems they encountered with tracking first-party data and personalization using Cloudflare Workers. We are happy to have guest bloggers Pooya Jaferian and Tasha Alfano from Twilio Segment to share their story.</p>
    <div>
      <h2>Introduction</h2>
      <a href="#introduction">
        
      </a>
    </div>
    <p>Twilio Segment is a customer data platform that collects, transforms, and activates first-party customer data. Segment helps developers collect user interactions within an application, form a unified customer record, and sync it to hundreds of different marketing, product, analytics, and data warehouse integrations.</p><p>There are two “unsolved” problem with app instrumentation today:</p><p><b>Problem #1:</b> Many important events that you want to track happen on the “wild-west” of the client, but collecting those events via the client can lead to <i>low data quality,</i> as events are dropped due to user configurations, browser limitations, and network connectivity issues.</p><p><b>Problem #2:</b> Applications need access to real-time (&lt;50ms) user state to personalize the application experience based on advanced computations and <a href="https://www.cloudflare.com/learning/access-management/what-is-network-segmentation/">segmentation logic</a> that <i>must</i> be executed on the cloud.</p><p>The Segment Edge SDK – built on Cloudflare Workers – solves for both. With Segment Edge SDK, developers can collect high-quality first-party data. Developers can also use Segment Edge SDK to access real-time user profiles and state, to deliver personalized app experiences without managing a ton of infrastructure.</p><p>This post goes deep on how and why we built the Segment Edge SDK. We chose the Cloudflare Workers platform as the runtime for our SDK for a few reasons. First, we needed a scalable platform to collect billions of events per day. Workers running with no cold-start made them the right choice. Second, our SDK needed a fast storage solution, and Workers KV fitted our needs perfectly. Third, we wanted our SDK to be easy to use and deploy, and Workers’ ease and speed of deployment was a great fit.</p><p>It is important to note that the Segment Edge SDK is in early development stages, and any features mentioned are subject to change.</p>
    <div>
      <h2>Serving a JavaScript library 700M+ times per day</h2>
      <a href="#serving-a-javascript-library-700m-times-per-day">
        
      </a>
    </div>
    <p>analytics.js is our core JavaScript UI SDK that allows web developers to send data to any tool without having to learn, test, or use a new API every time.</p><p>Figure 1 illustrates how Segment can be used to collect data on a web application. Developers add Segment’s web SDK, <a href="https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/"><b>analytics.js</b></a>, to their websites by <a href="https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/#step-2-add-the-segment-snippet">including a JavaScript snippet</a> to the <code>HEAD</code> of their web pages. The snippet can immediately collect and buffer events while it also loads the full library asynchronously from the Segment CDN. Developers can then use analytics.js to identify the visitors, e.g., <code>**analytics**.identify('john')</code>, and track user behavior, e.g., <code>analytics.track('**Order** **Completed**')</code>. Calling the `analytics.js methods such as <i>identify</i> or <i>track</i> will send data to Segment’s API (<code>api.segment.io</code>). Segment’s platform can then deliver the events to different tools, as well as create a profile for the user (e.g., build a profile for user “John”, associate “Order Completed”, as well as add all future activities of john to the profile).</p><p>Analytics.js also stores state in the browser as first-party cookies (e.g., storing an <code>ajs_user_id</code> cookie with the value of john, with cookie scoped at the example.com domain) so that when the user visits the website again, the user identifier stored in the cookie can be used to recognize the user.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3cFwIwnC5QZKPZ0617hgDb/91afc6d5094f5d60288778ddfb3c7e97/image2-49.png" />
            
            </figure><p>Figure 1- How analytics.js loads on a website and tracks events</p><p>While analytics.js only tracks first-party data (i.e., the data is collected and used by the website that the user is visiting), certain browser controls incorrectly identify analytics.js as a third-party tracker, because the SDK is loaded from a third-party domain (cdn.segment.com) and the data is going to a third-party domain (api.segment.com). Furthermore, despite using first-party cookies to store user identity, some browsers such as Safari have limited the TTL for non-HTTPOnly cookies to 7-days, making it challenging to maintain state for long periods of time.</p><p>To overcome these limitations, we have built a Segment Edge SDK (currently in early development) that can automatically add Segment’s library to a web application, eliminate the use of third-party domains, and maintain user identity using HTTPOnly cookies. In the process of solving the first-party data problem, we realized that the Edge SDK is best positioned to act as a personalization library, given it has access to the user identity on every request (in the form of cookies), and it can resolve such identity to a full-user profile stored in Segment. The user profile information can be used to deliver personalized content to users directly from the Cloudflare Workers platform.</p><p>The remaining portions of this post will cover how we solved the above problems. We first explain how the Edge SDK helps with first-party collection. Then we cover how the Segment profiles database becomes available on the Cloudflare Workers platform, and how to use such data to drive personalization.</p>
    <div>
      <h2>Segment Edge SDK and first-party data collection</h2>
      <a href="#segment-edge-sdk-and-first-party-data-collection">
        
      </a>
    </div>
    <p>Developers can set up the Edge SDK by creating a Cloudflare Worker sitting in front of their web application (via <a href="https://developers.cloudflare.com/workers/platform/routing/routes">Routes</a>) and importing the Edge SDK via npm. The Edge SDK will handle requests and automatically injects analytics.js snippets into every webpage. It also configures first-party endpoints to download the SDK assets and send tracking data. The Edge SDK also captures user identity by looking at the Segment events and instructs the browser to store such identity as HTTPOnly cookies.</p>
            <pre><code>import { Segment } from "@segment/edge-sdk-cloudflare";

export default {
   async fetch(request: Request, env: Env): Promise&lt;Response&gt; {
       const segment = new Segment(env.SEGMENT_WRITE_KEY); 

       const resp = await segment.handleEvent(request, env);

       return resp;
   }
};</code></pre>
            
    <div>
      <h2>How the Edge SDK works under the hood to enable first-party data collection</h2>
      <a href="#how-the-edge-sdk-works-under-the-hood-to-enable-first-party-data-collection">
        
      </a>
    </div>
    <p>The Edge SDK's internal router checks the inbound request URL against predefined patterns. If the URL matches a route, the router runs the route's chain of handlers to process the request, fetch the origin, or modify the response.</p>
            <pre><code>export interface HandlerFunction {
 (
   request: Request,
   response: Response | undefined,
   context: RouterContext
 ): Promise&lt;[Request, Response | undefined, RouterContext]&gt;;
}</code></pre>
            <p>Figure 2 demonstrates the routing of incoming requests. The Worker calls  <code>segment.handleEvent</code> method with the request object (step 1), then the router matches the <code>request.url</code> and <code>request.method</code> against a set of predefined routes:</p><ul><li><p>GET requests with <code>/seg/assets/*</code> path are proxied to Segment CDN (step 2a)</p></li><li><p>POST requests with <code>/seg/events/*</code> path are proxied to Segment tracking API (step 2b)</p></li><li><p>Other requests are proxied to the origin (step 2c) and the HTML responses are enriched with the analytics.js snippet (step 3)</p></li></ul><p>Regardless of the route, the router eventually returns a response to the browser (step 4) containing data from the origin, the response from Segment tracking API, or analytics.js assets. When Edge SDK detects the user identity in an incoming request (more on that later), it sets an HTTPOnly cookie in the response headers to persist the user identity in the browser.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7cKXch0aUGqaIrvETjhR3K/3d9ae7aea0999ed55a00c30bb65fa03b/image1-65.png" />
            
            </figure><p>Figure 2- Edge SDK router flow‌‌</p><p>In the subsequent three sections, we explain how we inject analytics.js, proxy Segment endpoints, and set server-side cookies.</p>
    <div>
      <h3>Injecting Segment SDK on requests to origin</h3>
      <a href="#injecting-segment-sdk-on-requests-to-origin">
        
      </a>
    </div>
    <p>For all the incoming requests routed to the origin, the Edge SDK fetches the HTML page and then adds the analytics.js snippet to the <code>&lt;HEAD&gt;</code> tag, embeds the <a href="https://segment.com/docs/getting-started/02-simple-install/#find-your-write-key">write key</a>, and configures the snippet to download the subsequent javascript bundles from the first-party domain <code>([first-party host]/seg/assets/*)</code> and sends data to the first-party domain as well <code>([first-party host]/seg/events/*)</code>. This is accomplished using the <a href="https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/">HTMLRewriter</a> API.</p>
            <pre><code>import snippet from "@segment/snippet"; // Existing Segment package that generates snippet

class ElementHandler {
   constructor(host: string, writeKey: string)

   element(element: Element) {
     // generate Segment snippet and configure it with first-party host info
     const snip = snippet.min({
         host: `${this.host}/seg`,
         apiKey: this.writeKey,
       })
     element.append(`&lt;script&gt;${snip}&lt;/script&gt;`, { html: true });
   }
 }
  
export const enrichWithAJS: HandlerFunction = async (
   request,
   response,
   context
 ) =&gt; {
   const {
     settings: { writeKey },
   } = context;
   const host = request.headers.get("host") || "";
    return [
     request,
     new HTMLRewriter().on("head",
         new ElementHandler(host, writeKey))
       .transform(response),
     context,
   ];
 };</code></pre>
            
    <div>
      <h3>Proxy SDK bundles and Segment API</h3>
      <a href="#proxy-sdk-bundles-and-segment-api">
        
      </a>
    </div>
    <p>The Edge SDK proxies the Segment CDN and API under the first-party domain. For example, when the browser loads a page with the injected analytics.js snippet, the snippet loads the full analytics.js bundle from <code>https://example.com/seg/assets/sdk.js</code>, and the Edge SDK will proxy that request to the Segment CDN:</p>
            <pre><code>https://cdn.segment.com/analytics.js/v1/&lt;WRITEKEY&gt;/analytics.min.js</code></pre>
            
            <pre><code>export const proxyAnalyticsJS: HandlerFunction = async (request, response, ctx) =&gt; {
 const url = `https://cdn.segment.com/analytics.js/v1/${ctx.params.writeKey}/analytics.min.js`;
 const resp = await fetch(url);
 return [request, resp, ctx];
};</code></pre>
            <p>Similarly, analytics.js collects events and sends them via a POST request to <code>https://example.com/seg/events/[method]</code> and the Edge SDK will proxy such requests to the Segment tracking API:</p>
            <pre><code>https://api.segment.io/v1/[method]</code></pre>
            
            <pre><code>export const handleAPI: HandlerFunction = async (request, response, context) =&gt; {
 const url = new URL(request.url);
 const parts = url.pathname.split("/");
 const method = parts.pop();
 let body: { [key: string]: any } = await request.json();

 const init = {
   method: "POST",
   headers: request.headers,
   body: JSON.stringify(body),
 };

 const resp = await fetch(`https://api.segment.io/v1/${method}`, init);

 return [request, resp, context];
};</code></pre>
            
    <div>
      <h3>First party server-side cookies</h3>
      <a href="#first-party-server-side-cookies">
        
      </a>
    </div>
    <p>The Edge SDK also re-writes existing client-side analytics.js cookies as HTTPOnly cookies. When Edge SDK intercepts an <code>identify</code> event e.g., <code>**analytics**.identify('john')</code>, it extracts the user identity (“john”) and then sets a server-side cookie when sending a response back to the user. Therefore, any subsequent request to the Edge SDK can be associated with “john” using request cookies.</p>
            <pre><code>export const enrichResponseWithIdCookies: HandlerFunction = async (
 request, response, context) =&gt; {


 const host = request.headers.get("host") || "";
 const body = await request.json();
 const userId = body.userId;

 […]

 const headers = new Headers(response.headers);
 const cookie = cookie.stringify("ajs_user_id", userId, {
   httponly: true,
   path: "/",
   maxage: 31536000,
   domain: host,
 });
 headers.append("Set-Cookie", cookie);
 
 const newResponse = new Response(response.body, {
   ...response,
   headers,
 });

 return [request, newResponse, newContext];
};</code></pre>
            <p>Intercepting the <code>ajs_user_id</code> on the Workers, and using the cookie identifier to associate each request to a user, is quite powerful, and it opens the door for delivering personalized content to users. The next section covers how Edge SDK can drive personalization.</p>
    <div>
      <h2>Personalization on the Supercloud</h2>
      <a href="#personalization-on-the-supercloud">
        
      </a>
    </div>
    <p>The Edge SDK offers a <code>registerVariation</code> method that can customize how a request to a given route should be fetched from the origin. For example, let's assume we have three versions of a landing page in the origin: <code>/red</code>, <code>/green</code>, and  <code>/</code> (default), and we want to deliver one of the three versions based on the visitor traits. We can use Edge SDK as follows:</p>
            <pre><code>   const segment = new Segment(env.SEGMENT_WRITE_KEY); 
   segment.registerVariation("/", (profile) =&gt; {
     if (profile.red_group) {
       return "/red"
     } else if (profile.green_group) 
       return "/green"
     }
   });

   const resp = await segment.handleEvent(request, env);

   return resp</code></pre>
            <p>The <code>registerVariation</code> accepts two inputs: the path that displays the personalized content, and a decision function that should return the origin address for the personalized content. The decision function receives a profile object visitor in Segment. In the example, when users visit <code>example.com/(root path)</code>, personalized content is delivered by checking if the visitor has a <code>red_group</code> or <code>green_group</code> trait and subsequently requesting the content from either <code>/red</code> or <code>/green</code> path at the origin.</p><p>We already explained that Edge SDK knows the identity of the user via <code>ajs_user_id</code> cookie, but we haven’t covered how the Edge SDK has access to the full profile object. The next section explains how the full profile becomes available on the Cloudflare Workers platform.</p>
    <div>
      <h3>How does personalization work under the hood?</h3>
      <a href="#how-does-personalization-work-under-the-hood">
        
      </a>
    </div>
    <p>The Personalization feature of the Edge SDK requires storage of profiles on the Cloudflare Workers platform. A <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">Cloudflare KV</a> should be created for the Worker running the Edge SDK and passed to the Edge SDK during initialization. Edge SDK will store profiles in KV, where keys are the ajs_user_id, and values are the serialized profile object. To move <a href="https://segment.com/docs/profiles/">Profiles</a> data from Segment to the KV, the SDK uses two methods:</p><ul><li><p><i>Profiles data push from Segment to the Cloudflare Workers platform:</i> The Segment product <a href="https://segment.com/docs/engage/using-engage-data/">can sync user profiles database with different tools</a>, including pushing the data to a webhook. The Edge SDK automatically exposes a webhook endpoint under the first-party domain (e.g., <code>example.com/seg/profiles-webhook</code>) that Segment can call periodically to sync user profiles. The webhook handler receives incoming sync calls from Segment, and writes profiles to the KV.</p></li><li><p><i>Pulling data from Segment by the Edge SDK:</i> If the Edge SDK queries the KV for a user id, and doesn’t find the profile (i.e., data hasn’t synced yet), it requests the user profile from the <a href="https://segment.com/docs/profiles/profile-api/">Segment API</a>, and stores it in the KV.</p></li></ul><p>Figure 3 demonstrates how the personalization flow works. In <i>step 1</i>, the user requests content for the root path ( / ), and the Worker sends the request to the Edge SDK <i>(step 2)</i>. The Edge SDK router determines that a variation is registered on the route, therefore, extracts the <code>ajs_user_id</code> from the request cookies, and goes through the full profile extraction <i>(step 3)</i>. The SDK first checks the KV for a record with the key of <code>ajs_user_id</code> value and if not found, queries Segment API to fetch the profile, and stores the profile in the KV. Eventually, the profile is extracted and passed into the decision function to decide which path should be served to the user <i>(step 4)</i>. The router eventually fetches the variation from the origin <i>(step 5)</i> and returns the response under the / path to the browser <i>(step 6).</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1KzFSDxZVXQ1P7YN7IeyKC/a56a6d820e939601ff6ca4d6385308c4/image4-28.png" />
            
            </figure><p>Figure 3- Personalization flow</p>
    <div>
      <h2>Summary</h2>
      <a href="#summary">
        
      </a>
    </div>
    <p>In this post we covered how the Cloudflare Workers platform can help with tracking first-party data and personalization. We also explained how we built a Segment Edge SDK to enable Segment customers to get those benefits out of the box, without having to create their own DIY solution. The Segment Edge SDK is currently in early development, and we are planning to launch a private pilot and open-source it in the near future.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Guest Post]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">6xXo9Rwt825JWe0JE45bbO</guid>
            <dc:creator>Pooya Jaferian (Guest Blogger)</dc:creator>
            <dc:creator>Tasha Alfano (Guest Author)</dc:creator>
        </item>
        <item>
            <title><![CDATA[Keep track of Workers’ code and configuration changes with Deployments]]></title>
            <link>https://blog.cloudflare.com/deployments-for-workers/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ Today we’re happy to announce Deployments for Workers. Deployments allow developers to audit changes made to their applications. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today we’re happy to introduce Deployments for Workers. Deployments allow developers to keep track of changes to their Worker; not just the code, but the configuration and bindings as well. With deployments, developers now have access to a powerful audit log of changes to their production applications.</p><p>And tracking changes is just the beginning! Deployments provide a strong foundation to add: automated deployments, rollbacks, and integration with version control.</p><p>Today we’ll dive into the details of deployments, how you can use them, and what we’re thinking about next.</p>
    <div>
      <h3>Deployments</h3>
      <a href="#deployments">
        
      </a>
    </div>
    <p>Deployments are a powerful new way to track changes to your Workers. With them, you can track who’s making changes to your Workers, where those changes are coming from, and when those changes are being made.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6qNd7JL2HjmxbVVNqK83hQ/05929c250e068877efc5f92da0d9fed9/image2-43.png" />
            
            </figure><p>Cloudflare reports on deployments made from wrangler, API, dashboard, or Terraform anytime you make changes to your Worker’s code, edit resource bindings and environment variables, or modify configuration like name or usage model.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2eKs2HQ4xMx5UgrGa7HBfn/e4d63e1d631f7afa53e1253720d7dd87/image5-13.png" />
            
            </figure><p>We expose the source of your deployments, so you can track where changes are coming from. For example, if you have a CI job that’s responsible for changes, and you see a user made a change through the Cloudflare dashboard, it’s easy to flag that and dig into whether the deployment was a mistake.</p>
    <div>
      <h3>Interacting with deployments</h3>
      <a href="#interacting-with-deployments">
        
      </a>
    </div>
    <p>Cloudflare tracks the authors, sources, and timestamps of deployments. If you have a set of users responsible for deployment, or an API Token that’s associated with your CI tool, it’s easy to see which made recent deployments. Each deployment also includes a timestamp, so you can track when those changes were made.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7hSjh1SH7GGpG7B3ixCE94/d4c31d4cfee9943ba898f0eb623f5155/unnamed-3.png" />
            
            </figure><p>You can access all this deployment information in your Cloudflare dashboard, under your <i>Worker’s Deployments</i> tab. We also report on the active version right at the front of your Worker’s detail page. Wrangler will also report on deployment information. <code>wrangler publish</code> now reports the latest deployed version, and a new `<a href="https://developers.cloudflare.com/workers/platform/deployments">wrangler deployments</a>` command can be used to view a deployment history.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SImxu5FugyTmsz6S199S8/7222d4b30bfb8ca547030697db4d15a1/image1-58.png" />
            
            </figure><p>To learn more about the details of deployments, head over to our <a href="https://developers.cloudflare.com/workers/platform/deployments">Developer Documentation</a>.</p>
    <div>
      <h3>What’s next?</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>We’re excited to share deployments with our customers, available today in an open beta. As we mentioned up front, we’re just getting started with deployments. We’re also excited for more on-platform tooling like rollbacks, deploy status, deployment rules, and a view-only mode to historical deployments. Beyond that, we want to ensure deployments can be automated from commits to your repository, which means working on version control integrations to services like GitHub, Bitbucket, and Gitlab. We’d love to hear more about how you're currently using Workers and how we can improve developer experience. If you’re interested, <a href="http://www.cloudflare.com/lp/developer-week-deployments">let’s chat</a>.</p><p>If you’d like to join the conversation, head over to <a href="https://discord.gg/cloudflaredev">Cloudflare’s Developer Discord</a> and give us a shout! We love hearing from our customers, and we’re excited to see what you build with Cloudflare.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4NT66c2xlxdnFSu7w4b1Gd</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Incremental adoption of micro-frontends with Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/fragment-piercing/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ With Cloudflare Workers, our fragment-based micro-frontend architecture, and fragment piercing technique, engineering teams can incrementally improve large frontends in a fraction of the time, yielding significant user and developer experience gains. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Q4iw5pvW4frJry6aDHbW1/72ed68595be7d127566b976c0a4114a6/image5-11.png" />
            
            </figure>
    <div>
      <h2>Bring micro-frontend benefits to legacy Web applications</h2>
      <a href="#bring-micro-frontend-benefits-to-legacy-web-applications">
        
      </a>
    </div>
    <p>Recently, we wrote about <a href="/better-micro-frontends/">a new fragment architecture</a> for building Web applications that is fast, cost-effective, and scales to the largest projects, while enabling a fast iteration cycle. The approach uses multiple collaborating Cloudflare Workers to render and stream micro-frontends into an application that is interactive faster than traditional client-side approaches, leading to better user experience and SEO scores.</p><p>This approach is great if you are starting a new project or have the capacity to rewrite your current application from scratch. But in reality most projects are too large to be rebuilt from scratch and can adopt architectural changes only in an incremental way.</p><p>In this post we propose a way to replace only selected parts of a legacy client-side rendered application with server-side rendered fragments. The result is an application where the most important views are interactive sooner, can be developed independently, and receive all the benefits of the micro-frontend approach, while avoiding large rewrites of the legacy codebase. This approach is framework-agnostic; in this post we demonstrate fragments built with React, Qwik, and SolidJS.</p>
    <div>
      <h2>The pain of large frontend applications</h2>
      <a href="#the-pain-of-large-frontend-applications">
        
      </a>
    </div>
    <p>Many large frontend applications developed today fail to deliver good user experience. This is often caused by architectures that require large amounts of JavaScript to be downloaded, parsed and executed before users can interact with the application. Despite efforts to defer non-critical JavaScript code via lazy loading, and the use of server-side rendering, these large applications still take too long to become interactive and respond to the user's inputs.</p><p>Furthermore, large monolithic applications can be complex to build and deploy. Multiple teams may be collaborating on a single codebase and the effort to coordinate testing and deployment of the project makes it hard to develop, deploy and iterate on individual features.</p><p>As outlined in our <a href="/better-micro-frontends/">previous post</a>, micro-frontends powered by <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> can solve these problems but converting an application monolith to a micro-frontend architecture can be difficult and expensive. It can take months, or even years, of engineering time before any benefits are perceived by users or developers.</p><p>What we need is an approach where a project can incrementally adopt micro-frontends into the most impactful parts of the application incrementally, without needing to rewrite the whole application in one go.</p>
    <div>
      <h2>Fragments to the rescue</h2>
      <a href="#fragments-to-the-rescue">
        
      </a>
    </div>
    <p>The goal of a fragment based architecture is to significantly <a href="https://www.cloudflare.com/solutions/ecommerce/optimization/">decrease loading and interaction latency</a> for large web applications (as measured via <a href="https://web.dev/vitals/">Core Web Vitals</a>) by breaking the application into micro-frontends that can be quickly rendered (and cached) in Cloudflare Workers. The challenge is how to integrate a micro-frontend fragment into a legacy client-side rendered application with minimal cost to the original project.</p><p>The technique we propose allows us to convert the most valuable parts of a legacy application’s UI, in isolation from the rest of the application.</p><p>It turns out that, in many applications, the most valuable parts of the UI are often nested within an application “shell” that provides header, footer, and navigational elements. Examples of these include a login form, product details panel in an <a href="https://www.cloudflare.com/ecommerce/">e-commerce application</a>, the inbox in an email client, etc.</p><p>Let’s take a login form as an example. If it takes our application several seconds to display the login form, the users will dread logging in, and we might lose them. We can however convert the login form into a server-side rendered fragment, which is displayed and interactive immediately, while the rest of the legacy application boots up in the background. Since the fragment is interactive early, the user can even submit their credentials before the legacy application has started and rendered the rest of the page.</p><div></div>
<p><small>Animation showing the login form being available before the main application</small></p><p>This approach enables engineering teams to deliver valuable improvements to users in just a fraction of the time and engineering cost compared to traditional approaches, which either sacrifice user experience improvements, or require a lengthy and high-risk rewrite of the entire application. It allows teams with monolithic single-page applications to adopt a micro-frontend architecture incrementally, target the improvements to the most valuable parts of the application, and therefore front-load the return on investment.</p><p>An interesting challenge in extracting parts of the UI into server-side rendered fragments is that, once displayed in the browser, we want the legacy application and the fragments to feel like a single application. The fragments should be neatly embedded within the legacy application shell, keeping the application accessible by correctly forming the DOM hierarchy, but we also want the server-side rendered fragments to be displayed and become interactive as quickly as possible — even before the legacy client-side rendered application shell comes into existence. How can we embed UI fragments into an application shell that doesn’t exist yet? We resolved this problem via a technique we devised, which we call “fragment piercing”.</p>
    <div>
      <h2>Fragment piercing</h2>
      <a href="#fragment-piercing">
        
      </a>
    </div>
    <p>Fragment piercing combines HTML/DOM produced by server-side rendered micro-frontend fragments with HTML/DOM produced by a legacy client-side rendered application.</p><p>The micro-frontend fragments are rendered directly into the top level of the HTML response, and are designed to become immediately interactive. In the background, the legacy application is client-side rendered as a sibling of these fragments. When it is ready, the fragments are “pierced” into the legacy application – the DOM of each fragment is moved to its appropriate place within the DOM of the legacy application – without causing any visual side effects, or loss of client-side state, such as focus, form data, or text selection. Once “pierced”, a fragment can begin to communicate with the legacy application, effectively becoming an integrated part of it.</p><p>Here, you can see a “login” fragment and the empty legacy application “root” element at the top level of the DOM, before piercing.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;&lt;/div&gt;
  &lt;piercing-fragment-host fragment-id="login"&gt;
    &lt;login q:container...&gt;...&lt;/login&gt;
  &lt;/piercing-fragment-host&gt;
&lt;/body&gt;</code></pre>
            <p>And here you can see that the fragment has been pierced into the “login-page” div in the rendered legacy application.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;
    &lt;header&gt;...&lt;/header&gt;
    &lt;main&gt;
      &lt;div class="login-page"&gt;
        &lt;piercing-fragment-outlet fragment-id="login"&gt;
          &lt;piercing-fragment-host fragment-id="login"&gt;
            &lt;login  q:container...&gt;...&lt;/login&gt;
          &lt;/piercing-fragment-host&gt;
        &lt;/piercing-fragment-outlet&gt;
      &lt;/div&gt;
    &lt;/main&gt;
    &lt;footer&gt;...&lt;/footer&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre>
            <p>To keep the fragment from moving and causing a visible layout shift during this transition, we apply CSS styles that position the fragment in the same way before and after piercing.</p><p>At any time an application can be displaying any number of pierced fragments, or none at all. This technique is not limited only to the initial load of the legacy application. Fragments can also be added to and removed from an application, at any time. This allows fragments to be rendered in response to user interactions and client-side routing.</p><p>With fragment piercing, you can start to incrementally adopt micro-frontends, one fragment at a time. You decide on the granularity of fragments, and which parts of the application to turn into fragments. The fragments don’t all have to use the same Web framework, which can be useful when switching stacks, or during a post-acquisition integration of multiple applications.</p>
    <div>
      <h2>The “Productivity Suite” demo</h2>
      <a href="#the-productivity-suite-demo">
        
      </a>
    </div>
    <p>As a demonstration of fragment piercing and incremental adoption we have developed a <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">“productivity suite” demo</a> application that allows users to manage to-do lists, read hacker news, etc. We implemented the shell of this application as a client-side rendered React application — a common tech choice in corporate applications. This is our “legacy application”. There are three routes in the application that have been updated to use micro-frontend fragments:</p><ul><li><p><code>/login</code> - a simple dummy login form with client-side validation, displayed when users are not authenticated (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p><code>/todos</code> - manages one or more todo lists, implemented as two collaborating fragments:</p><ul><li><p>Todo list selector - a component for selecting/creating/deleting Todo lists (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p>Todo list editor - a clone of the <a href="https://todomvc.com/">TodoMVC</a> app (implemented in <a href="https://reactjs.org/docs/react-dom-server.html">React</a>).</p></li></ul></li><li><p><code>/news</code> - a clone of the <a href="https://github.com/solidjs/solid-hackernews">HackerNews</a> demo (implemented in <a href="https://www.solidjs.com/">SolidJS</a>).</p></li></ul><p>This demo showcases that different independent technologies can be used for both the legacy application and for each of the fragments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/38jTYqRteZyGozPUoqXd8D/60b003aa2b53395b4adcb0cf31dcd2fc/image2-41.png" />
            
            </figure><p>A visualization of the fragments that are pierced into the legacy application</p><p>The application is deployed at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>To try it out, you first need to log in – simply use any username you like (no password needed). The user’s data is saved in a cookie, so you can log out and back in using the same username. After you’ve logged in, navigate through the various pages using the navigation bar at the top of the application. In particular, take a look at the “<a href="https://productivity-suite.web-experiments.workers.dev/todos">Todo Lists</a>” and “<a href="https://productivity-suite.web-experiments.workers.dev/news">News</a>” pages to see the piercing in action.</p><p>At any point, try reloading the page to see that fragments are rendered instantly while the legacy application loads slowly in the background. Try interacting with the fragments even before the legacy application has appeared!</p><p>At the very top of the page there are controls to let you see the impact of fragment piercing in action.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11Y8DDksAEPz1AMQT8jjSG/8c69e3cc1d99b8a67a01410b146f2c02/image1-56.png" />
            
            </figure><ul><li><p>Use the “Legacy app bootstrap delay” slider to set the simulated delay before the legacy application starts.</p></li><li><p>Toggle “Piercing Enabled” to see what the user experience would be if the app did not use fragments.</p></li><li><p>Toggle “Show Seams” to see where each fragment is on the current page.</p></li></ul>
    <div>
      <h2>How it works</h2>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>The application is composed of a number of building blocks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/47B8E1C6o3kWhsEzUWYL7J/ca1e6348128985d560bd28f0dc32615f/Frame-653.png" />
            
            </figure><p>An overview of the collaborating Workers and legacy application host</p><p>The <b>Legacy application host</b> in our demo serves the files that define the client-side React application (HTML, JavaScript and stylesheets). Applications built with other tech stacks would work just as well. The <b>Fragment Workers</b> host the micro-frontend fragments, as described in our previous <a href="/better-micro-frontends/">fragment architecture</a> post. And the <b>Gateway Worker</b> handles requests from the browser, selecting, fetching and combining response streams from the legacy application and micro-frontend fragments.</p><p>Once these pieces are all deployed, they work together to handle each request from the browser. Let’s look at what happens when you go to the `/login` route.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2atnhkVHu0tNa1MKGWnOPs/b80838e713fd7b64a177a82b06377a16/image4-22.png" />
            
            </figure><p>The flow of requests when viewing the login page</p><p>The user navigates to the application and the browser makes a request to the Gateway Worker to get the initial HTML. The Gateway Worker identifies that the browser is requesting the login page. It then makes two parallel sub-requests – one to fetch the index.html of the legacy application, and another to request the server-side rendered login fragment. It then combines these two responses into a single response stream containing the HTML that is delivered to the browser.</p><p>The browser displays the HTML response containing the empty root element for the legacy application, and the server-side rendered login fragment, which is immediately interactive for the user.</p><p>The browser then requests the legacy application’s JavaScript. This request is proxied by the Gateway Worker to the Legacy application host. Similarly, any other assets for the legacy application or fragments get routed through the Gateway Worker to the legacy application host or appropriate Fragment Worker.</p><p>Once the legacy application’s JavaScript has been downloaded and executed, rendering the shell of the application in the process, the fragment piercing kicks in, moving the fragment into the appropriate place in the legacy application, while preserving all of its UI state.</p><p>While focussed on the login fragment to explain fragment piercing, the same ideas apply to the other fragments implemented in the <code>/todos</code> and <code>/news</code> routes.</p>
    <div>
      <h2>The piercing library</h2>
      <a href="#the-piercing-library">
        
      </a>
    </div>
    <p>Despite being implemented using different Web frameworks, all the fragments are integrated into the legacy application in the same way using helpers from a “<a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">Piercing Library</a>”. This library is a collection of server-side and client-side utilities that we developed, for the demo, to handle integrating the legacy application with micro-frontend fragments. The main features of the library are the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class, <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5">fragment host</a> and <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31">fragment outlet</a> custom elements, and the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18"><code>MessageBus</code></a> class.</p>
    <div>
      <h3>PiercingGateway</h3>
      <a href="#piercinggateway">
        
      </a>
    </div>
    <p>The <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class can be used to instantiate a Gateway Worker that handles all requests for our application’s HTML, JavaScript and other assets. The `PiercingGateway` routes requests through to the appropriate Fragment Workers or to the host of the Legacy Application. It also combines the HTML response streams from these fragments with the response from the legacy application into a single HTML stream that is returned to the browser.</p><p>Implementing a Gateway Worker is straightforward using the Piercing Library. Create a new <code>gateway</code> instance of <code>PiercingGateway</code>, passing it the URL to the legacy application host and a function to determine whether piercing is enabled for the given request. Export the <code>gateway</code> as the default export from the Worker script so that the Workers runtime can wire up its <code>fetch()</code> handler.</p>
            <pre><code>const gateway = new PiercingGateway&lt;Env&gt;({
  // Configure the origin URL for the legacy application.
  getLegacyAppBaseUrl: (env) =&gt; env.APP_BASE_URL,
  shouldPiercingBeEnabled: (request) =&gt; ...,
});
...

export default gateway;</code></pre>
            <p>Fragments can be registered by calling the <code>registerFragment()</code> method so that the <code>gateway</code> can automatically route requests for a fragment’s HTML and assets to its Fragment Worker. For example, registering the login fragment would look like:</p>
            <pre><code>gateway.registerFragment({
  fragmentId: "login",
  prePiercingStyles: "...",
  shouldBeIncluded: async (request) =&gt; !(await isUserAuthenticated(request)),
});</code></pre>
            
    <div>
      <h3>Fragment host and outlet</h3>
      <a href="#fragment-host-and-outlet">
        
      </a>
    </div>
    <p>Routing requests and combining HTML responses in the Gateway Worker is only half of what makes piercing possible. The other half needs to happen in the browser where the fragments need to be pierced into the legacy application using the technique we described earlier.</p><p>The fragment piercing in the browser is facilitated by a pair of <a href="https://html.spec.whatwg.org/multipage/custom-elements.html">custom elements</a>, the fragment host (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5"><code>&lt;piercing-fragment-host&gt;</code></a>) and the fragment outlet (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31"><code>&lt;piercing-fragment-outlet&gt;</code></a>).</p><p>The Gateway Worker wraps the HTML for each fragment in a fragment host. In the browser, the fragment host manages the life-time of the fragment and is used when moving the fragment’s DOM into position in the legacy application.</p>
            <pre><code>&lt;piercing-fragment-host fragment-id="login"&gt;
  &lt;login q:container...&gt;...&lt;/login&gt;
&lt;/piercing-fragment-host&gt;</code></pre>
            <p>In the legacy application, the developer marks where a fragment should appear when it is pierced by adding a fragment outlet. Our demo application’s Login route looks as follows:</p>
            <pre><code>export function Login() {
  …
  return (
    &lt;div className="login-page" ref={ref}&gt;
      &lt;piercing-fragment-outlet fragment-id="login" /&gt;
    &lt;/div&gt;
  );
}</code></pre>
            <p>When a fragment outlet is added to the DOM, it searches the current document for its associated fragment host. If found, the fragment host and its contents are moved inside the outlet. If the fragment host is not found, the outlet will make a request to the gateway worker to fetch the fragment HTML, which is then streamed directly into the fragment outlet, using the <a href="https://github.com/marko-js/writable-dom">writable-dom library</a> (a small but powerful library developed by the <a href="https://markojs.com/">MarkoJS</a> team).</p><p>This fallback mechanism enables client-side navigation to routes that contain new fragments. This way fragments can be rendered in the browser via both initial (hard) navigation and client-side (soft) navigation.</p>
    <div>
      <h3>Message bus</h3>
      <a href="#message-bus">
        
      </a>
    </div>
    <p>Unless the fragments in our application are completely presentational or self-contained, they also need to communicate with the legacy application and other fragments. The <code>[MessageBus](https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18)</code> is a simple asynchronous, isomorphic, and framework-agnostic communication bus that the legacy application and each of the fragments can access.</p><p>In our demo application the login fragment needs to inform the legacy application when the user has authenticated. This <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L51-L57">message dispatch</a> is implemented in the Qwik <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L38"><code>LoginForm</code></a> component as follows:</p>
            <pre><code>const dispatchLoginEvent = $(() =&gt; {
  getBus(ref.value).dispatch("login", {
    username: state.username,
    password: state.password,
  });
  state.loading = true;
});</code></pre>
            <p>The legacy application can then <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/legacy-app/src/auth.tsx#L24-L34">listen for these messages</a> like this:</p>
            <pre><code>useEffect(() =&gt; {
  return getBus().listen&lt;LoginMessage&gt;("login", async (user) =&gt; {
    setUser(user);
    await addUserDataIfMissing(user.username);
    await saveCurrentUser(user.username);
    getBus().dispatch("authentication", user);
    navigate("/", { replace: true, });
  });
}, []);</code></pre>
            <p>We settled on this message bus implementation because we needed a solution that was framework-agnostic, and worked well on both the server as well as client.</p>
    <div>
      <h2>Give it a go!</h2>
      <a href="#give-it-a-go">
        
      </a>
    </div>
    <p>With fragments, fragment piercing, and Cloudflare Workers, you can improve performance as well as the development cycle of legacy client-side rendered applications. These changes can be adopted incrementally, and you can even do so while implementing fragments with a Web framework for your choice.</p><p>The “Productivity Suite” application demonstrating these capabilities can be found at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>All the code we have shown is open-source and published to Github: <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite</a>.</p><p>Feel free to clone the repository. It is easy to run locally and even deploy your own version (for free) to Cloudflare. We tried to make the code as reusable as possible. Most of the core logic is in the <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">piercing library</a> that you could try in your own projects. We’d be thrilled to receive feedback, suggestions, or hear about applications you’d like to use it for. Join our <a href="https://github.com/cloudflare/workers-web-experiments/discussions/64">GitHub discussion</a> or also reach us on our <a href="https://discord.com/channels/595317990191398933/1041751020340002907">discord channel</a>.</p><p>We believe that combining Cloudflare Workers with the latest ideas from frameworks will drive the next big steps forward in improved experiences for both users and developers in Web applications. Expect to see more demos, blog posts and collaborations as we continue to push the boundaries of what the Web can offer. And if you’d also like to be directly part of this journey, we are also happy to share that <a href="https://boards.greenhouse.io/cloudflare/jobs/4619341">we are hiring</a>!</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4vnFkyih2W2DD0QcaALcdf</guid>
            <dc:creator>Peter Bacon Darwin</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
        <item>
            <title><![CDATA[Build your next big idea with Cloudflare Pages]]></title>
            <link>https://blog.cloudflare.com/big-ideas-on-pages/</link>
            <pubDate>Fri, 05 Aug 2022 16:30:00 GMT</pubDate>
            <description><![CDATA[ We believe every developer deserves to dream big without breaking the bank. That's why on Cloudflare Pages you can get unlimited requests, bandwidth, seats and projects --- for free! ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Have you ever had a surge of inspiration for a project? That feeling when you have a great idea – a big idea — that you just can’t shake? When all you can think about is putting your hands to your keyboard and hacking away? Building a website takes courage, creativity, passion and drive, and with <a href="/cloudflare-pages-ga/">Cloudflare Pages</a> we believe nothing should stand in the way of that vision.</p><p><b>Especially not a price tag.</b></p>
    <div>
      <h3>Big ideas</h3>
      <a href="#big-ideas">
        
      </a>
    </div>
    <p>We built Pages to be at the center of your developer experience – a way for you to get started right away without worrying about the heavy lift of setting up a fullstack app.  A quick commit to your git provider or direct upload to our platform, and your rich and powerful site is deployed to our <a href="/mid-2022-new-cities/">network of 270+ data centers in <b>seconds</b></a>. And above all, we built Pages to scale with you as you grow exponentially without getting hit by an unexpected bill.</p>
    <div>
      <h3>The limit does not exist</h3>
      <a href="#the-limit-does-not-exist">
        
      </a>
    </div>
    <p>We’re a platform that’s invested in your vision – no matter how wacky and wild (the best ones usually are!). That’s why for many parts of Pages we want your experience to be <i>limitless</i>.</p><p><b>Unlimited requests</b>: As your idea grows, so does your traffic. While thousands and millions of end users flock to your site, Pages is prepared to handle all of your traffic with no extra cost to you – even when millions turn to <i>billions.</i></p><p><b>Unlimited bandwidth:</b> As your traffic grows, you’ll need more bandwidth – and with Pages, we got you. If your site takes off in popularity one day, the next day shouldn’t be a cause for panic because of a nasty bill. It should be a day of celebration and pride. We’re giving unlimited bandwidth so you can keep your eyes focused on moving up and to the right.</p><p><b>Unlimited free seats:</b> With a rise in demand for your app, you’re going to need more folks working with you. We know from experience that more great ideas don’t just come from one person but a strong team of people. We want to be there every step of the way along with every person you want on this journey with you. Just because your team grows doesn’t mean your bill has to.</p><p><b>Unlimited projects</b>: With one great idea, means many more to follow. With Pages, you can deploy as many projects as you want – keep them coming! Not every idea is going to be the right one – we know this! Prove out your vision in private org-owned repos for free! Try out a plethora of development frameworks until you’ve found the perfect combination for your idea. Test your changes locally using our <a href="/wrangler-v2-beta/">Wrangler integration</a> so you can be confident in whatever you choose to put out into the world.</p>
    <div>
      <h3>Quick, easy and free integrations</h3>
      <a href="#quick-easy-and-free-integrations">
        
      </a>
    </div>
    <p><b>Workers:</b> Take your idea from static to dynamic with Pages’ native <a href="https://developers.cloudflare.com/pages/platform/functions/">integration with Cloudflare Workers</a>, our serverless functions offering. Drop your functions into your <b>functions</b> folder and deploy them alongside your static assets no extra configuration required! We announced <a href="/cloudflare-pages-goes-full-stack/">built-in support for Cloudflare Workers</a> back in November and have since announced framework integrations with Remix, Sveltekit and Qwik, and are working on fullstack support for Next.js within the year!</p><p><b>Cloudflare Access:</b> Working with more than just a team of developers? Send your staging changes to product managers and marketing teams with a unique preview URL for every deployment. And what’s more? You can enable protection for your preview links using our native integration with <a href="https://www.cloudflare.com/products/zero-trust/access/">Cloudflare Access</a> at no additional cost. With one click, send around your latest version without fear of getting into the wrong hands.</p><p><b>Custom domains:</b> With every Pages project, get a free <code>pages.dev</code> subdomain to deploy your project under. When you’re ready for the big day, with built in <a href="https://www.cloudflare.com/ssl-for-saas-providers/">SSL for SaaS</a>, bring that idea to life with a custom domain of its own!</p><p><b>Web Analytics:</b> When launch day comes around, check out just how well it’s going with our deep, privacy-first integration with <a href="https://www.cloudflare.com/web-analytics/">Cloudflare’s Web Analytics</a>. Track every single one of your sites’ progress and performance, including metrics about your traffic and core web vitals with just one click – completely on us!</p>
    <div>
      <h3>Wicked fast performance</h3>
      <a href="#wicked-fast-performance">
        
      </a>
    </div>
    <p>And the best part? Our generous free tier never means compromising <a href="/cloudflare-pages-is-lightning-fast/">site performance</a>. Bring your site closer to your users on your first deployment no matter where they are in the world. The Cloudflare network spans across 270+ cities around the globe and your site is distributed to each of them faster than you can say “it’s go time”. There’s also no need to choose regions for your deployments, we want you to have them all and get even more granular, so your big idea can truly go global.</p>
    <div>
      <h3>What else?</h3>
      <a href="#what-else">
        
      </a>
    </div>
    <p>Building on Pages is just the start of what your idea could grow to become. In the coming months you can expect deep integrations with our new Cloudflare storage offerings like <a href="https://www.cloudflare.com/developer-platform/products/r2/">R2</a>, our <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage</a> service with zero <a href="https://www.cloudflare.com/learning/cloud/what-are-data-egress-fees/">egress fees</a> (open beta), and <a href="/introducing-d1/">D1</a> our first SQL database on the edge (private beta).</p><p>We’ve talked a lot about building your entire platform on Cloudflare. We’re reimagining this experience to be even easier and even more powerful.</p><p>Using just Cloudflare, you’ll be able to build big projects – like an entire store! You can use R2 to host the images, D1 to store product info, inventory data and user details, and Pages to seamlessly build and deploy. A frictionless dev experience for a full stack app that can live and work entirely from the edge. Best of all, don’t worry about getting hung up on cost, we’ll always have a generous free tier so you can get started right away.</p><p>At Cloudflare, we believe that every developer deserves to dream big. For the developers who love to build, who are curious, who explore, let us take you there – no surprises! Leave the security and scalability to us, so you can put your fingers to the keyboard and do what you do best!</p>
    <div>
      <h3>Give it a go</h3>
      <a href="#give-it-a-go">
        
      </a>
    </div>
    <p><a href="https://pages.cloudflare.com/">Learn more about Pages</a> and check out our <a href="https://developers.cloudflare.com/pages">developer documentation</a>. Be sure to join our active <a href="https://discord.com/invite/cloudflaredev">Cloudflare Developer Discord</a> and meet our community of developers building on our platform. You can chat directly with our product and engineering teams and get exclusive access to our offered betas!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">5gp6Zu82o0DkQgrDq0pKHK</guid>
            <dc:creator>Nevi Shah</dc:creator>
        </item>
        <item>
            <title><![CDATA[Building a scheduling system with Workers and Durable Objects]]></title>
            <link>https://blog.cloudflare.com/building-scheduling-system-with-workers-and-durable-objects/</link>
            <pubDate>Fri, 05 Aug 2022 15:45:57 GMT</pubDate>
            <description><![CDATA[ In this post we're going to show you how to build a scalable service that will schedule HTTP requests on a specific schedule or as one-off at a specific time  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>We rely on technology to help us on a daily basis – if you are not good at keeping track of time, your calendar can remind you when it's time to prepare for your next meeting. If you made a reservation at a really nice restaurant, you don't want to miss it! You appreciate the app to remind you a day before your plans the next evening.</p><p>However, who tells the application when it's the right time to send you a notification? For this, we generally rely on scheduled events. And when you are relying on them, you really want to make sure that they occur. Turns out, this can get difficult. The scheduler and storage backend need to be designed with scale in mind - otherwise you may hit limitations quickly.</p><p>Workers, Durable Objects, and Alarms are actually a perfect match for this type of workload. Thanks to the distributed architecture of Durable Objects and their storage, they are a reliable and scalable option. Each Durable Object has access to its own isolated storage and alarm scheduler, both being automatically replicated and failover in case of failures.</p><p>There are many use cases where having a reliable scheduler can come in handy: running a webhook service, sending emails to your customers a week after they sign up to keep them engaged, sending invoices reminders, and more!</p><p>Today, we're going to show you how to build a scalable service that will schedule HTTP requests on a specific schedule or as one-off at a specific time as a way to guide you through any use case that requires scheduled events.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5QsLhy4QmfImRWULDDCpRx/07a879528b6632d850fda9c141ba8707/Scheduling-system-with-Workers-and-Durable-Objects---Diagram.png" />
            
            </figure>
    <div>
      <h3>Quick intro into the application stack</h3>
      <a href="#quick-intro-into-the-application-stack">
        
      </a>
    </div>
    <p>Before we dive in, here are some of the tools we’re going to be using today:</p><ul><li><p><a href="https://developers.cloudflare.com/workers/wrangler/get-started/#install">Wrangler</a> - CLI tool to develop and publish Workers</p></li><li><p><a href="https://developers.cloudflare.com/workers">Cloudflare Workers</a> - runtime</p></li><li><p><a href="https://developers.cloudflare.com/workers/learning/using-durable-objects/">Cloudflare Durable Objects</a> - storage for HTTP requests and <a href="/durable-objects-alarms/">Alarms</a> to schedule them</p></li></ul><p>The application is going to have the following components:</p><ul><li><p>Scheduling system API to accept scheduled requests and manage Durable Objects</p></li><li><p>Unique Durable Object per scheduled request, each with</p></li><li><p>Storage - keeping the request metadata, such as URL, body, or headers.</p></li><li><p>Alarm - a timer (trigger) to wake Durable Object up.</p></li></ul><p>While we will focus on building the application, the Cloudflare global network will take care of the rest – storing and replicating our data, and making sure to wake our Durable Objects up when the time's right. Let’s build it!</p>
    <div>
      <h3>Initialize new Workers project</h3>
      <a href="#initialize-new-workers-project">
        
      </a>
    </div>
    <p>Get started by generating a completely new Workers project using the <code>wrangler init</code> command, which makes creating new projects quick &amp; easy.</p><p><b>wrangler init -y durable-objects-requests-scheduler</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6vgw0sXTG850ifbluBAbEy/57b6965270f884980aa64ebd4d90a914/pasted-image-0.png" />
            
            </figure><p>For more information on how to install, authenticate, or update Wrangler, check out <a href="https://developers.cloudflare.com/workers/wrangler/get-started">https://developers.cloudflare.com/workers/wrangler/get-started</a></p>
    <div>
      <h3>Preparing TypeScript types</h3>
      <a href="#preparing-typescript-types">
        
      </a>
    </div>
    <p>From my personal experience, at least a draft of TypeScript types significantly helps to be more productive down the road, so let's prepare and describe our scheduled request in advance. Create a file types.ts in src directory and paste the following TypeScript definitions.</p><p><b>src/types.ts</b></p>
            <pre><code>export interface Env {
    DO_REQUEST: DurableObjectNamespace
}
 
export interface ScheduledRequest {
  url: string // URL of the request
  triggerAt?: number // optional, unix timestamp in milliseconds, defaults to `new Date()`
  requestInit?: RequestInit // optional, includes method, headers, body
}</code></pre>
            
    <div>
      <h3>A scheduled request Durable Object class &amp; alarm</h3>
      <a href="#a-scheduled-request-durable-object-class-alarm">
        
      </a>
    </div>
    <p>Based on our architecture design, each scheduled request will be saved into its own Durable Object, effectively separating storage and alarms from each other and allowing our scheduling system to scale horizontally - there is no limit to the number of Durable Objects we create.</p><p>In the end, the Durable Object class is a matter of a couple of lines. The code snippet below accepts and saves the request body to a persistent storage and sets the alarm timer. Workers runtime will wake up the Durable Object and call the <code>alarm()</code> method to process the request.</p><p>The alarm method reads the scheduled request data from the storage, then processes the request, and in the end reschedules itself in case it's configured to be executed on a recurring schedule.</p><p><b>src/request-durable-object.ts</b></p>
            <pre><code>import { ScheduledRequest } from "./types";
 
export class RequestDurableObject {
  id: string|DurableObjectId
  storage: DurableObjectStorage
 
  constructor(state:DurableObjectState) {
    this.storage = state.storage
    this.id = state.id
  }
 
    async fetch(request:Request) {
    // read scheduled request from request body
    const scheduledRequest:ScheduledRequest = await request.json()
     
    // save scheduled request data to Durable Object storage, set the alarm, and return Durable Object id
    this.storage.put("request", scheduledRequest)
    this.storage.setAlarm(scheduledRequest.triggerAt || new Date())
    return new Response(JSON.stringify({
      id: this.id.toString()
    }), {
      headers: {
        "content-type": "application/json"
      }
    })
  }
 
  async alarm() {
    // read the scheduled request from Durable Object storage
    const scheduledRequest:ScheduledRequest|undefined = await this.storage.get("request")
 
    // call fetch on scheduled request URL with optional requestInit
    if (scheduledRequest) {
      await fetch(scheduledRequest.url, scheduledRequest.requestInit ? webhook.requestInit : undefined)
 
      // cleanup scheduled request once done
      this.storage.deleteAll()
    }
  }
}</code></pre>
            
    <div>
      <h3>Wrangler configuration</h3>
      <a href="#wrangler-configuration">
        
      </a>
    </div>
    <p>Once we have the Durable Object class, we need to create a Durable Object binding by instructing Wrangler where to find it and what the exported class name is.</p><p><b>wrangler.toml</b></p>
            <pre><code>name = "durable-objects-request-scheduler"
main = "src/index.ts"
compatibility_date = "2022-08-02"
 
# added Durable Objects configuration
[durable_objects]
bindings = [
  { name = "DO_REQUEST", class_name = "RequestDurableObject" },
]
 
[[migrations]]
tag = "v1"
new_classes = ["RequestDurableObject"]</code></pre>
            
    <div>
      <h3>Scheduling system API</h3>
      <a href="#scheduling-system-api">
        
      </a>
    </div>
    <p>The API Worker will accept POST HTTP methods only, and is expecting a JSON body with scheduled request data - what URL to call, optionally what method, headers, or body to send. Any other method than POST will return 405 - Method Not Allowed HTTP error.</p><p>HTTP <code>POST /:scheduledRequestId?</code> will create or override a scheduled request, where :scheduledRequestId is optional Durable Object ID returned from a scheduling system API before.</p><p><b>src/index.ts</b></p>
            <pre><code>import { Env } from "./types"
export { RequestDurableObject } from "./request-durable-object"
 
export default {
    async fetch(
        request: Request,
        env: Env
    ): Promise&lt;Response&gt; {
        if (request.method !== "POST") {
            return new Response("Method Not Allowed", {status: 405})
        }
 
        // parse the URL and get Durable Object ID from the URL
        const url = new URL(request.url)
        const idFromUrl = url.pathname.slice(1)
 
        // construct the Durable Object ID, use the ID from pathname or create a new unique id
        const doId = idFromUrl ? env.DO_REQUEST.idFromString(idFromUrl) : env.DO_REQUEST.newUniqueId()
 
        // get the Durable Object stub for our Durable Object instance
        const stub = env.DO_REQUEST.get(doId)
 
        // pass the request to Durable Object instance
        return stub.fetch(request)
    },
}</code></pre>
            <p>It's good to mention that the script above does not implement any listing of scheduled or processed webhooks. Depending on how the scheduling system would be integrated, you can save each created Durable Object ID to your existing backend, or write your own registry – using one of the <a href="https://developers.cloudflare.com/workers/get-started/storage-objects/">Workers storage options</a>.</p>
    <div>
      <h3>Starting a local development server and testing our application</h3>
      <a href="#starting-a-local-development-server-and-testing-our-application">
        
      </a>
    </div>
    <p>We are almost done! Before we publish our scheduler system to the Cloudflare edge, let's start Wrangler in a completely local mode to run a couple of tests against it and to see it in action – which will work even without an Internet connection!</p>
            <pre><code>wrangler dev --local</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2O2DcPjirepk7sgbtlMpbn/7cef2cc7d230dc4c0010ceb08c84da06/pasted-image-0--1-.png" />
            
            </figure><p>The development server is listening on localhost:8787, which we will use for scheduling our first request. The JSON request payload should match the TypeScript schema we defined in the beginning – required URL, and optional <code>triggerEverySeconds</code> number or <code>triggerAt</code> unix timestamp. When only the required URL is passed, the request will be dispatched right away.</p><p>An example of request payload that will send a GET request to <a href="https://example.com">https://example.com</a> every 30 seconds.</p>
            <pre><code>{
	"url":  "https://example.com",
	"triggerEverySeconds": 30,
}</code></pre>
            
            <pre><code>&gt; curl -X POST -d '{"url": "https://example.com", "triggerEverySeconds": 30}' http://localhost:8787
{"id":"000000018265a5ecaa5d3c0ab6a6997bf5638fdcb1a8364b269bd2169f022b0f"}</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/74kGUzg7MYccSbLySud2e5/a7cdb2781a32c2b95953d25109f3bcce/pasted-image-0--2-.png" />
            
            </figure><p>From the wrangler logs we can see the scheduled request ID <code>000000018265a5ecaa5d3c0ab6a6997bf5638fdcb1a8364b269bd2169f022b0f</code> is being triggered in 30s intervals.</p><p>Need to double the interval? No problem, just send a new POST request and pass the request ID as a pathname.</p>
            <pre><code>&gt; curl -X POST -d '{"url": "https://example.com", "triggerEverySeconds": 60}' http://localhost:8787/000000018265a5ecaa5d3c0ab6a6997bf5638fdcb1a8364b269bd2169f022b0f
{"id":"000000018265a5ecaa5d3c0ab6a6997bf5638fdcb1a8364b269bd2169f022b0f"}</code></pre>
            <p>Every scheduled request gets a unique Durable Object ID with its own storage and alarm. As we demonstrated, the ID becomes handy when you need to change the settings of the scheduled request, or to deschedule them completely.</p>
    <div>
      <h3>Publishing to the network</h3>
      <a href="#publishing-to-the-network">
        
      </a>
    </div>
    <p>Following command will bundle our Workers application, export and bind Durable Objects, and deploy it to our workers.dev subdomain.</p>
            <pre><code>wrangler publish</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3p4tcXHgshwEhYihU8blKm/2dd1a3d76fcb72e056e94540228c2c3a/pasted-image-0--3-.png" />
            
            </figure><p>That's it, we are live! ? The URL of your deployment is shown in the Workers logs. In a reasonably short period of time we managed to write our own scheduling system that is ready to handle requests at scale.</p><p>You can check full source code in <a href="https://github.com/cloudflare/templates/tree/main/worker-example-request-scheduler">Workers templates repository</a>, or experiment from your browser without installing any dependencies locally using the <a href="https://workers.new/example-request-scheduler">StackBlitz template</a>.</p><p><b>What to see or build next</b></p><ul><li><p>New to Workers? Check our Get started guide.</p></li><li><p>Use <a href="https://www.cloudflare.com/products/zero-trust/access/">Access</a> or <a href="https://developers.cloudflare.com/workers/runtime-apis/service-bindings/">service bindings</a> if you want to protect your API from unauthorized access.</p></li><li><p>Got an idea for a Worker, get started in seconds =&gt; <a href="https://workers.new/typescript">https://workers.new/typescript</a> (<a href="https://workers.new/list">full list of StackBlitz supported templates</a>)</p></li><li><p>Dive into more Workers examples</p></li><li><p><a href="https://developers.cloudflare.com/workers/examples/">https://developers.cloudflare.com/workers/examples/</a></p></li><li><p><a href="https://github.com/cloudflare/templates">https://github.com/cloudflare/templates</a></p></li></ul> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Durable Objects]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">65UG8ITOXBu4pXT5pj8bSj</guid>
            <dc:creator>Adam Janiš</dc:creator>
        </item>
        <item>
            <title><![CDATA[Packet captures at the edge]]></title>
            <link>https://blog.cloudflare.com/packet-captures-at-edge/</link>
            <pubDate>Thu, 17 Mar 2022 12:59:12 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to announce the general availability of on-demand packet captures from Cloudflare’s global network ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Packet captures are a critical tool used by network and security engineers every day. As more network functions migrate from legacy on-prem hardware to cloud-native services, teams risk losing the visibility they used to get by capturing 100% of traffic funneled through a single device in a datacenter rack. We know having easy access to packet captures across all your network traffic is important for troubleshooting problems and deeply understanding traffic patterns, so today, we’re excited to announce the general availability of on-demand packet captures from Cloudflare’s global network.</p>
    <div>
      <h3>What are packet captures and how are they used?</h3>
      <a href="#what-are-packet-captures-and-how-are-they-used">
        
      </a>
    </div>
    <p>A packet capture is a file that contains all packets that were seen by a particular network box, usually a firewall or router, during a specific time frame. Packet captures are a powerful and commonly used tool for debugging network issues or getting better visibility into attack traffic to tighten security (e.g. by adding firewall rules to block a specific attack pattern).</p><p>A network engineer might use a pcap file in combination with other tools, like <a href="https://www.cloudflare.com/learning/network-layer/what-is-mtr/">mtr</a>, to troubleshoot problems with reachability to their network. For example, if an end user reports intermittent connectivity to a specific application, an engineer can set up a packet capture filtered to the user’s source IP address to record all packets received from their device. They can then analyze that packet capture and compare it to other sources of information (e.g. pcaps from the end user’s side of the network path, traffic logs and analytics) to understand the magnitude and isolate the source of the problem.</p><p>Security engineers can also use packet captures to gain a better understanding of potentially malicious traffic. Let’s say an engineer notices an unexpected spike in traffic that they suspect could be an attempted attack. They can grab a packet capture to record the traffic as it’s hitting their network and analyze it to determine whether the packets are valid. If they’re not, for example, if the packet payload is randomly generated gibberish, the security engineer can create a firewall rule to block traffic that looks like this from entering their network.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6OKUEiSlnlCSXLryTslrWR/2a862de65c9be324e99751a35b0cee69/image1-65.png" />
            
            </figure><p><i>Example of a packet capture from a recent DDoS attack targeted at Cloudflare infrastructure. The contents of this pcap can be used to create a “signature” to block the attack.</i></p>
    <div>
      <h3>Fragmenting traffic creates gaps in visibility</h3>
      <a href="#fragmenting-traffic-creates-gaps-in-visibility">
        
      </a>
    </div>
    <p>Traditionally, users capture packets by logging into their router or firewall and starting a process like <a href="https://www.tcpdump.org/">tcpdump</a>. They’d set up a filter to only match on certain packets and grab the file. But as networks have become more fragmented and users are moving security functions out to the edge, it’s become increasingly challenging to collect packet captures for relevant traffic. Instead of just one device that all traffic flows through (think of a drawbridge in the “<a href="https://www.cloudflare.com/learning/access-management/castle-and-moat-network-security/">castle and moat</a>” analogy) engineers may have to capture packets across many different physical and virtual devices spread across locations. Many of these packets may not allow taking pcaps at all, and then users have to try to  stitch them back together to create a full picture of their network traffic. This is a nearly impossible task today and only getting harder as networks become more fractured and complex.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2oAYhpR8RTzQKGijeIYDbq/a815a486f88dc8a31a5d9bcb0a91df78/image2-56.png" />
            
            </figure>
    <div>
      <h3>On-demand packet captures from the Cloudflare global network</h3>
      <a href="#on-demand-packet-captures-from-the-cloudflare-global-network">
        
      </a>
    </div>
    <p>With Cloudflare, you can regain this visibility. With <a href="https://www.cloudflare.com/magic-transit/">Magic Transit</a> and <a href="https://www.cloudflare.com/magic-wan/">Magic WAN</a>, customers route all their public and private IP traffic through Cloudflare’s network to make it more secure, faster, and more reliable, but also to increase visibility. You can think of Cloudflare like a giant, globally distributed version of the drawbridge in our old analogy: because we act as a single cloud-based router and firewall across all your traffic, we can capture packets across your entire network and deliver them back to you in one place.</p>
    <div>
      <h3>How does it work?</h3>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>Customers can request a packet capture using our <a href="https://developers.cloudflare.com/magic-firewall/how-to/collect-pcaps/">Packet Captures API</a>. To get the packets you’re looking for you can provide a filter with the IP address, ports, and protocol of the packets you want.</p>
            <pre><code>curl -X POST https://api.cloudflare.com/client/v4/accounts/${account_id}/pcaps \
-H 'Content-Type: application/json' \
-H 'X-Auth-Email: user@example.com' \
-H 'X-Auth-Key: 00000000000' \
--data '{
        "filter_v1": {
               "source_address": "1.2.3.4",
               "protocol": 6
        },
        "time_limit": 300,
        "byte_limit": "10mb",
        "packet_limit": 10000,
        "type": "simple",
        "system": "magic-transit"
}'</code></pre>
            <p>Example of a request for packet capture using our API.</p><p>We leverage <a href="https://netfilter.org/projects/nftables/">nftables</a> to apply the filter to the customer’s incoming packets and log them using <a href="https://www.netfilter.org/projects/libnetfilter_log/index.html">nflog</a>:</p>
            <pre><code>table inet pcaps_1 {
    chain pcap_1 {
        ip protocol 6 ip saddr 1.2.3.4 log group 1 comment “packet capture”
    }
}</code></pre>
            <p>Example nftables configuration used to filter log customer packets</p><p>nflog creates a netfilter socket through which logs of a packet are sent from the Linux kernel to user space. In user space, we use tcpdump to read packets off the netfilter socket and generate a packet capture file:</p>
            <pre><code>tcpdump -i nflog:1 -w pcap_1.pcap</code></pre>
            <p>Example tcpdump command to create a packet capture file.</p><p>Usually tcpdump is used by listening to incoming packets on a network interface, but in our case we configure it to read packet logs from an nflog group. tcpdump will convert the packet logs into a packet capture file.</p><p>Once we have a packet capture file, we need to deliver it to customers. Because packet capture files can be large and contain sensitive information (e.g. packet payloads), we send them to customers directly from our machines to a cloud storage service of their choice. This means we never store sensitive data, and it’s easy for customers to manage and store these large files.</p>
    <div>
      <h3>Get started today</h3>
      <a href="#get-started-today">
        
      </a>
    </div>
    <p>On-demand packet captures are now generally available for customers who have purchased the Advanced features of Magic Firewall. The <a href="https://developers.cloudflare.com/magic-firewall/how-to/collect-pcaps/">packet capture API</a> allows customers to capture the first 160 bytes of packets, sampled at a default rate of 1/100. More functionality including full packet captures and on-demand packet capture control in the Cloudflare Dashboard is coming in the following weeks. Contact your account team to stay updated on the latest!</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Serverless]]></category>
            <guid isPermaLink="false">3lCeST96Ji4kMdmRbpTRav</guid>
            <dc:creator>Annika Garbers</dc:creator>
            <dc:creator>Nadin El-Yabroudi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Smart Edge Revalidation]]></title>
            <link>https://blog.cloudflare.com/introducing-smart-edge-revalidation/</link>
            <pubDate>Wed, 28 Jul 2021 12:59:17 GMT</pubDate>
            <description><![CDATA[ When both Last-Modified and Etag headers are absent from the origin response, Smart Edge Revalidation will use the time the object was cached on Cloudflare as the Last-Modified header value. When  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today we’re excited to announce Smart Edge Revalidation. It was designed to ensure that compute resources are synchronized efficiently between our edge and a browser. Right now, as many as 30% of objects cached on Cloudflare’s edge do not have the HTTP response headers required for revalidation. This can result in unnecessary origin calls. Smart Edge Revalidation fixes this: it does the work to ensure that these headers are present, even when an origin doesn’t send them to us. The advantage of this? There’s less wasted bandwidth and compute for objects that do not need to be redownloaded. And there are faster browser page loads for users.</p>
    <div>
      <h2>So What Is Revalidation?</h2>
      <a href="#so-what-is-revalidation">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/gTlB9tlXkMhFpf88xVu9O/6fd36a3a9baa63e2c20b9ea49d54bd03/Cache-Hit--Miss.png" />
            
            </figure><p>Revalidation is one part of a longer story about efficiently serving objects that live on an origin server from an intermediary cache. Visitors to a website want it to be fast. One foundational way to make sure that a website is fast for visitors is to serve objects from cache. In this way, requests and responses do not need to transit unnecessary parts of the Internet back to an origin and, instead, can be served from a data center that is closer to the visitor. As such, website operators generally only want to serve content from an origin when content has changed. So how do objects stay in cache for as long as necessary?</p><p>One way to do that is with HTTP response headers.</p><p>When Cloudflare gets a response from an origin, included in that response are a number of headers. You can see these headers by opening any webpage, inspecting the page, going to the network tab, and clicking any file. In the response headers section there will generally be a header known as “<code>Cache-Control</code>.” This header is a way for origins to answer caching intermediaries’ questions like: is this object eligible for cache? How long should this object be in cache? And what should the caching intermediary do after that time expires?</p><div></div><p>How long something should be in cache can be specified through the <code>max-age</code> or <code>s-maxage</code> directives. These directives specify a TTL or time-to-live for the object in seconds. Once the object has been in cache for the requisite TTL, the clock hits 0 (zero) and it is marked as expired. Cache can no longer safely serve expired content to requests without figuring out if the object has changed on the origin or if it is the same.</p><p>If it has changed, it must be redownloaded from the origin. If it hasn’t changed, then it can be marked as fresh and continue to be served. This check, again, is known as revalidation.</p><p>We’re excited that Smart Edge Revalidation extends the efficiency of revalidation to everyone, regardless of an origin sending the necessary response headers</p>
    <div>
      <h2>How is Revalidation Accomplished?</h2>
      <a href="#how-is-revalidation-accomplished">
        
      </a>
    </div>
    <p>Two additional headers, <a href="https://datatracker.ietf.org/doc/html/rfc7232#section-2.2"><code>Last-Modified</code></a> and <a href="https://datatracker.ietf.org/doc/html/rfc7232#section-2.3"><code>ETag</code></a>, are set by an origin in order to distinguish different versions of the same URL/object across modifications. After the object expires and the revalidation check occurs, if the <code>ETag</code> value hasn’t changed or a more recent <code>Last-Modified</code> timestamp isn’t present, the object is marked “revalidated” and the expired object can continue to be served from cache. If there has been a change as indicated by the <code>ETag</code> value or <code>Last-Modified</code> timestamp, then the new object is downloaded and the old object is removed from cache.</p><p>Revalidation checks occur when a browser sends a request to a cache server using <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since"><code>If-Modified-Since</code></a> or <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match"><code>If-None-Match</code></a> headers. These <i>request headers</i> are questions sent from the browser cache about when an object has last changed that can be answered via the <code>ETag</code> or <code>Last-Modified</code> <i>response headers</i> on the cache server. For example, if the browser sends a request to a cache server with <code>If-Modified-Since: Tue, 8 Nov 2021 07:28:00 GMT</code> the cache server must look at the object being asked about and if it has not changed since November 8 at 7:28 AM, it will respond with a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304">304 status code</a> indicating it’s unchanged. If the object has changed, the cache server will respond with the new object.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5prstYJc5fXhuiLi8ulIxg/78a8cbd21546b33b2afea42bf3ea9826/Cache-Response-Header.png" />
            
            </figure><p>Sending a 304 status code that indicates an object can be reused is much more efficient than sending the entire object. It’s like if you ran a news website that updated every 24 hours. Once the content is updated for the day, you wouldn’t want to keep redownloading the same unchanged content from the origin and instead, you would prefer to make sure that the day’s content was just reused by sending a lightweight signal to that effect, until the site changes the next day.</p><p>The problem with this system of browser questions and revalidation responses is that sometimes origins don’t set <code>ETag</code> or <code>Last-Modified</code> headers, or they aren’t configured by the website’s admin, making revalidation impossible. This means that every time an object expires, it must be redownloaded regardless of if there has been a change or not, because we have to assume that the asset has been updated, or else risk serving stale content.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1RvLJ1Px9qitcuzYQmdlUO/86c73964fd7f8d18ed2697ce3b4fc350/Origin-Response-Header.png" />
            
            </figure><p>This is an incredible waste of resources which costs hundreds of GB/sec of needless bandwidth between the edge and the visitor. Meaning browsers are downloading hundreds of GB/sec of content they <i>may already have</i>. If our baseline of revalidation is around 10% of all traffic and in initial tests, Smart Edge Revalidation increased revalidation just under 50%, this means that without a user needing to configure <b>anything</b>, we can increase total revalidations by around 5%!</p><p>Such a large reduction in bandwidth use also comes with potential environmental benefits. Based on Cloudflare's carbon emissions per byte, the needless bandwidth being used could amount to 2000+ metric tons CO2e/year, the equivalent of the <a href="https://www.epa.gov/energy/greenhouse-gas-equivalencies-calculator">CO2 emissions</a> from more than 400 cars in a year.</p><p>Revalidation also comes with a performance improvement because it usually means a browser is downloading less than 1KB of data to check if the asset has changed or not, while pulling the full asset can be 100sKB. This can improve performance and reduce the bandwidth between the visitor and our edge.</p>
    <div>
      <h2>How Smart Edge Revalidation Works</h2>
      <a href="#how-smart-edge-revalidation-works">
        
      </a>
    </div>
    <p>When both <code>Last-Modified</code> and <code>Etag</code> headers are <i>absent</i> from the origin server response, Smart Edge Revalidation will use the time the object was cached on Cloudflare’s edge as the <code>Last-Modified</code> header value. When a browser sends a revalidation request to Cloudflare using <code>If-Modified-Since</code> or <code>If-None-Match</code>, our edge can answer those revalidation questions using the <code>Last-Modified</code> header generated from Smart Edge Revalidation. In this way, our edge can ensure efficient revalidation even if the headers are not sent from the origin.</p><p>Smart Edge Revalidation will be enabled automatically for all Cloudflare customers over the coming weeks. If this behavior is undesired, you can always ensure that Smart Edge Revalidation is not activated by <a href="https://web.dev/http-cache/#response-headers">confirming your origin</a> is sending <code>ETag</code> or <code>Last-Modified</code> headers when you want to indicate changed content. Additionally, you could have your origin direct your desired revalidation behavior by making sure it sets appropriate <a href="https://support.cloudflare.com/hc/en-us/articles/115003206852-Understanding-Origin-Cache-Control">cache-control headers</a>.</p><p>Smart Edge Revalidation is a win for everyone: visitors will get more content faster from cache, website owners can serve and revalidate additional content from Cloudflare efficiently, and the Internet will get a bit greener and more efficient.</p><p>Smart Edge Revalidation is the latest announcement to join the list of ways we're making our network more sustainable to help build a greener Internet — check out posts from earlier this week to learn about our <a href="/cloudflare-committed-to-building-a-greener-internet/">climate commitments</a>, <a href="/announcing-green-compute/">Green Compute with Workers</a>, <a href="https://assets.ctfassets.net/slt3lc6tev37/2YzIeTtzSbyKkM4GsryP5S/62ce0dff98e92a142281a0b462ce4408/Cloudflare_Emissions_Inventory_-_2020.pdf">Carbon Impact Report</a>, <a href="/green-hosting-with-cloudflare-pages/">Pages x Green Web Foundation partnership</a>, and <a href="/crawler-hints-how-cloudflare-is-reducing-the-environmental-impact-of-web-searches/">crawler hints</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/guKMx74ygDjoGcziS5Usf/41e7cf39b57f8c9ba7c7052e2f4fbd62/Green-Cache.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Impact Week]]></category>
            <category><![CDATA[Cache]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">6DXhhWwE8C0xQ3eKp7tthE</guid>
            <dc:creator>Alex Krivit</dc:creator>
            <dc:creator>Yuchen Wu</dc:creator>
        </item>
        <item>
            <title><![CDATA[Automating data center expansions with Airflow]]></title>
            <link>https://blog.cloudflare.com/automating-data-center-expansions-with-airflow/</link>
            <pubDate>Wed, 27 Jan 2021 16:46:02 GMT</pubDate>
            <description><![CDATA[ How we automated data center expansions and cut by 90% the amount of time our team spent on tedious operational work. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare’s network keeps growing, and that growth doesn’t just come from building new data centers in new cities. We’re also upgrading the capacity of existing data centers by adding newer generations of servers — a process that makes our network safer, faster, and more reliable for our users.</p><p>Connecting new Cloudflare servers to our network has always been complex, in large part because of the amount of manual effort that used to be required. Members of our Data Center and Infrastructure Operations, Network Operations, and Site Reliability Engineering teams had to carefully follow steps in an extremely detailed standard operating procedure (SOP) document, often copying command-line snippets directly from the document and pasting them into terminal windows.</p><p>But such a manual process can only scale so far, and we knew must be a way to automate the installation of new servers.</p><p>Here’s how we tackled that challenge by building our own Provisioning-as-a-Service (PraaS) platform and cut by 90% the amount of time our team spent on mundane operational tasks.</p>
    <div>
      <h3>Choosing and using an automation framework</h3>
      <a href="#choosing-and-using-an-automation-framework">
        
      </a>
    </div>
    <p>When we began our automation efforts, we quickly realized it made sense to replace each of these manual SOP steps with an API-call equivalent and to present them in a self-service web-based portal.</p><p>To organize these new automatic steps, we chose <a href="https://airflow.apache.org/">Apache Airflow</a>, an open-source workflow management platform. Airflow is built around directed acyclic graphs, or DAGs, which are collections of all the tasks you want to run, organized in a way that reflects their relationships and dependencies.</p><p>In this new system, each SOP step is implemented as a task in the DAG. The majority of these tasks are API calls to Salt — software which automates the management and configuration of any infrastructure or application, and which we use to manage our servers, switches, and routers. Other DAG tasks are calls to query Prometheus (systems monitoring and alerting toolkit), Thanos (a highly available Prometheus setup with long-term storage capabilities), Google Chat webhooks, JIRA, and other internal systems.</p><p>Here is an example of one of these tasks. In the original SOP, SREs were given the following instructions to enable anycast:</p><ol><li><p><b><i>Login to a remote system.</i></b></p></li><li><p><b><i>Copy and Paste the command in the terminal.</i></b></p></li><li><p><b><i>Replace the router placeholder in the command snippet with the actual value.</i></b></p></li><li><p><b><i>Execute the command.</i></b></p></li></ol><p>In our new workflow, this step becomes a single task in the DAG named “enable_anycast”:</p>
            <pre><code>enable_anycast = builder.wrap_class(AsyncSaltAPIOperator)(
             task_id='enable_anycast',
             target='{{ params.netops }}',
             function='cmd.run',
             fun_kwargs={'cmd': 'salt {{ get_router(params.colo_name) }} '
                         'anycast.enable --out=json --out-indent=-1'},
             salt_conn_id='salt_api',
             trigger_rule='one_success')</code></pre>
            <p>As you can see, automation eliminates the need for a human operator to login to a remote system, and to figure out the router that will be used to replace the placeholder in the command to be executed.</p><p>In Airflow, a task is an implementation of an Operator. The Operator in the automated step is the “AsyncSaltAPIOperator”, a custom operator built in-house. This extensibility is one of the many reasons that made us decide to use Apache Airflow. It allowed us to extend its functionality by writing custom operators that suit our needs.</p><p>SREs from various teams have written quite a lot of custom Airflow Operators that integrate with Salt, Prometheus, Bitbucket, Google Chat, JIRA, PagerDuty, among others.</p>
    <div>
      <h3>Manual SOP steps transformed into a feature-packed automation</h3>
      <a href="#manual-sop-steps-transformed-into-a-feature-packed-automation">
        
      </a>
    </div>
    <p>The tasks that replaced steps in the SOP are marvelously feature-packed. Here are some highlights of what they are capable of, on top of just executing a command:</p><p><b>Failure Handling</b>When a task fails for whatever reason, it automatically retries until it exhausts its maximum retry limit that we set for the task. We employ various retry strategies, including instructing tasks to not retry at all, especially when it’s impractical to retry, or when we deliberately do not want it to retry at all regardless of whether there are any retry attempts remaining, such as when an exception is encountered or a condition that is unlikely to change for the better.</p><p><b>Logging</b>Each task provides a comprehensive log during executions. We’ve written our tasks to ensure that we log as much information as possible that would help us audit and troubleshoot issues.</p><p><b>Notifications</b>We’ve written our tasks to send a notification with information such as the name of the DAG, the name of the task, its task state, the number of attempts it took to reach a certain state, and a link to view the task logs.</p><p>When a task fails, we definitely want to be notified, so we also set tasks to additionally provide information such as the number of retry attempts and links to view relevant wiki pages or Grafana dashboards.</p><p>Depending on the criticality of the failure, we can also instruct it to page the relevant on-call person on the provisioning shift, should it require immediate attention.</p><p><b>Jinja Templating</b>Jinja templating allows providing dynamic content using code to otherwise static objects such as strings. We use this in combination with macros wherein we provide parameters that can change during the execution, since macros are evaluated while the task gets run.</p><p><b>Macros</b>Macros are used to pass dynamic information into task instances at runtime. Macros are a way to expose objects to templates. In other words, macros are functions that take input, modify that input, and give the modified output.</p>
    <div>
      <h3>Adapting tasks for preconditions and human intervention</h3>
      <a href="#adapting-tasks-for-preconditions-and-human-intervention">
        
      </a>
    </div>
    <p>There are a few steps in the SOP that require certain preconditions to be met. We use sensors to set dependencies between these tasks, and even between different DAGs, so that one does not run until the dependency has been met.</p><p>Below is an example of a sensor that waits until all nodes resolve to their assigned DNS records:</p>
            <pre><code>verify_node_dns = builder.wrap_class(DNSSensor)(
            task_id='verify_node_dns',
            zone=domain,
            nodes_from='{{ to_json(run_ctx.globals.import_nodes_via_mpl) }}',
            timeout=60 * 30,
            poke_interval=60 * 10,
	mode='reschedule')</code></pre>
            <p>In addition, some of our tasks still require input from a  human operator. In these circumstances, we use sensors as blocking tasks that prevent work from starting until certain preconditions are met. We use these to set dependencies between tasks and even DAGs so that one does not run until the dependency has finished successfully.</p><p>The code below is a simple example of a task that will send notifications to get the attention of a human operator, and waits until a Change Request ticket has been provided and verified:</p>
            <pre><code>verify_jira_input = builder.wrap_class(InputSensor)(
            task_id='verify_jira_input',
            var_key='jira',
            prompt='Please provide the Change Request ticket.',
            notify=True,
            require_human=True)</code></pre>
            <p>Another sensor task example is waiting until a zone has been deployed by a Cloudflare engineer as described in <a href="/improving-the-resiliency-of-our-infrastructure-dns-zone/">https://blog.cloudflare.com/improving-the-resiliency-of-our-infrastructure-dns-zone/</a>.</p><p>In order for PraaS to be able to accept human inputs, we’ve written a separate DAG we call our DAG Manager. Whenever we need to submit input back to a running expansion DAG, we simply trigger the DAG Manager and pass in our input as a JSON configuration, which will then be processed accordingly and submit the input back to the expansion DAG.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1y5JUysuUMGAzvWpiPEb4G/162771c50b9be51d4a496cc0aa019c4c/image2-20.png" />
            
            </figure>
    <div>
      <h3>Managing Dependencies Between Tasks</h3>
      <a href="#managing-dependencies-between-tasks">
        
      </a>
    </div>
    <p>Replacing SOP steps with DAG tasks was only the first part of our journey towards greater automation. We also had to define the dependencies between these tasks and construct the workflow accordingly.</p><p>Here’s an example of what this looks like in code:</p>
            <pre><code>verify_cr &gt;&gt; parse_cr &gt;&gt; [execute_offline, execute_online]
        execute_online &gt;&gt; silence_highstate_runner &gt;&gt; silence_metals &gt;&gt; \
            disable_highstate_runner</code></pre>
            <p>The code simply uses bit shift operators to chain the operations. A list of tasks can also be set as dependencies:</p>
            <pre><code>change_metal_status &gt;&gt;  [wait_for_change_metal_status, verify_zone_update] &gt;&gt; \
evaluate_ecmp_management</code></pre>
            <p>With the bit shift operator, chaining multiple dependencies becomes concise.</p><p>By default, a downstream task will only run if its upstream has succeeded. For a more complex dependency setup, we set a trigger_rule which defines the rule by which the generated task gets triggered.</p><p>All operators have a trigger_rule argument. The Airflow scheduler decides whether to run the task or not depending on what rule was specified in the task. An example rule that we use a lot in PraaS is “one_success” — it fires as soon as at least one parent succeeds, and it does not wait for all parents to be done.</p>
    <div>
      <h3>Solving Complex Workflows with Branching and Multi-DAGs</h3>
      <a href="#solving-complex-workflows-with-branching-and-multi-dags">
        
      </a>
    </div>
    <p>Having complex workflows means that we need a workflow to branch, or only go down a certain path, based on an arbitrary condition, which is typically related to something that happened in an upstream task. Branching is used to perform conditional logic, that is, execute a set of tasks based on a condition. We use BranchPythonOperator to achieve this.</p><p>At some point in the workflow, our data center expansion DAGs trigger various external DAGs to accomplish complex tasks. This is why we have written our DAGs to be fully reusable. We did not try to incorporate all the logic into a single DAG; instead, we created other separable DAGs that are fully reusable and can be triggered on-demand manually by hand or programmatically — our DAG Manager and the “helper” DAG is an example of this.</p><p>The Helper DAG comprises logic that allows us to mimic a “for loop” by having the DAG respawn itself if needed, technically doing cycles. If you recall, a DAG is acyclic, but we have some tasks in our workflow that require us to do complex loops and are solved by using a helper DAG.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4JEhMEgEZswPJWxkgSAtck/11453cefd2e4c6543181bdf241c9d2ea/image3-31.png" />
            
            </figure><p>We designed reusable DAGs early on, which allowed us to build complex automation workflows from separable DAGs, each of which handles distinct and well-defined tasks. Each data center DAG could easily reuse other DAGs by triggering them programmatically.</p><p>Having separate DAGs that run independently, that are triggered by other DAGs, and that keep inter-dependencies between them, is a pattern we use a lot. It has allowed us to execute very complex workflows.</p>
    <div>
      <h3>Creating DAGs that Scale and Executing Tasks at Scale</h3>
      <a href="#creating-dags-that-scale-and-executing-tasks-at-scale">
        
      </a>
    </div>
    <p>Data center expansions are done in two phases:</p><p><b>Phase 1</b> - this is the phase in which servers are powered on. It boots our custom Linux kernel, and begins the provisioning process.</p><p><b>Phase 2</b> - this is the phase in which newly provisioned servers are enabled in the cluster to receive production traffic.</p><p>To reflect these phases in the automation workflow, we also wrote two separate DAGs, one for each phase. However, we have over 200 data centers, so if we were to write a pair of DAGs for each, we would end up writing and maintaining 400 files!</p><p>A viable option could be to parameterize our DAGs. At first glance, this approach sounds reasonable. However, it poses one major challenge: tracking the progress of DAG runs will be too difficult and confusing for the human operator using PraaS.</p><p>Following the software design principle called DRY (Don’t Repeat Yourself), and inspired by the Factory Method design pattern in programming, we’ve instead written both phase 1 and phase 2 DAGs in a way that allow them to dynamically create multiple different DAGs with exactly the same tasks, and to fully reuse the exact same code. As a result, we only maintain one code base, and as we add new data centers, we are also able to generate a DAG for each new data center instantly, without writing a single line of code.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/32tu5HSvTbXRo6yrRAqlq9/dff87e0fd133a1b8ce83b49ef616e0c3/image1-37.png" />
            
            </figure><p>And Airflow even made it easy to put a simple customized web UI on top of the process, which made it simple to use by more employees who didn’t have to understand all the details.</p>
    <div>
      <h3>The death of SOPs?</h3>
      <a href="#the-death-of-sops">
        
      </a>
    </div>
    <p>We would like to think that all of this automation removes the need for our original SOP document. But this is not really the case.  Automation can fail, the components in it can fail, and   a particular task in the DAG may fail. When this happens, our SOPs will be used again to prevent provisioning and expansion activities from stopping completely.</p><p>Introducing automation paved the way for what we call an SOP-as-Code practice. We made sure that every task in the DAG had an equivalent manual step in the SOP that SREs can execute by hand, should the need arise, and that every change in the SOP has a corresponding pull request (PR) in the code.</p>
    <div>
      <h3>What’s next for PraaS</h3>
      <a href="#whats-next-for-praas">
        
      </a>
    </div>
    <p>Onboarding of the other provisioning activities into PraaS, such as decommissioning, is already ongoing.</p><p>For expansions, our ultimate goal is a fully autonomous system that monitors whether new servers have been racked in our edge data centers — and automatically triggers expansions — with no human intervention.</p>
    <div>
      <h3>Watch it on Cloudflare TV</h3>
      <a href="#watch-it-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div>
<p></p> ]]></content:encoded>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Data Center]]></category>
            <guid isPermaLink="false">7m9h6gYslgZrVO36QyRv8a</guid>
            <dc:creator>Jet Mariscal</dc:creator>
        </item>
        <item>
            <title><![CDATA[An introduction to three-phase power and PDUs]]></title>
            <link>https://blog.cloudflare.com/an-introduction-to-three-phase-power-and-pdus/</link>
            <pubDate>Fri, 04 Dec 2020 14:05:37 GMT</pubDate>
            <description><![CDATA[ This blog is a quick Electrical Engineering 101 session going over specifically how 3-phase PDUs work, along with some good practices on how we use them ]]></description>
            <content:encoded><![CDATA[ <p>Our fleet of over 200 locations comprises various generations of servers and routers. And with the ever changing landscape of services and computing demands, it’s imperative that we manage power in our data centers right. This blog is a brief Electrical Engineering 101 session going over specifically how power distribution units (PDU) work, along with some good practices on how we use them. It appears to me that we could all use a bit more knowledge on this topic, and more love and appreciation of something that’s critical but usually taken for granted, like hot showers and opposable thumbs.</p><p>A PDU is a device used in data centers to distribute power to multiple rack-mounted machines. It’s an industrial grade power strip typically designed to power an average consumption of about <a href="https://www.eia.gov/tools/faqs/faq.php?id=97&amp;t=3">seven US households</a>. Advanced models have monitoring features and can be accessed via <a href="https://www.cloudflare.com/learning/access-management/what-is-ssh/">SSH</a> or webGUI to turn on and off power outlets. How we choose a PDU depends on what country the data center is and what it provides in terms of <a href="https://www.worldstandards.eu/electricity/three-phase-electric-power/">voltage, phase, and plug type</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5VQToUAxoKYdAqSWgqOyDS/15afd55e2d243d5ae444bf39929cc88c/Artboard-1.png" />
            
            </figure><p>For each of our racks, all of our dual power-supply (PSU) servers are cabled to one of the two vertically mounted PDUs. As shown in the picture above, one PDU feeds a server’s PSU via a red cable, and the other PDU feeds that server’s other PSU via a blue cable. This is to ensure we have power redundancy maximizing our service uptime; in case one of the PDUs or server PSUs fail, the redundant power feed will be available keeping the server alive.</p>
    <div>
      <h3>Faraday’s Law and Ohm’s Law</h3>
      <a href="#faradays-law-and-ohms-law">
        
      </a>
    </div>
    <p>Like most high-voltage applications, PDUs and servers are designed to use AC power. Meaning voltage and current aren’t constant — they’re sine waves with magnitudes that can alternate between positive and negative at a certain frequency. For example, a voltage feed of 100V is not constantly at 100V, but it bounces between 100V and -100V like a sine wave. One complete sine wave cycle is one phase of 360 degrees, and running at 50Hz means there are 50 cycles per second.</p><p>The sine wave can be explained by Faraday’s Law and by looking at how an AC power generator works. Faraday’s Law tells us that a current is induced to flow due to a changing magnetic field. Below illustrates a simple generator with a permanent magnet rotating at constant speed and a coil coming in contact with the magnet’s magnetic field. Magnetic force is strongest at the North and South ends of the magnet. So as it rotates on itself near the coil, current flow fluctuates in the coil. One complete rotation of the magnet represents one phase. As the North end approaches the coil, current increases from zero. Once the North end leaves, current decreases to zero. The South end in turn approaches, now the current “increases” in the opposite direction. And finishing the phase, the South end leaves returning the current back to zero. Current <i>alternates</i> its direction at every half cycle, hence the naming of Alternating Current.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7CcXLX2BlNWmNeVBk9YOJz/68fd03d71ec5a29a4c13454e4ce5fb72/Artboard-2-1.png" />
            
            </figure><p>Current and voltage in AC power fluctuate in-phase, or “in tandem”, with each other. So by Ohm's Law of Power = Voltage x Current, power will always be positive. Notice on the graph below that AC power (Watts) has two peaks per cycle. But for practical purposes, we'd like to use a constant power value. We do that by interpreting AC power into "DC" power using root-mean-square (RMS) averaging, which takes the max value and divides it by √2. For example, in the US, our conditions are 208V 24A at 60Hz. When we look at spec sheets, all of these values can be assumed as RMS'd into their constant DC equivalent values. When we say we're fully utilizing a PDU's max capacity of 5kW, it actually means that the power consumption of our machines bounces between 0 and 7.1kW (5kW x √2).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3fM63pmekFqVlqHI6opB4V/c26220c91da05301a7573407d513eafe/image13.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6n22LgiBvxJ4hrF6UdL0em/058bcddee79584ca845603f0ae6ca706/image6.png" />
            
            </figure><p>It’s also critical to figure out the sum of power our servers will need in a rack so that it falls under the PDU’s design max power capacity. For our US example, a PDU is typically 5kW (208 volts x 24 amps); therefore, we're budgeting 5kW and fit as many machines as we can under that. If we need more machines and the total sum power goes above 5kW, we'd need to provision another power source. That would lead to possibly another set of PDUs and racks that we may not fully use depending on demand; e.g. more underutilized costs. All we can do is abide by P = V x I.</p><p>However there is a way we can increase the max power capacity economically — 3-phase PDU. Compared to single phase, its max capacity is √3 or 1.7 times higher. A 3-phase PDU of the same US specs above has a capacity of 8.6kW (5kW x √3), allowing us to power more machines under the same source. Using a 3-phase setup might mean it has thicker cables and bigger plug; but despite being more expensive than a 1-phase, its value is higher compared to two 1-phase rack setups for these reasons:</p><ul><li><p>It’s more cost-effective, because there are fewer hardware resources to buy</p></li><li><p>Say the computing demand adds up to 215kW of hardware, we would need 25 3-phase racks compared to 43 1-phase racks.</p></li><li><p>Each rack needs two PDUs for power redundancy. Using the example above, we would need 50 3-phase PDUs compared to 86 1-phase PDUs to power 215kW worth of hardware.</p></li><li><p>That also means a smaller rack footprint and fewer power sources provided and charged by the data center, saving us up to √3 or 1.7 times in opex.</p></li><li><p>It’s more resilient, because there are more circuit breakers in a 3-phase PDU — one more than in a 1-phase. For example, a 48-outlet PDU that is 1-phase would be split into two circuits of 24 outlets. While a 3-phase one would be split into 3 circuits of 16 outlets. If a breaker tripped, we’d lose 16 outlets using a 3-phase PDU instead of 24.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6j4fgzSlb8GU9fsGwywC11/a761ff04c713ff3cbc5a5b04f5e067b0/image5-1.png" />
            
            </figure><p>The PDU shown above is a 3-phase model of 48 outlets. We can see three pairs of circuit breakers for the three branches that are intertwined with each other <b>—</b> white, grey, and black. Industry demands today pressure engineers to maximize compute performance and minimize physical footprint, making the 3-phase PDU a widely-used part of operating a data center.</p>
    <div>
      <h3>What is 3-phase?</h3>
      <a href="#what-is-3-phase">
        
      </a>
    </div>
    <p>A 3-phase AC generator has three coils instead of one where the coils are 120 degrees apart inside the cylindrical core, as shown in the figure below. Just like the 1-phase generator, current flow is induced by the rotation of the magnet thus creating power from each coil sequentially at every one-third of the magnet’s rotation cycle. In other words, we’re generating three 1-phase power offset by 120 degrees.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7JNipgmV5CwJESOZLyeiy2/8a8cd916f8af4b820b8de6d93ed237ce/Artboard-3-1.png" />
            
            </figure><p>A 3-phase feed is set up by joining any of its three coils into line pairs. L1, L2, and L3 coils are live wires with each on their own <i>phase</i> carrying their own <i>phase</i> voltage and <i>phase</i> current. Two phases joining together form one <i>line</i> carrying a common <i>line</i> voltage and <i>line</i> current. L1 and L2 phase voltages create the L1/L2 line voltage. L2 and L3 phase voltages create the L2/L3 line voltage. L1 and L3 phase voltages create the L1/L3 line voltage.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/aEGubJ4ZOZYPBdqTyT2If/6d9ec3e4af13c9a9b8edfc3ddf4235ab/image9-1.jpg" />
            
            </figure><p>Let’s take a moment to clarify the terminology. Some other sources may refer to line voltage (or current) as line-to-line or phase-to-phase voltage (or current). It can get confusing, because line voltage is the same as phase voltage in 1-phase circuits, as there’s only one phase. Also, the <i>magnitude</i> of the line voltage is equal to the <i>magnitude</i> of the phase voltage in 3-phase Delta circuits, while the <i>magnitude</i> of the line current is equal to the <i>magnitude</i> of the phase current in 3-phase Wye circuits.</p><p>Conversely, the line current equals to phase current times √3 in Delta circuits. In Wye circuits, the line voltage equals to phase voltage times √3.</p><p>In Delta circuits:</p><p>V<sub>line</sub> = V<sub>phase</sub></p><p>I<sub>line</sub> = √3 x I<sub>phase</sub></p><p>In Wye circuits:</p><p>V<sub>line</sub> = √3 x V<sub>phase</sub></p><p>I<sub>line</sub> = I<sub>phase</sub></p><p>Delta and Wye circuits are the two methods that three wires can join together. This happens both at the power source with three coils and at the PDU end with three branches of outlets. Note that the generator and the PDU don’t need to match each other’s circuit types.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/yBiiAFcHTedr9s3s3QSPL/52c6822b94b0816b139570c334c62998/image8.png" />
            
            </figure><p>On PDUs, these phases join when we plug servers into the outlets. So we conceptually use the wirings of coils above and replace them with resistors to represent servers. Below is a simplified wiring diagram of a 3-phase Delta PDU showing the three line pairs as three modular branches. Each branch carries two phase currents and its own one common voltage drop.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xV9Yds4Dzc7VOVFEmrg7X/f398859d93ca13341b641ca3ec533753/image15.png" />
            
            </figure><p>And this one below is of a 3-phase Wye PDU. Note that Wye circuits have an additional line known as the neutral line where all three phases meet at one point. Here each branch carries one phase and a neutral line, therefore one common current. The neutral line isn’t considered as one of the phases.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6P2JKqoppmgeEjomv9jmmY/8fe420607f4a0f0abba13cdf82aa117a/image20.png" />
            
            </figure><p>Thanks to a neutral line, a Wye PDU can offer a second voltage source that is √3 times lower for smaller devices, like laptops or monitors. Common voltages for Wye PDUs are 230V/400V or 120V/208V, particularly in North America.</p>
    <div>
      <h3>Where does the √3 come from?</h3>
      <a href="#where-does-the-3-come-from">
        
      </a>
    </div>
    <p>Why are we multiplying by √3? As the name implies, we are adding phasors. Phasors are complex numbers representing sine wave functions. Adding phasors is like adding vectors. Say your GPS tells you to walk 1 mile East (vector a), then walk a 1 mile North (vector b). You walked 2 miles, but you only moved by 1.4 miles NE from the original location (vector a+b). That 1.4 miles of “work” is what we want.</p><p>Let’s take in our application L1 and L2 in a Delta circuit. we add phases L1 and L2, we get a L1/L2 line. We assume the 2 coils are identical. Let’s say α represents the voltage magnitude for each phase. The 2 phases are 120 degrees offset as designed in the 3-phase power generator:</p>
            <pre><code>|L1| = |L2| = α
L1 = |L1|∠0° = α∠0°
L2 = |L2|∠-120° = α∠-120°</code></pre>
            <p>Using vector addition to solve for L1/L2:</p>
            <pre><code>L1/L2 = L1 + L2</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2NtOvL0EuHAqcE84gSFJsf/64042e5e1175757f216824d49222b611/image10.png" />
            
            </figure><p>$$\text{L1/L2} = α∠\text{0°} - α∠\text{-120°}$$</p><p>$$\text{L1/L2} = (α\cos{\text{0°}}+jα\sin{\text{0°}})-(α\cos{\text{-120°}}+jα\sin{\text{-120°}})$$</p><p>$$\text{L1/L2} = (α\cos{\text{0°}}-α\cos{\text{-120°}})+j(α\sin{\text{0°}}-α\sin{\text{-120°}})$$</p><p>$$\text{L1/L2} = \frac{3}{2}α+j\frac{\sqrt3}{2}α$$</p><p>Convert L1/L2 into polar form:</p><p>$$\text{L1/L2} = \sqrt{(\frac{3}{2}α)^2 + (\frac{\sqrt3}{2}α)<sup>2}∠\tan</sup>{-1}(\frac{\frac{\sqrt3}{2}α}{\frac{3}{2}α})$$</p><p>$$\text{L1/L2} = \sqrt 3α∠\text{30°}$$</p><p>Since voltage is a scalar, we’re only interested in the “work”:</p>
            <pre><code>|L1/L2| = √3α</code></pre>
            <p>Given that α also applies for L3. This means for any of the three line pairs, we multiply the phase voltage by √3 to calculate the line voltage.</p><p>V<sub>line</sub> = √3 x V<sub>phase</sub></p><p>Now with the three line powers being equal, we can add them all to get the overall effective power. The derivation below works for both Delta and Wye circuits.</p><p>P<sub>overall</sub> = 3 x P<sub>line</sub></p><p>P<sub>overall</sub> = 3 x (V<sub>line</sub> x I<sub>line</sub>)</p><p>P<sub>overall</sub> = (3/√3) x (V<sub>phase</sub> x I<sub>phase</sub>)</p><p>P<sub>overall</sub> = √3 x V<sub>phase</sub> x I<sub>phase</sub></p><p></p><p>Using the US example, V<sub>phase</sub> is 208V and I<sub>phase</sub> is 24A. This leads to the overall 3-phase power to be 8646W (√3 x 208V x 24A) or 8.6kW. There lies the biggest advantage for using 3-phase systems. Adding 2 sets of coils and wires (ignoring the neutral wire), we’ve turned a generator that can produce √3 or 1.7 times more power!</p>
    <div>
      <h3>Dealing with 3-phase</h3>
      <a href="#dealing-with-3-phase">
        
      </a>
    </div>
    <p>The derivation in the section above assumes that the magnitude at all three phases is equal, but we know in practice that's not always the case. In fact, it's barely ever. We rarely have servers and switches evenly distributed across all three branches on a PDU. Each machine may have different loads and different specs, so power could be wildly different, potentially causing a dramatic phase imbalance. Having a heavily imbalanced setup could potentially hinder the PDU's available capacity.</p><p>A perfectly balanced and fully utilized PDU at 8.6kW means that each of its three branches has 2.88kW of power consumed by machines. Laid out simply, it's spread 2.88 + 2.88 + 2.88. This is the best case scenario. If we were to take 1kW worth of machines out of one branch, spreading power to 2.88 + 1.88 + 2.88. Imbalance is introduced, the PDU is underutilized, but we're fine. However, if we were to put back that 1kW into another branch — like 3.88 + 1.88 + 2.88 — the PDU is over capacity, even though the sum is still 8.6kW. In fact, it would be over capacity even if you just added 500W instead of 1kW on the wrong branch, thus reaching 3.18 + 1.88 + 2.88 (8.1kW).</p><p>That's because a 8.6kW PDU is spec'd to have a maximum of 24A for each phase current. Overloading one of the branches can force phase currents to go over 24A. Theoretically, we can reach the PDU’s capacity by loading one branch until its current reaches 24A and leave the other two branches unused. That’ll render it into a 1-phase PDU, losing the benefit of the √3 multiplier. In reality, the branch would have fuses rated less than 24A (usually 20A) to ensure we won't reach that high and cause overcurrent issues. Therefore the same 8.6kW PDU would have one of its branches tripped at 4.2kW (208V x 20A).</p><p>Loading up one branch is the easiest way to overload the PDU. Being heavily imbalanced significantly lowers PDU capacity and increases risk of failure. To help minimize that, we must:</p><ul><li><p>Ensure that total power consumption of all machines is under the PDU's max power capacity</p></li><li><p>Try to be as phase-balanced as possible by spreading cabling evenly across the three branches</p></li><li><p>Ensure that the sum of phase currents from powered machines at each branch is under the fuse rating at the circuit breaker.</p></li></ul><p><a href="https://www.raritan.com/blog/how-to-calculate-current-on-a-3-phase-208v-rack-pdu-power-strip">This spreadsheet</a> from Raritan is very useful when designing racks.</p><p>For the sake of simplicity, let's ignore other machines like switches. Our latest 2U4N servers are rated at 1800W. That means we can only fit a maximum of four of these 2U4N chassis (8600W / 1800W = 4.7 chassis). Rounding them up to 5 would reach a total rack level power consumption of 9kW, so that's a no-no.</p><p>Splitting 4 chassis into 3 branches evenly is impossible, and will force us to have one of the branches to have 2 chassis. That would lead to a non-ideal phase balancing:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1vAVIr6zDrLo4vIWjmtQYL/50f9ae2af265da7f953e05e96003f47f/image17.png" />
            
            </figure><p>Keeping phase currents under 24A, there's only 1.1A (24A - 22.9A) to add on L1 or L2 before the PDU gets overloaded. Say we want to add as many machines as we can under the PDU’s power capacity. One solution is we can add up to 242W on the L1/L2 branch until both L1 and L2 currents reach their 24A limit.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3NP9EOPb0h2lEXuvlSdg3B/9a6f53163c45fd3b8f2ee01964c0bd86/image18.png" />
            
            </figure><p>Alternatively, we can add up to 298W on the L2/L3 branch until L2 current reaches 24A. Note we can also add another 298W on the L3/L1 branch until L1 current reaches 24A.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/64ECQoHBzqhPuq38sNizat/6070a8f2acad6cb76fae4e32f1c566df/image16.png" />
            
            </figure><p>In the examples above, we can see that various solutions are possible. Adding two 298W machines each at L2/L3 and L3/L1 is the most phase balanced solution, given the parameters. Nonetheless, PDU capacity isn't optimized at 7.8kW.</p><p>Dealing with a 1800W server is not ideal, because whichever branch we choose to power one would significantly swing the phase balance unfavorably. Thankfully, our <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">Gen X servers</a> take up less space and are more power efficient. Smaller footprint allows us to have more flexibility and fine-grained control over our racks in many of our diverse data centers. Assuming each 1U server is 450W, as if we physically split the 1800W 2U4N into fours each with their own power supplies, we're now able to fit 18 nodes. That's 2 more nodes than the four 2U4N setup:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7eBaV5AwmB0D31FSgEw65b/bfd1bd88c6e6078edf84523c2852081a/image21.png" />
            
            </figure><p>Adding two more servers here means we've increased our value by 12.5%. While there are more factors not considered here to calculate the Total Cost of Ownership, this is still a great way to show we can be smarter with asset costs.</p><p>Cloudflare provides the back-end services so that our customers can enjoy the performance, reliability, security, and global scale of our edge network. Meanwhile, we manage all of our hardware in over 100 countries with various power standards and compliances, and ensure that our physical infrastructure is as reliable as it can be.</p><p>There’s no Cloudflare without hardware, and there’s no Cloudflare without power. Want to know more? Watch this Cloudflare TV segment about power: <a href="https://cloudflare.tv/event/7E359EDpCZ6mHahMYjEgQl">https://cloudflare.tv/event/7E359EDpCZ6mHahMYjEgQl</a>.</p> ]]></content:encoded>
            <category><![CDATA[Hardware]]></category>
            <category><![CDATA[Data Center]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">4Qx2ZCOM4gEYCEIljEKvAi</guid>
            <dc:creator>Rob Dinh</dc:creator>
        </item>
        <item>
            <title><![CDATA[Unimog - Cloudflare’s edge load balancer]]></title>
            <link>https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/</link>
            <pubDate>Wed, 09 Sep 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Unimog is the Layer 4 Load Balancer for Cloudflare’s edge data centers.  This post explains the problems it solves and how it works. ]]></description>
            <content:encoded><![CDATA[ <p>As the scale of Cloudflare’s edge network has grown, we sometimes reach the limits of parts of our architecture. About two years ago we realized that our existing solution for spreading load within our data centers could no longer meet our needs. We embarked on a project to deploy a <i>Layer 4 Load Balancer</i>, internally called <i>Unimog</i>, to improve the reliability and operational efficiency of our edge network. Unimog has now been deployed in production for over a year.</p><p>This post explains the problems Unimog solves and how it works. Unimog builds on techniques used in other Layer 4 Load Balancers, but there are many details of its implementation that are tailored to the needs of our edge network.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/44EsYFcb0zDeaXqYUX6Jxg/3bb5afc4bd624ccb66e53cd091b8bed9/image3-1.png" />
            
            </figure>
    <div>
      <h3>The role of Unimog in our edge network</h3>
      <a href="#the-role-of-unimog-in-our-edge-network">
        
      </a>
    </div>
    <p>Cloudflare operates an anycast network, meaning that our data centers in <a href="https://www.cloudflare.com/network/">200+ cities</a> around the world serve the same IP addresses. For example, our own cloudflare.com website uses Cloudflare services, and one of its IP addresses is 104.17.175.85. All of our data centers will accept connections to that address and respond to HTTP requests. By the magic of Internet routing, when you visit cloudflare.com and your browser connects to 104.17.175.85, your connection will usually go to the closest (and therefore fastest) data center.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ADAIlOzgQE4V4C7yHERdq/5ae28f8121ca6d39d79acc1a73733d42/internet-2.png" />
            
            </figure><p>Inside those data centers are many servers. The number of servers in each varies greatly (the biggest data centers have a hundred times more servers than the smallest ones). The servers run the <a href="https://www.cloudflare.com/application-services/">application services</a> that implement our products (our caching, DNS, WAF, DDoS mitigation, Spectrum, WARP, etc). Within a single data center, any of the servers can handle a connection for any of our services on any of our anycast IP addresses. This uniformity keeps things simple and avoids bottlenecks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1653XjnZj666YAdzKgPcCN/d0146fc7d280e556ca4d36cfddbdd5ca/colo-4.png" />
            
            </figure><p>But if any server within a data center can handle any connection, when a connection arrives from a browser or some other client, what controls which server it goes to? That’s the job of Unimog.</p><p>There are two main reasons why we need this control. The first is that we regularly move servers in and out of operation, and servers should only receive connections when they are in operation. For example, we sometimes remove a server from operation in order to perform maintenance on it. And sometimes servers are automatically removed from operation because health checks indicate that they are not functioning correctly.</p><p>The second reason concerns the management of the load on the servers (by load we mean the amount of computing work each one needs to do). If the load on a server exceeds the capacity of its hardware resources, then the quality of service to users will suffer. The performance experienced by users degrades as a server approaches saturation, and if a server becomes sufficiently overloaded, users may see errors. We also want to prevent servers being underloaded, which would reduce the value we get from our investment in hardware. So Unimog ensures that the load is spread across the servers in a data center. This general idea is called load balancing (<i>balancing</i> because the work has to be done somewhere, and so for the load on one server to go down, the load on some other server must go up).</p><p>Note that in this post, we’ll discuss how Cloudflare balances the load on its own servers in edge data centers. But load balancing is a requirement that occurs in many places in distributed computing systems. Cloudflare also has a Layer 7 <a href="https://www.cloudflare.com/load-balancing/">Load Balancing</a> product to allow our customers to balance load across their servers. And Cloudflare uses load balancing in other places <a href="/high-availability-load-balancers-with-maglev/">internally</a>.</p><p>Deploying Unimog led to a big improvement in our ability to balance the load on our servers in our edge data centers. Here’s a chart for one data center, showing the difference due to Unimog. Each line shows the processor utilization of an individual server (the colour of the lines indicates <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">server model</a>). The load on the servers varies during the day with the activity of users close to this data center. The white line marks the point when we enabled Unimog. You can see that after that point, the load on the servers became much more uniform. We saw similar results when we deployed Unimog to our other data centers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6ysIFd2DX2oOFyrujMX8OV/a66dc6b3fdabe9c3015887fae118efcb/image1.png" />
            
            </figure>
    <div>
      <h2>How Unimog compares to other load balancers</h2>
      <a href="#how-unimog-compares-to-other-load-balancers">
        
      </a>
    </div>
    <p>There are a variety of techniques for load balancing. Unimog belongs to a category called <i>Layer 4 Load Balancers (L4LBs)</i>. L4LBs direct packets on the network by inspecting information up to layer 4 of the OSI network model, which distinguishes them from the more common <i>Layer 7 Load Balancers</i>.</p><p>The advantage of L4LBs is their efficiency. They direct packets without processing the payload of those packets, so they avoid the overheads associated with higher level protocols. For any load balancer, it’s important that the resources consumed by the load balancer are low compared to the resources devoted to useful work. At Cloudflare, we already pay close attention to the efficient implementation of our services, and that sets a high bar for the load balancer that we put in front of those services.</p><p>The downside of L4LBs is that they can only control which connections go to which servers. They cannot modify the data going over the connection, which prevents them from participating in higher-level protocols like TLS, HTTP, etc. (in contrast, Layer 7 Load Balancers act as proxies, so they can modify data on the connection and participate in those higher-level protocols).</p><p>L4LBs are not new. They are mostly used at companies which have scaling needs that would be hard to meet with L7LBs alone. Google has published about <a href="https://research.google.com/pubs/archive/44824.pdf">Maglev</a>, Facebook open-sourced <a href="https://engineering.fb.com/open-source/open-sourcing-katran-a-scalable-network-load-balancer/">Katran</a>, and Github has open-sourced their <a href="https://github.blog/2018-08-08-glb-director-open-source-load-balancer/">GLB</a>.</p><p>Unimog is the L4LB that Cloudflare has built to meet the needs of our edge network. It shares features with other L4LBs, and it is particularly strongly influenced by GLB. But there are some requirements that were not well-served by existing L4LBs, leading us to build our own:</p><ul><li><p>Unimog is designed to run on the same general-purpose servers that provide application services, rather than requiring a separate tier of servers dedicated to load balancing.</p></li><li><p>It performs dynamic load balancing: measurements of server load are used to adjust the number of connections going to each server, in order to accurately balance load.</p></li><li><p>It supports long-lived connections that remain established for days.</p></li><li><p>Virtual IP addresses are managed as ranges (Cloudflare serves hundreds of thousands of IPv4 addresses on behalf of our customers, so it is impractical to configure these individually).</p></li><li><p>Unimog is tightly integrated with our existing DDoS mitigation system, and the implementation relies on the same XDP technology in the Linux kernel.</p></li></ul><p>The rest of this post describes these features and the design and implementation choices that follow from them in more detail.</p><p>For Unimog to balance load, it’s not enough to send the same (or approximately the same) number of connections to each server, because the performance of our servers varies. We regularly update our server hardware, and we’re now on our <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">10th generation</a>. Once we deploy a server, we keep it in service for as long as it is cost effective, and the lifetime of a server can be several years. It’s not unusual for a single data center to contain a mix of server models, due to expansion and upgrades over time. Processor performance has increased significantly across our server generations. So within a single data center, we need to send different numbers of connections to different servers to utilize the same percentage of their capacity.</p><p>It’s also not enough to give each server a fixed share of connections based on static estimates of their capacity. Not all connections consume the same amount of CPU. And there are other activities running on our servers and consuming CPU that are not directly driven by connections from clients. So in order to accurately balance load across servers, Unimog does <i>dynamic load balancing</i>: it takes regular measurements of the load on each of our servers, and uses a control loop that increases or decreases the number of connections going to each server so that their loads converge to an appropriate value.</p>
    <div>
      <h2>Refresher: TCP connections</h2>
      <a href="#refresher-tcp-connections">
        
      </a>
    </div>
    <p>The relationship between TCP packets and connections is central to the operation of Unimog, so we’ll briefly describe that relationship.</p><p>(Unimog supports UDP as well as TCP, but for clarity most of this post will focus on the TCP support. We explain how UDP support differs towards the end.)</p><p>Here is the outline of a TCP packet:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1uN86MCb8Xmcxhd8Eupv0R/5c82ac004e4fe1f80c6ba68ea49186d0/image12.png" />
            
            </figure><p>The TCP connection that this packet belongs to is identified by the four labelled header fields, which span the IPv4/IPv6 (i.e. layer 3) and TCP (i.e. layer 4) headers: the source and destination addresses, and the source and destination ports. Collectively, these four fields are known as the 4-tuple. When we say the Unimog sends a connection to a server, we mean that all the packets with the 4-tuple identifying that connection are sent to that server.</p><p>A TCP connection is established via a three-way handshake between the client and the server handling that connection. Once a connection has been established, it is crucial that all the incoming packets for that connection go to that same server. If a TCP packet belonging to the connection is sent to a different server, it will signal the fact that it doesn’t know about the connection to the client with a TCP RST (reset) packet. Upon receiving this notification, the client terminates the connection, probably resulting in the user seeing an error. So a misdirected packet is much worse than a dropped packet. As usual, we consider the network to be unreliable, and it’s fine for occasional packets to be dropped. But even a single misdirected packet can lead to a broken connection.</p><p>Cloudflare handles a wide variety of connections on behalf of our customers. Many of these connections carry HTTP, and are typically short lived. But some HTTP connections are used for websockets, and can remain established for hours or days. Our Spectrum product supports arbitrary TCP connections. TCP connections can be terminated or stall for <a href="/when-tcp-sockets-refuse-to-die/">many reasons</a>, and ideally all applications that use long-lived connections would be able to reconnect transparently, and applications would be designed to support such reconnections. But not all applications and protocols meet this ideal, so we strive to maintain long-lived connections. Unimog can maintain connections that last for many days.</p>
    <div>
      <h3>Forwarding packets</h3>
      <a href="#forwarding-packets">
        
      </a>
    </div>
    <p>The previous section described that the function of Unimog is to steer connections to servers. We’ll now explain how this is implemented.</p><p>To start with, let’s consider how one of our data centers might look without Unimog or any other load balancer. Here’s a conceptual view:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1sGaljcZUwny7RxYvQzF0z/4d15981b1a6d5442cadf99c8c789a857/colo-simple.png" />
            
            </figure><p>Packets arrive from the Internet, and pass through the router, which forwards them on to servers (in reality there is usually additional network infrastructure between the router and the servers, but it doesn’t play a significant role here so we’ll ignore it).</p><p>But is such a simple arrangement possible? Can the router spread traffic over servers without some kind of load balancer in between? Routers have a feature called ECMP (equal cost multipath) routing. Its original purpose is to allow traffic to be spread across multiple paths between two locations, but it is commonly repurposed to spread traffic across multiple servers within a data center. In fact, Cloudflare relied on ECMP alone to spread load across servers before we deployed Unimog. ECMP uses a hashing scheme to ensure that packets on a given connection use the same path (Unimog also employs a hashing scheme, so we’ll discuss how this can work in further detail below) . But ECMP is vulnerable to changes in the set of active servers, such as when servers go in and out of service. These changes cause rehashing events, which break connections to all the servers in an ECMP group. Also, routers impose limits on the sizes of ECMP groups, which means that a single ECMP group cannot cover all the servers in our larger edge data centers. Finally, ECMP does not allow us to do dynamic load balancing by adjusting the share of connections going to each server. These drawbacks mean that ECMP alone is not an effective approach.</p><p>Ideally, to overcome the drawbacks of ECMP, we could program the router with the appropriate logic to direct connections to servers in the way we want. But although programmable network data planes have been a hot research topic in recent years, commodity routers are still essentially fixed-function devices.</p><p>We can work around the limitations of routers by having the router send the packets to some load balancing servers, and then programming those load balancers to forward packets as we want. If the load balancers all act on packets in a consistent way, then it doesn’t matter which load balancer gets which packets from the router (so we can use ECMP to spread packets across the load balancers). That suggests an arrangement like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1QsaXd0zJOinv3P3kXgFRm/9baef06d0e1fe4e8106bc1029288329e/colo-lb.png" />
            
            </figure><p>And indeed L4LBs are often deployed like this.</p><p>Instead, Unimog makes <i>every</i> server into a load balancer. The router can send any packet to any server, and that initial server will forward the packet to the right server for that connection:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/77gUuMMXWbIGKclOV82dLp/f89ee9080e094307f1a8b0e8a0a01e6d/colo-unimog.png" />
            
            </figure><p>We have two reasons to favour this arrangement:</p><p>First, in our edge network, we avoid specialised roles for servers. We run the same software stack on the servers in our edge network, providing all of our product features, whether <a href="https://www.cloudflare.com/learning/ddos/how-to-prevent-ddos-attacks/">DDoS attack prevention</a>, website performance features, Cloudflare Workers, WARP, etc. This uniformity is key to the efficient operation of our edge network: we don’t have to manage how many load balancers we have within each of our data centers, because all of our servers act as load balancers.</p><p>The second reason relates to stopping attacks. Cloudflare’s edge network is the target of incessant attacks. Some of these attacks are volumetric - large packet floods which attempt to overwhelm the ability of our data centers to process network traffic from the Internet, and so impact our ability to service legitimate traffic. To successfully mitigate such attacks, it’s important to filter out attack packets as early as possible, minimising the resources they consume. This means that our attack mitigation system needs to occur <i>before</i> the forwarding done by Unimog. That mitigation system is called l4drop, and <a href="/l4drop-xdp-ebpf-based-ddos-mitigations/">we’ve written about it before</a>. l4drop and Unimog are closely integrated. Because l4drop runs on all of our servers, and because l4drop comes before Unimog, it’s natural for Unimog to run on all of our servers too.</p>
    <div>
      <h3>XDP and xdpd</h3>
      <a href="#xdp-and-xdpd">
        
      </a>
    </div>
    <p>Unimog implements packet forwarding using a Linux kernel facility called <a href="https://en.wikipedia.org/wiki/Express_Data_Path"><i>XDP</i></a>. XDP allows a program to be attached to a network interface, and the program gets run for every packet that arrives, before it is processed by the kernel’s main network stack. The XDP program returns an action code to tell the kernel what to do with the packet:</p><ul><li><p>PASS: Pass the packet on to the kernel’s network stack for normal processing.</p></li><li><p>DROP: Drop the packet. This is the basis for l4drop.</p></li><li><p>TX: Transmit the packet back out of the network interface. The XDP program can modify the packet data before transmission. This action is the basis for Unimog forwarding.</p></li></ul><p>XDP programs run within the kernel, making this an efficient approach even at <a href="/how-to-drop-10-million-packets/">high packet rates</a>. XDP programs are expressed as eBPF bytecode, and run within an in-kernel virtual machine. Upon loading an XDP program, the kernel compiles its eBPF code into machine code. The kernel also verifies the program to check that it does not compromise security or stability. eBPF is not only used in the context of XDP: many recent Linux kernel innovations employ eBPF, as it provides a convenient and efficient way to extend the behaviour of the kernel.</p><p>XDP is much more convenient than alternative approaches to packet-level processing, particularly in our context where the servers involved also have many other tasks. We have continued to enhance Unimog since its initial deployment. Our deployment model for new versions of our Unimog XDP code is essentially the same as for userspace services, and we are able to deploy new versions on a weekly basis if needed. Also, established techniques for optimizing the performance of the Linux network stack provide good performance for XDP.</p><p>There are two main alternatives for efficient packet-level processing:</p><ul><li><p>Kernel-bypass networking (such as <a href="https://www.dpdk.org/">DPDK</a>), where a program in userspace manages a network interface (or some part of one) directly without the involvement of the kernel. This approach works best when servers can be dedicated to a network function (due to the need to dedicate processor or network interface hardware resources, and awkward integration with the normal kernel network stack; <a href="/kernel-bypass/">see our old post about this</a>). But we avoid putting servers in specialised roles. (Github’s open-source GLB uses DPDK, and this is one of the main factors that made GLB unsuitable for us.)</p></li><li><p>Kernel modules, where code is added to the kernel to perform the necessary network functions. The Linux IPVS (IP Virtual Server) subsystem falls into this category. But developing, testing, and deploying kernel modules is cumbersome compared to XDP.</p></li></ul><p>The following diagram shows an overview of our use of XDP. Both l4drop and Unimog are implemented by an XDP program. l4drop matches attack packets, and uses the DROP action to discard them. Unimog forwards packets, using the TX action to resend them. Packets that are not dropped or forwarded pass through to the normal Linux network stack. To support our elaborate use of XDP, we have developed the <i>xdpd daemon</i> which performs the necessary supervisory and support functions for our XDP programs.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/mwuvfgu1KFhyMtYkI7c4M/f1450237c9417b357b3c11144f90f048/xdpd.png" />
            
            </figure><p>Rather than a single XDP program, we have a chain of XDP programs that must be run for each packet (l4drop, Unimog, and others we have not covered here). One of the responsibilities of xdpd is to prepare these programs, and to make the appropriate system calls to load them and assemble the full chain.</p><p>Our XDP programs come from two sources. Some are developed in a conventional way: engineers write C code, our build system compiles it (with clang) to eBPF ELF files, and our release system deploys those files to our servers. Our Unimog XDP code works like this. In contrast, the l4drop XDP code is dynamically generated by xdpd based on information it receives from attack detection systems.</p><p>xdpd has many other duties to support our use of XDP:</p><ul><li><p>XDP programs can be supplied with data using data structures called <i>maps</i>. xdpd populates the maps needed by our programs, based on information received from control planes.</p></li><li><p>Programs (for instance, our Unimog XDP program) may depend upon configuration values which are fixed while the program runs, but do not have universal values known at the time their C code was compiled. It would be possible to supply these values to the program via maps, but that would be inefficient (retrieving a value from a map requires a call to a helper function). So instead, xdpd will fix up the eBPF program to insert these constants before it is loaded.</p></li><li><p>Cloudflare carefully monitors the behaviour of all our software systems, and this includes our XDP programs: They emit metrics (via another use of maps), which xdpd exposes to our metrics and alerting system (prometheus).</p></li><li><p>When we deploy a new version of xdpd, <a href="/graceful-upgrades-in-go">it gracefully upgrades</a> in such a way that there is no interruption to the operation of Unimog or l4drop.</p></li></ul><p>Although the XDP programs are written in C, xdpd itself is written in Go. Much of its code is specific to Cloudflare. But in the course of developing xdpd, we have collaborated with Cilium to develop <a href="https://github.com/cilium/ebpf">https://github.com/cilium/ebpf</a>, an open source Go library that provides the operations needed by xdpd for manipulating and loading eBPF programs and related objects. We’re also collaborating with the Linux eBPF community to share our experience, and extend the core eBPF technology in ways that make features of xdpd obsolete.</p><p>In evaluating the performance of Unimog, our main concern is efficiency: that is, the resources consumed for load balancing relative to the resources used for customer-visible services. Our measurements show that Unimog costs less than 1% of the processor utilization, compared to a scenario where no load balancing is in use. Other L4LBs, intended to be used with servers dedicated to load balancing, may place more emphasis on maximum throughput of packets. Nonetheless, our experience with Unimog and XDP in general indicates that the throughput is more than adequate for our needs, even during large volumetric attacks.</p><p>Unimog is not the first L4LB to use XDP. In 2018, <a href="https://engineering.fb.com/open-source/open-sourcing-katran-a-scalable-network-load-balancer/">Facebook open sourced Katran</a>, their XDP-based L4LB data plane. We considered the possibility of reusing code from Katran. But it would not have been worthwhile: the core C code needed to implement an XDP-based L4LB is relatively modest (about 1000 lines of C, both for Unimog and Katran). Furthermore, we had requirements that were not met by Katran, and we also needed to integrate with existing components and systems at Cloudflare (particularly l4drop). So very little of the code could have been reused as-is.</p>
    <div>
      <h2>Encapsulation</h2>
      <a href="#encapsulation">
        
      </a>
    </div>
    <p>As discussed as the start of this post, clients make connections to one of our edge data centers with a destination IP address that can be served by any one of our servers. These addresses that do not correspond to a specific server are known as <i>virtual IPs</i> (VIPs). When our Unimog XDP program forwards a packet destined to a VIP, it must replace that VIP address with the <i>direct IP (DIP)</i> of the appropriate server for the connection, so that when the packet is retransmitted it will reach that server. But it is not sufficient to overwrite the VIP in the packet headers with the DIP, as that would hide the original destination address from the server handling the connection (the original destination address is often needed to correctly handle the connection).</p><p>Instead, the packet must be <i>encapsulated</i>: Another set of packet headers is prepended to the packet, so that the original packet becomes the payload in this new packet. The DIP is then used as the destination address in the outer headers, but the addressing information in the headers of the original packet is preserved. The encapsulated packet is then retransmitted. Once it reaches the target server, it must be <i>decapsulated</i>: the outer headers are stripped off to yield the original packet as if it had arrived directly.</p><p>Encapsulation is a general concept in computer networking, and is used in a variety of contexts. The headers to be added to the packet by encapsulation are defined by an <i>encapsulation format</i>. Many different encapsulation formats have been defined within the industry, tailored to the requirements in specific contexts. Unimog uses a format called <a href="https://www.ietf.org/id/draft-ietf-intarea-gue-09.txt"><i>GUE</i> (Generic UDP Encapsulation)</a>, in order to allow us to re-use the glb-redirect component from github’s GLB (glb-redirect is discussed below).</p><p>GUE is a relatively simple encapsulation format. It encapsulates within a UDP packet, placing a GUE-specific header between the outer IP/UDP headers and the payload packet to allow extension data to be carried (and we’ll see how Unimog takes advantage of this):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ImbRfgHVop1QjM0K3R6Lr/8f7ce8fdab72ee2dfacc454d59bc1171/image8.png" />
            
            </figure><p>When an encapsulated packet arrives at a server, the encapsulation process must be reversed. This step is called <i>decapsulation</i>. The headers that were added during the encapsulation process are removed, leaving the original packet to be processed by the network stack as if it had arrived directly from the client.</p><p>An issue that can arise with encapsulation is hitting limits on the maximum packet size, because the encapsulation process makes packets larger. The de-facto maximum packet size on the Internet is 1500 bytes, and not coincidentally this is also the maximum packet size on ethernet networks. For Unimog, encapsulating a 1500-byte packet results in a 1536-byte packet. To allow for these enlarged encapsulated packets, we have enabled jumbo frames on the networks inside our data centers, so that the 1500-byte limit only applies to packets headed out to the Internet.</p>
    <div>
      <h2>Forwarding logic</h2>
      <a href="#forwarding-logic">
        
      </a>
    </div>
    <p>So far, we have described the technology used to implement the Unimog load balancer, but not how our Unimog XDP program selects the DIP address when forwarding a packet. This section describes the basic scheme. But as we’ll see, there is a problem, so then we’ll describe how this scheme is elaborated to solve that problem.</p><p>In outline, our Unimog XDP program processes each packet in the following way:</p><ol><li><p>Determine whether the packet is destined for a VIP address. Not all of the packets arriving at a server are for VIP addresses. Other packets are passed through for normal handling by the kernel’s network stack. (xdpd obtains the VIP address ranges from the Unimog control plane.)</p></li><li><p>Determine the DIP for the server handling the packet’s connection.</p></li><li><p>Encapsulate the packet, and retransmit it to the DIP.</p></li></ol><p>In step 2, note that all the load balancers must act consistently - when forwarding packets, they must all agree about which connections go to which servers. The rate of new connections arriving at a data center is large, so it’s not practical for load balancers to agree by communicating information about connections amongst themselves. Instead L4LBs adopt designs which allow the load balancers to reach consistent forwarding decisions independently. To do this, they rely on hashing schemes: Take the 4-tuple identifying the packet’s connection, put it through a hash function to obtain a key (the hash function ensures that these key values are uniformly distributed), then perform some kind of lookup into a data structure to turn the key into the DIP for the target server.</p><p>Unimog uses such a scheme, with a data structure that is simple compared to some other L4LBs. We call this data structure the <i>forwarding table</i>, and it consists of an array where each entry contains a DIP specifying the target server for the relevant packets (we call these entries <i>buckets</i>). The forwarding table is generated by the Unimog control plane and broadcast to the load balancers (more on this below), so that it has the same contents on all load balancers.</p><p>To look up a packet’s key in the forwarding table, the low N bits from the key are used as the index for a bucket (the forwarding table is always a power-of-2 in size):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/PTRbsbXdTdzDPg0zTNtkJ/c22c4634097466ff8e57844465713398/hashing.png" />
            
            </figure><p>Note that this approach does not provide per-connection control - each bucket typically applies to many connections. All load balancers in a data center use the same forwarding table, so they all forward packets in a consistent manner. This means it doesn’t matter which packets are sent by the router to which servers, and so ECMP re-hashes are a non-issue. And because the forwarding table is immutable and simple in structure, lookups are fast.</p><p>Although the above description only discusses a single forwarding table, Unimog supports multiple forwarding tables, each one associated with a <i>trafficset</i> - the traffic destined for a particular service. Ranges of VIP addresses are associated with a trafficset. Each trafficset has its own configuration settings and forwarding tables. This gives us the flexibility to differentiate how Unimog behaves for different services.</p><p>Precise load balancing requires the ability to make fine adjustments to the number of connections arriving at each server. So we make the number of buckets in the forwarding table more than 100 times the number of servers. Our data centers can contain hundreds of servers, and so it is normal for a Unimog forwarding table to have tens of thousands of buckets. The DIP for a given server is repeated across many buckets in the forwarding table, and by increasing or decreasing the number of buckets that refer to a server, we can control the share of connections going to that server. Not all buckets will correspond to exactly the same number of connections at a given point in time (the properties of the hash function make this a statistical matter). But experience with Unimog has demonstrated that the relationship between the number of buckets and resulting server load is sufficiently strong to allow for good load balancing.</p><p>But as mentioned, there is a problem with this scheme as presented so far. Updating a forwarding table, and changing the DIPs in some buckets, would break connections that hash to those buckets (because packets on those connections would get forwarded to a different server after the update). But one of the requirements for Unimog is to allow us to change which servers get new connections without impacting the existing connections. For example, sometimes we want to drain the connections to a server, maintaining the existing connections to that server but not forwarding new connections to it, in the expectation that many of the existing connections will terminate of their own accord. The next section explains how we fix this scheme to allow such changes.</p>
    <div>
      <h2>Maintaining established connections</h2>
      <a href="#maintaining-established-connections">
        
      </a>
    </div>
    <p>To make changes to the forwarding table without breaking established connections, Unimog adopts the “daisy chaining” technique described in the paper <a href="https://www.usenix.org/system/files/conference/nsdi18/nsdi18-olteanu.pdf"><i>Stateless Datacenter Load-balancing with Beamer</i></a>.</p><p>To understand how the Beamer technique works, let’s look at what can go wrong when a forwarding table changes: imagine the forwarding table is updated so that a bucket which contained the DIP of server A now refers to server B. A packet that would formerly have been sent to A by the load balancers is now sent to B. If that packet initiates a new connection (it’s a TCP SYN packet), there’s no problem - server B will continue the three-way handshake to complete the new connection. On the other hand, if the packet belongs to a connection established before the change, then the TCP implementation of server B has no matching TCP socket, and so sends a RST back to the client, breaking the connection.</p><p>This explanation hints at a solution: the problem occurs when server B receives a forwarded packet that does not match a TCP socket. If we could change its behaviour in this case to forward the packet a second time to the DIP of server A, that would allow the connection to server A to be preserved. For this to work, server B needs to know the DIP for the bucket <i>before</i> the change.</p><p>To accomplish this, we extend the forwarding table so that each bucket has two slots, each containing the DIP for a server. The first slot contains the current DIP, which is used by the load balancer to forward packets as discussed (and here we refer to this forwarding as the <i>first hop</i>). The second slot preserves the previous DIP (if any), in order to allow the packet to be forwarded again on a <i>second hop</i> when necessary.</p><p>For example, imagine we have a forwarding table that refers to servers A, B, and C, and then it is updated to stop new connections going to server A, but maintaining established connections to server A. This is achieved by replacing server A’s DIP in the first slot of any buckets where it appears, but preserving it in the second slot:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7hTdYu2s3dFhNKhsASADKa/44fe175c6f406753d82390f7a046669f/image9-1.png" />
            
            </figure><p>In addition to extending the forwarding table, this approach requires a component on each server to forward packets on the second hop when necessary. This diagram shows where this <i>redirector</i> fits into the path a packet can take:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6f5CQLIjy5hO2eWKW8i36H/ab18994ccdf15961e8a87d1e3890d2da/sausage.png" />
            
            </figure><p>The redirector follows some simple logic to decide whether to process a packet locally on the first-hop server or to forward it on the second-hop server:</p><ul><li><p>If the packet is a SYN packet, initiating a new connection, then it is always processed by the first-hop server. This ensures that new connections go to the first-hop server.</p></li><li><p>For other packets, the redirector checks whether the packet belongs to a connection with a corresponding TCP socket on the first-hop server. If so, it is processed by that server.</p></li><li><p>Otherwise, the packet has no corresponding TCP socket on the first-hop server. So it is forwarded on to the second-hop server to be processed there (in the expectation that it belongs to some connection established on the second-hop server that we wish to maintain).</p></li></ul><p>In that last step, the redirector needs to know the DIP for the second hop. To avoid the need for the redirector to do forwarding table lookups, the second-hop DIP is placed into the encapsulated packet by the Unimog XDP program (which already does a forwarding table lookup, so it has easy access to this value). This second-hop DIP is carried in a GUE extension header, so that it is readily available to the redirector if it needs to forward the packet again.</p><p>This second hop, when necessary, does have a cost. But in our data centers, the fraction of forwarded packets that take the second hop is usually less than 1% (despite the significance of long-lived connections in our context). The result is that the practical overhead of the second hops is modest.</p><p>When we initially deployed Unimog, we adopted the <a href="https://github.com/github/glb-director/tree/master/src/glb-redirect">glb-redirect iptables module</a> from github’s GLB to serve as the redirector component. In fact, some implementation choices in Unimog, such as the use of GUE, were made in order to facilitate this re-use. glb-redirect worked well for us initially, but subsequently we wanted to enhance the redirector logic. glb-redirect is a custom Linux kernel module, and developing and deploying changes to kernel modules is more difficult for us than for eBPF-based components such as our XDP programs. This is not merely due to Cloudflare having invested more engineering effort in software infrastructure for eBPF; it also results from the more explicit boundary between the kernel and eBPF programs (for example, we are able to run the same eBPF programs on a range of kernel versions without recompilation). We wanted to achieve the same ease of development for the redirector as for our XDP programs.</p><p>To that end, we decided to write an eBPF replacement for glb-redirect. While the redirector could be implemented within XDP, like our load balancer, practical concerns led us to implement it as a <a href="https://man7.org/linux/man-pages/man8/tc-bpf.8.html">TC classifier program</a> instead (TC is the traffic control subsystem within the Linux network stack). A downside to XDP is that the packet contents prior to processing by the XDP program <a href="/xdpcap/">are not visible using conventional tools such as tcpdump</a>, complicating debugging. TC classifiers do not have this downside, and in the context of the redirector, which passes most packets through, the performance advantages of XDP would not be significant.</p><p>The result is cls-redirect, a redirector implemented as a TC classifier program. We have contributed our <a href="https://github.com/torvalds/linux/blob/c4ba153b6501fa7ccfdc7e57946fb1d6011e36e8/tools/testing/selftests/bpf/progs/test_cls_redirect.c">cls-redirect code as part of the Linux kernel test suite</a>. In addition to implementing the redirector logic, cls-redirect also implements decapsulation, removing the need to separately configure GUE tunnel endpoints for this purpose.</p><p>There are some features suggested in the Beamer paper that Unimog does not implement:</p><ul><li><p>Beamer embeds <i>generation numbers</i> in the encapsulated packets to address a potential corner case where a ECMP rehash event occurs at the same time as a forwarding table update is propagating from the control plane to the load balancers. Given the combination of circumstances required for a connection to be impacted by this issue, we believe that in our context the number of affected connections is negligible, and so the added complexity of the generation numbers is not worthwhile.</p></li><li><p>In the Beamer paper, the concept of daisy-chaining encompasses third hops etc. to preserve connections across a series of changes to a bucket. Unimog only uses two hops (the first and second hops above), so in general it can only preserve connections across a single update to a bucket. But our experience is that even with only two hops, a careful strategy for updating the forwarding tables permits connection lifetimes of days.</p></li></ul><p>To elaborate on this second point: when the control plane is updating the forwarding table, it often has some choice in which buckets to change, depending on the event that led to the update. For example, if a server is being brought into service, then some buckets must be assigned to it (by placing the DIP for the new server in the first slot of the bucket). But there is a choice about <i>which</i> buckets. A strategy of choosing the least-recently modified buckets will tend to minimise the impact to connections.</p><p>Furthermore, when updating the forwarding table to adjust the balance of load between servers, Unimog often uses a novel trick: due to the redirector logic, exchanging the first-hop and second-hop DIPs for a bucket only affects which server receives new connections for that bucket, and never impacts any established connections. Unimog is able to achieve load balancing in our edge data centers largely through forwarding table changes of this type.</p>
    <div>
      <h2>Control plane</h2>
      <a href="#control-plane">
        
      </a>
    </div>
    <p>So far, we have discussed the Unimog data plane - the part that processes network packets. But much of the development effort on Unimog has been devoted to the control plane - the part that generates the forwarding tables used by the data plane. In order to correctly maintain the forwarding tables, the control plane consumes information from multiple sources:</p><ul><li><p>Server information: Unimog needs to know the set of servers present in a data center, some key information about each one (such as their DIP addresses), and their operational status. It also needs signals about transitional states, such as when a server is being withdrawn from service, in order to gracefully drain connections (preventing the server from receiving new connections, while maintaining its established connections).</p></li><li><p>Health: Unimog should only send connections to servers that are able to correctly handle those connections, otherwise those servers should be removed from the forwarding tables. To ensure this, it needs health information at the node level (indicating that a server is available) and at the service level (indicating that a service is functioning normally on a server).</p></li><li><p>Load: in order to balance load, Unimog needs information about the resource utilization on each server.</p></li><li><p>IP address information: Cloudflare serves hundreds of thousands of IPv4 addresses, and these are something that we have to treat as a dynamic resource rather than something statically configured.</p></li></ul><p>The control plane is implemented by a process called the <i>conductor</i>. In each of our edge data centers, there is one active conductor, but there are also standby instances that will take over if the active instance goes away.</p><p>We use <a href="https://www.consul.io/">Hashicorp’s Consul</a> in a number of ways in the Unimog control plane (we have an independent Consul server cluster in each data center):</p><ul><li><p>Consul provides a key-value store, with support for blocking queries so that changes to values can be received promptly. We use this to propagate the forwarding tables and VIP address information from the conductor to xdpd on the servers.</p></li><li><p>Consul provides server- and service-level health checks. We use this as the source of health information for Unimog.</p></li><li><p>The conductor stores its state in the Consul KV store, and uses Consul’s distributed locks to ensure that only one conductor instance is active.</p></li></ul><p>The conductor obtains server load information from Prometheus, which we already use for metrics throughout our systems. It balances the load across the servers using a control loop, periodically adjusting the forwarding tables to send more connections to underloaded servers and less connections to overloaded servers. The load for a server is defined by a Prometheus metric expression which measures processor utilization (with some intricacies to better handle characteristics of our workloads). The determination of whether a server is underloaded or overloaded is based on comparison with the average value of the load metric, and the adjustments made to the forwarding table are proportional to the deviation from the average. So the result of the feedback loop is that the load metric for all servers converges on the average.</p><p>Finally, the conductor queries internal Cloudflare APIs to obtain the necessary information on servers and addresses.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/25doef0hn0sJTI8oGzgoOk/fb025967eab0ee87dcb9ead2724dc755/control.png" />
            
            </figure><p>Unimog is a critical system: incorrect, poorly adjusted or stale forwarding tables could cause incoming network traffic to a data center to be dropped, or servers to be overloaded, to the point that a data center would have to be removed from service. To maintain a high quality of service and minimise the overhead of managing our many edge data centers, we have to be able to upgrade all components. So to the greatest extent possible, all components are able to tolerate brief absences of the other components without any impact to service. In some cases this is possible through careful design. In other cases, it requires explicit handling. For example, we have found that Consul can temporarily report inaccurate health information for a server and its services when the Consul agent on that server is restarted (for example, in order to upgrade Consul). So we implemented the necessary logic in the conductor to detect and disregard these transient health changes.</p><p>Unimog also forms a complex system with feedback loops: The conductor reacts to its observations of behaviour of the servers, and the servers react to the control information they receive from the conductor. This can lead to behaviours of the overall system that are hard to anticipate or test for. For instance, not long after we deployed Unimog we encountered surprising behaviour when data centers became overloaded. This is of course a scenario that we strive to avoid, and we have automated systems to remove traffic from overloaded data centers if it does. But if a data center became sufficiently overloaded, then health information from its servers would indicate that many servers were degraded to the point that Unimog would stop sending new connections to those servers. Under normal circumstances, this is the correct reaction to a degraded server. But if enough servers become degraded, diverting new connections to other servers would mean those servers became degraded, while the original servers were able to recover. So it was possible for a data center that became temporarily overloaded to get stuck in a state where servers oscillated between healthy and degraded, even after the level of demand on the data center had returned to normal. To correct this issue, the conductor now has logic to distinguish between isolated degraded servers and such data center-wide problems. We have continued to improve Unimog in response to operational experience, ensuring that it behaves in a predictable manner over a wide range of conditions.</p>
    <div>
      <h2>UDP Support</h2>
      <a href="#udp-support">
        
      </a>
    </div>
    <p>So far, we have described Unimog’s support for directing TCP connections. But Unimog also supports UDP traffic. UDP does not have explicit connections between clients and servers, so how it works depends upon how the UDP application exchanges packets between the client and server. There are a few cases of interest:</p>
    <div>
      <h3>Request-response UDP applications</h3>
      <a href="#request-response-udp-applications">
        
      </a>
    </div>
    <p>Some applications, such as DNS, use a simple request-response pattern: the client sends a request packet to the server, and expects a response packet in return. Here, there is nothing corresponding to a connection (the client only sends a single packet, so there is no requirement to make sure that multiple packets arrive at the same server). But Unimog can still provide value by spreading the requests across our servers.</p><p>To cater to this case, Unimog operates as described in previous sections, hashing the 4-tuple from the packet headers (the source and destination IP addresses and ports). But the Beamer daisy-chaining technique that allows connections to be maintained does not apply here, and so the buckets in the forwarding table only have a single slot.</p>
    <div>
      <h3>UDP applications with flows</h3>
      <a href="#udp-applications-with-flows">
        
      </a>
    </div>
    <p>Some UDP applications have long-lived <i>flows</i> of packets between the client and server. Like TCP connections, these flows are identified by the 4-tuple. It is necessary that such flows go to the same server (even when Cloudflare is just passing a flow through to the origin server, it is convenient for detecting and mitigating certain kinds of attack to have that flow pass through a single server within one of Cloudflare’s data centers).</p><p>It's possible to treat these flows by hashing the 4-tuple, skipping the Beamer daisy-chaining technique as for request-response applications. But then adding servers will cause some flows to change servers (this would effectively be a form of consistent hashing). For UDP applications, we can’t say in general what impact this has, as we can for TCP connections. But it’s possible that it causes some disruption, so it would be nice to avoid this.</p><p>So Unimog adapts the daisy-chaining technique to apply it to UDP flows. The outline remains similar to that for TCP: the same redirector component on each server decides whether to send a packet on a second hop. But UDP does not have anything corresponding to TCP’s SYN packet that indicates a new connection. So for UDP, the part that depends on SYNs is removed, and the logic applied for each packet becomes:</p><ul><li><p>The redirector checks whether the packet belongs to a connection with a corresponding UDP socket on the first-hop server. If so, it is processed by that server.</p></li><li><p>Otherwise, the packet has no corresponding UDP socket on the first-hop server. So it is forwarded on to the second-hop server to be processed there (in the expectation that it belongs to some flow established on the second-hop server that we wish to maintain).</p></li></ul><p>Although the change compared to the TCP logic is not large, it has the effect of switching the roles of the first- and second-hop servers: For UDP, new flows go to the second-hop server. The Unimog control plane has to take account of this when it updates a forwarding table. When it introduces a server into a bucket, that server should receive new connections or flows. For a TCP trafficset, this means it becomes the first-hop server. For UDP trafficset, it must become the second-hop server.</p><p>This difference between handling of TCP and UDP also leads to higher overheads for UDP. In the case of TCP, as new connections are formed and old connections terminate over time, fewer packets will require the second hop, and so the overhead tends to diminish. But with UDP, new connections always involve the second hop. This is why we differentiate the two cases, taking advantage of SYN packets in the TCP case.</p><p>The UDP logic also places a requirement on services. The redirector must be able to match packets to the corresponding sockets on a server according to their 4-tuple. This is not a problem in the TCP case, because all TCP connections are represented by connected sockets in the BSD sockets API (these sockets are obtained from an accept system call, so that they have a local address and a peer address, determining the 4-tuple). But for UDP, unconnected sockets (lacking a declared peer address) can be used to send and receive packets. So some UDP services only use unconnected sockets. For the redirector logic above to work, services must create connected UDP sockets in order to expose their flows to the redirector.</p>
    <div>
      <h3>UDP applications with sessions</h3>
      <a href="#udp-applications-with-sessions">
        
      </a>
    </div>
    <p>Some UDP-based protocols have explicit <i>sessions</i>, with a session identifier in each packet. Session identifiers allow sessions to persist even if the 4-tuple changes. This happens in mobility scenarios - for example, if a mobile device passes from a WiFi to a cellular network, causing its IP address to change. An example of a UDP-based protocol with session identifiers is <a href="/the-road-to-quic/">QUIC</a> (which calls them connection IDs).</p><p>Our Unimog XDP program allows a <i>flow dissector</i> to be configured for different trafficsets. The flow dissector is the part of the code that is responsible for taking a packet and extracting the value that identifies the flow or connection (this value is then hashed and used for the lookup into the forwarding table). For TCP and UDP, there are default flow dissectors that extract the 4-tuple. But specialised flow dissectors can be added to handle UDP-based protocols.</p><p>We have used this functionality in our WARP product. We extended the Wireguard protocol used by WARP in a backwards-compatible way to include a session identifier, and added a flow dissector to Unimog to exploit it. There are more details in <a href="/warp-technical-challenges/">our post on the technical challenges of WARP</a>.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Unimog has been deployed to all of Cloudflare’s edge data centers for over a year, and it has become essential to our operations. Throughout that time, we have continued to enhance Unimog (many of the features described here were not present when it was first deployed). So the ease of developing and deploying changes, due to XDP and xdpd, has been a significant benefit. Today we continue to extend it, to support more services, and to help us manage our traffic and the load on our servers in more contexts.</p> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Load Balancing]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">7wYPcBFe2ItK009d2hZ5FS</guid>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[Rendering React on the Edge with Flareact and Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/rendering-react-on-the-edge-with-flareact-and-cloudflare-workers/</link>
            <pubDate>Thu, 03 Sep 2020 12:00:00 GMT</pubDate>
            <description><![CDATA[ Page speed is critical. You need to get content to your audience as quickly as possible on every device. You also need to render ads in a speedy way to maintain a good user experience and make money to support your journalism. ]]></description>
            <content:encoded><![CDATA[ <p><i>The following is a guest post from </i><a href="https://twitter.com/jplhomer"><i>Josh Larson</i></a><i>, Engineer at </i><a href="https://www.voxmedia.com/"><i>Vox Media</i></a><i>.</i></p><p>Imagine you’re the maintainer of a high-traffic media website, and your DNS is already hosted on Cloudflare.</p><p>Page speed is critical. You need to get content to your audience as quickly as possible on every device. You also need to render ads in a speedy way to maintain a good user experience and make money to support your journalism.</p><p>One solution would be to <b>render your site statically</b> and cache it at the edge. This would help ensure you have top-notch delivery speed because you don’t need a server to return a response. However, your site has decades worth of content. If you wanted to make even a small change to the site design, you would need to regenerate <i>every single page</i> during your next deploy. This would take ages.</p><p>Another issue is that your site would be static — and future updates to content or new articles would not be available until you deploy again.</p><p>That’s not going to work.</p><p>Another solution would be to <b>render each page dynamically</b> on your server. This ensures you can return a dynamic response for new or updated articles.</p><p>However, you’re going to need to pay for some beefy servers to be able to handle spikes in traffic and respond to requests in a timely manner. You’ll also probably need to implement a system of internal caches to optimize the performance of your app, which could lead to a more complicated development experience. That also means you’ll be at risk of a thundering herd problem if, for any reason, your cache becomes invalidated.</p><p>Neither of these solutions are great, and you’re forced to <b>make a tradeoff</b> between one of these two approaches.</p><p>Thankfully, you’ve recently come across a project like <a href="https://nextjs.org/">Next.js</a> which offers a hybrid approach: static-site generation along with incremental regeneration. You’re in love with the patterns and developer experience in Next.js, but you’d also love to take advantage of the Cloudflare Workers platform to <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">host your site</a>.</p><p><a href="https://workers.cloudflare.com/">Cloudflare Workers</a> allow you to run your code on the edge quickly, efficiently and at scale. Instead of paying for a server to host your code, you can host it directly inside the datacenter — reducing the number of network trips required to load your application. In a perfect world, we wouldn’t need to find hosting for a Next.js site, because Cloudflare offers the same JavaScript hosting functionality with the Workers platform. With their dynamic runtime and edge caching capabilities, we wouldn’t need to worry about making a tradeoff between static and dynamic for our site.</p><p>Unfortunately, frameworks like Next.js and Cloudflare Workers don’t mesh together particularly well due to technical constraints. Until now:</p>
    <div>
      <h4>I’m excited to announce <a href="https://flareact.com">Flareact</a>, a new open-source React framework built for Cloudflare Workers.</h4>
      <a href="#im-excited-to-announce-a-new-open-source-react-framework-built-for-cloudflare-workers">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5iZAEv0uTQCFMSWABs8KLe/3ed9b1c1f576ae5498c03b58a41d6e3b/Flareact_2x.png" />
            
            </figure><p>With Flareact, you don’t need to make the tradeoff between a static site and a dynamic application.</p><p>Flareact allows you to <b>render your React apps at the edge</b> rather than on the server. It is modeled after Next.js, which means it supports file-based page routing, dynamic page paths and edge-side data fetching APIs.</p><p>Not only are Flareact pages rendered at the edge — they’re also cached at the edge using the <a href="https://developers.cloudflare.com/workers/runtime-apis/cache">Cache API</a>. This allows you to provide a dynamic content source for your app without worrying about traffic spikes or response times.</p><p>With no servers or origins to deal with, your site is <i>instantly available</i> to your audience. <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> gives you a 0ms cold start and responses from the edge within milliseconds.</p><p>You can <a href="https://flareact.com/">check out the docs</a> and get started now by clicking the button below:</p>
            <figure>
            <a href="https://deploy.workers.cloudflare.com/?url=https://github.com/flareact/flareact-template&amp;paid=true">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/DQKtrrjIPNSa9vCdzGlEo/91db1c0c29bfb1af1a4929292f4afb91/button" />
            </a>
            </figure><p>To get started manually, install the latest wrangler, and use the handy <code>wrangler generate</code> command below to create your first project:</p>
            <pre><code>npm i @cloudflare/wrangler -g
wrangler generate my-project https://github.com/flareact/flareact-template</code></pre>
            
    <div>
      <h2>What’s the big deal?</h2>
      <a href="#whats-the-big-deal">
        
      </a>
    </div>
    <p>Hosting React apps on Cloudflare Workers Sites is <i>not a new concept</i>. In fact, you’ve always been able to <a href="https://developers.cloudflare.com/workers/tutorials/deploy-a-react-app-with-create-react-app">deploy a create-react-app project to Workers Sites</a> in addition to static versions of other frameworks like <a href="https://www.gatsbyjs.com/docs/deploying-to-cloudflare-workers/">Gatsby</a> and Next.js.</p><p>However, Flareact renders your React application at the edge. This allows you to provide an initial server response with HTML markup — which can be helpful for search engine crawlers. You can also cache the response at the edge and optionally invalidate that cache on a timed basis — meaning your static markup will be regenerated if you need it to be fresh.</p><p>This isn’t a new pattern: Next.js has done the hard work in defining the shape of this API with <a href="https://nextjs.org/blog/next-9-3#next-gen-static-site-generation-ssg-support">SSG support</a> and <a href="https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration">Incremental Static Regeneration</a>. While there are nuanced differences in the implementation between Flareact and Next.js, they serve a similar purpose: to get your application to your end-user in the quickest and most-scalable way possible.</p>
    <div>
      <h2>A focus on developer experience</h2>
      <a href="#a-focus-on-developer-experience">
        
      </a>
    </div>
    <p>A <a href="/making-magic-reimagining-developer-experiences-for-the-world-of-serverless/">magical developer experience</a> is a crucial ingredient to any successful product.</p><p>As a longtime fan and user of Next.js, I wanted to experiment with running the framework on Cloudflare Workers. However, Next.js and its APIs are framed around the <a href="https://nodejs.org/api/http.html">Node.js HTTP Server API</a>, while Cloudflare Workers use <a href="https://developers.cloudflare.com/workers/learning/how-workers-works">V8 isolates</a> and are modeled after the <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch-event">FetchEvent type</a>.</p><p>Since we don’t have typical access to a filesystem inside V8 isolates, it’s tough to mimic the environment required to run a dynamic Next.js server at the edge. Though projects like <a href="https://fab.dev">Fab</a> have come up with workarounds, I decided to approach the project with a clean slate and use existing patterns established in Next.js in a brand-new framework.</p><p>As a developer, I absolutely love the simplicity of exporting an asynchronous function from my page to have it supply props to the component. Flareact implements this pattern by allowing you to export a <code>getEdgeProps</code> function. This is similar to <code>getStaticProps</code> in Next.js, and it matches the expected return shape of that function in Next.js — including a <code>revalidate</code> parameter. <a href="https://flareact.com/docs/data-fetching">Learn more about data fetching in Flareact</a>.</p><p>I was also inspired by the <a href="https://nextjs.org/docs/api-routes/introduction">API Routes feature of Next.js</a> when I implemented the <a href="https://flareact.com/docs/api-routes">API Routes feature of Flareact</a> — enabling you to write standard Cloudflare Worker scripts directly within your React app.</p><p>I hope porting over an existing Next.js project to Flareact is a breeze!</p>
    <div>
      <h2>How it works</h2>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>When a <code>FetchEvent</code> request comes in, Flareact inspects the URL pathname to decide how to handle it:</p><p>If the request is for a <b>page</b> or for <b>page props</b>, it checks the cache for that request and returns it if there’s a hit. If there is a cache miss, it generates the page request or props function, stores the result in the cache, and returns the response.</p><p>If the request is for an <b>API route</b>, it sends the entire <code>FetchEvent</code> along to the user-defined API function, allowing the user to respond as they see fit.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/13wlNGHLGEn1TNpLkV2bDU/845168ed4eaeaa27bdb589b1b10daa64/Flareact-diagram-2_2x.png" />
            
            </figure><p>If you want your cached page to be revalidated after a certain amount of time, you can return an additional <code>revalidate</code> property from <code>getEdgeProps()</code>. This instructs Flareact to cache the endpoint for that number of seconds before generating a new response.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6wlcQVsaVBpMW4bbggCwbM/9ecb3ed46b62f6fcd2b2176dffd9b350/Flareact-diagram-1_2x.png" />
            
            </figure><p>Finally, if the request is for a static asset, it returns it directly from the <a href="https://developers.cloudflare.com/workers/runtime-apis/kv#background">Workers KV</a>.</p>
    <div>
      <h3>The Worker</h3>
      <a href="#the-worker">
        
      </a>
    </div>
    <p>The core responsibilities of the <b>Worker</b> — or in a traditional SSR framework, the <i>server</i> — are to:</p><ol><li><p>Render the initial React page component into static HTML markup.</p></li><li><p>Provide the initial page props as a JSON object, embedded into the static markup in a script tag.</p></li><li><p>Load the client-side JavaScript bundles and stylesheets necessary to render the interactive page.</p></li></ol><p>One challenge with building Flareact is that the Webpack targets the <code>webworker</code> output rather than the <code>node</code> output. This makes it difficult to inform the worker which pages exist in the filesystem, since there is no access to the filesystem.</p><p>To get around this, Flareact leverages <a href="https://webpack.js.org/guides/dependency-management/#requirecontext"><code>require.context</code></a>, a Webpack-specific API, to inspect the project and build a manifest of pages on the client and the worker. I’d love to replace this with a smarter bundling strategy on the client-side eventually.</p>
    <div>
      <h3>The Client</h3>
      <a href="#the-client">
        
      </a>
    </div>
    <p>In addition to handling incoming Worker requests, Flareact compiles a client bundle containing the code necessary for routing, data fetching and more from the browser.</p><p>The core responsibilities of the <b>client</b> are to:</p><ol><li><p>Listen for routing events</p></li><li><p>Fetch the necessary page component and its props from the worker over AJAX</p></li></ol><p>Building a client router from scratch has been a challenge. It listens for changes to the internal route state, updates the URL pathname with <a href="https://developer.mozilla.org/en-US/docs/Web/API/History/pushState">pushState</a>, makes an AJAX request to the worker for the page props, and then updates the current component in the render tree with the requested page.</p><p>It was fun building a <a href="https://flareact.com/docs/flareact-link">flareact/link</a> component similar to next/link:</p>
            <pre><code>import Link from "flareact/link";

export default function Index() {
  return (
    &lt;div&gt;
      &lt;Link href="/about"&gt;
        &lt;a&gt;Go to About&lt;/a&gt;
      &lt;/Link&gt;
    &lt;/div&gt;
  );
}</code></pre>
            <p>I also set out to build a custom version of <a href="https://nextjs.org/docs/api-reference/next/head">next/head</a> for Flareact. As it turns out, this was non-trivial! With lots of interesting stuff going on behind the scenes to support SSR and client-side routing events, I decided to make <a href="https://flareact.com/docs/flareact-head">flareact/head</a> a simple wrapper around <a href="https://github.com/nfl/react-helmet">react-helmet</a> instead:</p>
            <pre><code>import Head from "flareact/head";

export default function Index() {
  return (
    &lt;div&gt;
      &lt;Head&gt;
        &lt;title&gt;My page title&lt;/title&gt;
      &lt;/Head&gt;
      &lt;h1&gt;Hello, world.&lt;/h1&gt;
    &lt;/div&gt;
  );
}</code></pre>
            
    <div>
      <h3>Local Development</h3>
      <a href="#local-development">
        
      </a>
    </div>
    <p>The local developer experience of Flareact leverages the new <code>wrangler dev</code> command, sending server requests through a local tunnel to the Cloudflare edge and back to your machine.</p>
<p>This is a huge win for productivity, since you don’t need to manually build and deploy your application to see how it will perform in a production environment.</p><p>It’s also a really exciting update to the serverless toolchain. Running a robust development environment in a serverless world has always been a challenge, since your code is executing in a non-traditional context. Tunneling local code to the edge and back is <i>such a great addition</i> to Cloudflare’s developer experience.</p>
    <div>
      <h2>Use cases</h2>
      <a href="#use-cases">
        
      </a>
    </div>
    <p>Flareact is a great candidate for a lot of <a href="https://jamstack.org/">Jamstack</a>-adjacent applications, like blogs or static marketing sites.</p><p>It could also be used for more dynamic applications, with robust API functions and authentication mechanisms — all implemented using Cloudflare Workers.</p><p>Imagine building a high-traffic e-commerce site with Flareact, where both site reliability and dynamic rendering for things like price changes and stock availability are crucial.</p><p>There are also untold possibilities for integrating the Workers KV into your edge props or API functions as a <a href="https://www.cloudflare.com/developer-platform/products/d1/">first-class database solution</a>. No need to reach for an externally-hosted database!</p><p>While the project is still in its early days, here are a couple real-world examples:</p><ul><li><p><a href="https://github.com/flareact/flareact-site/">The Flareact docs site</a>, powered by <a href="https://blog.cloudflare.com/markdown-for-agents/">Markdown files</a></p></li><li><p><a href="https://github.com/flareact/flareact/tree/master/examples/with-cms-wordpress">A blog site</a>, powered by a headless WordPress API</p></li></ul>
    <div>
      <h2>The road ahead</h2>
      <a href="#the-road-ahead">
        
      </a>
    </div>
    <p>I have to be honest: creating a server-side rendered React framework with little prior knowledge was very difficult. There’s still a ton to learn, and Flareact has a long way to go to reach parity with Next.js in the areas of optimization and production-readiness.</p><p>Here’s what I’m hoping to add to Flareact in the near future:</p><ul><li><p>Smarter client bundling and Webpack chunks to reduce individual page weight</p></li><li><p>A more feature-complete client-side router</p></li><li><p>The ability to extend and customize the root document of the app</p></li><li><p>Support for more style frameworks (CSS-in-JS, Sass, CSS modules, etc)</p></li><li><p>A more stable development environment</p></li><li><p>Documentation and support for environment variables, secrets and KV namespaces</p></li><li><p>A guide for deploying from GitHub Actions and other CI tools</p></li></ul><p>If the project sounds interesting to you, be sure to <a href="https://github.com/flareact/flareact">check out the source code on GitHub</a>. Contributors are welcome!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">4WvkWgYtoiP8WKxt0ptLQx</guid>
            <dc:creator>Guest Author</dc:creator>
        </item>
    </channel>
</rss>