구독해서 새 게시물에 대한 알림을 받으세요.

Rust 덕분에 더 빠르고 안전해진 Cloudflare

2025-09-26

12분 읽기
이 게시물은 English, Français, Deutsch, 日本語, Nederlands简体中文로도 이용할 수 있습니다.

Cloudflare는 세계에서 가장 빠른 네트워크를 구축 및 운영하기 위해 끊임없이 노력합니다. Cloudflare는 2021년부터 네트워크 성능을 추적하고 보고하고 있습니다. 최신 업데이트는 여기에서 확인할 수 있습니다.

가장 빠른 네트워크를 구축하려면 여러 분야에서 노력이 필요합니다. 우리는 효율적이고 빠른 장비를 갖추기 위해 하드웨어에 많은 시간을 투자하고, 인터넷의 모든 지점과 최소한의 지연으로 통신할 수 있도록 피어링 구성에 투자하고 있습니다. 이와 더불어, 새로운 제품이 도입될 때마다 처리 지연이 늘어날 수 있기 때문에 네트워크를 운영하는 소프트웨어에도 지속적으로 투자해야 합니다.

메시지가 아무리 빠르게 도착하더라도, 소프트웨어가 요청을 처리하고 응답하는 데 너무 오래 걸린다면 병목 현상이 발생합니다. 오늘은 응답하는 데 걸리는 중간 지연 시간을 10ms 단축하고, 제3자 CDN 성능 테스트 기준으로 25%의 성능 향상을 이뤄낸 중요한 소프트웨어 업그레이드를 기쁜 마음으로 공유해 드립니다.

지난 한 해 동안 우리는 시스템의 주요 구성 요소를 재구축했으며, 수백만 고객이 사용하는 네트워크 트래픽의 대기 시간을 획기적으로 단축했습니다. 동시에, 시스템 보안을 강화하고, 신제품을 개발하고 출시하는 데 소요되는 시간을 단축했습니다. 

우리의 시작은 어디였을까요?

Cloudflare에 도달하는 모든 요청은 우리 네트워크를 통해 여정을 시작합니다. 이는 웹페이지를 로드하는 브라우저, API를 호출하는 모바일 애플리케이션, 혹은 다른 서비스에서 오는 자동화 트래픽일 수 있습니다. 이러한 요청은 먼저 HTTP 및 TLS 계층에서 종료된 후, FL이라고 부르는 시스템으로 전달되고, 마지막으로 Pingora를 통해 캐시 조회를 수행하거나 필요한 경우 원본 데이터를 가져옵니다.

FL은 Cloudflare의 핵심입니다. 요청이 FL에 도달하면, 우리는 네트워크의 다양한 보안 및 성능 기능을 실행합니다. 이는 각 고객의 고유한 구성 및 설정을 적용하며, WAF 규칙DDoS 보호를 적용하고 트래픽을 개발자 플랫폼R2로 라우팅합니다. 

15년 전에 구축된 FL은 Cloudflare 네트워크의 핵심 역할을 해 왔습니다. 이로 인해 다양한 기능을 제공할 수 있었지만, 시간이 지남에 따라 이러한 유연성은 오히려 문제가 되었습니다. 제품을 추가할수록 FL은 유지 관리가 더 어려워지고, 요청 처리 속도가 느려졌으며, 확장성도 떨어졌습니다. 새로운 기능을 추가할 때마다 기존 로직을 꼼꼼히 확인해야 했고, 그럴수록 대기 시간이 늘어나 원하는 성능을 유지하기가 점점 더 어려워졌습니다.

FL이 왜 우리 시스템의 핵심인지 여기서 잘 드러납니다. 우리는 FL을 종종 Cloudflare의 ‘두뇌’라고 부르곤 했습니다. 또한 이는 우리 시스템에서 가장 오래된 구성 요소 중 하나이기도 합니다. 초기 출시 훨씬 전에 공동 창업자 Lee Holloway가 코드베이스에 첫 커밋을 남겼습니다. 이번 주는 우리가 15주년을 맞이한 기념 주간인데, 이 시스템은 그보다 9개월이나 먼저 시작됐습니다!

