雲原生 API 網關 APISIX 入門教程

Apache APISIX 是 Apache 軟件基金會下的雲原生 API 網關,它具有動態、實時、高性能等特點,提供了負載均衡、動態上游、灰度發佈(金絲雀發佈)、服務熔斷、限速、防禦惡意攻擊、身份認證、可觀測性等豐富的流量管理功能。我們可以使用 Apache APISIX 來處理傳統的南北向流量,也可以處理服務間的東西向流量。同時,它也支持作爲 Kubernetes Ingress Controller 來使用。

APISIX 基於 Nginx 和 etcd,與傳統 API 網關相比,APISIX 具有動態路由和熱加載插件功能,避免了配置之後的 reload 操作,同時 APISIX 支持 HTTP(S)、HTTP2、Dubbo、QUIC、MQTT、TCP/UDP 等更多的協議。而且還內置了 Dashboard,提供強大而靈活的界面。同樣也提供了豐富的插件支持功能,而且還可以讓用戶自定義插件。

主要具有以下幾個特點:

安裝 APISIX

爲了簡單,我們這裏可以直接在本地使用 docker 方式來啓動 APISIX,首先 Clone 官方提供的 apisix-docker 倉庫:

➜ git clone https://github.com/apache/apisix-docker.git
➜ cd apisix-docker

在項目根目錄下面的 example 目錄中有啓動 APISIX 的 docker-compose 配置文件,如下所示:

version: "3"

services:
  apisix-dashboard:
    image: apache/apisix-dashboard:3.0.0-alpine
    restart: always
    volumes:
      - ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml
    ports:
      - "9000:9000"
    networks:
      apisix:

  apisix:
    image: apache/apisix:3.2.0-debian
    restart: always
    volumes:
      - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
    depends_on:
      - etcd
    ports:
      - "9180:9180/tcp"
      - "9080:9080/tcp"
      - "9091:9091/tcp"
      - "9443:9443/tcp"
      - "9092:9092/tcp"
    networks:
      apisix:

  etcd:
    image: rancher/coreos-etcd:v3.4.15-arm64
    user: root
    restart: always
    volumes:
      - ./etcd_data:/etcd-data
    environment:
      ETCD_UNSUPPORTED_ARCH: "arm64"
      ETCD_ENABLE_V2: "true"
      ALLOW_NONE_AUTHENTICATION: "yes"
      ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379"
      ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
      ETCD_DATA_DIR: "/etcd-data"
    ports:
      - "2379:2379/tcp"
    networks:
      apisix:

  web1:
    image: nginx:1.19.10-alpine
    restart: always
    volumes:
      - ./upstream/web1.conf:/etc/nginx/nginx.conf
    ports:
      - "9081:80/tcp"
    environment:
      - NGINX_PORT=80
    networks:
      apisix:

  web2:
    image: nginx:1.19.10-alpine
    restart: always
    volumes:
      - ./upstream/web2.conf:/etc/nginx/nginx.conf
    ports:
      - "9082:80/tcp"
    environment:
      - NGINX_PORT=80
    networks:
      apisix:

networks:
  apisix:
    driver: bridge

該 compose 裏面主要包含了 APISIX 的三個容器:apisix、etcd 以及 apisix-dashboard。現在我們就可以使用 docker-compose 來進行一鍵啓動:

➜ docker-compose -f docker-compose.yml up -d

另外兩個 nginx 容器是用於測試的:

請確保其他系統進程沒有佔用 9000、9080、9091、9092、9180、9443 和 2379 端口。如果啓動有錯誤,可以嘗試爲 examples 目錄設置成 777 權限,保證 etcd 數據有權限寫入。

當 APISIX 啓動完成後我們就可以通過 curl 來訪問正在運行的 APISIX 實例。比如,可以發送一個簡單的 HTTP 請求來驗證 APISIX 運行狀態是否正常。

➜ curl "http://127.0.0.1:9080" --head
HTTP/1.1 404 Not Found
Date: Tue, 21 Mar 2023 07:38:45 GMT
Content-Type: text/plain; charset=utf-8
Connection: keep-alive
Server: APISIX/3.2.0

現在,你已經成功安裝並運行了 APISIX !

功能測試

接下來我們來了解下 APISIX 的一些功能。在瞭解之前我們需要對 APISIX 的幾個主要概念和組件簡單瞭解下:

上游

Upstream 也稱爲上游,上游是對虛擬主機的抽象,即應用層服務或節點的抽象。

