今天的目的是想要总结一下在 k8s 集群中部署 APISIX 的经验吧。

有这么一个需求,是因为在最新的负载均衡需求评审会议上,我们需要直接同步 consul 和 APISIX 的 upstream 信息。这是因为 APISIX 目前没有对 consul 服务发现的比较好的实现方式,于是,项目组决定手动创建一个新的 consul 同步助手类似的项目来实现这个功能。

选择什么样的集群方案呢?

又是一个提问,我们既然知道了目的,那该确定下验证的方式了。

目前可选的 k8s 集群操作方式非常多,此处稍微列举一下:

  1. kind 集群;
  2. 组里三个 node(182、183、184)组成的 oci 为 containerd 的集群;
  3. 公司申请的开发集群(tke);
  4. 公司申请的测试集群(tke-kafka);
  5. 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:

  1. 使用集群,使用 APISIX 的 helm charts 包来进行安装;
  2. 使用 docker 部署安装。

使用 Docker 部署安装

我只能选择在本地的 Docker 中进行部署安装。

最初我使用 brew install etcd 安装好 etcd 之后,然后准备让 APISIX 容器,连接到宿主机上的 etcd 服务。

但是一直没有找到相关的办法,从 Docker 官方文档中也没有找到如果在 macos 系统中,让容器连接到宿主机的服务。

其中我好像找到了两种方法。

  1. openvpn 进行网络的重新搭建;

  2. 使用类似于 host.docker.internal 之类的端口,从容器内部访问宿主机的服务,具体的 demo 例子可以查看这个link.

    1. 首先部署 busybox,命令如下:

      docker run -it --rm busybox
    2. 然后在容器内部监听 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
    3. 然后在宿主机监听 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"
}