1. 소개

Kong 은 오픈 소스 API 게이트웨이 및 마이크로서비스 관리 계층입니다.

Nginx 및 lua-nginx-module (특히 OpenResty )을 기반으로 하는 Kong의 플러그형 아키텍처는 유연하고 강력합니다.

2. 핵심 개념

코드 샘플을 살펴보기 전에 Kong의 주요 개념을 살펴보겠습니다.

  • API 개체 – 특정 작업을 수행하거나 일부 서비스를 제공하는 모든 HTTP Endpoints의 속성을 래핑합니다. 구성에는 HTTP 메서드, Endpoints URI, API 서버를 가리키는 업스트림 URL이 포함되며 프록시 요청, 최대 폐기, 속도 제한, 시간 초과 등에 사용됩니다.
  • 소비자 개체 – API Endpoints을 사용하는 모든 사람의 속성을 래핑합니다. 추적, 액세스 제어 등에 사용됩니다.
  • 업스트림 개체 – 수신 요청이 가상 호스트 이름으로 표시되는 프록시 또는 부하 분산 방법을 설명합니다.
  • 대상 개체 – 구현 및 제공되는 서비스를 나타내며 호스트 이름(또는 IP 주소) 및 포트로 식별됩니다. 모든 업스트림의 대상은 추가하거나 비활성화할 수만 있습니다. 대상 변경 내역은 업스트림에서 유지 관리됩니다.
  • 플러그인 개체 – 요청 및 응답 수명 주기 동안 애플리케이션의 기능을 강화하는 플러그형 기능입니다. 예를 들어 관련 플러그인을 활성화하여 API 인증 및 속도 제한 기능을 추가할 수 있습니다. Kong은 플러그인 갤러리 에서 매우 강력한 플러그인을 제공합니다.
  • Admin API – Kong 구성, 엔드포인트, 소비자, 플러그인 등을 관리하는 데 사용되는 RESTful API 엔드포인트

아래 그림은 Kong이 레거시 아키텍처와 어떻게 다른지 보여줍니다. 이러한 개념을 도입한 이유를 이해하는 데 도움이 될 수 있습니다.

