整合一套高性能網關Kong

前言

    相信大家對Api網關都比較的熟悉,我們之前的文章也介紹過ASP.NET Core的網關Ocelot,也介紹過Spring Cloud Gateway。說到網關的主要功能,其實總結起來就兩個字"統一",無論是作爲應用的入口、認證授權、熔斷限流等等主要都是爲了統一的地方做一些事情。今天我們介紹一款性能更高的網關Kong,相對於Ocelot或Gateway這些類型的網關來說,Kong的優勢是具有更高的性能,主要因爲Kong是基於Nginx+Lua爲核心的,接下來我們就詳細介紹一下。

概念介紹

    在使用Kong之前我們先來大致的介紹一下Kong是什麼,Kong是基於OpenResty的開源網關。而OpenResty是基於Nginx與Lua的高性能Web平臺。經常使用Nginx的同學們都知道,如果修改了Nginx的配置是需要重啓Nginx的。而OpenResty讓Nginx具備了動態編程的能力,使得Nginx成爲了一個應用服務器軟件,Kong正是基於OpenResty的,Nginx的性能不必多說,所以Kong可以理解爲運行在Nginx上的高性能網關,在學習的過程中我們可以類比着Nginx進行了解。

Kong

說了這麼多接下來我們大致介紹一下Kong自學三件套

Kong有一點做的還是比較好的,無論是GitHub還是官方文檔介紹的都比較詳細,而且比較通俗易懂,這裏我們就不過多的介紹了,有興趣的同學可以自行了解一下。介紹完了自學三件套之後,接下來我們瞭解一下搭建一套Kong的幾個組成部分,總結起來就是三個Kong服務、Kong依賴的存儲、Kong可視化界面,下面我們大致的介紹一下。

  • 首先是Kong服務,一套可以正常工作的Kong會包含兩個對外提供服務的端口,一個是網關常規使用的端口,即對外提供訪問的入口。另一個則是管理Kong的Admin端口,比如對Kong管理服務的增刪改查以及Kong常用的Plugin管理以及一些常規的配置等,Kong的插件非常的豐富,基本上可以到達常規的一些操作比如限流、認證授權、鏈路跟蹤、監控等都有而且形式非常的豐富。
  • 其次是Kong存儲服務,因爲在Kong上配置的轉發服務、插件、環境變量、認證等相關的信息都是需要存儲的,但是外部存儲不是必須的,Kong可以將這些信息存儲到進程內的緩存中,但是重啓Kong之後這些配置將會丟失,因此在正常的使用過程中我們總會給他提供一個外部數據庫來存儲這些信息。可供Kong使用的存儲數據庫也有好幾個選擇分別是Mysql、MongoDB、Postgresql等。本次演示我們使用的是Postgresql,也是官方推薦的方式。
  • 最後是Kong的可視化UI,當然這個不是必須的。Kong提供了Admin管理接口的形式對服務和Plugin查看、新增、修改、刪除等操作,但是有一個可視化的管理界面,無疑讓有些操作變得更加簡單清晰,而且這些可視化系統正式基於Kong的Admin接口開發的。可供選擇的可視化UI也比較多,比較出名的有Konga、kong-ui、kong-admin-ui。本次演示我們選擇的是Konga,也是推薦使用的最多的一個。
Konga

    Konga並非Kong官方出品的可視化UI,但是它是最流行的一個,也是使用最多的一個,目前最新版本是v0.6.3。Konga是基於Nodejs開發的,它的Github地址是https://github.com/pantsel/konga,同樣的Konga的GitHub文檔上介紹的也非常的詳細。它的部署方式有兩種,一個是直接克隆GitHub上的倉庫上的代碼通過npm的方式運行,另一種則是使用docker的方式部署。兩種方式都非常的簡單,本次我們選擇docker的方式。

環境搭建

    關於Kong的搭建,官方網站給出了好幾種部署方式可以基於Docker或K8S,也可以在Liunx操作系統Centos、Ubuntu、RHEL等都支持,目前最穩定版本爲2.3.x,這些在官方文檔上講解的非常詳細非常易懂,具體可參閱官方文檔https://konghq.com/install/。如果想能快速的搭建起一套Kong+Postgresql+Konga的環境,docker-compose無疑是一個比較好的選擇,接下來我們將演示用過這種方式快速搭建起一條完整的Kong環境。
    Kong官方GitHub有專門的docker-kong倉庫地址爲https://github.com/Kong/docker-kong,也已將這個倉庫Clone下來,找到compose文件運行。但是我們這裏還要整合可視化界面Konga,Konga並非官方出品,所以我要要修改一下compose文件整合進去Konga,完整的呈現如下所示