上游的作用是按照配置規則對服務節點進行負載均衡,它的地址信息可以直接配置到路由或服務上。當多個路由或服務引用同一個上游時,可以通過創建上游對象,在路由或服務中使用上游的 ID 方式引用上游,減輕維護壓力。

路由

Route 也稱爲路由,是 APISIX 中最基礎和最核心的資源對象。

APISIX 可以通過路由定義規則來匹配客戶端請求,根據匹配結果加載並執行相應的插件,最後把請求轉發給到指定的上游服務。路由中主要包含三部分內容:匹配規則、插件配置和上游信息。

服務

Service 也稱爲服務,是某類 API 的抽象(也可以理解爲一組 Route 的抽象)。它通常與上游服務抽象是一一對應的,Route 與 Service 之間,通常是 N:1 的關係。

消費者

Consumer 是某類服務的消費者,需要與用戶認證配合纔可以使用。當不同的消費者請求同一個 API 時,APISIX 會根據當前請求的用戶信息,對應不同的 Plugin 或 Upstream 配置。如果 Route、Service、Consumer 和 Plugin Config 都綁定了相同的插件,只有消費者的插件配置會生效。插件配置的優先級由高到低的順序是:Consumer > Route > Plugin Config > Service。

對於 API 網關而言,一般情況可以通過請求域名、客戶端 IP 地址等字段識別到某類請求方,然後進行插件過濾並轉發請求到指定上游。但有時候該方式達不到用戶需求,因此 APISIX 支持了 Consumer 對象。

插件

Plugin 也稱之爲插件,它是擴展 APISIX 應用層能力的關鍵機制,也是在使用 APISIX 時最常用的資源對象。插件主要是在 HTTP 請求或響應生命週期期間執行的、針對請求的個性化策略。插件可以與路由、服務或消費者綁定。

如果路由、服務、插件配置或消費者都綁定了相同的插件,則只有一份插件配置會生效,插件配置的優先級由高到低順序是:消費者 > 路由 > 插件配置 > 服務。同時在插件執行過程中也會涉及 6 個階段,分別是 rewriteaccessbefore_proxyheader_filterbody_filterlog

Admin API

APISIX 提供了強大的 Admin API 和 Dashboard 供用戶使用,Admin API 是一組用於配置 Apache APISIX 路由、上游、服務、SSL 證書等功能的 RESTful API。

我們可以通過 Admin API 來獲取、創建、更新以及刪除資源。同時得益於 APISIX 的熱加載能力,資源配置完成後 APISIX 將會自動更新配置,無需重啓服務,具體的架構原理可以查看下面的架構圖:

主要分爲兩個部分:

APISIX 在其核心中,提供了路由匹配、負載均衡、服務發現、API 管理等重要功能,以及配置管理等基礎性模塊。除此之外,APISIX 插件運行時也包含其中,提供原生 Lua 插件的運行框架和多語言插件的運行框架,以及實驗性的 Wasm 插件運行時等。APISIX 多語言插件運行時提供多種開發語言的支持,比如 Golang、Java、Python、JS 等。

APISIX 目前也內置了各類插件,覆蓋了 API 網關的各種領域,如認證鑑權、安全、可觀測性、流量管理、多協議接入等。當前 APISIX 內置的插件使用原生 Lua 實現,關於各個插件的介紹與使用方式,後續我們再介紹。

創建路由

下面的示例中我們先使用 Admin API 來創建一個 Route 並與 Upstream 綁定,當一個請求到達 APISIX 時,APISIX 會將請求轉發到指定的上游服務中。

以下示例代碼中,我們將爲路由配置匹配規則,以便 APISIX 可以將請求轉發到對應的上游服務:

➜ curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "methods": ["GET"],
  "host": "youdianzhishi.com",
  "uri": "/anything/*",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1
    }
  }
}' -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
# 正常會得到如下所示的結果
{"value":{"create_time":1679392758,"methods":["GET"],"host":"youdianzhishi.com","status":1,"priority":0,"update_time":1679392758,"upstream":{"pass_host":"pass","hash_on":"vars","type":"roundrobin","nodes":{"httpbin.org:80":1},"scheme":"http"},"id":"1","uri":"/anything/*"},"key":"/apisix/routes/1"}

其中的 X-API-KEY 的值在 APISIX 的配置文件中 apisix_config.yaml 中有配置,位於 deployment.admin.admin_key 下面。

該配置意味着,當請求滿足下述的所有規則時,請求將被轉發到上游服務(httpbin.org:80):