commit 39c72e5edc1f05ae4c04929eda4e4d125f86c5ce
Author: Lee Holloway <q@t60.(none)>
Date:   Wed Jan 6 09:57:55 2010 -0800

    nginx-fl initial configuration

커밋에서 알 수 있듯이, FL의 첫 번째 버전은 NGINX 웹 서버를 기반으로 구현되었고, 제품 로직은 PHP로 구현되었습니다.  3년 후, 시스템이 너무 복잡해져서 효과적으로 관리하기 어려워졌고, 응답 속도도 너무 느려져서 실행 중인 시스템을 거의 완전히 재작성해야 했습니다. 이로 인해 또 다른 중요한 커밋이 발생했으며, 이번 커밋은 현재 CTO인 Dane Knecht의 손에서 이루어졌습니다.

commit bedf6e7080391683e46ab698aacdfa9b3126a75f
Author: Dane Knecht
Date:   Thu Sep 19 19:31:15 2013 -0700

    remove PHP.

이 시점부터 FL은 NGINX, OpenResty 프레임워크, LuaJIT를 사용하여 구현되었습니다.  이후 오랫동안 훌륭하게 버텨줬지만, 지난 몇 년 동안은 서서히 한계와 노후화가 드러나기 시작했습니다. LuaJIT의 모호한 버그를 수정하거나 해결하는 데 점점 더 많은 시간을 쏟아야 했습니다. Lua 코드의 매우 동적이고 비정형적인 특성은 로직을 빠르게 구현하려 할 때는 축복이었지만, 많은 양의 복잡한 제품 로직을 통합하려 할 때는 오류와 지연의 원인이 되었습니다. 새로운 제품이 출시될 때마다 기존 제품들이 새로운 로직에 영향을 받는지 확인해야 했습니다.

다시 생각할 때가 되었다는 점은 분명했습니다. 그래서 2024년 7월, 우리는 완전히 새롭고 기존과는 근본적으로 다른 구현의 첫 커밋을 올렸습니다. 새로운 이름에 합의하는 데 시간을 낭비하지 않기 위해, 그냥 "FL2"라고 부르기로 하고, 원래 FL을 "FL1"이라고 부르기 시작했습니다.

commit a72698fc7404a353a09a3b20ab92797ab4744ea8
Author: Maciej Lechowski
Date:   Wed Jul 10 15:19:28 2024 +0100

    Create fl2 project

Rust 및 경직된 모듈화

아예 처음부터 시작하는 것은 아니었습니다. 우리는 이전에 블로그에서 다른 하나의 레거시 시스템을 Tokio 런타임을 사용하여 Rust 프로그래밍 언어로 구축된 Pingora로 대체한 방법에 대해 설명한 적이 있습니다. 우리는 Rust에서 프록시를 구축하기 위한 내부 프레임워크인 Oxy에 대한 블로그 게시물도 작성했습니다. 우리는 Rust를 많이 사용하며, 꽤 능숙해졌습니다.

Oxy에서 Rust로 FL2를 구축했으며, FL2의 모든 로직을 구조화하기 위해 엄격한 모듈 프레임워크를 구축했습니다.

왜 Oxy일까요?

FL2를 만들기 시작했을 때 우리는 단순히 오래된 시스템을 교체하는 게 아니라, Cloudflare의 기반 자체를 다시 세우는 작업이라는 걸 잘 알고 있었습니다. 그 말은, 단순한 프록시가 아니라 우리와 함께 진화하고, 거대한 네트워크 규모를 감당하며, 안전성과 성능을 해치지 않으면서도 각 팀이 빠르게 움직일 수 있게 해주는 프레임워크가 필요했다는 뜻입니다. 