version: '3.3'
#創建kong_data卷
volumes:
  kong_data: {}
#創建kong-net網絡
networks:
  kong-net:
    external: false

services:
  #數據庫運行完成之後需要執行kong進行初始化操作
  kong-migrations:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations bootstrap
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  #遷移過程依賴db
  kong-migrations-up:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations up && kong migrations finish
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  # kong服務
  kong:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    user: "${KONG_USER:-kong}"
    depends_on:
      - db
    environment:
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: '0.0.0.0:8001'
      KONG_CASSANDRA_CONTACT_POINTS: db
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    #kong的端口,非https使用8000和8001
    ports:
      - "8000:8000/tcp"
      - "8001:8001/tcp"
      - "8443:8443/tcp"
      - "8444:8444/tcp"
    #健康檢查
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 10s
      retries: 10
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure

  #konga可視化界面
  konga:
    image: pantsel/konga
    networks:
      - kong-net
    depends_on:
      - db
    ports:
      - "1337:1337/tcp"
    environment:
      TOKEN_SECRET: konga
      DB_ADAPTER: postgres
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: kong
      DB_PASSWORD: kong
      DB_DATABASE: kong
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  # postgres數據庫
  db:
    image: postgres:9.6
    environment:
      POSTGRES_DB: ${KONG_PG_DATABASE:-kong}
      POSTGRES_USER: ${KONG_PG_USER:-kong}
      POSTGRES_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "${KONG_PG_USER:-kong}"]
      interval: 30s
      timeout: 30s
      retries: 3
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
    stdin_open: true
    tty: true
    ports:
      - 5432:5432
    networks:
      - kong-net
    volumes:
      - kong_data:/var/lib/postgresql/data

# 用文件統一管理數據庫密碼
secrets:
  kong_postgres_password:
    file: ./POSTGRES_PASSWORD

通過docker-compose直接運行上面的yaml文件,便可以直接運行一套完整的kong項目,這裏在強調一下上面說過GitHub上有官方專門提供的docker-kong倉庫,直接Clone便可以直接得到運行Kong的yaml,但是本示例我們加入了Konga,如果需要可直接下載我修改後的包[點擊下載👈]下載完成之後進入docker-compose.yml所在的文件夾,執行docker-compose up -d指令,首次的話會下載依賴的鏡像文件可能稍微慢一點,出現如下界面的時候說明正常運行完成

完成之後打開http://localhost:8000/這個是kong提供轉發服務的地址,出現如下運行結果

{
  "message": "no Route matched with those values"
}

因爲我們沒有配置任何路由規則,所以會提升匹配不到路由。然後我們在瀏覽器打開konga的地址http://localhost:1337/出現如下界面,則表示kong和konga運行成功

操作使用

通過上面的操作我們已經成功運行起來了Kong+Konga+Postgresql的運行環境,然後我們就可以使用這套網關完成服務的轉發相關的操作,常用的方式有兩種,一種是通過Konga直接配置,另一種則是直接通過Kong服務的8001端口訪問管理的restful接口進行操作,當然Konga也是基於這套接口來操作的,接下來我們就來大致演示一下使用這兩種方式完成相關操作。

通過Konga界面配置

打開Konga之後首次會讓註冊登錄信息,註冊完成之後會讓完成Konga的配置連接操作,主要就是配置Kong的管理接口