클라이언트 - Bean
(출처: https://getkong.org/)

3. 설정

공식 문서는 다양한 환경에 대한 자세한 지침 을 제공합니다.

4. API 관리

Kong을 로컬로 설정한 후 간단한 주식 쿼리 Endpoints을 프록시하여 Kong의 강력한 기능을 살펴보겠습니다.

@RestController
@RequestMapping("/stock")
public class QueryController {

    @GetMapping("/{code}")
    public String getStockPrice(@PathVariable String code){
        return "BTC".equalsIgnoreCase(code) ? "10000" : "0";
    }
}

4.1. API 추가

다음으로 쿼리 API를 Kong에 추가해 보겠습니다.

관리 API는 http://localhost:8001 을 통해 액세스할 수 있으므로 모든 API 관리 작업은 이 기본 URI로 수행됩니다.

APIObject stockAPI = new APIObject(
  "stock-api", "stock.api", "http://localhost:8080", "/");
HttpEntity<APIObject> apiEntity = new HttpEntity<>(stockAPI);
ResponseEntity<String> addAPIResp = restTemplate.postForEntity(
  "http://localhost:8001/apis", apiEntity, String.class);

assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

여기에서 다음 구성으로 API를 추가했습니다.

{
    "name": "stock-api",
    "hosts": "stock.api",
    "upstream_url": "http://localhost:8080",
    "uris": "/"
}
  • "이름" 은 동작을 조작할 때 사용되는 API의 식별자입니다.
  • "hosts" 는 "Host" 헤더 와 일치하여 들어오는 요청을 지정된 "upstream_url" 로 라우팅하는 데 사용됩니다.
  • 상대 경로는 구성된 "uris"와 일치합니다.

API를 더 이상 사용하지 않거나 구성이 잘못된 경우 간단히 제거할 수 있습니다.

restTemplate.delete("http://localhost:8001/apis/stock-api");

API가 추가되면 http://localhost:8000 을 통해 API를 사용할 수 있습니다 .

String apiListResp = restTemplate.getForObject(
  "http://localhost:8001/apis/", String.class);
 
assertTrue(apiListResp.contains("stock-api"));

HttpHeaders headers = new HttpHeaders();
headers.set("Host", "stock.api");
RequestEntity<String> requestEntity = new RequestEntity<>(
  headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc"));
ResponseEntity<String> stockPriceResp 
  = restTemplate.exchange(requestEntity, String.class);

assertEquals("10000", stockPriceResp.getBody());

위의 코드 샘플에서는 방금 Kong에 추가한 API를 통해 주가를 쿼리하려고 합니다.

http://localhost:8000/stock/btc 를 요청 하면 http://localhost:8080/stock/btc 에서 직접 쿼리하는 것과 동일한 서비스를 받습니다 .

4.2. API 소비자 추가

이제 Security에 대해 이야기해 보겠습니다. 특히 API에 액세스하는 사용자를 위한 인증에 대해 이야기하겠습니다.

나중에 인증 기능을 활성화할 수 있도록 주식 쿼리 API에 소비자를 추가해 보겠습니다.

API에 대한 소비자를 추가하는 것은 API를 추가하는 것만큼 간단합니다. 소비자의 이름(또는 ID)은 모든 소비자 속성의 유일한 필수 필드입니다.

ConsumerObject consumer = new ConsumerObject("eugenp");
HttpEntity<ConsumerObject> addConsumerEntity = new HttpEntity<>(consumer);
ResponseEntity<String> addConsumerResp = restTemplate.postForEntity(
  "http://localhost:8001/consumers/", addConsumerEntity, String.class);
 
assertEquals(HttpStatus.CREATED, addConsumerResp.getStatusCode());

여기에 "eugenp"를 새 소비자로 추가했습니다.

{
    "username": "eugenp"
}

4.3. 인증 활성화

여기에 Kong의 가장 강력한 기능인 플러그인이 있습니다.

이제 프록시된 주식 쿼리 API에 인증 플러그인을 적용할 것입니다.

PluginObject authPlugin = new PluginObject("key-auth");
ResponseEntity<String> enableAuthResp = restTemplate.postForEntity(
  "http://localhost:8001/apis/stock-api/plugins", 
  new HttpEntity<>(authPlugin), 
  String.class);
assertEquals(HttpStatus.CREATED, enableAuthResp.getStatusCode());

프록시 URI를 통해 주식 가격을 쿼리하려고 하면 요청이 거부됩니다.

HttpHeaders headers = new HttpHeaders();
headers.set("Host", "stock.api");
RequestEntity<String> requestEntity = new RequestEntity<>(
  headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc"));
ResponseEntity<String> stockPriceResp = restTemplate
  .exchange(requestEntity, String.class);
 
assertEquals(HttpStatus.UNAUTHORIZED, stockPriceResp.getStatusCode());

Eugen 은 API 소비자 중 하나이므로 인증 키를 추가하여 이 API를 사용하도록 허용해야 합니다 .

String consumerKey = "eugenp.pass";
KeyAuthObject keyAuth = new KeyAuthObject(consumerKey);
ResponseEntity<String> keyAuthResp = restTemplate.postForEntity(
  "http://localhost:8001/consumers/eugenp/key-auth", 
  new HttpEntity<>(keyAuth), 
  String.class); 
assertTrue(HttpStatus.CREATED == keyAuthResp.getStatusCode());

그런 다음 Eugen 은 이전과 같이 이 API를 사용할 수 있습니다.

HttpHeaders headers = new HttpHeaders();
headers.set("Host", "stock.api");
headers.set("apikey", consumerKey);
RequestEntity<String> requestEntity = new RequestEntity<>(
  headers, 
  HttpMethod.GET, 
  new URI("http://localhost:8000/stock/btc"));
ResponseEntity<String> stockPriceResp = restTemplate
  .exchange(requestEntity, String.class);
 
assertEquals("10000", stockPriceResp.getBody());

5. 고급 기능

기본 API 프록시 및 관리 외에도 Kong은 API 로드 밸런싱, 클러스터링, 상태 확인 및 모니터링 등을 지원합니다.

이 섹션에서는 Kong으로 요청을 로드 밸런싱하는 방법과 admin API를 보호하는 방법을 살펴보겠습니다.

5.1. 부하 분산

Kong은 백엔드 서비스에 대한 로드 밸런싱 요청의 두 가지 전략인 동적 링 밸런서와 간단한 DNS 기반 방법을 제공합니다. 단순화를 위해 링 밸런서 를 사용합니다 .

앞서 언급했듯이 업스트림은 로드 밸런싱에 사용되며 각 업스트림은 여러 대상을 가질 수 있습니다.

Kong은 가중 라운드 로빈과 해시 기반 밸런싱 알고리즘을 모두 지원합니다. 기본적으로 weighted-round-robin 체계가 사용되며 여기서 요청은 가중치에 따라 각 대상에 전달됩니다.

먼저 업스트림을 준비합시다.

UpstreamObject upstream = new UpstreamObject("stock.api.service");
ResponseEntity<String> addUpstreamResp = restTemplate.postForEntity(
  "http://localhost:8001/upstreams", 
  new HttpEntity<>(upstream), 
  String.class);
 
assertEquals(HttpStatus.CREATED, addUpstreamResp.getStatusCode());

그런 다음 업스트림에 대한 두 대상, 즉 weight=10 인 테스트 버전과 weight=40 인 릴리스 버전을 추가합니다 .

TargetObject testTarget = new TargetObject("localhost:8080", 10);
ResponseEntity<String> addTargetResp = restTemplate.postForEntity(
  "http://localhost:8001/upstreams/stock.api.service/targets",
  new HttpEntity<>(testTarget), 
  String.class);
 
assertEquals(HttpStatus.CREATED, ddTargetResp.getStatusCode());

TargetObject releaseTarget = new TargetObject("localhost:9090",40);
addTargetResp = restTemplate.postForEntity(
  "http://localhost:8001/upstreams/stock.api.service/targets",
  new HttpEntity<>(releaseTarget), 
  String.class);
 
assertEquals(HttpStatus.CREATED, addTargetResp.getStatusCode());

위의 구성을 사용하면 요청의 1/5이 테스트 버전으로 이동하고 4/5가 릴리스 버전으로 이동한다고 가정할 수 있습니다.

APIObject stockAPI = new APIObject(
  "balanced-stock-api", 
  "balanced.stock.api", 
  "http://stock.api.service", 
  "/");
HttpEntity<APIObject> apiEntity = new HttpEntity<>(stockAPI);
ResponseEntity<String> addAPIResp = restTemplate.postForEntity(
  "http://localhost:8001/apis", apiEntity, String.class);
 
assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

HttpHeaders headers = new HttpHeaders();
headers.set("Host", "balanced.stock.api");
for(int i = 0; i < 1000; i++) {
    RequestEntity<String> requestEntity = new RequestEntity<>(
      headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc"));
    ResponseEntity<String> stockPriceResp
     = restTemplate.exchange(requestEntity, String.class);
 
    assertEquals("10000", stockPriceResp.getBody());
}
 
int releaseCount = restTemplate.getForObject(
  "http://localhost:9090/stock/reqcount", Integer.class);
int testCount = restTemplate.getForObject(
  "http://localhost:8080/stock/reqcount", Integer.class);

assertTrue(Math.round(releaseCount * 1.0 / testCount) == 4);

가중 라운드 로빈 체계는 백엔드 서비스에 대한 요청의 균형을 대략적으로 가중치 비율로 조정하므로 위 코드의 마지막 줄에 반영된 비율의 근사치만 확인할 수 있습니다.

5.2. 관리 API Security

기본적으로 Kong은 로컬 인터페이스의 관리자 요청만 수락하므로 대부분의 경우 충분한 제한 사항입니다. 그러나 다른 네트워크 인터페이스를 통해 관리하려는 경우 kong.conf에서 admin_listen 값을 변경하고 방화벽 규칙구성할 수 있습니다.

또는 Kong이 Admin API 자체의 프록시 역할을 하도록 할 수 있습니다. "/admin-api" 경로로 API를 관리하고 싶다고 가정하면 다음과 같이 API를 추가할 수 있습니다.

APIObject stockAPI = new APIObject(
  "admin-api", 
  "admin.api", 
  "http://localhost:8001", 
  "/admin-api");
HttpEntity<APIObject> apiEntity = new HttpEntity<>(stockAPI);
ResponseEntity<String> addAPIResp = restTemplate.postForEntity(
  "http://localhost:8001/apis", 
  apiEntity, 
  String.class);
 
assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

이제 프록시된 관리 API를 사용하여 API를 관리할 수 있습니다.

HttpHeaders headers = new HttpHeaders();
headers.set("Host", "admin.api");
APIObject baeldungAPI = new APIObject(
  "baeldung-api", 
  "baeldung.com", 
  "http://ww.baeldung.com", 
  "/");
RequestEntity<APIObject> requestEntity = new RequestEntity<>(
  baeldungAPI, 
  headers, 
  HttpMethod.POST, 
  new URI("http://localhost:8000/admin-api/apis"));
ResponseEntity<String> addAPIResp = restTemplate
  .exchange(requestEntity, String.class);

assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode());

확실히 우리는 프록시 API를 보호하기를 원합니다. 이는 프록시된 관리 API에 대한 인증 플러그인을 활성화하여 쉽게 달성할 수 있습니다.

6. 요약

이 기사에서는 마이크로서비스 API 게이트웨이용 플랫폼인 Kong을 소개했으며 핵심 기능인 API 관리, 업스트림 서버로 요청 라우팅, 로드 밸런싱과 같은 일부 고급 기능에 중점을 두었습니다.

그러나 탐색할 수 있는 더 많은 견고한 기능이 있으며 필요한 경우 자체 플러그인을 개발할 수 있습니다 . 여기에서 공식 문서 를 계속 탐색할 수 있습니다 .

항상 그렇듯이 전체 구현은 Github 에서 찾을 수 있습니다 .

Generic footer banner