Oxy는 성능, 안전 및 유연성을 강력하게 결합하여 제공합니다. Rust로 구축된 FL2는 C급 성능을 유지하면서도 메모리 안전성 문제나 데이터 레이스 같은 Nginx/LuaJIT 기반 FL1을 괴롭히던 버그들을 통째로 제거했습니다. Cloudflare 규모에서 이는 있으면 좋고 말고의 문제가 아니라 필수적입니다. 요청마다 마이크로초라도 줄이면 사용자 경험이 눈에 띄게 좋아지고, 크래시나 에지 케이스를 피할 때마다 인터넷이 훨씬 안정적으로 돌아갑니다. Rust의 엄격한 컴파일 타임 보장은 각 제품 모듈과 그 입출력 사이에 명확한 규약을 적용하는 FL2의 모듈식 아키텍처와도 완벽하게 맞물립니다.

하지만 선택은 단순히 언어에 관한 문제만은 아니었습니다. Oxy는 고성능 프록시를 만들어온 수년간의 경험이 집약된 결과물입니다. Cloudflare의 Zero Trust 게이트웨이부터 Apple의 iCloud 비공개 릴레이에 이르기까지 여러 주요 Cloudflare 서비스를 구동하고 있었기에, FL2가 맞닥뜨릴 다양한 트래픽 패턴과 프로토콜 조합도 충분히 처리할 수 있다는 걸 우리는 잘 알고 있었습니다. 그 확장성 모델 덕분에 우리는 3계층부터 7계층까지의 트래픽을 가로채고, 분석하고, 조작할 수 있으며, 필요하면 다른 계층에서 디캡슐레이션해 다시 처리하는 것까지 가능합니다. 그 유연성은 FL2 설계의 핵심입니다. 덕분에 우리는 HTTP부터 순수 IP 트래픽까지 모든 것을 일관된 방식으로 처리할 수 있고, 핵심 구성 요소를 갈아엎지 않아도 새로운 프로토콜과 기능을 계속 추가하며 플랫폼을 발전시킬 수 있습니다.

Oxy에는 예전 같으면 방대한 맞춤 코드를 짜야 했던 기능들이 이미 풍부한 기본 기능 세트로 내장되어 있습니다. 모니터링, 소프트 리로드, 동적 구성 로딩과 교체 같은 기능들이 모두 프레임워크에 기본으로 포함되어 있습니다. 덕분에 제품 팀들은 매번 기반 기능을 다시 만들 필요 없이, 자기 모듈의 고유한 비즈니스 로직에만 집중할 수 있습니다. 탄탄한 기반 덕분에 우리는 변경을 자신 있게 적용하고, 빠르게 배포하며, 실제 배포 후에도 예상한 그대로 동작할 것이라는 믿음을 가질 수 있습니다.

원활한 재시작 - 인터넷의 원활한 흐름을 그대로 유지

Oxy가 가져온 가장 큰 개선 중 하나는 재시작 처리 방식입니다. 지속적으로 개발되고 개선되는 소프트웨어라면 언젠가는 업데이트가 필요하기 마련입니다. 데스크탑 소프트웨어라면 간단합니다. 프로그램을 종료하고, 업데이트를 설치한 뒤, 다시 실행하면 되기 때문입니다. 웹에서는 상황이 훨씬 더 어렵습니다. Cloudflare의 소프트웨어는 끊임없이 사용되고 있어서, 그냥 멈출 수가 없습니다. HTTP 요청 하나만 떨어져도 페이지가 로드되지 않을 수 있고, 연결이 끊기면 화상 통화에서 그대로 튕겨나가게 됩니다. 안정성은 선택이 아닌 필수입니다.

FL1에서는 업그레이드가 프록시 프로세스의 재시작을 의미했습니다. 프록시를 재시작한다는 건 프로세스를 완전히 종료한다는 뜻이었고, 그 순간 모든 활성 연결이 바로 끊어지는 문제가 발생했습니다. 이는 WebSocket, 스트리밍 세션, 실시간 API처럼 오래 지속되는 연결에 특히 치명적이었습니다. 계획된 업그레이드조차 사용자가 체감할 수 있는 중단을 유발했고, 사고 상황에서의 예상치 못한 재시작은 더 심각한 문제를 일으키곤 했습니다.

