雲原生API閘道器全生命週期管理Apache APISIX探究實操

2023-03-17 06:01:18

@

概述

定義

Apache APISIX官網地址 https://apisix.apache.org/ 最新版本3.2.0

Apache APISIX官網檔案地址 https://apisix.apache.org/docs/apisix/getting-started/

Apache APISIX原始碼地址 https://github.com/apache/apisix

Apache APISIX 是一個動態、實時、高效能、可延伸的雲原生 API 閘道器,提供瞭如負載均衡、動態上游、灰度釋出(金絲雀釋出)、服務熔斷、身份認證、可觀測性等豐富的流量管理功能。可以使用APISIX API Gateway來處理傳統的南北向流量,也可以用來處理服務之間的東西向流量;還可以用作k8s入口控制器。

Apache APISIX是第一個包含內建低程式碼儀表板的開源API閘道器,它為開發人員提供了強大而靈活的UI;雲原生時代,動態和可觀測性成為衡量 API 閘道器的標準之一;Apache APISIX 自誕生之初就一直跟隨著雲原生的腳步前行。

備註:南北向流量可以簡單理解使用者端到伺服器端,也即是我們傳統上使用Nginx;東西向流量更多的是後臺微服務之間的通訊。

NGINX 與 Kong 的痛點

在單體服務時代,使用 NGINX 可以應對大多數的場景,而到了雲原生時代,NGINX 因為其自身架構的原因則會出現兩個問題:

  • 首先是 NGINX 不支援叢集管理。幾乎每家網際網路廠商都有自己的 NGINX 設定管理系統,系統雖然大同小異但是一直沒有統一的方案。
  • 其次是 NGINX 不支援設定的熱載入。很多公司一旦修改了設定,重新載入 NGINX 的時間可能需要半個小時以上。並且在 Kubernetes 體系下,上游會經常發生變化,如果使用 NGINX 來處理就需要頻繁重啟服務,這對於企業是不可接受的。

而 Kong 的出現則解決了 NGINX 的痛點,但是又帶來了新的問題:

  • Kong 需要依賴於 PostgreSQL 或 Cassandra 資料庫,這使 Kong 的整個架構非常臃腫,並且會給企業帶來高可用的問題。如果資料庫故障了,那麼整個 API 閘道器都會出現故障。
  • Kong 的路由使用的是遍歷查詢,當閘道器內有超過上千個路由時,它的效能就會出現比較急劇的下降。

APISIX 的技術優勢

而 APISIX 的出現則解決了上述所有問題,成為了雲原生時代最完美的 API 閘道器。Apache APISIX 和 Kong相比在技術方面的主要優勢大部分都是在底層模組上的優化和創新;在簡單的 PoC 上並不一定能夠體現出這些技術的優勢,但在複雜的生產環境中,Apache APISIX 的這些優勢將會造成巨大的差距。

  • 無資料庫依賴:在 APISIX 專案出現之前,也有非常多的商業 API 閘道器或開源 API 閘道器產品,但這些產品大多數都把 API 資料、路由、證書和設定等資訊存放在一個關係型資料庫中。閘道器作為一個基礎中介軟體處理了所有來自使用者端的流量,因此對於可用性的要求便會非常高。如果API 閘道器依賴了一個關係型資料庫,也就意味著關係型資料庫一旦出現了故障(比如宕機、丟失資料),API 閘道器也會因此受到影響,整個業務系統的可用性也會大打折扣。
  • 高效能路由匹配演演算法和 IP 匹配演演算法:Apache APISIX 使用的是 RadixTree,它提供了 KV 儲存查詢的資料結構並對只有一個子節點的中間節點進行了壓縮,因此它又被稱為壓縮字首樹;高效且支援模糊匹配的搜尋資料結構。

特性

