Apisix 1.5 升級到 2.2 踩坑備忘
零、前言
線上運行的 APISIX 為 1.5 版本,而社區(qū)已經(jīng)發(fā)布了 Apisix 2.2,是時候需要升級到最新版了,能夠享受最版本帶來的大量的BugFix,性能增強(qiáng),以及新增特性的支持等~
從Apisix 1.5升級到Apisix 2.2過程中,不是一帆風(fēng)順的,中間踩了不少坑,所謂前車之鑒后事之師,這里給大家簡單梳理一下我們團(tuán)隊所在具體業(yè)務(wù)環(huán)境下,升級過程中踩的若干坑,以及一些需要避免的若干注意事項等。
下文所說原先版本,皆指Apisix 1.5,新版則是Apisix 2.2版本。
一、已有服務(wù)發(fā)現(xiàn)機(jī)制無法正常工作
針對上游Upstream沒有使用服務(wù)發(fā)現(xiàn)的路由來講,本次升級沒有遇到什么問題。
公司內(nèi)部線上業(yè)務(wù)大都基于Consul KV方式實現(xiàn)服務(wù)注冊和服務(wù)發(fā)現(xiàn),因此我們自行實現(xiàn)了一個 consul_kv.lua
模塊實現(xiàn)服務(wù)發(fā)現(xiàn)流程。
這在Apisix 1.5下面一切工作正常。
但在Apisix 2.2下面,就無法直接工作了,原因如下:
- 服務(wù)發(fā)現(xiàn)配置指令變了
- 上游對象包含服務(wù)發(fā)現(xiàn)時需增加字段
discovery_type
進(jìn)行索引
2.1 服務(wù)發(fā)現(xiàn)配置指令變了
原先運行中僅支持一種服務(wù)發(fā)現(xiàn)機(jī)制,需要配置在 apisix
層級下面:
apisix:
......
discover: consul_kv
......
新版需要直接在config*.yaml
文件中頂層層級下進(jìn)行配置,可支持多種不同的路由發(fā)現(xiàn)機(jī)制,如下:
discovery: # service discovery center
eureka:
host: # it's possible to define multiple eureka hosts addresses of the same eureka cluster.
- "http://127.0.0.1:8761"
prefix: "/eureka/"
fetch_interval: 30 # default 30s
weight: 100 # default weight for node
timeout:
connect: 2000 # default 2000ms
send: 2000 # default 2000ms
read: 5000
我們有所變通,直接在配置文件頂層配置consul_kv多個集群相關(guān)參數(shù),避免 discovery
層級過深。
discovery:
consul_kv: 1
consul_kv:
servers:
-
host: "172.19.5.30"
port: 8500
-
host: "172.19.5.31"
port: 8500
prefix: "upstreams"
timeout:
connect: 6000
read: 6000
wait: 60
weight: 1
delay: 5
connect_type: "long" # long connect
......
當(dāng)然,這僅僅保證了服務(wù)發(fā)現(xiàn)模塊能夠在啟動時被正常加載。
推薦閱讀:
2.2 upstream對象新增字段discovery_type
Apisix當(dāng)前同時支持多種服務(wù)發(fā)現(xiàn)機(jī)制,這個很贊。對應(yīng)的代價,就是需要額外引入 discovery_type
字段,用于索引可能同時存在的多個服務(wù)發(fā)現(xiàn)機(jī)制。
以 Cousul KV方式服務(wù)發(fā)現(xiàn)為例,那么需要在已有的 upstream
對象中需要添加該字段:
"discovery_type" : "consul_kv"
原先的一個upstream
對象,僅僅需要 service_name
字段屬性指定服務(wù)發(fā)現(xiàn)相關(guān)地址即可:
{
"id": "d6c1d325-9003-4217-808d-249aaf52168e",
"name": "grpc_upstream_hello",
......
"service_name": "http://172.19.5.30:8500/v1/kv/upstreams/grpc/grpc_hello",
"create_time": 1610437522,
"desc": "demo grpc service",
"type": "roundrobin"
}
而新版的則需要添加discovery_type
字段,表明該service_name
字段對應(yīng)的具體模塊名稱,效果如下:
{
"id": "d6c1d325-9003-4217-808d-249aaf52168e",
"name": "grpc_upstream_hello",
......
"service_name": "http://172.19.5.30:8500/v1/kv/upstreams/grpc/grpc_hello",
"create_time": 1610437522,
"desc": "demo grpc service",
"type": "roundrobin",
"discovery_type":"consul_kv"
}
后面我們?nèi)糁С諧onsul Service或ETCD KV方式服務(wù)發(fā)現(xiàn)機(jī)制,則會非常彈性和清晰。
調(diào)整了配置指令,添加上述字段之后,后端服務(wù)發(fā)現(xiàn)其實就已經(jīng)起作用了。
但gRPC代理路由并不會生效……
二、gRPC當(dāng)前不支持upstream_id
在我們的系統(tǒng)中,上游和路由是需要單獨分開管理的,因此創(chuàng)建的HTTP或GRPC路由需要處理支持upstream_id
的索引。
這在1.5版本中,grpc路由是沒問題的,但到了apisix 2.2版本中,維護(hù)者 @spacewander
暫時沒做支持,原因是規(guī)劃grpc路由和dubbo路由處理邏輯趨于一致,更為緊湊。從維護(hù)角度我是認(rèn)可的,但作為使用者來講,這就有些不合理了,直接丟棄了針對以往數(shù)據(jù)的支持。
作為當(dāng)前Geek一些方式,在 apisix/init.lua
中,最小成本 (優(yōu)雅和成本成反比)修改如下,找到如下代碼:
-- todo: support upstream id
api_ctx.matched_upstream = (route.dns_value and
route.dns_value.upstream)
or route.value.upstream
直接替換為下面代碼即可解決燃眉之急:
local up_id = route.value.upstream_id
if up_id then
local upstreams = core.config.fetch_created_obj("/upstreams")
if upstreams then
local upstream = upstreams:get(tostring(up_id))
if not upstream then
core.log.error("failed to find upstream by id: " .. up_id)
return core.response.exit(502)
end
if upstream.has_domain then
local err
upstream, err = lru_resolved_domain(upstream,
upstream.modifiedIndex,
parse_domain_in_up,
upstream)
if err then
core.log.error("failed to get resolved upstream: ", err)
return core.response.exit(500)
end
end
if upstream.value.pass_host then
api_ctx.pass_host = upstream.value.pass_host
api_ctx.upstream_host = upstream.value.upstream_host
end
core.log.info("parsed upstream: ", core.json.delay_encode(upstream))
api_ctx.matched_upstream = upstream.dns_value or upstream.value
end
else
api_ctx.matched_upstream = (route.dns_value and
route.dns_value.upstream)
or route.value.upstream
end
三、自定義auth插件需要微調(diào)
新版的apisix auth授權(quán)插件支持多個授權(quán)插件串行執(zhí)行,這個功能也很贊,但此舉導(dǎo)致了先前為具體業(yè)務(wù)定制的授權(quán)插件無法正常工作,這時需要微調(diào)一下。
原先調(diào)用方式:
local consumers = core.lrucache.plugin(plugin_name, "consumers_key",
consumer_conf.conf_version,
create_consume_cache, consumer_conf)
因為新版的lrucache
不再提供 plugin
函數(shù),需要微調(diào)一下:
local lrucache = core.lrucache.new({
type = "plugin",
})
......
local consumers = lrucache("consumers_key", consumer_conf.conf_version,
create_consume_cache, consumer_conf)
另一處是,順利授權(quán)之后,需要賦值consumer
相關(guān)信息:
ctx.consumer = consumer
ctx.consumer_id = consumer.consumer_id
此時需要替換成如下方式,為(可能存在的)后續(xù)的授權(quán)插件繼續(xù)作用。
consumer_mod.attach_consumer(ctx, consumer, consumer_conf)
更多請參考:apisix/plugins/key-auth.lua
源碼。
四、ETCD V2數(shù)據(jù)遷移到V3
遷移分為三步:
- 升級線上已有ETCD 3.3.*版本到3.4.*,滿足新版Apisix的要求,這時ETCD實例同時支持了V2和V3格式數(shù)據(jù)
- 遷移V2數(shù)據(jù)到V3
- 因為數(shù)據(jù)量不是非常多,我采取了一個非常簡單和原始的方式
- 使用 etcdctl 完成V2數(shù)據(jù)到導(dǎo)出
- 然后使用文本編輯器vim等完成數(shù)據(jù)的替換,生成etcdctl v3格式的數(shù)據(jù)導(dǎo)入命令腳本
- 運行之后V3數(shù)據(jù)導(dǎo)入腳本,完成V2到V3的數(shù)據(jù)導(dǎo)入
- 修改V3
/apisix/upstreams
中包含服務(wù)注冊的數(shù)據(jù),一一添加"discovery_type" : "consul_kv"
屬性
基于以上操作之后,從而完成了ETCD V2到V3的數(shù)據(jù)遷移。
五、啟動apisix后發(fā)現(xiàn)ETCD V3已有數(shù)據(jù)無法加載
我們在運維層面,使用 /usr/local/openresty/bin/openresty -p /usr/local/apisix -g daemon off;
方式運行網(wǎng)關(guān)程序。
這也就導(dǎo)致,自動忽略了官方提倡的:apisix start
命令自動提前為ETCD V3初始化的一些鍵值對內(nèi)容。
因此,需要提前為ETCD V3建立以下鍵值對內(nèi)容:
Key Value
/apisix/routes : init_dir
/apisix/upstreams : init_dir
/apisix/services : init_dir
/apisix/plugins : init_dir
/apisix/consumers : init_dir
/apisix/node_status : init_dir
/apisix/ssl : init_dir
/apisix/global_rules : init_dir
/apisix/stream_routes : init_dir
/apisix/proto : init_dir
/apisix/plugin_metadata : init_dir
不提前建立的話,就會導(dǎo)致apisix重啟后,無法正常加載ETCD中已有數(shù)據(jù)。
其實有一個補(bǔ)救措施,需要修改 apisix/init.lua
內(nèi)容,找到如下代碼:
if not dir_res.nodes then
dir_res.nodes = {}
end
比較geek的行為,使用下面代碼替換一下即可完成兼容:
if dir_res.key then
dir_res.nodes = { clone_tab(dir_res) }
else
dir_res.nodes = {}
end
六、apisix-dashboard的支持
我們基于apisix-dashboard定制開發(fā)了大量的針對公司實際業(yè)務(wù)非常實用的企業(yè)級特性,但也導(dǎo)致了無法直接升級到最新版的apisix-dashboard。
因為非常基礎(chǔ)的上游和路由沒有發(fā)生多大改變,因此這部分升級的需求可以忽略。
實際上,只是在提交上游表單時,包含服務(wù)注冊信息JSON字符串中需要增加 discovery_type
字段和對應(yīng)值即可完成支持。
七、小結(jié)
花費了一些時間完成了從Apisix 1.5升級到Apisix 2.2的行為,雖然有些坑,但整體來講,還算順利。目前已經(jīng)上線并全量部署運行,目前運行良好。
針對還停留在Apisix 1.5的用戶,新版增加了Control API以及多種服務(wù)發(fā)現(xiàn)等新特性支持,還是非常值得升級的。
升級之前,不妨仔細(xì)閱讀每一個版本的升級日志(地址:https://github.com/apache/apisix/blob/2.2/CHANGELOG.md ),然后需要根據(jù)具體業(yè)務(wù)做好兼容測試準(zhǔn)備和準(zhǔn)備升級步驟,這些都是非常有必要的。
針對我們團(tuán)隊來講,升級到最新版,一方面降低了版本升級的壓力,另一方面也能夠輔助我們能參與到開源社區(qū)中去,挺好~
posted on 2021-02-23 14:57 nieyong 閱讀(3036) 評論(0) 編輯 收藏 所屬分類: HTTP 、移動后端