Oxy가 이 문제를 뒤집었습니다. Oxy에는 가능한 한 연결을 끊지 않고 새로운 버전을 배포할 수 있게 해주는 그레이스풀 재시작 메커니즘이 기본으로 내장돼 있습니다. Oxy 기반 서비스의 새 인스턴스가 시작되면, 기존 인스턴스는 새 연결은 받지 않지만 기존 연결은 계속 처리하여, 세션이 자연스럽게 종료될 때까지 중단 없이 이어질 수 있도록 합니다.

즉, 우리가 새 버전을 배포하더라도 진행 중인 WebSocket 세션은 재시작 때문에 끊기는 일 없이, 세션이 자연스럽게 끝날 때까지 그대로 유지될 수 있다는 뜻입니다. Cloudflare 전체 서버에 걸쳐 배포가 몇 시간에 걸쳐 순차적으로 진행되기 때문에, 전체 롤아웃 과정은 매우 부드럽고 대부분의 최종 사용자는 눈치채지도 못합니다.

여기에 systemd 소켓 활성화까지 활용해 한 단계 더 나아갑니다. 각 프록시가 직접 소켓을 관리하게 두는 대신, systemd가 소켓을 생성하고 소유하도록 합니다. 이 방식은 소켓의 생명주기를 Oxy 애플리케이션의 생명주기와 분리해 줍니다. Oxy 프로세스가 재시작되거나 장애로 종료되더라도 소켓은 그대로 열려 있어 새 연결을 받을 준비를 유지하며, 새 프로세스가 뜨는 즉시 그 연결들을 처리하게 됩니다. 이로써 FL1에서 재시작 중 발생하던 ‘연결 거부’ 오류가 사라지고, 업그레이드 동안 전반적인 가용성도 크게 향상됩니다.

우리는 Go의 tableflip 같은 라이브러리를 대체하기 위해, shellflip이라는 자체 조정 메커니즘을 Rust로 직접 구현하기도 했습니다. 이는 재시작 조정용 소켓을 사용하여, 구성 설정을 검증하고 새 인스턴스를 생성하며, 기존 인스턴스를 종료하기 전에 새 버전이 정상 상태인지 확인합니다. 이로 인해 피드백 루프가 개선되고, 자동화 도구가 실패를 즉시 감지하고 대응할 수 있게 되어, 신호 기반으로 무작정 재시작하는 방식에 의존하지 않게 됩니다.

모듈을 통해 FL2 구성

FL1에서 발생했던 문제들을 방지하기 위해 우리는 제품 로직 간의 모든 상호작용이 명확하고 이해하기 쉬운 설계를 원했습니다. 

그래서 Oxy가 제공하는 기반 위에, 우리는 제품용 로직을 모두 명확하게 정의된 모듈로 분리하는 플랫폼을 구축했습니다. 몇 차례 실험과 연구 끝에, 우리는 몇 가지 엄격한 규칙을 적용하는 모듈 시스템을 설계했습니다.

  • 모듈은 입출력(IO)을 수행할 수 없습니다.

  • 모듈은 단계 목록을 제공합니다.

  • 단계는 엄격하게 정의된 순서대로 평가되며, 모든 요청에 대해 동일한 순서가 적용됩니다.

  • 각 단계는 플랫폼이 제공하는 입력 집합과 모듈이 내보낼 수 있는 출력 집합을 정의합니다.

다음은 모듈 단계 정의가 어떻게 보이는지에 대한 예입니다.