配置成功之後左側菜單欄會出現配置相關的操作比如Service、Route、Target相關如下圖所示
如果只是配置類似代理的功能,即通過網關轉發到一個真實地址的場景,那麼只需要添加Service,並且配置Route即可,首先添加Service
然後添加Route信息
當然這是最簡單的方式去實現類似反向代理的操作,有的時候其實我們需要的是不僅僅能夠實現轉發,而且還能實現負載均衡的場景。這時候的請求模式就是一個高可用和負載均衡的模式,這個時候僅僅通過配置Service和Route是不夠的,我們需要的是Nginx的Upstream的情況,一個Upstream對應多個真實的後端地址。
首先配置Upstreams,由於kong本質處理請求還是通過nginx進行的,所以概念可以直接和nginx類比過來,其實就是是類似配置一個轉發操作,這個操作既可以是一個服務地址,也可以是一組服務集合
然後對這個upstream的信息進行配置,除了Name必填其他都可以選填
如果選擇了header或cookie策略,就要爲對應的策略設置具體的值,以header爲例
健康檢查相關分爲主動檢測和被動檢測。主動監測是kong定時像Target發送請求探測,根據探測的結果判斷服務是否健康。被動檢測是通過攔截外部請求到Target,然後根據Target的響應狀態判斷轉發的節點是否健康
下圖爲被動檢查的相關配置,其實無論是主動監測還是被動檢測,他們的概念都是一樣的只是方式不一樣,一個是Kong主動探測,一個是根據外部請求攔截真實服務節點的返回狀態判斷。它們判斷的依據和Nginx健康檢查的判斷邏輯也是一致的,主要通過http狀態碼、tpc連接是否正常、請求是否超時爲主要評估依據。
Upstream除了Name之外其他的配置都是選填的,配置完成直接點擊保存即可,這樣Upstream就配置完成了。接下來就是爲Upstreams配置Target。Target等同於Nginx上在Upstream節點內配置真實的轉發節點地址,如果用一句話描述Upstream與Target代表的含義的話,可以理解爲Upstream是一組可以提供相同服務的虛擬節點,而Target是這一組真實節點中的一個,是真實提供服務的節點之一。
Target的配置也很簡單,一個是配置Target的地址,另一個是當前這個Target的權重。上面咱們說過,一個Upstream是一組Target的集合,所以每個Target配置都可以配置對應的權重,咱們說的權重其實一種優先級的概念,每一次請求優先級比較高的會優先被訪問到。如果真是的服務器配置基本上一致的話,那麼權重理應是一樣的,如果存在弱機的情況,那麼弱機的權重應該配置的稍微低一些。
接下來就是添加Service,Service在Kong網關裏的概念就是一個服務,比如我要訪問商品服務、訂單服務、優惠券服務,而Service正是表示着這種概念。如果說Upstreams代表着一組真實的服務,那麼Service就是Upstream的門面,通過Service才能訪問到Upstream。
配置Service主要配置Name和訪問地址即可,這裏的訪問地址在Konga裏有兩種配置形式,一個是直接配置Url,另一個是配置protocol、host、port、path。比如上圖的Url屬性等同於下面的 協議+主機+端口+路徑,均是針對Upstream的訪問配置,和具體訪問路由屬於不同概念,如果是通過Service訪問Upstream的場景,那麼Host就是是Upstream的名稱或真實的訪問目標地址名稱
接下來就是配置Route,上面咱們說到了Service和Upstream的概念,那麼Route起到的作用就是,當我訪問了Service那麼我通過什麼樣的路由規則才能訪問到Upstream,比如特定的Host頭信息或者路徑信息等。在Konga裏,通過Service列表界面點擊具體Service進入ServiceDetail界面配置Route
點擊ADD ROUTE會彈出添加Route信息的彈窗,主要配置Name、Host Paths Strip Path具體含義可看截圖
這裏咱們重點說一下Strip Path,它代表的含義是如果通過一個路徑訪問到了真實的服務地址,那麼在訪問真實的服務地址的時候是否要帶上這個Path信息,還是這個Path只是爲了提供訪問到具體服務的一個標識。比如真實的OrderService服務裏獲取訂單詳情的地址爲http://localhost:5001/order/get/1,我們在通過網關訪問OrderService的時候地址是http://localhost:8000/orderservice/order/get/1,這種情況Path裏的orderservice對我來說只是爲了能訪問到OrderService的一個路徑標識,真實服務OrderService的時候並不需要,這個時候我就可以設置Strip Path的值爲true,這樣在轉發地址的時候就不會在Url上帶上orderservice這個標識了。到這裏,關於通過Konga配置的常用操作介紹的就差不多了,截圖上也標註了常用字段的含義,想更詳細的瞭解還需要自己搭建一套Konga實操一下。

通過Kong的Admin接口配置

    上面我們介紹了通過Konga的方式配置Kong的服務,我們開始的時候也說過,Konga也是通過Kong Admin API的接口完成對服務的增刪改查的操作的,Kong Admin API是Kong自帶的管理接口,通過這些接口我們可以通過更原生的方式去操作Kong。而且如果你通過Konga配置的時候不太瞭解哪些字段是必須的,或者需要理解每個字段詳細代表的含義,這時候就可以通過原生的接口瞭解,這樣有助於更深刻的瞭解。Admin API的官方文檔地址位於https://docs.konghq.com/gateway-oss/2.3.x/admin-api/文檔介紹的非常的詳細,接下來我們就大致的介紹Admin API的一些常規操作。

  • 默認情況Admin API的監聽端口是8001,如果是https的話默認爲8444
  • API可接收的內容格式即ContentType爲application/json、application/x-www-form-urlencoded、multipart/form-data
  • Kong Admin API是標準的Restful風格的接口,這對於我們操作來說非常的便捷