當路由創建完成後,現在我們就可以通過以下命令訪問上游服務了:

➜ curl -i -X GET "http://127.0.0.1:9080/anything/foo?arg=10" -H "Host: youdianzhishi.com"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 443
Connection: keep-alive
Date: Tue, 21 Mar 2023 08:25:49 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.2.0

{
  "args"{
    "arg""10"
  },
  "data""",
  "files"{},
  "form"{},
  "headers"{
    "Accept""*/*",
    "Host""youdianzhishi.com",
    "User-Agent""curl/7.85.0",
    "X-Amzn-Trace-Id""Root=1-64196a0d-1d2b654b29cbed3f7a9302c7",
    "X-Forwarded-Host""youdianzhishi.com"
  },
  "json": null,
  "method""GET",
  "origin""172.22.0.1, 221.11.206.200",
  "url""http://youdianzhishi.com/anything/foo?arg=10"
}

該請求將被 APISIX 轉發到 http://httpbin.org:80/anything/foo?arg=10,我們可以和直接訪問上游數據進行對比。

使用上游服務創建路由

我們還可以通過以下命令創建一個上游,並在路由中使用它,而不是直接將其配置在路由中:

➜ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" -X PUT -d '
{
  "type": "roundrobin",
  "nodes": {
    "httpbin.org:80": 1
  }
}' -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
# 正常會得到如下所示的輸出
{"value":{"type":"roundrobin","create_time":1679392818,"pass_host":"pass","hash_on":"vars","update_time":1679392818,"nodes":{"httpbin.org:80":1},"id":"1","scheme":"http"},"key":"/apisix/upstreams/1"}

該上游配置與上一節配置在路由中的上游相同。同樣使用了 roundrobin 作爲負載均衡機制,並設置了 httpbin.org:80 爲上游服務。爲了將該上游綁定到路由,此處需要把 upstream_id 設置爲 "1"。

上游服務創建完成後,現在我們可以通過以下命令將其綁定到指定的 /get 路由:

➜ curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "uri": "/get",
  "host": "httpbin.org",
  "upstream_id": "1"
}' -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
# 正常會得到如下所示的輸出
{"value":{"upstream_id":"1","status":1,"create_time":1679392758,"host":"httpbin.org","update_time":1679392834,"priority":0,"id":"1","uri":"/get"},"key":"/apisix/routes/1"}

我們已經創建了路由與上游服務,現在可以通過以下命令訪問上游服務:

➜ curl -i -X GET "http://127.0.0.1:9080/get?foo1=bar1&foo2=bar2" -H "Host: httpbin.org"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 370
Connection: keep-alive
Date: Tue, 21 Mar 2023 08:40:19 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.2.0

{
  "args"{
    "foo1""bar1",
    "foo2""bar2"
  },
  "headers"{
    "Accept""*/*",
    "Host""httpbin.org",
    "User-Agent""curl/7.85.0",
    "X-Amzn-Trace-Id""Root=1-64196d73-165daa124c362e5d4c6bb79d",
    "X-Forwarded-Host""httpbin.org"
  },
  "origin""172.22.0.1, 221.11.206.200",
  "url""http://httpbin.org/get?foo1=bar1&foo2=bar2"
}

同樣該請求也會被 APISIX 轉發到 http://httpbin.org:80/anything/foo?arg=10

使用 Dashboard

同樣我們還可以使用 APISIX Dashboard 創建和配置類似於上述步驟中所創建的路由。

如果你已經完成上述操作步驟,正常現在我們已經可以通過 localhost:9000 來訪問 APISIX Dashboard 了。

默認的用戶名和密碼均爲 admin,在 examples 目錄中 dashboard_conf 下面的 conf.yaml 進行配置:

authentication:
  secret: secret
  expire_time: 3600
  users:
    - username: admin
      password: admin
    - username: user
      password: user

上面我們的 docker-compose 中也已經啓動了 Grafana,所以登錄後我們也可以在首頁儀表盤上配置 Grafana,地址爲 http://localhost:3000

登錄後單擊側邊欄中的路由,可以查看已經配置的路由列表,可以看到在上述步驟中使用 Admin API 創建的路由。

你也可以通過單擊創建按鈕並按照提示創建新路由:

如果想利用 APISIX 實現身份驗證、安全性、限流限速和可觀測性等功能,可通過添加插件實現。

限流限速和安全插件