Phase {
    name: phases::SERVE_ERROR_PAGE,
    request_types_enabled: PHASE_ENABLED_FOR_REQUEST_TYPE,
    inputs: vec![
        InputKind::IPInfo,
        InputKind::ModuleValue(
            MODULE_VALUE_CUSTOM_ERRORS_FETCH_WORKER_RESPONSE.as_str(),
        ),
        InputKind::ModuleValue(MODULE_VALUE_ORIGINAL_SERVE_RESPONSE.as_str()),
        InputKind::ModuleValue(MODULE_VALUE_RULESETS_CUSTOM_ERRORS_OUTPUT.as_str()),
        InputKind::ModuleValue(MODULE_VALUE_RULESETS_UPSTREAM_ERROR_DETAILS.as_str()),
        InputKind::RayId,
        InputKind::StatusCode,
        InputKind::Visitor,
    ],
    outputs: vec![OutputValue::ServeResponse],
    filters: vec![],
    func: phase_serve_error_page::callback,
}

이 단계는 우리의 맞춤형 오류 페이지 제품을 위한 것입니다.  입력값으로는 방문자 IP 정보, 일부 헤더 및 기타 HTTP 정보, 그리고 몇 가지 '모듈 값'이 주어집니다. 모듈 값은 한 모듈이 다른 모듈에 정보를 전달할 수 있게 해주며, 모듈 시스템의 엄격한 규칙을 현실적으로 적용할 수 있게 하는 핵심 요소입니다. 예를 들어, 이 모듈은 규칙 기반 맞춤 오류 제품의 출력("MODULE_VALUE_RULESETS_CUSTOM_ERRORS_OUTPUT" 입력)에서 생성되는 일부 정보를 필요로 합니다. 이러한 입력과 출력 정의는 컴파일 시점에 강제 적용됩니다.

규칙이 엄격하지만, 우리는 이 프레임워크 내에서 모든 제품 로직을 구현할 수 있다는 사실을 확인했습니다. 그렇게 하면 어떤 제품이 서로에게 영향을 줄 수 있는지 즉시 파악할 수 있다는 이점이 있습니다.

실행 중인 시스템 교체 방법

프레임워크를 구축하는 것은 시작에 불과합니다. 고객이 성능 개선 외에는 아무것도 인지하지 못하도록 제품 로직을 모두 구축하고 정확하게 구현하는 것 또한 중요한 과제입니다.

FL 코드베이스는 15년에 걸친 Cloudflare 제품을 지원하며, 동시에 끊임없이 변화하고 있습니다. 우리는 개발을 멈출 수 없었습니다. 그래서 우리의 첫 번째 과제 중 하나는 마이그레이션을 더 쉽고 안전하게 만드는 방법을 찾는 것이었습니다.

1단계 - OpenResty의 Rust 모듈

제품 로직을 Rust로 다시 만드는 것만으로도 고객에게 제품을 출시하는 데 상당한 방해가 됩니다. 모든 팀에게 제품 로직의 두 가지 버전을 유지하게 하고, 마이그레이션이 끝날 때까지 모든 변경 사항을 두 번째로 다시 구현하게 하는 것은 너무 큰 부담이었습니다.

그래서 우리는 기존 NGINX와 OpenResty 기반 FL에 새로운 모듈을 실행할 수 있는 레이어를 구현했습니다. 병행 구현을 유지하는 대신, 팀들은 자신들의 로직을 Rust로 구현하고, 기존 Lua 로직을 그것으로 교체할 수 있었으며, 구시스템 전체가 완전히 교체될 때까지 기다릴 필요가 없었습니다.

예를 들어, 앞서 정의한 맞춤 오류 페이지 모듈 단계의 구현 일부는 다음과 같습니다(좀 지루한 세부 내용은 생략했기 때문에, 그대로는 컴파일되지 않습니다).