Apache APISIX的特性較多,下面只是列出部分特性和說明。

  • 平臺支援:APISIX 提供了平臺級解決方案,不但支援裸機執行,也支援在 Kubernetes 中雲原生使用。

  • 眾多協定支援:如TCP/UDP Proxy、Dubbo Proxy、Dynamic MQTT Proxy、gRPC proxy、HTTP(S) Forward Proxy等

  • 全動態能力

    • Apache APISIX提供了熱更新和熱外掛,可以在不重新啟動的情況下持續更新設定,節省了開發時間和壓力。
    • 健康檢查:開啟上游節點健康檢查功能,負載均衡時自動過濾不健康節點,保證系統穩定。
    • 斷路器:智慧跟蹤不健康的上游服務。
    • 流量分流:可以在不同的上游之間增量地引導流量的百分比。
  • 精細化路由

    • Apache APISIX Gateway提供了編寫自己的自定義外掛的能力,在平衡器階段使用自定義負載平衡演演算法進行擴充套件,自定義路由演演算法用於對路由進行精細控制;支援全路徑匹配和字首匹配。
    • 支援所有Nginx內建變數作為路由條件,可以使用cookie, args等作為路由條件來實現金絲雀釋出,A/B測試。
    • 支援自定義路由匹配功能。
  • 安全

    • 豐富的身份驗證和授權支援。Apache APISIX Gateway提供了多個用於身份驗證和API驗證的安全外掛,包括CORS、JWT、Key Auth、OpenID Connect (OIDC)、Keycloak等。
    • IP黑白名單、Referer黑白名單。
    • Anti-ReDoS(正規表示式拒絕服務):無需設定,內建防ReDoS策略。
    • 防CSRF攻擊。
  • 運維友好

    • 支援與多種工具和平臺如Zipkin、SkyWalking、Consul、Nacos、Eureka、Zookeeper、Prometheus、HashiCorp Vault等整合。
    • 通過使用APISIX Dashboard,運維人員可以通過友好且直觀的 UI 設定 APISIX。
    • 版本控制:支援操作回滾。
    • 命令列:通過命令列啟動/停止/重新載入APISIX。
  • 高可延伸

    • 自定義外掛:允許掛載常見階段,如重寫,存取,頭過濾器,主體過濾器和紀錄檔。外掛可用Java/Go/Python編寫,也可用代理Wasm SDK編寫。
    • 自定義負載均衡演演算法:可以在均衡器階段使用自定義負載均衡演演算法。
    • 自定義路由:支援使用者自行實現路由演演算法。
  • 多語言支援:Apache APISIX是一個用於外掛開發的多語言閘道器,並通過RPC和Wasm提供支援。

  • Serverless:支援與Lua函數、AWS Lambda、Azure Functions、Apache OpenWhisk等雲服務整合。

架構

Apache APISIX基於NGINX和etcd。與傳統的API閘道器相比,APISIX具有動態路由、熱載入外掛等特性。

APISIX 的架構主要分成如下兩部分「

  • 資料面:真正去處理來自使用者端請求的一個元件,去處理使用者的真實流量,包括像身份驗證、證書解除安裝、紀錄檔分析和可觀測性等功能。資料面本身並不會儲存任何資料,所以它是一個無狀態結構。
  • 控制面:APISIX 在控制面上並沒有使用傳統的類似於像 MySQL 去做設定儲存,而是選擇使用 etcd。這樣做的好處在於:
    • 與產品架構的雲原生技術體系更統一
    • 更貼合 API 閘道器存放的資料型別
    • 能更好地體現高可用特性
    • 擁有低於毫秒級別的變化通知

應用場景

