Openresty 및 Google BigQuery를 사용한 마이크로서비스 사용량 로깅

Descartes Labs에서는 플랫폼 구축에 마이크로서비스 아키텍처를 사용해 왔습니다. 마이크로서비스에 익숙하지 않은 경우 마이크로서비스는 종종 HTTP 또는 GRPC를 통해 통신하는 독립 서비스 모음입니다. 자세한 내용은 Martin Fowler의 2014article를 확인하십시오.



Descartes Labs의 마이크로서비스

전송된 로깅 바이트



로깅, 특히 사용 로깅의 주요 요구 사항은 전체 위성 이미지 모음에 대한 API 액세스를 제공하므로 사용자에게 전송된 바이트 수를 캡처하는 것입니다. 이것은 egress와 같은 비용과 가장 근접하게 일치하며 고객에게 제공되는 기본 가치를 나타낼 수 있습니다.

NGINX와 업스트림 서비스의 청크 응답의 문제는 NGINX에서 $bytes_sent를 가져오는 것이 로깅 단계에서만 발생할 수 있다는 것입니다. 또는 body_filter_by_lua*를 사용하여 각 청크에서 바이트를 추적할 수 있지만 추가된 복잡성으로 인해 확실히 두 번째 옵션입니다.

작동하지 않는 첫 번째 작업은 다음과 같습니다.

log_by_lua_block {
    local payload = {
        bytes = tonumber(ngx.var.bytes_sent),
        status = tonumber(ngx.var.status),
        timestamp = ngx.now() 
        ... -- more values
    }
    -- post payload to favorite backend for timeseries analysis
}



위의 두 가지 문제가 있습니다. 중요한 문제는 로깅 단계에서 논블로킹 IO용 Lua 코소켓을 사용할 수 없다는 것입니다. 두 번째는 일괄 처리를 원한다는 것입니다.

분리된 스레드 사용



우리가 구현한 솔루션은 각 NGINX 작업자 및 공유 스레드 안전 버퍼에서 분리된 스레드를 사용하는 것입니다.



OpenResty 아키텍처

NGINX Lua 블록은 다음과 같습니다.

lua_shared_dict usage_logging 10m;

init_worker_by_lua_block {
    local Logging = require "descarteslabs.logging"
    l = Logging.new()
    l:watch(ngx.shared.usage_logging)
}

location = /test {
    proxy_pass service;
    log_by_lua_block {
        local payload = {
            bytes = tonumber(ngx.var.bytes_sent),
            status = tonumber(ngx.var.status),
            timestamp = ngx.now() 
            ... -- more values
        }
        local Logging = require "descarteslabs.logging"
        l = Logging.save(ngx.shared.usage_logging, payload)
    }    
}


버퍼 확인



ngx.timer.at 메커니즘은 이 버퍼를 감시하는 것을 간단하게 만듭니다. 유일한 요령은 각 작업자가 버퍼를 감시하므로 약간의 무작위성을 추가해야 한다는 것입니다.

local check_function
check_function = function(premature)
    if not premature then
        while true do
            local requests = self:get(size)
            pcall(_M.save, self, rows)

            if #rows < size then
                break
            end
        end

        local ok, err = ngx.timer.at(delay(), check_function)
        if not ok then
            log(ERR, "failed to create timer: ", err)
            return
        end
    end
end


BigQuery 사용



Google Cloud Platform의 주요 고객으로서 Cloud Storage, BigQuery 및 Stackdriver와 같은 많은 Google Cloud API를 위한 맞춤형 Lua 클라이언트를 보유하고 있습니다. 이 특정 사용 사례를 위해 BigQuery를 사용해 보고 있습니다. 쿼리는 다음과 같습니다.

SELECT 
  COUNT(*) as calls,
  SUM(bytes) as bytes, 
  DATETIME_TRUNC(DATETIME(timestamp), `second` ) as w
FROM `project.dataset.table`
WHERE
  status=200 
GROUP BY
  w
ORDER BY 
  w desc
LIMIT 100


다음 테이블이 출력됩니다.



부하 테스트를 위해 Vegeta를 사용한 Google BigQuery 결과

이 시점에 도달하면 고객 또는 서비스와 같이 그룹화할 몇 가지 필드를 추가하고 다양한 기간 및 세분성으로 청구를 제공하는 것은 간단합니다.

좋은 웹페이지 즐겨찾기