pub(crate) fn callback(_services: &mut Services, input: &Input<'_>) -> Output {
    // Rulesets produced a response to serve - this can either come from a special
    // Cloudflare worker for serving custom errors, or be directly embedded in the rule.
    if let Some(rulesets_params) = input
        .get_module_value(MODULE_VALUE_RULESETS_CUSTOM_ERRORS_OUTPUT)
        .cloned()
    {
        // Select either the result from the special worker, or the parameters embedded
        // in the rule.
        let body = input
            .get_module_value(MODULE_VALUE_CUSTOM_ERRORS_FETCH_WORKER_RESPONSE)
            .and_then(|response| {
                handle_custom_errors_fetch_response("rulesets", response.to_owned())
            })
            .or(rulesets_params.body);

        // If we were able to load a body, serve it, otherwise let the next bit of logic
        // handle the response
        if let Some(body) = body {
            let final_body = replace_custom_error_tokens(input, &body);

            // Increment a metric recording number of custom error pages served
            custom_pages::pages_served("rulesets").inc();

            // Return a phase output with one final action, causing an HTTP response to be served.
            return Output::from(TerminalAction::ServeResponse(ResponseAction::OriginError {
                rulesets_params.status,
                source: "rulesets http_custom_errors",
                headers: rulesets_params.headers,
                body: Some(Bytes::from(final_body)),
            }));
        }
    }
}

각 모듈 내부의 로직은 데이터 처리와 깔끔하게 분리되어 있으며, Rust 언어 설계 덕분에 명확하고 명시적인 오류 처리가 장려됩니다.

가장 활발히 개발되는 모듈들 중 다수가 이 방식으로 처리되어, 팀들이 마이그레이션 동안에도 변경 속도를 유지할 수 있었습니다.

2단계 - 테스트 및 자동 롤아웃

이러한 마이그레이션을 처리하기 위해서는 매우 강력한 테스트 프레임워크를 갖추는 것이 필수적입니다.  우리는 내부적으로 Flamingo라 부르는 시스템을 구축하여, 수천 개의 전체 엔드투엔드 테스트 요청을 프로덕션 및 프리프로덕션 시스템에서 동시에 실행할 수 있게 했습니다. 동일한 테스트를 FL1과 FL2 모두에서 실행하여, 동작 방식이 달라지지 않았다는 확신을 얻을 수 있습니다.

변경 사항을 배포할 때마다, 그것은 점차적으로 여러 단계에 걸쳐 롤아웃되며, 점점 더 많은 트래픽이 적용됩니다. 각 단계는 자동으로 평가되며, 테스트 전체가 성공적으로 실행되고, 전반적인 성능과 자원 사용량이 허용 범위 내에 있을 때만 통과됩니다. 이 시스템은 완전히 자동화되어 있으며, 테스트가 실패할 경우 변경 사항을 일시 중지하거나 롤백합니다.

장점은 FL2에서는 새로운 제품 기능을 48시간 내에 개발하고 출시할 수 있다는 점인데, FL1에서는 몇 주가 걸렸을 것입니다. 사실 이번 주 발표 중 최소 하나는 이런 변화를 포함하고 있었습니다!

3단계 - 폴백

100명 이상의 엔지니어가 FL2에 참여했으며, 130개 이상의 모듈을 보유하고 있습니다. 그리고 아직 완전히 끝난 것은 아닙니다. 우리는 아직 시스템에 마지막 손질을 하고 있으며, FL1의 모든 동작을 그대로 재현할 수 있도록 하고 있습니다.

그렇다면 모든 것을 처리할 수 없는 상태에서 FL2로 트래픽을 어떻게 보낼 수 있을까요? 만약 FL2가 처리 방법을 모르는 요청이나 요청 구성을 수신하면, 이를 포기하고 폴백이라는 동작을 수행합니다. 즉, 모든 것을 FL1으로 전달하는 것입니다. 이 과정은 네트워크 수준에서 이루어지며, 단순히 바이트를 FL1으로 전달합니다.

FL2가 완전히 완성되지 않은 상태에서도 트래픽을 보낼 수 있게 해줄 뿐 아니라, 이것은 또 하나의 큰 이점을 제공합니다. FL2에 새로운 기능을 구현했지만 FL1과 동일하게 작동하는지 다시 확인하고 싶을 때, 우리는 FL2에서 기능을 평가한 후 폴백을 실행할 수 있습니다. 두 시스템의 동작을 비교할 수 있어, 우리의 구현이 올바르게 되었는지 높은 확신을 가질 수 있습니다.