APISIX 目標是統一代理基礎設施,其核心為高效能代理服務,自身不繫結任何環境屬性。當它演變為 Ingress、服務網格等產品時,都是外部服務與 APISIX 配合,變化的是外部程式而不是 APISIX 自身。

  • Load Balancer 和 API 閘道器:針對傳統的 LB 和 API 閘道器場景, APISIX 基於 NGINX + LuaJIT 實現天然具備高效能、安全等特性,並且原生支援了動態 SSL 證書解除安裝、SSL 握手優化等功能,在負載均衡的服務能力上也更優秀。從 NGINX 切換到 APISIX 不僅效能不會下降,而且可以享受到動態、統一管理等特性帶來的管理效率的提升。
  • 微服務閘道器:APISIX 目前支援多種語言編寫擴充套件外掛,可以解決東西向微服務 API 閘道器面臨的主要問題——異構多語言和通用問題。內建支援的服務註冊中心有 Nacos、etcd、Eureka 等,還有標準的 DNS 方式,可以平滑替代 Zuul、Spring Cloud Gateway、Dubbo 等微服務 API 閘道器。
  • Kubernetes Ingress: K8s 官方 Kubernetes Ingress Controller 專案主要基於 NGINX 組態檔的方式,所以在路由能力和載入模式上稍顯不足,並且存在一些明顯劣勢。比如新增、修改任何 API 時,需要重啟服務才能完成新 NGINX 設定的更新,但重啟服務,對線上流量的影響是非常大的。而 APISIX Ingress Controller 則完美解決了上面提到的所有問題:支援全動態,無需重啟載入。同時繼承了 APISIX 的所有優勢,還支援原生 Kubernetes CRD,方便使用者遷移。

  • 服務網格:APISIX 也提前基於雲原生模式架構下的服務網格架構,通過調研和技術分析後,APISIX 已經支援了 xDS 協定,APISIX Mesh 就此誕生,在服務網格領域 APISIX 也擁有了一席之地。

主要概念

APISIX 主要分為兩個部分:

  1. APISIX 核心:包括 Lua 外掛、多語言外掛執行時(Plugin Runner)、Wasm 外掛執行時等;
  2. 功能豐富的各種內建外掛:包括可觀測性、安全、流量控制等。

概念/元件 描述
Route 通過路由定義規則來匹配使用者端請求,根據匹配結果載入並執行相應的外掛,最後把請求轉發給到指定的上游應用。
Upstream 上游的作用是按照設定規則對服務節點進行負載均衡,它的地址資訊可以直接設定到路由或服務上。
Admin API 使用者可以通過 Admin API 控制 APISIX 範例。

外掛載入流程

外掛內部結構

部署

快速入門

quickstart安裝

# quickstart 指令碼快速安裝並啟動,該命令在本地安裝並執行了基於 Docker 的 APISIX 和 etcd 容器,其中 APISIX 採用 etcd 儲存和同步設定資訊。APISIX 和 etcd 容器使用 host 的 Docker 網路模式,因此可以從本地直接存取。
curl -sL https://run.api7.ai/apisix/quickstart | sh
# 請確保其他系統程序沒有佔用 9080、9180、9443 和 2379 埠。可以通過 curl 來存取正在執行的 APISIX 範例。比如,你可以傳送一個簡單的 HTTP 請求來驗證 APISIX 執行狀態是否正常。
curl "http://127.0.0.1:9080" --head | grep Server

Admin API建立路由

# 以httpbin.org這個簡單的HTTP請求和響應服務作為演示,詳細可以直接查閱http://httpbin.org/,使用httpbin.org測試介面來體驗下,如果希望這個介面在本地執行,可採用docker部署,docker Run -p 80:80 kennethreitz/httpbin
curl --location --request GET "http://httpbin.org/get?param1=value1&param2=value2"

  • 建立路由,使用 Admin API 建立一個 Route並與Upstream繫結,當一個請求到達 APISIX 時,APISIX 會將請求轉發到指定的上游服務中。
curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "methods": ["GET"],
  "host": "mytest.com",
  "uri": "/anything/*",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1
    }
  }
}'

# 當路由建立完成後,可以通過以下命令存取上游服務,該請求將被 APISIX 轉發到 http://httpbin.org:80/anything/foo?arg=10
curl -i -X GET "http://127.0.0.1:9080/anything/foo?arg=10" -H "Host: mytest.com"

也可以設定hosts對映,然後在瀏覽器直接存取http://mytest.com:9080/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
  }
}'

然後在通過以下命令繫結到指定路由

curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "uri": "/get",
  "host": "httpbin.org",
  "upstream_id": "1"
}'

可以通過下面命令存取上游服務,該請求將被 APISIX 轉發到 http://httpbin.org:80/anything/foo?arg=10

curl -i -X GET "http://127.0.0.1:9080/get?foo1=bar1&foo2=bar2" -H "Host: httpbin.org"

RPM安裝

安裝etcd

APISIX使用etcd儲存和同步設定,在安裝APISIX之前需要安裝etcd。

