整合一套高性能網關 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 可視化界面,下面我們大致的介紹一下。

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

{
    "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 倉庫上的一些操作都非常的完整而且很詳細。我每次寫這種類型的文章其實初衷都非常的簡單,就是能讓對這些東西有興趣的同學,能通過這篇文章入門,算是能有一個好的開始吧。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/hkVa4OvCYqisWdvHX9VRhw