4단계 - 롤아웃

우리는 2025년 초부터 고객 트래픽을 FL2로 보내기 시작했으며, 연중 점진적으로 처리 트래픽 양을 늘려왔습니다. 본질적으로 우리는 두 개의 그래프를 관찰해왔습니다. 하나는 FL2로 라우팅되는 트래픽 비율이 증가하는 그래프이고, 다른 하나는 FL2에서 처리하지 못해 FL1로 폴백되는 트래픽 비율이 감소하는 그래프입니다.

이 과정은 무료 고객의 트래픽을 시스템에 통과시키면서 시작했습니다. 우리는 시스템이 올바르게 작동함을 입증할 수 있었고, 주요 모듈들의 폴백 비율을 낮출 수 있었습니다. 우리 Cloudflare 커뮤니티 MVP들은 조기 경보 시스템 역할을 하며, 새 플랫폼이 새로 보고된 문제의 원인일 수 있다고 의심될 때 스모크 테스트를 하고 표시를 해주었습니다. 무엇보다도, 그들의 지원 덕분에 우리 팀은 신속하게 조사하고, 필요한 부분만 수정하거나 FL2로의 이전이 문제의 원인이 아님을 확인할 수 있었습니다.

그 다음에는 유료 고객으로 확대하여, 시스템을 이용하는 고객 수를 점진적으로 늘려갔습니다. 또한 FL2의 성능 혜택을 원하는 일부 대형 고객과 긴밀히 협력하여, 시스템에 대한 많은 피드백을 받는 조건으로 이들을 조기에 온보딩했습니다.

현재 대부분의 고객이 FL2를 사용하고 있습니다. 아직 완료해야 할 몇 가지 기능이 남아 있고 모든 사용자를 온보딩할 준비가 완전히 되지는 않았지만, 목표는 몇 개월 안에 FL1을 종료하는 것입니다.

FL2의 영향

이 글의 초반에 설명한 바와 같이, FL2는 FL1보다 상당히 빠릅니다. 가장 큰 이유는 단순히 FL2가 처리해야 할 작업량이 적기 때문입니다. 모듈 정의 예시에서 한 줄을 눈치채셨을지도 모릅니다.

    filters: vec![],

각 모듈은 실행 여부를 제어하는 필터 세트를 제공할 수 있습니다. 즉, 모든 요청에 대해 모든 제품의 로직을 실행하는 것이 아니라, 필요한 모듈 세트만 쉽게 선택해서 실행할 수 있다는 뜻입니다. 우리가 새 제품을 개발할 때 발생하는 추가 비용이 사라졌습니다.

또 다른 큰 성능 향상의 이유는 FL2가 성능에 최적화된 언어로 구현된 단일 코드베이스이기 때문입니다. 이에 비해, FL1은 NGINX(C로 작성됨) 기반에 LuaJIT(Lua 및 C 인터페이스 계층)을 결합하고, 많은 Rust 모듈도 포함하고 있었습니다.  FL1에서는 한 언어가 필요로 하는 데이터 표현을 다른 언어가 필요로 하는 표현으로 변환하는 데 많은 시간과 메모리를 소모했습니다.

그 결과, 내부 측정에 따르면 FL2는 FL1의 절반도 안 되는 CPU를 사용하며, 메모리 사용량도 절반 이하입니다. 이건 엄청난 이점입니다. 절약된 CPU를 고객을 위한 더 많은 기능 제공에 사용할 수 있기 때문입니다!

우리가 더 나아지고 있는지 어떻게 측정할까요?

자체 도구와 CDNPerf와 같은 독립 벤치마크를 사용하여, 네트워크 전반에 FL2를 배포하면서 그 영향을 측정했습니다. 결과는 분명합니다. 웹사이트가 중앙값 기준으로 10ms 더 빠르게 응답하며, 이는 25% 성능 향상에 해당합니다.

보안

