맞춤형 솔루션 구현: Apache APISIX와 Node-Red의 조화로운 활용

March 6, 2024

Ecosystem

배경

많은 Apache APISIX 사용자들은 특정 시나리오에서 요구사항을 충족하기 위해 기업 환경에서 커스터마이징이 필요하다고 요청합니다. APISIX는 강력한 내장 플러그인 기능을 많이 제공하지만, 특정 상황에서는 추가적인 커스터마이징이 필요합니다.

사용자들은 종종 Lua로 플러그인을 작성하여 APISIX 인스턴스에 마운트하여 사용합니다. 그러나 Lua는 상대적으로 제한된 사용자층을 가지고 있습니다. 시작하기는 쉽지만, 숙달하기는 쉽지 않으며, 복잡한 데이터 변환 로직을 Lua로 구현하는 것은 상당히 복잡해질 수 있습니다. 현재 Java Plugin Runner는 개발자들이 호출할 수 있는 일부 훅만 노출하고 있어, 직접 지원되지 않는 기능을 구현하려면 Java Plugin Runner의 소스 코드를 수정해야 합니다.

아래 다이어그램은 세 가지 일반적인 플러그인 사용 패턴을 보여줍니다: Lua 플러그인은 APISIX 코어에 직접 내장되어 실행됩니다; Plugin Runner는 Java, Golang 등의 언어로 작성된 Plugin Runner와 RPC를 통해 통신합니다; 그리고 WASM 플러그인은 바이트코드로 변환되어 APISIX 코어 내부에서 실행됩니다.

APISIX의 플러그인과 플러그인 러너

사용자 피드백을 통해 커스텀 플러그인 요구사항은 종종 HTTP 요청 매개변수와 같은 데이터 변환 및 외부 API 호출을 통한 데이터 처리와 관련이 있음을 알 수 있었습니다.

이를 해결하기 위해 Apache APISIX의 기능을 강화하는 새로운 접근 방식을 제안합니다. 이 접근 방식은 Apache APISIX에서 내장 플러그인만 사용하여 인증 및 속도 제한과 같은 일반적인 기능을 구성하고, 새로 커스터마이징된 로직을 외부 서비스에 배치하는 것입니다. 외부 서비스는 바이너리 프로그램이거나 다른 API 서비스일 수 있으며, 이는 APISIX의 업스트림으로 처리됩니다. 이 외부 서비스는 미들웨어처럼 요청과 응답을 처리합니다. 이 접근 방식은 다른 API 게이트웨이나 프록시 서비스에도 적용할 수 있습니다.

시나리오 설명

