Starship - 동적 레이어 4 로드 밸런서

6160 단어 devopsshowdev
첫글인데 친절하게 봐주세요!

여러 클러스터 또는 지역에 걸쳐 Kubernetes에서 수천 개의 포드를 실행하는 것은 이제 Google과 함께 비교적 간단합니다.

그러나 사용자 포드가 클러스터 풀에 무작위로 배포되는 다중 테넌트 클러스터는 어떻습니까?

간단히 말해서 호스트 이름을 기반으로 하는 .com 및 b.com을 동적으로 올바른 클러스터로 라우팅하는 방법입니다.

이것은 최근 Remote Company에서 직면한 문제였으며 해결책을 찾아야 했습니다. 또한 Kubernetes 내에서 노드당 110개의 포드 문제를 해결해야 했으며, 이로 인해 매우 큰 클러스터 1개를 사용하지도 못했습니다.

우리는 이러한 한계에 매우 빨리 도달할 것이라는 것을 알고 있으며 처음부터 이를 극복하고 싶었습니다. 우리는 지난 달 수천 명의 사용자에게 코드 없는 제품을 출시했습니다Ycode Beta. Starship은 첫날부터 모든 트래픽을 처리해 왔으며 클라이언트 웹 앱을 통해 인프라 내에서 계속 확장하고 있습니다.

그래서 우선 우리는 주변에 무엇이 있는지 보기 위해 오래된 Google 검색으로 시작했습니다. 바퀴를 재발명할 필요가 없었습니다. 불행히도 우리의 요구에 맞는 것은 없었습니다. 우리는 유연한 솔루션과 너무 많은 노력 없이 현재 인프라에 맞출 수 있는 무언가를 원했습니다.

결국 우리는 맞춤형 무언가를 구축해야 한다는 결론에 도달했습니다. 저는 과거에 Dynamic Nginx SNI Load Balancer를 이미 구축했고 이 동일한 스택이 작동할 것이라고 확신했습니다. Nginx + Lua는 좋은 조합입니다.

우리는 OpenResty가 핵심에서 제공하는 다양한 다양한 모듈을 연구하기 시작했습니다. HTTP/S 트래픽을 라우팅해야 했기 때문에ngx_stream_lua_module 사용해야 했고 여기서 TLS를 종료하고 싶지 않았기 때문에 Nginx Ingress에서 다운스트림으로 수행됩니다.

거기에서 우리는 API에 대해 생각하고 Lua와 상호 작용하는 방법에 대해 생각하기 시작했습니다. 캐시 레이어가 필요했고 과거에 https://github.com/thibaultcha/lua-resty-mlcache을 사용했지만 다시 생각할 필요가 없었습니다. 내부의 다양한 수준에서 데이터를 캐싱하기 위한 정말 좋은 접근 방식을 제공합니다. Nginx 프로세스.

API는 간단합니다. 요청 시 호스트 조회(요청 하위 시스템에 따라 ngx.var.host 또는 ngx.var.ssl_preread_server_name를 통해) 및 캐시 콜백을 호출하여 프록시에 대한 업스트림 IP에 대한 내부 API를 쿼리합니다.

stream {
    ...

    init_by_lua_file "lualib/init.lua";

    server {
        listen 443;
        ...

        set $endpoint "default";

        preread_by_lua_block{
            stream: get_cluster()
        }

        proxy_pass $endpoint;
        ssl_preread on;
    }
}



For http we actually had to use another server block, I didn't like this but as the current stream Lua module doesn't have access_by_lua_* https://github.com/openresty/stream-lua-nginx-module#todo we cant read the host of a normal TCP request (not easily)



function _M.get_cluster() 
    ...

    res = stream_cache:get(host, nil, api.cache_callback, host)

    ...

    ngx.var.endpoint = res

end

function _M.cache_callback(host)

    local httpapi = http.new()

    local res, err = httpapi:request_uri(uri, {
        method = "GET"
    })

    ...

    return res
end


내부 API는 간단한 JSON 응답으로 응답합니다.

{
    app: 'A Simple App'
    host: 'a.com'
    endpoint: '10.0.0.24'
}


JSON 응답에는 엔드포인트가 포함되며 이는 내부 Google Load Balancer IP이므로 모든 트래픽은 Starship을 고려해야 합니다. 그런 다음 초기 요청은 원하는 업스트림 클러스터로 계속 진행되고 Kubernetes는 마법을 수행하고 TLS 등을 처리합니다.

이 아이디어를 구축하고 로컬에서 작업하고 모의 API 서버와 도커를 사용하여 위의 API를 구축하고 폴백 오류 페이지와 일부 오류 검사/로깅을 포함할 수 있었습니다.

API 요청이 실패했거나 해당 호스트가 없으면 내부 API는 nil을 반환하고 캐시는 특정 시간 동안 이 nil 값을 저장합니다. 이 경우 HTTP인 경우 404(앱을 찾을 수 없음) 페이지가 표시됩니다. 요청이 HTTPS인 경우 불행히도 아무 것도 반환할 수 없지만 기본 브라우저 응답보다 조금 더 나은 495 응답을 제공할 수 있습니다.

이것은 지금까지 우리에게 매우 잘 작동했으며 확장함에 따라 일부 구성을 조정해야 할 수도 있습니다. 이것은 아마도 Nginx 구성의 간단한 스크립팅이나 HAProxy를 사용하여 달성할 수 있었지만 이것은 더 복잡했을 것이라고 확신합니다. 유지 및 확장이 더 어렵습니다.

여기 The Remote Company에서 "우리는 단순함을 유지하는 힘을 믿습니다"

이에 대한 아이디어나 개선 방법이 있으면 저에게 연락하십시오 👍

좋은 웹페이지 즐겨찾기