Hacker News 의견들
  • 글 취지에는 동의함. 이런 용도라면 FastCGI가 HTTP보다 낫다고 봄
    WAS(Web Application Socket) 라는 프로토콜도 알리고 싶음. 16년 전에 직장에서 FastCGI도 충분히 좋지 않다고 느껴 직접 설계했음
    메인 소켓 프레이밍 대신 제어 소켓 1개와 raw 요청/응답 바디용 파이프 2개를 쓰고, WAS 앱과 웹 서버 모두 pipe에 대해 splice()를 활용할 수 있음
    프레이밍이 필요 없고, 요청 취소도 가능하며, 세 개의 파일 디스크립터를 언제나 복구할 수 있게 했음
    수년간 내부 애플리케이션과 웹호스팅 환경에서 써 왔고, PHP SAPI도 직접 작성했음. 꽤 많은 웹사이트가 내부적으로 WAS 위에서 동작함
    전부 오픈소스임
    library: https://github.com/CM4all/libwas
    documentation: https://libwas.readthedocs.io/en/latest/
    non-blocking library: https://github.com/CM4all/libcommon/tree/master/src/was/asyn...
    our web server: https://github.com/CM4all/beng-proxy
    WebDAV: https://github.com/CM4all/davos
    PHP fork with WAS SAPI: https://github.com/CM4all/php-src

    • FastCGI와 HTTP는 같은 층위가 아님
      HTTP는 브라우저와 서버 같은 양끝 간 데이터 전달용이고, FastCGI는 서버와 애플리케이션 사이에서 그 데이터를 처리하는 용도임
      방금 글을 훑어봤는데 저자가 둘을 서로 대체 가능한 것처럼 헷갈리게 쓰는 듯함. 실제로는 전혀 그렇지 않음
      참고로 나도 웹 고객 서비스에 fcgi를 10년 동안 써 왔음
  • 이 글은 빠진 내용이 많아서 오히려 흥미로움
    FastCGI vs. SCGI vs. HTTP 논쟁이 한창일 때 Web2.0 스타트업을 창업했고 프런트엔드 스택을 직접 구성했는데, 결국 HTTP가 이긴 이유는 단순함 때문이었음
    게이트웨이에서 이미 처리해야 하는 HTTP를 그대로 쓰면 다른 프로토콜을 스택에 추가할 필요가 없었고, 덕분에 reverse proxy를 여러 단계로 넣거나 인증·세션·SSL 종료·DDoS 필터링처럼 횡단 관심사를 역할별 서버로 분리하는 구성이 아주 쉬워졌음
    개발 환경에서는 앱 서버에 HTTP로 바로 붙고, 운영에서는 SSL·인증·남용 탐지를 reverse proxy가 담당하는 식으로 같은 앱 서버를 그대로 재사용할 수 있었음
    당시엔 nginx가 대부분의 FastCGI/SCGI 모듈보다 훨씬 빠르고 안정적이었던 것도 컸음. 처음엔 HTTP -> Lighttpd -> FastCGI -> Django로 구성했지만 그냥 nginx를 쓰는 편이 훨씬 빨랐음
    HTTP 사용은 웹판 End-to-End Principle처럼 작동했음. 네트워크와 프로토콜은 전달 내용에 무관해야 하고, 애플리케이션 로직은 필터링·리다이렉트하는 네트워크 노드가 아니라 끝단에 있어야 한다는 생각임
    다만 글이 짚는 핵심은 보안 측면에서는 최소 권한 원칙을 따르는 편이 더 나을 때가 많다는 것임. 예상한 통신만 allowlist로 통과시켜야 다른 지점의 침해에 무심코 기여하지 않게 됨
    결국 이 둘 사이엔 긴장이 있음. E2E는 유연성을 주지만 그 유연성이 악용될 여지도 키우고, PoLP는 보안을 주지만 설계한 것만 할 수 있게 되어 새 요구사항에 적응하기 어려워짐
    [1] https://en.wikipedia.org/wiki/End-to-end_principle
    [2] https://en.wikipedia.org/wiki/Principle_of_least_privilege

    • 그 비유는 잘 맞지 않는다고 봄. 특히 connection caching과 multiplexing 맥락에선 더 그럼
      중간 게이트웨이가 여러 HTTP 요청을 다른 HTTP 채널 하나로 multiplex하고, 그 채널이 listening service까지 직접 이어지며 애플리케이션 소켓 전에 demultiplex되지 않는다면, 그건 end-to-end 논리를 여러 방식으로 근본적으로 깨뜨림
      1:1 연결 대칭성이 유지될 때만 그 비유가 그나마 성립함
      reverse proxy 취약점들은 전부 end-to-end를 어긴 데서 직접 비롯됐다고 봄
      그 비유가 맞다면 여러 MX를 거치는 SMTP 전달도 end-to-end여야 하는데, 실제로는 아니고 reverse proxy와 비슷한 문제들, 예컨대 메시지 경계 desync도 많이 생김
      HTTP 요청을 메시지에 대응시키려는 의도는 알겠지만, 실제 TCP·HTTP 시맨틱과 온갖 프로토콜 세부사항 때문에 금방 무너짐
      end-to-end 원칙은 시맨틱을 대충 다루는 걸 허용하지 않음. 상태 관리와 전송 계층 경계에 매우 엄격한 규율을 요구함. 대충 end-to-end 비슷한 것은 end-to-end가 아님
    • 웹앱 개발자에게 HTTP semantics는 유용하지만, HTTP wire protocol 자체는 형편없음
      예를 들어 multiplexing도 HTTP 2.0 전까지 없었고, 그래서 reverse proxy와 backend 사이 통신에 HTTP를 그대로 쓰는 건 낭비가 큼
      보안 문제도 있음. 파서가 서로 요청 경계가 어디서 끝나는지조차 다르게 해석할 수 있음
      Google도 오래전부터 프런트 웹 서버와 애플리케이션 사이에서는 HTTP를 자체 Stubby 프로토콜로 감싸서 씀
      HTTP wire protocol보다 훨씬 빠르고 기능도 많음. 보통 회사엔 과하지만, 규모가 커지면 다른 wire protocol과 그 주변 툴링을 직접 만드는 비용이 충분히 정당화됨
    • 데이터센터 내부에서 end-to-end principle을 적용하는 건 별 의미가 없고, 글이 보여주듯 오히려 불안전한 동작을 허용하게 됨
    • 내가 nginx에서 싫어하는 건 문서화임. 사실상 거의 쓸모가 없다고 느낌
      httpd도 어느 순간 설정을 어렵게 만드는 방향으로 갔고, 설정 포맷을 갑자기 바꾼 시점에 버렸음
      적응할 수는 있었겠지만 그 대신 lighttpd로 옮겼고, 이후엔 ruby가 설정 생성을 자동화해서 기술적으로는 다시 httpd로 돌아갈 수도 있음
      그래도 돌아가고 싶진 않음. 웹서버 개발자라면 사용자가 새 포맷에 억지로 맞추게 만드는 일을 신중히 봐야 함
      정말 단순한 결정으로 설정 포맷을 바꿀 거라면, 최소한 yaml 설정 같은 걸 추가 옵션으로라도 제공해서 갑자기 새로운 if-clause 스타일 설정문을 강요하지 않았으면 함
  • WHATWG streams가 브라우저에 널리 퍼진 지금은, 장수명 HTTP 요청 위에 자체 WebSocket 비슷한 걸 구현하기가 꽤 쉬움
    그냥 바이트 스트림을 보내고 각 메시지 앞에 헤더를 붙이면 되며, 많은 경우 길이 값 하나면 충분함
    장점도 있음. WebSocket처럼 서버 계층에 별도 특수 경로가 필요 없고, backpressure를 쓸 수 있으며, HTTP/2·HTTP/3 개선을 공짜로 가져오고, 프레이밍 오버헤드도 더 낮음
    다만 AFAIK 요청 바디를 계속 스트리밍하면서 동시에 응답을 받는 건 아직 지원되지 않아서, 완전한 양방향 스트리밍에는 요청 두 개가 필요함

  • 오래된 plain CGI를 다시 발견했는데, 우리 플랫폼에서 사용자가 커스텀 페이지를 vibe code 하게 만들기엔 아주 좋음 [1]
    기본 제공 기능으로는 task list와 data viewer가 있지만, 사용자는 종종 Kanban 뷰나 데이터 필터·차트가 들어간 커스텀 대시보드처럼 훨씬 더 세밀한 맞춤 구성을 원함
    이 박스에는 coding agent가 있어서 우리가 전통적인 report builder를 만드는 대신 사용자가 원하는 걸 직접 코드로 만들 수 있음
    Go stdlib가 서버 측과 사용자 공간 양쪽에서 지원이 좋고, coding agent가 page-name/main.go를 만들어 CGI로 통신하게 하면 서버가 요청을 거기로 위임함
    데이터 규모와 페이지뷰가 전부 person scale이라 FastCGI 같은 최적화가 딱히 필요하지도 않음
    에이전트 시대엔 옛 기술이 다시 새로워짐

    1. https://housecat.com
    • CGI는 FastCGI와 달리 HTTP 헤더를 환경 변수로 전달해서 꽤 큰 함정이 있다는 걸 조심해야 함: https://httpoxy.org/
      Go의 CGI 서버 구현은 $HTTP_PROXY를 설정하지 않아서 그 부분은 안전하지만, 그래도 CGI가 환경 변수를 쓰는 방식은 여전히 마음에 들지 않음
  • reverse proxy 쪽은 대체로 단순한 작업만 해서 Nginx 내장 기능만 써도 충분했음
    그래도 더 복잡한 게 필요할 때 FastCGI를 쓰겠다는 발상은 나에겐 떠오르지 않았을 것 같음
    10년쯤 전에 C++ 코드 일부를 웹에서 돌리려고 FastCGI를 조금 써 보긴 했지만, 그 뒤로는 거의 쓰지 않았음

    • 요즘은 embedded server가 훨씬 더 흔함
      애플리케이션 안에 HTTP 서버를 직접 넣고, 게이트웨이 없이 필요한 일을 그냥 처리하면 됨
  • Red Hat 계열에서 배포되는 PHP/Apache 구성은 FPM(FastCGI Process Manager)
    RHEL 배포판에서 FastCGI를 다른 데서도 쓰는지는 모르겠음
    $ rpm -qi php-fpm | grep ^Summary
    Summary : PHP FastCGI Process Manager

  • uwsgi protocol도 있음
    이것도 사실상 거의 모든 것에 대한 RPC 같은 성격임

  • FCGI는 오케스트레이션 시스템이기도 함
    부하가 올라가면 서버 태스크를 더 띄우고, 부하가 줄면 내리며, 태스크가 죽으면 새 복사본을 띄움
    일종의 단일 시스템 Kubernetes 같음

    • 내 경험상 그 기능은 그다지 좋지 않았음
      듣기엔 좋아 보이지만, 평소 저부하에서는 잘 돌다가 고부하가 오면 worker를 더 생성하면서 메모리를 바닥내는 일이 자주 생김
      그래서 정적 worker 수를 두는 편이 대체로 더 나았음
      다만 crash recovery는 필요하다면 유용함
    • 우리도 정확히 그런 식으로 사용했음
  • HTTP 헤더의 부조리함을 잠깐만 감상해도 좋겠음
    True-Client-IP가 없을 때만 X-Real-IP를 쓴다면, 프록시가 X-Real-IP를 제대로 넣더라도 공격자가 True-Client-IP 헤더를 보내면 그대로 당할 수 있음
    X-Forwarded-For, X-Real-IP, CDN마다 제각각인 커스텀 헤더까지 있고, 어떤 건 콤마로 구분된 리스트이며 보통 우리 own LB의 IP까지 쓸모없이 붙어 들어감
    왜 그런지는 알지만 전혀 도움이 되지 않음
    게다가 이런 헤더는 전부 악의적인 user-agent가 삽입할 수도 있음. 신뢰 가능한 서버들이 파이프라인에서 중요한 정보를 어떻게 전달할지 아무도 합의를 못 한 셈 같음
    이런 혼란은 User-Agent 헤더의 부조리함과도 잘 어울림
    그쪽은 Apple이 프라이버시를 명분으로 완전히 가짜 정보, 예를 들면 거짓 OS 버전 같은 헛소리를 보내기로 하면서 한층 더 극단으로 갔음

  • 이 주장엔 일리가 많지만, FastCGIPATH_INFO 같은 부분에서 CGI/1.1을 따르기 때문에 손실이 생김
    URL 디코딩이 강제되어 encoded slash%2F를 표현할 수 없음
    구현에 따라 경로의 ///로 합치기도 하는데, 이건 여러 HTTP 구현에도 있는 문제이긴 함
    표현력 면에서는 HTTP보다 떨어지고, 그 차이가 중요할지는 애플리케이션에 달렸음
    나는 URL을 정확하게 다루는 쪽을 더 선호함