우리는 업스트림에서 데이터 쿼리 서비스를 제공하는 일련의 서비스를 가지고 있습니다 (이 글에서는 https://api-ninjas.com/api를 예로 사용합니다). 예를 들어, 도시 이름을 기반으로 최신 날씨 정보와 국가 정보(예: 국가의 GDP, 수도 이름, 통화 단위)를 검색할 수 있습니다.

우리의 주요 목표는 개발자에게 일반적인 요청 인터페이스를 제공하면서도 도시 이름과 데이터 범위 매개변수를 기반으로 검색하려는 데이터 내용을 결정할 수 있도록 하는 것입니다. 또한, 업스트림 서비스가 남용되지 않도록 보호하기 위해 개발자 인터페이스에 인증 서비스를 추가하여 올바른 API Key를 가진 요청만 업스트림 서비스로 전달되도록 해야 합니다.

문제 분석

Node-Red를 도입하기 전에, 위 요구사항을 충족하기 위해 APISIX 개발자들은 Lua 플러그인을 사용하는 것을 고려할 것입니다. Apache APISIX는 상세한 플러그인 개발 문서를 제공하지만, 비즈니스 개발자들은 Lua 구문과 튜닝 기술을 배우고, APISIX가 노출하는 다양한 요청 훅을 이해하고, 매개변수 추출 및 검증을 위한 로직을 작성하면서 지속적으로 플러그인을 다시 로드하여 검증해야 합니다. 테스트를 완료한 후에는 Lua 플러그인을 APISIX 프로그램에 패키징하거나 모든 APISIX 인스턴스에 배포하여 마운트해야 합니다.

이 블로그에서 제공하는 예제 요구사항은 클라이언트 요청에서 특정 매개변수를 파싱한 후 다른 업스트림 서비스에서 데이터를 검색하기 위한 요청을 구성하는 것입니다. 그러나 우리는 비즈니스 작성 외의 트랜잭션을 처리하는 데 많은 시간을 소비했습니다. 따라서 매개변수 변환, 형식 변환 또는 외부 호출과 같은 로직에 대해 더 가볍고 직관적인 접근 방식을 채택할 수 있으며, 이는 바로 Node-Red가 해결할 수 있는 문제입니다.

Node-Red 소개

Node-RED는 다양한 도메인에서 자동화 및 데이터 흐름 처리 작업에 적합한 강력하고 사용하기 쉬운 흐름 기반 프로그래밍 도구입니다. 프로그래밍 인터페이스, 풍부한 노드 라이브러리, 유연한 확장성을 통해 복잡한 흐름을 빠르게 구축하고 다양한 애플리케이션 시나리오를 구현할 수 있습니다. Node-RED가 제공하는 몇 가지 노드는 다음과 같습니다:

  • HTTP_IN 노드: 외부 서비스 호출을 위한 엔드포인트를 노출하며, 이를 APISIX의 업스트림 서비스로 사용할 것입니다.

  • Function 노드: 개발자가 JavaScript로 코드 함수를 작성하여 입력/출력을 수정, 삭제 등을 할 수 있습니다.

  • Switch 노드: 개발자가 조건을 설정하여 조건이 충족되면 다음 지정된 노드로 이동할 수 있습니다.

  • HTTP_Request 노드: URL 등을 설정하여 전체 워크플로우를 실행할 때 이 엔드포인트로 데이터를 전송할 수 있습니다.

  • Change 노드: 지정된 객체의 지정된 값을 추가, 수정 또는 삭제할 수 있습니다.

  • HTTP_Response 노드: 클라이언트에게 응답을 반환하는 데 사용됩니다.

위에 나열된 노드 외에도 Node-RED는 많은 다른 내장 노드를 제공합니다. 이 글은 Node-RED를 통해 위 요구사항을 구현하는 방법을 독자들에게 보여줄 것입니다.

예제 데모

환경 설정

우리는 컨테이너화를 통해 필요한 컴포넌트를 배포하고, DigitalOcean Droplet을 서버 리소스로 사용할 것입니다.

$ doctl compute ssh-key list

ID          Name             FingerPrint
25621060    Zhiyuan Ju       2c:84:b7:d8:14:0a:a0:0f:ca:fe:ca:24:06:a4:fe:39

$ doctl compute droplet create \
    --image docker-20-04 \
    --size s-2vcpu-4gb-amd \
    --region sgp1 \
    --vpc-uuid 646cf2b8-03d8-4f48-b7c8-57cdee60ad27 \
    --ssh-keys 25621060 \
    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01
  
$ doctl compute droplet list

ID           Name                                                    Public IPv4       Private IPv4    Public IPv6    Memory    VCPUs    Disk    Region    Image                                   VPC UUID                                Status    Tags    Features                            Volumes
404094941    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01    143.198.192.64    10.104.0.3                     4096      2        80      sgp1      Ubuntu Docker 25.0.3 on Ubuntu 22.04    646cf2b8-03d8-4f48-b7c8-57cdee60ad27    active            droplet_agent,private_networking

Apache APISIX 배포

APISIX Quickstart를 사용하여 새로운 APISIX 인스턴스를 시작할 것입니다. 자세한 문서는 https://docs.api7.ai/apisix/getting-started/를 참조하십시오.

$ curl -sL https://run.api7.ai/apisix/quickstart | sh

Node-RED 배포

Node-RED는 여러 배포 방법을 제공하며, Docker를 통해 빠르게 배포하고 기존 환경과 통합할 것입니다. 더 많은 배포 세부사항은 공식 문서를 참조하십시오: https://nodered.org/docs/getting-started/.

Node-RED를 배포할 때, 컨테이너가 APISIX 네트워크에 추가되어 APISIX와 통신하고 요청을 처리할 수 있도록 해야 합니다.

$ docker run -d -it -p 1880:1880 -v $PWD/configs/nodered/data:/data --network=apisix-quickstart-net --name mynodered -u Node-Red:dialout nodered/Node-Red

Node-RED 구성

  1. APISIX에서 Node-RED로 들어오는 요청을 처리하기 위해, Node-Red는 요청의 매개변수가 존재하고 유효한지 확인해야 합니다. 매개변수가 누락되었거나 유효하지 않으면 오류 메시지를 반환합니다. 유효한 경우 다음 노드를 실행합니다. 이 특정 시나리오에서는 스톡홀름(city=stockholm)과 베를린(city=berlin) 두 도시에 대한 데이터 쿼리만 허용합니다.

Node-Red를 사용하여 매개변수 확인

  1. 요청이 다음 노드로 들어가면, Node-RED는 요청된 데이터 유형을 결정해야 합니다. 이 시나리오에서는 날씨 정보(scope=weather), 도시가 위치한 국가 정보(scope=country), 도시가 위치한 국가의 GDP(scope=gdp) 세 가지 유형이 있습니다.

Node-Red를 사용하여 데이터 유형 타겟팅

  1. CityScope 매개변수가 모두 유효한 경우, Node-RED는 scope 값에 따라 데이터를 검색할 API를 결정합니다. URL, Method, Payload, X-API-Key 등을 설정한 후, Node-RED의 노드는 트리거될 때 해당 엔드포인트에 접근하여 데이터를 검색합니다.

scope에 따라 데이터를 얻기 위한 API 선택

  1. scope=gdp에 대한 데이터를 검색할 때, Node-RED는 외부 API의 응답 본문에서 GDP 값을 추출해야 합니다. 이는 Change 노드를 사용하여 추출하거나 Function 노드를 사용하여 수행할 수 있습니다.

외부 API의 응답 본문에서 GDP 값 파싱

  1. 마지막으로, Node-RED는 처리된 데이터를 APISIX로 반환하고, APISIX는 이를 클라이언트에게 전달합니다. 최종 Node-RED 다이어그램은 아래와 같습니다.

APISIX와 Node-Red를 결합한 흐름 차트

APISIX 라우트 생성

Node-Red 서비스를 클라이언트에게 제공하기 위해, Node-Red가 노출한 엔드포인트에 대한 역방향 프록시로 APISIX를 사용해야 합니다. 다음은 따라야 할 단계입니다:

  1. APISIX 라우트를 생성하고 mynodered:1880을 이 라우트의 업스트림으로 설정합니다. 이를 통해 이 엔드포인트로 전송된 모든 요청은 Node-Red 서비스로 전달됩니다.

  2. Key Authentication을 활성화하여 유효한 API Key를 가진 요청만 인증을 통과하고 Node-Red 서비스에 접근할 수 있도록 합니다.

위 단계를 따르면 Node-Red 서비스를 안전하게 클라이언트에게 노출하고, 권한이 있는 사용자만 접근할 수 있도록 할 수 있습니다.

$ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
  "id": "proxy-global-data-endpoint",
  "uri": "/global-data",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "mynodered:1880": 1
    }
  },
  "plugins": {
    "key-auth": {}
  }
}'