由於Admin API官方文檔介紹的非常詳細了,這裏咱們就不一一的介紹了,咱們這裏簡單的介紹一下有代表性的接口和一些注意相關的操作。強烈建議,學習Kong的話一定要看這個文檔,這樣的話能解決很多的困惑。首先介紹Service的List的接口,這個接口是返回註冊在Kong上的所有Service

  • 請求方式是curl http://localhost:8001/services
  • 成功的狀態碼爲HTTP 200 OK
  • 返回的json格式代表的含義,文檔中介紹的也非常詳細
{
    "data": [{
        "id": "a5fb8d9b-a99d-40e9-9d35-72d42a62d83a",
        "created_at": 1422386534,
        "updated_at": 1422386534,
        "name": "my-service",
        "retries": 5,
        "protocol": "http",
        "host": "example.com",
        "port": 80,
        "path": "/some_api",
        "connect_timeout": 60000,
        "write_timeout": 60000,
        "read_timeout": 60000,
        "tags": ["user-level", "low-priority"],
        "client_certificate": {"id":"51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"},
        "tls_verify": true,
        "tls_verify_depth": null,
        "ca_certificates": ["4e3ad2e4-0bc4-4638-8e34-c84a417ba39b", "51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"]
    }],
    "next": "http://localhost:8001/services?offset=6378122c-a0a1-438d-a5c6-efabae9fb969"
}

data爲數據結果,next是下一批數據查詢的地址。這一點做得還是考慮的比較周全的,如果Service比較多的話,一次性返回不是一個明智的操作,通過判斷next的方式可以知道是否存在分頁的情況,根據判斷狀態碼可以知道請求是否成功。
其次Service還有一個檢索接口,它主要用於返回指定的Service信息,它的請求方式是curl http://localhost:8001/services/{service name or id},如果返回的狀態碼爲404則說明服務沒有被註冊過,如果服務存在則狀態碼爲200並返回服務詳情的json。
還有一個比較實用的接口是Update Or Create Service翻譯過來就是修改或創建,即如果Service存在則執行更新操作,如果不存在則直接創建一個Service。試着想一下,如果不存在這個接口,我們每次註冊Service之前還要調用一下檢索接口判斷一下Service是否被註冊過。
請求方式是curl -X PUT -H "Content-Type: application/json" -d '{}' "http://localhost:8001/services/{service name or id}"它的請求數據格式和Add的是一樣的只是請求方式是PUT它的數據格式是

{
    "id": "9748f662-7711-4a90-8186-dc02f10eb0f5",
    "created_at": 1422386534,
    "updated_at": 1422386534,
    "name": "my-service",
    "retries": 5,
    "protocol": "http",
    "host": "example.com",
    "port": 80,
    "path": "/some_api",
    "connect_timeout": 60000,
    "write_timeout": 60000,
    "read_timeout": 60000,
    "tags": ["user-level", "low-priority"],
    "client_certificate": {"id":"4e3ad2e4-0bc4-4638-8e34-c84a417ba39b"},
    "tls_verify": true,
    "tls_verify_depth": null,
    "ca_certificates": ["4e3ad2e4-0bc4-4638-8e34-c84a417ba39b", "51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"]
}'

它返回的結果是HTTP 201 Created or HTTP 200 OK

這裏需要特別注意的是Add相關的接口返回的Http是HTTP 201 Created

    我們就通過關於Service的操作,大致介紹一下關於Admin API的大致操作方式,關於每一個Service、Route、Upstream、Target的操作都有完整的增刪改查接口,咱們上面介紹的是除了這些之外實用操作的接口。這裏還需要注意的是有些操作是有關聯關係的,比如Service和Route的操作,Upstream和Target的操作,因此在實際通過接口開發的過程中要注意對Service的Id或Upstream的Id的保存。

因爲Kong對註冊中心這些比如Consul、Eureka、Nacos這種支持的並不是很完善,雖然Kong支持自己編寫Lua腳本的方式完成這些操作,但是這些編寫成本還是比較高的,所以這個時候Admin API就顯得格外的實用。

總結

    本次我們主要講解了對Kong的大致入門操作,相信很多同學都聽說過或者用過它,我個人覺得Kong這種級別的組件很多時候能瞭解它的大致場景和結構並能搭建出來一套可運行的環境,那麼入門就已經完成一半了。我覺得Kong做的比較好的一點是,它的學習文檔和GitHub倉庫上的一些操作都非常的完整而且很詳細。我每次寫這種類型的文章其實初衷都非常的簡單,就是能讓對這些東西有興趣的同學,能通過這篇文章入門,算是能有一個好的開始吧。

👇歡迎掃碼關注我的公衆號👇
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章