今天的目的是想要总结一下在 k8s 集群中部署 APISIX 的经验吧。
有这么一个需求,是因为在最新的负载均衡需求评审会议上,我们需要直接同步 consul 和 APISIX 的 upstream 信息。这是因为 APISIX 目前没有对 consul 服务发现的比较好的实现方式,于是,项目组决定手动创建一个新的 consul 同步助手类似的项目来实现这个功能。
选择什么样的集群方案呢?
又是一个提问,我们既然知道了目的,那该确定下验证的方式了。
目前可选的 k8s 集群操作方式非常多,此处稍微列举一下:
- kind 集群;
- 组里三个 node(182、183、184)组成的 oci 为 containerd 的集群;
- 公司申请的开发集群(tke);
- 公司申请的测试集群(tke-kafka);
- docker-desktop 的 k8s-local(也是最终选择的方案)。
kind 集群
我没有试过,因为 kind 做出来的集群总的来说是会有网络上边的问题,所以我压根都没想过去试一下。
dev-ops Cluster
就是我之前搭建的集群,dev-ops Cluster,由 182、183、184 三个 IP 的 centos 服务器搭建而成。
遇到的问题如下所示:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 10m (x72 over 6h10m) default-scheduler 0/3 nodes are available: 3 pod has unbound immediate PersistentVolumeClaims. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
可以看到是 PVC 没有搭建好,导致我的 etcd 一直处于 pending 的状态,如下所示:
➜ ~ kbgp -nsa -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
apisix-7589ccf997-phngd 0/1 Init:0/1 0 6h12m 10.244.2.24 opsdev-184 <none> <none>
apisix-etcd-0 0/1 Pending 0 6h12m <none> <none> <none> <none>
apisix-etcd-1 0/1 Pending 0 6h12m <none> <none> <none> <none>
apisix-etcd-2 0/1 Pending 0 6h12m <none> <none> <none> <none>
果断换方案。
123-2 集群,开发集群
这个是负载均衡项目申请的开发集群,是 tke 的。
当我部署 APISIX 的时候,同样出现了 PVC 问题,问了相关责任人,得到的回复是,腾讯云的 PVC 服务需要额外花钱。
123-1 集群,测试集群
同开发集群遇到的问题一样。
docker desktop 自备的 k8s 集群(最终选择方案)
成功部署成功,如下图所示:
➜ ~ kbgp -nsa -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
apisix-5bbb87858f-mxjn2 1/1 Running 0 4h44m 10.1.0.39 docker-desktop <none> <none>
apisix-etcd-0 1/1 Running 1 (4h42m ago) 4h42m 10.1.0.42 docker-desktop <none> <none>
apisix-etcd-1 1/1 Running 1 (4h43m ago) 4h43m 10.1.0.41 docker-desktop <none> <none>
apisix-etcd-2 1/1 Running 1 (4h44m ago) 4h44m 10.1.0.40 docker-desktop <none> <none>
正式部署 APISIX
此处会有一些坑,让我娓娓道来。
首先,我有两种方案去部署 APISIX:
- 使用集群,使用 APISIX 的 helm charts 包来进行安装;
- 使用 docker 部署安装。
使用 Docker 部署安装
我只能选择在本地的 Docker 中进行部署安装。
最初我使用 brew install etcd
安装好 etcd 之后,然后准备让 APISIX 容器,连接到宿主机上的 etcd 服务。
但是一直没有找到相关的办法,从 Docker 官方文档中也没有找到如果在 macos 系统中,让容器连接到宿主机的服务。
其中我好像找到了两种方法。
-
openvpn 进行网络的重新搭建;
-
使用类似于
host.docker.internal
之类的端口,从容器内部访问宿主机的服务,具体的 demo 例子可以查看这个link.-
首先部署 busybox,命令如下:
docker run -it --rm busybox
-
然后在容器内部监听 nc server,命令如下:
➜ ~ docker run -it --rm busybox Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox ce2f991d7b89: Pull complete Digest: sha256:05a79c7279f71f86a2a0d05eb72fcb56ea36139150f0a75cd87e80a4272e4e39 Status: Downloaded newer image for busybox:latest / # nc host.docker.internal 23456 / # nc host.docker.internal 23456 hello from chever
-
然后在宿主机监听 23456 端口,命令如下:
➜ ~ docker run -it --rm busybox Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox ce2f991d7b89: Pull complete Digest: sha256:05a79c7279f71f86a2a0d05eb72fcb56ea36139150f0a75cd87e80a4272e4e39 Status: Downloaded newer image for busybox:latest / # nc host.docker.internal 23456 / # nc host.docker.internal 23456 hello from chever
-
所以我暂时放弃了这个方案。
使用 helm chart 在 k8s-local 中部署 APISIX
这边我本来是 helm fetch apisix/apisix
拿到官方的 APISIX 的 chart 包,然后解压缩之后,修改一些配置然后 push 到 harbor 仓库中,harbor 中的 loadbalance/apisix 这个仓库中。
但其实并不需要这么麻烦,改完配置后,只需要在上一级文件夹中运行如下命令:
helm upgrade apisix ./apisix-helm-chart \
--set gateway.type=NodePort \
--set admin.type=NodePort \
--namespace sa
即可安装部署 APISIX 了。
需要修改一些 APISIX 的配置
修改 APISIX 中的 values.yaml 文件中的 admin.allow.ipList
,如下所示:
allow:
# -- The client IP CIDR allowed to access Apache APISIX Admin API service.
ipList:
- 0.0.0.0/0
从 127.0.0.1/24
改为 0.0.0.0/0
。
这样改完配置之后,我就可以从本地访问 APISIX 服务了。
etcdctl 取得 APISIX 配置数据
首先确认集群中的 APISIX 状态,命令和结果如下:
➜ ~ kb get services -nsa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apisix-admin NodePort 10.105.17.133 <none> 9180:32181/TCP 47m
apisix-etcd NodePort 10.97.152.31 <none> 2379:30598/TCP,2380:31227/TCP 47m
apisix-etcd-headless ClusterIP None <none> 2379/TCP,2380/TCP 47m
apisix-gateway NodePort 10.98.64.77 <none> 80:32096/TCP 47m
etcd 需要先配置 endpoint
环境变量,命令如下:
ENDPOINTS=localhost:30598
etcdctl 获取 APISIX 的配置,命令如下:
➜ ~ etcdctl --endpoints=$ENDPOINTS get / --prefix --keys-only
/apisix/consumer_groups/
/apisix/consumers/
/apisix/global_rules/
/apisix/plugin_configs/
/apisix/plugin_metadata/
/apisix/plugins/
/apisix/protos/
/apisix/routes/
/apisix/secrets/
简单测试一下 etcdctl 的功能
创建一个类似于 hello world
的东西,命令如下:
etcdctl --endpoints=$ENDPOINTS put greeting "Hello, etcd"
etcdctl --endpoints=$ENDPOINTS get greeting
为 prefix 为 apisix_john 的 apisix 配置
此处需要重新修改一下 APISIX helm chart 的配置,配置如下:
# -- etcd configuration
# use the FQDN address or the IP of the etcd
etcd:
# -- install etcd(v3) by default, set false if do not want to install etcd(v3) together
enabled: true
# -- if etcd.enabled is false, use external etcd, support multiple address, if your etcd cluster enables TLS, please use https scheme, e.g. https://127.0.0.1:2379.
host:
# host or ip e.g. http://172.20.128.89:2379
- http://etcd.host:2379
# -- apisix configurations prefix
prefix: "/apisix_john"
然后继续运行 helm upgrade
命令更新,命令如下:
helm upgrade apisix ./apisix-helm-chart \
--set gateway.type=NodePort \
--set admin.type=NodePort \
--namespace sa
通过 APISIX 创建配置
创建 APISIX 的路由和上游,使得能够代理 httpbin。
创建路由
命令如下:
// create routes
curl "http://127.0.0.1:32181/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"methods": ["GET"],
"host": "example.com",
"uri": "/anything/*",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
验证创建的路由配置,命令如下:
➜ ~ curl -i -X GET "http://127.0.0.1:32096/anything/foo?arg=10" -H "Host: example.com"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 427
Connection: keep-alive
Date: Wed, 04 Jan 2023 10:56:49 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.1.0
{
"args": {
"arg": "10"
},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "example.com",
"User-Agent": "curl/7.87.0",
"X-Amzn-Trace-Id": "Root=1-63b55b71-412c79fc1a5b37b77d70eb8e",
"X-Forwarded-Host": "example.com"
},
"json": null,
"method": "GET",
"origin": "192.168.65.3, 63.222.122.101",
"url": "http://example.com/anything/foo?arg=10"
}
我们需要做的是,通过 etcdctl 修改 host 为 example_john.com
。然后,运行下面的命令:
curl -i -X GET "http://127.0.0.1:32096/anything/foo?arg=10" -H "Host: example_john.com"
使得继续拿到 200 的状态码。
etcdctl 修改路由配置
首先可以确定的是,上面所有的操作得到的路由配置,如下命令可见:
➜ ~ etcdctl --endpoints=$ENDPOINTS get /apisix_john/routes/1 --prefix
/apisix_john/routes/1
{"uri":"\/anything\/*","status":1,"create_time":1672829370,"host":"example.com","priority":0,"upstream":{"scheme":"http","type":"roundrobin","nodes":{"httpbin.org:80":1},"hash_on":"vars","pass_host":"pass"},"methods":["GET"],"update_time":1672829370,"id":"1"}
使用 etcdctl 修改路由配置,命令如下:
// 通过 etcdctl 命令,直接更改 etcd key 为 /apisix_john/routes/1 的 value,此处只需要注意的是,在 value 前后添加 '' 符号即可。
➜ ~ etcdctl --endpoints=$ENDPOINTS put /apisix_john/routes/1 '{"uri":"\/anything\/*","status":1,"create_time":1672829370,"host":"example_john.com","priority":0,"upstream":{"scheme":"http","type":"roundrobin","nodes":{"httpbin.org:80":1},"hash_on":"vars","pass_host":"pass"},"methods":["GET"],"update_time":1672829370,"id":"1"}'
OK
更改完的配置如下可见:
// 更改完 /apisix_john/routes/1 之后的 value 值
➜ ~ etcdctl --endpoints=$ENDPOINTS get /apisix_john/routes/1 --prefix
/apisix_john/routes/1
{"uri":"\/anything\/*","status":1,"create_time":1672829370,"host":"example_john.com","priority":0,"upstream":{"scheme":"http","type":"roundrobin","nodes":{"httpbin.org:80":1},"hash_on":"vars","pass_host":"pass"},"methods":["GET"],"update_time":1672829370,"id":"1"}
然后就可以继续发送请求得到想要的结果了。
➜ ~ curl -i -X GET "http://127.0.0.1:32096/anything/foo?arg=10" -H "Host: example_john.com"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 441
Connection: keep-alive
Date: Wed, 04 Jan 2023 16:21:41 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.1.0
{
"args": {
"arg": "10"
},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "example_john.com",
"User-Agent": "curl/7.87.0",
"X-Amzn-Trace-Id": "Root=1-63b5a795-5f3cb8b34bb3250c6e2730c4",
"X-Forwarded-Host": "example_john.com"
},
"json": null,
"method": "GET",
"origin": "192.168.65.3, 112.20.92.190",
"url": "http://example_john.com/anything/foo?arg=10"
}