ETCD_VERSION='3.5.7'
wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
tar -xvf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
cd etcd-v${ETCD_VERSION}-linux-amd64
cp -a etcd etcdctl /usr/bin/
nohup etcd >/tmp/etcd.log 2>&1 &

RPM安裝

APISIX RPM安裝適用於CentOS 7和CentOS 8

# 如果沒有安裝OpenResty,可執行以下命令同時安裝OpenResty和APISI倉庫yum install -y https://repos.apiseven.com/packages/centos/apache-apisix-repo-1.0-1.noarch.rpm# 如果安裝了OpenResty,下面的命令將安裝APISIX儲存庫:yum-config-manager --add-repo https://repos.apiseven.com/packages/centos/apache-apisix.repo# 安裝APISIXyum install apisix# 還可以通過指定APISIX來安裝特定版本的APISIXyum install apisix-3.1.0# 管理APISIX伺服器,安裝APISIX後,可以通過以下命令初始化組態檔和etcdapisix init# 檢查組態檔是否爭取apisix test

# 啟動apisixapisix start# 正常關閉apisix,確保停止之前完成所有收到的請求apisix quit# 強制關閉apisix並丟棄所有請求apisix stop

設定apisix

更改組態檔,路徑在/usr/local/apisix/conf/config.yaml

# 在啟動APISIX時,通過使用--config或-c標誌將路徑傳遞給組態檔,APISIX將使用此組態檔中新增的設定,如果沒有設定任何內容,則將退回到預設設定。apisix start -c <path to config file>

警告注意:

  • APISIX的預設設定可以在conf/config-default.yaml中找到,這個檔案不應該被修改,它與原始碼繫結,只能通過上面提到的config.yaml更改設定。
  • conf/nginx.conf檔案是自動生成的,不可修改。

建議修改Admin API金鑰以保證安全性,修改conf/config.yaml,修改後重啟apisix

deployment:  admin:    admin_key      -        name: "admin"        key: edd1c9f034335f136f87ad84b625c123        role: admin      
# 存取Admin API,你可以使用新的密碼curl http://127.0.0.1:9180/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c123 -i

Docker安裝

docker-compose安裝

# docker
git clone https://github.com/apache/apisix-docker.git
# 進入目錄
cd apisix-docker/example
# 可以檢視和修改檔案,這裡將apisix-dashboard暴露本機埠改為19000
vim docker-compose.yml
# 使用docker-compose啟動APISIX
docker-compose -p docker-apisix up -d

存取http://hadoop2:19000,輸入使用者名稱密碼admin/admin即可登入成功(密碼設定在docker-compose指定的組態檔dashboard_conf/conf.yaml中)

apisix-dashboard簡單使用

儀表板

dashboard 視覺化 WEB 控制檯,可以很直觀的進行 router 、upstream 等設定。登入後的首頁即為儀表板,這是通過在iframe中參照監視器頁面來支援它可以設定grafana的地址,比如設定http://grafanaip:3000,快捷操作grafana

設定測試上游

# 現在本地搭建httpbin服務
docker run -p 5080:80 kennethreitz/httpbin

點選上游選單,點選建立按鈕,目標節點新增httpbin.org和上面我們剛部署內網httpbin,其他暫時預設,點選下一步然後提交。

設定測試路由

點選路由選單,點選建立按鈕,目標節點新增httpbin.org和上面我們剛部署內網httpbin,其他暫時預設,點選下一步然後提交。

設定上游服務選擇前面建立的,其他先保持預設,最後提交

執行存取測試

curl -i -X GET "http://127.0.0.1:9080/anything/foo?arg=10"

整合Nacos

  • 先準備演示訂單微服務並向Nacos註冊,關於Nacos可翻看前面的文章

  • 在apisix 的conf/config.yaml中新增以下設定,由於這裡是用docker部署,路徑在apisix-docker/example/apisix_conf
discovery:
  nacos:
    host:
      - "http://nacos:[email protected]:8848"
    prefix: "/nacos/v1/"
    fetch_interval: 30    # default 30 sec
    weight: 100           # default 100
    timeout:
      connect: 2000       # default 2000 ms
      send: 2000          # default 2000 ms
      read: 5000          # default 5000 ms
  • 組態檔修改後重啟apisix
