Wed Sep 10 2025
...
FastComments가 이제 전 세계적으로 배포됩니다
새롭게 변경된 사항
이전에는 FastComments가 웹 애플리케이션에 대해 매우 전통적인 아키텍처를 가지고 있었습니다. 우리는 애플리케이션 서버, 데이터베이스, 그리고 몇 가지 다른 서비스를 운영했습니다. 이는 두 지역(미국 웨스트 및 유럽)에서 중복되었습니다. 만약 당신이 프랑스에 있고, 우리 글로벌 데이터 센터에서 호스팅된 고객의 댓글 스레드를 보려고 한다면, 요청은 댓글 데이터를 위해 미국 웨스트까지 가야 했습니다.
이제는 아닙니다! 이제 댓글 데이터와 모든 미디어 자산이 글로벌 배포의 고객을 위해 전 세계적으로 복제되며, EU 배포의 고객을 위해 데이터가 복제되는 세 개의 존재 포인트가 있습니다. 귀하의 요청은 EU 내에서 가장 가까운 노드로 전송됩니다.
이전의 작동 방식
여러 라이브 복제를 가진 데이터베이스를 제외하고, 모든 서비스는 서비스 유형별로 하나의 인스턴스에 배포되었습니다. 이는 각 지역마다 하나의 애플리케이션 서버, 하나의 퍼블리싱 및 구독 서버, 하나의 미디어 서버가 존재했음을 의미했습니다. 이는 매우 단순하게 유지하기 위해 가능한 동안 수직적으로 확장할 계획이었습니다. 코드를 작성하는 것은 쉬웠습니다 - 데이터베이스에 접근할 때 항상 "자신의 쓰기를 읽을 수 있다"는 것을 알고 있었습니다. 인프라는 보안 업데이트를 제외하면 간단했습니다. 보안 업데이트는 잠깐의 다운타임이 걸렸습니다.
문제점
문제는 용량에 도달했을 때 분명하게 시작되었습니다. 우리는 최적화를 하여 결국 해당 서비스의 인스턴스 크기를 늘려야 했습니다.
이는 Linode에서 비용이 막대하게 비싸지기 시작했습니다. 인스턴스 비용이 $144인데, 이는 우리의 기준 테스트에 기반하여 $20 OVH 노드와 비슷한 수준이며, 호스팅 제공업체를 바꿔도 실패 지점이 여기저기 생기기 마련입니다 - OVH와 같은 제공업체는 Linode보다 유지보수 문제에 대한 해결 시간이 더 길어지기 마련입니다.
RiR :)
FastComments의 퍼블리시 및 구독 서비스는 초기 몇 년 동안 Java로 작성되었습니다. Java는 입력된 노력에 비해 상대적으로 높은 성능을 보여주기 때문에 선택되었으며, GC를 수년간 조정하고 G1GC, Shenandoah, Z1을 시도한 후 우리는 더 이상 Java를 사용하지 않기로 결정했습니다. 메모리 사용 오버헤드는 단순히 너무 많았고, 이러한 서비스는 완료 후 매우 정적이기 때문에 Java의 이점이 사라졌습니다. 또한 이러한 서비스는 "우르르 몰려오는" 문제를 다루어야 했기 때문에 JVM은 JIT가 시작되기 전의 정점 트래픽을 처리하려고 시도하고 있었습니다. 이러한 서비스는 C++ 또는 Rust로 전환하기에 적합한 후보가 되었습니다.
우리는 C++ 전문가가 아니기 때문에 Rust를 선택했으며, 네트워크 코드에서 실수를 하면 한 고객의 데이터가 다른 고객에게 노출될 수 있습니다. Rust는 이러한 종류의 문제를 방지하는 데 도움을 줍니다.
우리는 어쨌든 이러한 서비스의 통합을 원했기 때문에, 그래도 GraalVM으로 또 다른 최적화를 할 수도 있었지만, 우리는 Rust로 이동하기로 결정하였습니다.
마이그레이션은 문제 없이 진행되지는 않았습니다. 이러한 서비스는 SSL을 종료하고 HTTP 1.1, HTTP/2 등을 지원해야 합니다. 그들은 여러 데이터 스트림을 동시에 관리하고, 엣지에서 디스크 lru 캐시, S3, 데이터베이스에서 미디어를 읽고, 메시에서 통신하는 등의 작업을 수행해야 합니다. Java 생태계인 Vertx와 Netty는 이러한 작업에 매우 좋았습니다. 우리는 라이브러리 생태계를 한계까지 추진하고 있었으며, Rust 라이브러리에 대한 경험이 부족했기 때문에 몇 가지 시행착오를 겪어야 했습니다. 이로 인해 몇 차례의 다운타임이 발생하였고, 이에 대해 사과드립니다.
우리는 또한 다양한 메모리 관리자를 실험했으며, 커스텀 DNS 서버에는 mimalloc을, 전송 계층에는 libc를 사용하기로 정했습니다. 우리는 Nginx나 Apache를 실행하지 않고, 대신 Maxmind로부터 매주 재구성된 인메모리 색인에 기반하여 클라이언트를 전 세계적으로 라우팅하는 커스텀 DNS 서버의 조합과 Rust로 된 우리의 전송 계층을 사용합니다. 전송 계층은 SSL을 종료하고, 퍼블리셔 및 구독 작업을 처리하며, 우리의 CDN 역할을 합니다. 이점은 서비스 간의 데이터 이동 시 오버헤드가 줄어들고 인프라 오버헤드/추상화가 줄어드는 것입니다. 단점은 가시성이 줄어들기 때문에 좋은 메트릭이 중요하다는 것입니다.
결과적인 성능 측면에서, Rust 서비스는 우리의 작업에도 불구하고 Java 서비스의 약 10-30%의 메모리를 사용하였습니다. 나는 재미삼아 Java Concurrency in Practice와 같은 책을 읽기 때문에 전문가는 아니지만 JVM 서비스를 빠르게 작성하는 것에 대해 몇 가지를 알고 있으며, Rust로 이러한 것을 더 쉽게 이루는 것이 훨씬 수월했습니다. 게다가, 대량의 구독자에게 메시지가 스파이크 될 때, JVM 서비스가 GC에 40%의 시간을 소비하는 반면 CPU 사용량은 거의 차지하지 않았습니다. 우리는 우리의 코드를 게임 엔진처럼 작성하는데 집중했으며, 전형적인 서버처럼 작성하지 않았습니다. 나는 Rust의 열렬한 팬이라고 할 수는 없지만, 초기 개발 후 많은 작업을 수행하고 변동이 적은 서비스에는 완벽한 선택입니다. 우리의 주요 비즈니스 로직은 여전히 TypeScript에 남아 있습니다.
새로운 아키텍처
새로운 아키텍처에는 더 이상 "애완" 노드가 없습니다. 대신, 각 서버는 동일한 서비스와 거의 동일한 구성으로 완전한 복제본입니다. 그들은 각각 전송, DNS, 애플리케이션 서버 및 데이터베이스의 복사본을 실행합니다. 모든 노드는 전체 디스크 암호화를 유지하며 Dropbear로 자동 잠금을 해제합니다.
각 서버는 요청을 종료하고 웹소켓, HTTP 스트림 또는 CDN 요청으로 처리하는 라우팅 전송을 실행합니다. 이러한 서버는 서로 연결되어 있으며, 각 서버는 글로벌 네트워크의 매핑을 해당 로컬 DNS 서버에 제공하여 실시간으로 각 라이브 노드의 글로벌 위치를 알려줍니다.
새로운 아키텍처의 장점은 중복성입니다. 한 지역의 고객이 우리에게 매우 강하게 요청할 때, 다른 지역은 온라인 상태를 유지합니다.
이제 애플리케이션 코드는 가장 가까운 노드에 도달할 수 있는 쿼리와 데이터베이스의 주 노드로 가야 하는 쿼리에 대해 매우 주의해야 합니다. 실수를 하면 성능이 급격히 감소할 수 있습니다. 이는 또한 일부 지역에서의 쓰기 속도가 느릴 수 있음을 의미하며, 이는 신중한 조정 및 최적화가 필요합니다. 우리는 이제 데이터베이스에 접근하는 모든 메소드가 readPreference 인자를 갖도록 내부에서 패턴을 따릅니다. 그렇게 함으로써 호출자는 최상위까지 명시적으로 쿼리를 선택해야 합니다.
이점은 읽기에 대한 매우 좋은 수평 확장성을 제공합니다. FastComments는 매우 읽기 중심이지만, 우리는 우리의 중재자들을 잊지 말아야 합니다! 중재자들은 전 세계에서 하루 종일 일하며, 우리는 그들의 경험이 좋게 유지되기를 원합니다. 이 롤아웃의 일환으로 우리는 몇몇 중재자와 협력하여 중재 도구가 여전히 빠르게 유지되도록 하고 있습니다.
우리는 또한 하드웨어를 손수 선택할 수 있으며, 이는 재미있고 보람 있습니다. 새로운 서버는 i5-13500과 Ryzen 5 5600X 박스를 혼합한 것이며, EU 지역에 있는 서버는 구형 Xeon을 사용하고 있습니다. 우리의 벤치마크에서 이 모든 서버는 다른 제공업체에서 탐색 중인 보다 비싼 서버보다 훨씬 빠르게 나타났습니다. 단점은 더 많은 설정 작업이 필요하다는 점이지만, 우리는 이를 자동화하였고, 실패에 대한 SMART 디스크 모니터링 등도 설정했습니다.
이러한 일을 수행함으로써 우리는 경쟁력 있는 가격을 계속 제공할 수 있습니다.
롤아웃
서비스를 다시 작성하고 새로운 호스팅 제공업체로 이동하는 동안 지난 몇 개월 간의 롤아웃은 순조롭지 않았으며, 여러분의 인내에 감사드립니다.
초기 롤아웃에서는 우리의 메트릭이 요청 시간의 증가를 나타냈습니다. 요청이 > 100ms 이상이 되는 경우는 많지 않아야 합니다.
우리는 여전히 일부 지역의 성능 향상을 위해 점진적으로 노력하고 있습니다. 지금까지 피드백을 제공해 주신 모든 분들께 감사합니다.
API 사용 시 고려 사항
API는 여전히 강력하게 일관성을 유지합니다 - 자신이 쓴 내용을 읽을 수 있도록 하여 하위 호환성을 유지하고 개발자들에게 단순성을 제공합니다. 우리는 개발자들에게 일관성보다 성능을 선택할 수 있도록 readPreference 매개변수를 노출할 계획입니다. 이의 이점은 이러한 API 호출에 대해 크레딧 할인을 제공할 수도 있다는 것입니다.
댓글 위젯을 제공하는 것과 같은 모든 공개 엔드포인트는 항상 해당 노드의 가장 가까운(로컬) 데이터베이스에서 읽습니다.
일반 CDN을 사용하지 않는 이유
댓글 스레드는 정적이지 않으며, 라이브입니다. 정적 데이터 위에 라이브 스트림을 적용하는 것도 작동하지 않습니다. Anonymous 사용자로 스레드를 볼 때 "익명 세션"을 받습니다. 이 익명 세션 내에서 다른 사용자 차단 및 신고와 같은 작업을 수행해야 하며, 익명 사용자가 특정 댓글을 좋아했는지 여부도 표시해야 합니다. 댓글 스레드는 SSO, 인증 또는 사용자 그룹에 따라 잠길 수도 있습니다.
마지막으로, 동적 데이터가 CDN의 정적 데이터에 매핑되는 "점진적 향상"은 콘텐츠가 움직이거나 몇 초 후에 변경되는 나쁜 경험을 제공합니다. 우리는 이런 일을 하고 싶지 않습니다.
이제 내 데이터는 어디에 있나요?
귀하의 데이터는 더 이상 Linode에 저장되지 않습니다. 데이터는 Hetzner와 OVH 간에 실시간으로 복제되며 전체 디스크 암호화가 적용되고, 백엔드 서버 간 모든 통신은 TLS로 이루어집니다. 우리는 아웃바운드 웹후크 프록시를 위해 몇 개의 레거시 Linode 인스턴스를 유지하고 있지만, 더 이상 Linode에 데이터나 미디어가 저장되지 않습니다.
결론
모든 주요 릴리스와 마찬가지로 최적화하고 테스트하며 이 변경 사항을 적절히 릴리스할 수 있어 기쁘게 생각합니다. 이 작업 덕분에 FastComments는 더 나은 확장성과 안정성을 가지고 장기적으로 향상될 것입니다. 문제를 발견하시면 아래에 알려주세요.