FL2는 FL1보다 설계상으로도 더 안전합니다. 완벽한 소프트웨어 시스템은 존재하지 않지만, Rust 언어는 LuaJIT에 비해 상당한 이점을 제공합니다. Rust는 강력한 컴파일 타임 메모리 검사 기능과 다양한 종류의 오류를 방지하는 타입 시스템을 갖추고 있습니다. 우리의 엄격한 모듈 시스템과 결합하여 대부분의 변경 사항을 높은 신뢰도로 적용할 수 있습니다.

물론 잘못 사용하면 어떤 시스템도 안전하지 않습니다. Rust에서 메모리 손상을 일으키는 코드를 작성하는 것은 쉽습니다. 위험을 줄이기 위해, 우리는 강력한 컴파일 타임 린팅(linting)과 검사, 엄격한 코딩 표준, 테스트 및 리뷰 프로세스를 함께 유지하고 있습니다.

우리는 오래전부터, 시스템에서 발생한 설명되지 않는 충돌은 최우선으로 조사해야 한다는 정책을 따라왔습니다. 우리는 이 정책을 완화하지 않을 것입니다. 다만 지금까지 FL2에서 새로 발생한 충돌의 주요 원인은 하드웨어 고장입니다. 충돌 발생률이 크게 줄어들면서, 우리는 이러한 조사를 제대로 수행할 시간을 확보할 수 있을 것입니다.

다음은?

FL1에서 FL2로의 마이그레이션을 2025년까지 마무리하면서, 2026년 초에 FL1을 종료할 예정입니다. Cloudflare는 이미 고객 성능 및 개발 속도 측면에서 이점을 확인하고 있으며, 이러한 이점을 모든 고객에게 제공할 수 있기를 기대합니다.

마지막으로 완전히 마이그레이션할 서비스가 하나 남아 있습니다. 맨 위 다이어그램의 “HTTP & TLS Termination” 상자도 NGINX 서비스이며, 현재 Rust로의 재작성 중간 단계에 있습니다. 이 마이그레이션 작업은 순조롭게 진행 중이며, 내년 초에 완료될 것으로 예상합니다.

그 후에는 모든 것이 모듈화되고, Rust로 구현되며, 테스트와 확장이 완료되어 진정한 최적화를 시작할 수 있습니다! 모듈 간 연결 방식을 재정리하고 단순화하며, RPC나 스트림 같은 비-HTTP 트래픽 지원을 확장하는 등 다양한 개선을 진행할 것입니다. 

이 여정에 동참하고 싶은 분은 채용 페이지에서 채용 중인 직무를 확인해 주세요. Cloudflare는 더 나은 인터넷을 구축할 수 있도록 도와줄 새로운 인재를 늘 찾고 있습니다.

Cloudflare에서는 전체 기업 네트워크를 보호하고, 고객이 인터넷 규모의 애플리케이션을 효과적으로 구축하도록 지원하며, 웹 사이트와 인터넷 애플리케이션을 가속화하고, DDoS 공격을 막으며, 해커를 막고, Zero Trust로 향하는 고객의 여정을 지원합니다.

어떤 장치로든 1.1.1.1에 방문해 인터넷을 더 빠르고 안전하게 만들어 주는 Cloudflare의 무료 애플리케이션을 사용해 보세요.

더 나은 인터넷을 만들기 위한 Cloudflare의 사명을 자세히 알아보려면 여기에서 시작하세요. 새로운 커리어 경로를 찾고 있다면 채용 공고를 확인해 보세요.
창립기념일 주간RustNGINX자세히 보기엔지니어링

X에서 팔로우하기

Cloudflare|@cloudflare

관련 게시물

2025년 10월 28일 오후 1:00

인터넷의 빠른 속도와 보안 유지: 머클 트리 인증서 소개

Cloudflare는 Chrome으로 실험을 시작하여 성능을 저하시키거나 WebPKI 신뢰 관계를 변경하지 않고도 빠르고 확장 가능하며 양자 준비 Merkle Tree 인증서를 평가하고 있습니다....