在很多時候,我們的 API 並不是處於一個非常安全的狀態,它隨時會收到不正常的訪問,一旦訪問流量突增,可能就會導致你的 API 發生故障,這個時候我們就可以通過速率限制來保護 API 服務,限制非正常的訪問請求。對此,我們可以使用如下方式進行:

APISIX 提供了多個內置的限流限速的插件,包括 limit-connlimit-countlimit-req

這裏我們就以 limit-count 插件爲例,來說明如何通過限流限速插件保護我們的 API 服務。如下所示。

使用下面的命令首先創建一條路由:

➜ curl -i http://127.0.0.1:9180/apisix/admin/routes/1 -X PUT -d '
{
    "uri": "/index.html",
    "plugins": {
        "limit-count": {
            "count": 2,
            "time_window": 60,
            "rejected_code": 503,
            "key_type": "var",
            "key": "remote_addr"
        }
    },
  "upstream_id": "1"
}' -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'

這裏我們直接使用前面已經創建的上游(ID 爲 1)來創建 / 更新一條路由,並且在 plugins 中啓用了 limit-count 插件,該插件僅允許客戶端在 60 秒內,訪問上游服務 2 次,超過兩次,就會返回 503 錯誤碼。

上面的指令執行成功後,接下來我們連續使用下面的命令訪問三次後,則會出現如下錯誤。

➜ curl http://127.0.0.1:9080/index.html
➜ curl http://127.0.0.1:9080/index.html
➜ curl http://127.0.0.1:9080/index.html

正常情況下就會出現如下所示的 503 錯誤,則表示 limit-count 插件已經配置成功。

<html>
  <head>
    <title>503 Service Temporarily Unavailable</title>
  </head>
  <body>
    <center><h1>503 Service Temporarily Unavailable</h1></center>
    <hr />
    <center>openresty</center>
    <p>
      <em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em>
    </p>
  </body>
</html>

緩存響應

當我們在構建一個 API 時,肯定希望他能夠儘量保持簡單和快速,一旦讀取相同數據的併發需求增加,可能會面臨一些問題,一般我們直接的辦法就是引入緩存,當然我們可以在不同層面去進行緩存的。

反向代理緩存是另一種緩存機制,通常在 API 網關內實現。它可以減少對你的端點的調用次數,也可以通過緩存上游的響應來改善對你的 API 請求的延遲。如果 API Gateway 的緩存中有所請求資源的新鮮副本,它就會使用該副本直接滿足請求,而不是向端點發出請求。如果沒有找到緩存的數據,請求就會轉到預定的上游服務(後端服務)。

我們這裏主要了解的是 API 網關層的緩存,也就是 APISIX 提供的 API 緩存,它也可以和其他插件一起使用,目前支持基於磁盤的緩存,也可以在插件配置中指定緩存過期時間或內存容量等。

比如我們現在有一個 /products 的 API 接口,通常每天只更新一次,而該端點每天都會收到重複的數十億次請求,以獲取產品列表數據,現在我們就可以使用 APISIX 提供的一個名爲 proxy-cache 的插件來緩存該接口的響應。

這裏我們還是使用前面 ID 爲 1 的上游對象,使用 /anything/products 來模擬產品接口,直接執行下面的命令來更新路由的插件:

➜ curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '{
  "name": "Route for API Caching",
  "methods": [
    "GET"
  ],
  "uri": "/anything/*",
  "plugins": {
    "proxy-cache": {
      "cache_key": [
        "$uri",
        "-cache-id"
      ],
      "cache_bypass": [
        "$arg_bypass"
      ],
      "cache_method": [
        "GET"
      ],
      "cache_http_status": [
        200
      ],
      "hide_cache_headers": true,
      "no_cache": [
        "$arg_test"
      ]
    }
  },
  "upstream_id": "1"
}'

更新完成後現在我們來對該接口發起幾次請求:

➜ curl http://localhost:9080/anything/products -i

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 398
Connection: keep-alive
Date: Tue, 21 Mar 2023 10:48:42 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.2.0
Apisix-Cache-Status: MISS

➜ curl http://localhost:9080/anything/products -i

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 398
Connection: keep-alive
Date: Tue, 21 Mar 2023 10:48:42 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.2.0
Apisix-Cache-Status: HIT

正常每次都應該收到 HTTP 200 OK 響應,但是第一次響應中的 Apisix-Cache-Status 顯示爲 MISS,這意味着當請求第一次進入路由時,響應還沒有被緩存。而後面的幾次請求會得到一個緩存的響應,緩存指標變爲了 HIT,表示我們的響應緩存成功了。

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