$ curl -i "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '
{
  "username": "tom",
  "plugins": {
    "key-auth": {
      "key": "secret-key"
    }
  }
}'

요청 검증

APISIX와 Node-Red가 예상대로 동작하는지 확인하기 위해 여러 시나리오를 시도할 것입니다:

시나리오 1

  • 시나리오 설명: 잘못된 Key로 API에 접근합니다.

  • 예상 결과: 제공된 API Key가 잘못되었으므로 요청이 거부되고 해당 오류 메시지가 반환되어야 합니다.

$ curl http://143.198.192.64:9080/global-data -H "apikey: invalid-key" -i

HTTP/1.1 401 Unauthorized
Date: Mon, 04 Mar 2024 07:47:24 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.8.0

{"message":"Invalid API key in request"}

시나리오 2

  • 시나리오 설명: 올바른 Key로 API에 접근하지만, 유효하지 않은 City 필드가 있습니다.

  • 예상 결과: 요청 매개변수가 요구사항을 충족하지 않으므로 해당 오류 메시지가 반환되어야 하며, City 필드가 유효하지 않음을 나타내야 합니다.

$ curl "http://143.198.192.64:9080/global-data?city=singapore&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 69
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"45-IOhgB2XkDHi2Kt4PP42n1xa8Gys"
Date: Mon, 04 Mar 2024 07:48:02 GMT
Server: APISIX/3.8.0

{"errorCode":400,"message":"Allowed city Options: Stockholm, Berlin"}