docker-compose -p docker-apisix down
docker-compose -p docker-apisix up -d
  • 在新增order上游,輸入服務型別資訊點選下一步和提交

新增order路由,輸入路徑資訊點選下一步

路由中選擇前面新增order上游,其他預設最終提交

存取apisix監聽地址,http://hadoop2:9080/order/add,最後成功返回訂單微服務的介面資料

開啟SkyWalking外掛鏈路追蹤

  • 部署skywalking,詳細可以翻看前面關於skywalking的文章
  • 同樣在組態檔新增以下設定
plugins:
  - skywalking          # 啟用skywalking外掛
  
plugin_attr:
  skywalking: # 設定skywalking的屬性
    service_name: APISIX_GATEWAY
    service_instance_name: "APISIX_INSTANCE_GATEWAY"
    endpoint_addr: http://192.168.3.113:12800  # skywalking的地址,本機預設是這個,可自行修改

  • 組態檔修改後重啟apisix
  • 在前面建立的order路由中開啟SkyWalking外掛,點選可觀察性找到skywalking點選啟動按鈕

在彈出的頁面的外掛設定中點選啟用按鈕然後提交,然後一直下一步最後提交

返回路由列表也可以通過操作-更多-檢視,顯示資料編輯器內容

  • 在訂單微服務中新增依賴
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>8.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>8.14.0</version>
        </dependency>

在logback-spring.xml新增列印skywalking鏈路紀錄檔資訊

<configuration debug="false" scan="false">
	<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
	<property name="log.path" value="logs/${spring.application.name}"/>
	<!-- 彩色紀錄檔格式 -->
	<property name="CONSOLE_LOG_PATTERN"
			  value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
	<!-- 彩色紀錄檔依賴的渲染類 -->
	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
	<conversionRule conversionWord="wex"
					converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
	<conversionRule conversionWord="wEx"
					converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
	<!-- Console log output -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
		</encoder>
	</appender>

	<!-- Log file debug output -->
	<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${log.path}/debug.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
			<maxFileSize>50MB</maxFileSize>
			<maxHistory>30</maxHistory>
		</rollingPolicy>
		<encoder>
			<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
		</encoder>
	</appender>

	<!-- Log file error output -->
	<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${log.path}/error.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
			<maxFileSize>50MB</maxFileSize>
			<maxHistory>30</maxHistory>
		</rollingPolicy>
		<encoder>
			<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
		</encoder>
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>ERROR</level>
		</filter>
	</appender>
	<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
			<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
				<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
			</layout>
		</encoder>
	</appender>

	<appender name="grpc" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
			<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
				<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
			</layout>
		</encoder>
	</appender>
	<!--nacos 心跳 INFO 遮蔽-->
	<logger name="com.alibaba.nacos" level="OFF">
		<appender-ref ref="error"/>
	</logger>

	<!-- Level: FATAL 0  ERROR 3  WARN 4  INFO 6  DEBUG 7 -->
	<root level="INFO">
    <!-- <appender-ref ref="console"/>-->
		<appender-ref ref="debug"/>
		<appender-ref ref="error"/>
		<appender-ref ref="stdout"/>
		<appender-ref ref="grpc"/>
	</root>
</configuration>

將agent.config 拷貝到訂單微服務的resources目錄下,庫存微服務修改agent.config下面兩個設定,詳細查閱skywalking前面的文章

agent.service_name=${SW_AGENT_NAME:ecom-storage-service}
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.3.113:11800}
# 啟動jvm引數增加
-javaagent:F:\commoms\skywalking-agent\skywalking-agent.jar
-Dskywalking_config=F:\dev\simple-ecommerce\ecom-storage-service\src\main\resources\agent.config

存取http://hadoop2:9080/order/add多次看下,在普通服務就可以看到訂單微服務和APISIX_GATEWAY。

通過Trace可以看到紀錄檔鏈路經過了APISIX_GATEWAY和訂單微服務,至此基本整合演示完畢

  • 本人部落格網站IT小神 www.itxiaoshen.com