시나리오 3

  • 시나리오 설명: 올바른 Key와 유효한 CityScope 필드로 API에 접근하여 국가 데이터를 검색합니다.

  • 예상 결과: 요청이 성공하고, City가 위치한 국가에 대한 관련 정보가 반환되어야 합니다.

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 947
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"3b3-XDlm9OHfuUrWH+g42q8L1F2uu/o"
Date: Mon, 04 Mar 2024 07:48:26 GMT
Server: APISIX/3.8.0

[{"gdp":556086,"sex_ratio":100.4,"surface_area":438574,"life_expectancy_male":80.8,"unemployment":6.7,"imports":158710,"homicide_rate":1.1,"currency":{"code":"SEK","name":"Swedish Krona"},"iso2":"SE","employment_services":80.7,"employment_industry":17.7,"urban_population_growth":1.1,"secondary_school_enrollment_female":157.9,"employment_agriculture":1.6,"capital":"Stockholm","co2_emissions":37.6,"forested_area":68.9,"tourists":7440,"exports":160538,"life_expectancy_female":84.4,"post_secondary_enrollment_female":82.1,"post_secondary_enrollment_male":52.7,"primary_school_enrollment_female":127.4,"infant_mortality":2,"gdp_growth":2.2,"threatened_species":98,"population":10099,"urban_population":87.7,"secondary_school_enrollment_male":148.1,"name":"Sweden","pop_growth":0.7,"region":"Northern Europe","pop_density":24.6,"internet_users":92.1,"gdp_per_capita":55766.8,"fertility":1.8,"refugees":310.4,"primary_school_enrollment_male":125.8}]

시나리오 4

  • 시나리오 설명: 올바른 Key와 유효한 CityScope 필드로 API에 접근하여 GDP 데이터를 검색합니다.

  • 예상 결과: 요청이 성공하고, City가 위치한 국가의 GDP 데이터가 반환되어야 합니다.

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=gdp" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 6
Connection: keep-alive
Access-Control-Allow-Origin: *
ETag: W/"6-j8I5kokycgWjCeKC1c2UfJW7AQY"
Date: Mon, 04 Mar 2024 07:48:48 GMT
Server: APISIX/3.8.0

556086

이 네 가지 시나리오를 검증함으로써 APISIX와 Node-Red가 예상대로 동작하며 다양한 유형의 요청을 올바르게 처리할 수 있음을 확인할 수 있습니다.

요약

우리는 커스텀 기능 개발 문제를 더 영리하게 해결하기 위한 새로운 접근 방식을 제공하며, 이를 상세한 예제로 보여주었습니다.

  1. API 요청 라우팅 및 신원 확인: 먼저, Apache APISIX의 라우팅 기능과 인증 플러그인을 활용하여, 제공된 자격 증명이 유효할 때 APISIX가 클라이언트 요청을 Node-Red 서비스로 전달합니다.

  2. 요청 처리 및 변환: Node-Red에서 들어오는 API 요청을 처리하기 위한 흐름을 생성합니다. HTTP 입력 노드를 사용하여 APISIX로부터 요청을 받고, 요청 매개변수를 파싱하고 검증하여 비즈니스 요구사항을 충족하는지 확인합니다.

  3. 비즈니스 로직 처리: 유효한 요청을 받으면, Node-Red에서 비즈니스 로직을 실행할 수 있습니다. 예를 들어, 매개변수에 따라 다른 비즈니스 API에 요청을 보내 데이터를 검색하고, 응답에서 필요한 필드를 추출합니다. 이러한 작업을 완료한 후 최종 데이터를 APISIX로 반환합니다.

  4. 오류 처리 및 로깅: 처리 중에 오류나 예외가 발생하면, Node-Red에 오류 처리 노드를 추가하여 예외 상황을 포착하고 처리할 수 있습니다. 또한, 로깅 노드를 사용하여 처리 중의 주요 정보를 기록하여 이후 문제 해결 및 분석에 활용할 수 있습니다. 이 예제에서는 이를 보여주지 않았습니다.

APISIX와 Node-Red를 결합함으로써, 복잡한 코드나 플러그인을 작성하지 않고도 시각적으로 완전한 요청 처리 프로세스를 구현할 수 있습니다. 이는 요청 라우팅, 데이터 처리, 비즈니스 로직 등을 포함하며, 유연하고 커스터마이징 가능한 솔루션으로 시스템 기능을 더 빠르게 구축하고 조정할 수 있게 해줍니다. 이는 개발 효율성을 높이고 개발 비용을 줄이며, 시스템의 안정성과 확장성을 보장합니다.

Tags: