实战:OpenELB的安装与使用-2023.3.7(测试成功)
k8s
实战:OpenELB的安装与使用-2023.3.7(测试成功)
目录
文章目录
实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.22.2
containerd: v1.5.5
实验软件
链接:https://pan.baidu.com/s/1KwOXnNSrlNwYobzu-2HRMA?pwd=qs8b
提取码:qs8b
2023.3.7-实战:OpenELB的安装与使用-2023.3.7(测试成功)
1、前言
除了比较流行的 MetalLB 之外,最近国内青云开源的另外一个负载均衡器 OpenELB 也有不少热度。OpenELB 之前叫PorterLB,是为物理机(Bare-metal)、边缘(Edge)和私有化环境设计的负载均衡器插件,可作为 Kubernetes、K3s、KubeSphere 的 LB 插件对集群外暴露 LoadBalancer 类型的服务,现阶段是 CNCF 沙箱项目,核心功能包括:
- 基于 BGP 与 Layer2 和 VIP 模式的负载均衡 (注意:这里的
VIP
指的是keepalived
) - 基于路由器 ECMP 的负载均衡
- IP 地址池管理
- 使用 CRD 进行 BGP 配置
- 官方链接
此外 OpenElB 还支持 BGP 和 VIP 模式以及集群多路由的场景,更新使用方法可以查看官方文档https://openelb.io/docs/overview/ 了解更多相关信息。
OpenElb github地址:
https://github.com/openelb/openelb
2、与 MetalLB 对比
OpenELB 作为后起之秀,采用了更加 Kubernetes-native 的实现方式,可以直接通过 CRD 进行配置管理(现在MetalLB 也采用 CRD 方式进行配置),下面是关于 OpenELB 与 MetaLB 的简单对比。
-
云原生架构:在 OpenELB 中,不管是地址管理,还是 BGP 配置管理,你都可以使用 CRD 来配置。对于习惯了Kubectl 的用户而言, OpenELB 十分友好。
在 MetalLB 中,需通过 ConfigMap 来配置,感知它们的状态需要通过查看监控或者日志。(MetalLB老版本是这样的,新版本也是通过CRD来实现的) -
灵活的地址管理:OpenELB 通过 EIP 这个自定义资源对象来管理地址,它定义子资源 Status 来存储地址分配状态,这样就不会存在分配地址时各副本发生冲突的情况。
-
使用 gobgp 发布路由:不同于 MetalLB 自己实现 BGP 协议, OpenELB 采用标准的 gobgp 来发布路由,这样做的好处如下:
- 开发成本低,且有 gobgp 社区支持
- 可以利用 gobgp 丰富特性
- 通过 BgpConf/BgpPeer CRD 动态配置 gobgp,用户无需重启 OpenELB 即可动态加载最新的配置信息
- gobgp 作为 lib 使用时, 社区提供了基于 protobuf 的 API,OpenELB 在实现 BgpConf/BgpPeer CRD时也是参照该 API,并保持兼容
- OpenELB 也提供 status 用于查看 BGP neighbor 配置,状态信息丰富
-
架构简单,资源占用少 :OpenELB 目前只用部署 Deployment 即可,通过多副本实现高可用,部分副本崩溃后并不会影响已建立的正常连接。BGP 模式下, Deployment 不同副本都会与路由器建立连接用于发布等价路由,所以正常情况下我们部署两个副本即可。在 Layer 2 模式下,不同副本之间通过 Kubernetes 提供的 Leader Election机制选举 Leader,进而应答 ARP/NDP。
github star对比:
推荐MetalLB,哈哈。🤣
3、安装
在 Kubernetes 集群中,安装 OpenELB 后,会安装一个 openelb-manager Pod,openelb-manager Pod 为整个Kubernetes 集群实现了 OpenELB 的功能。我们也可以扩展 openelb-manager 副本,将多个 OpenELB 副本分配给多个集群节点,保证高可用。
⚠️ 注意:
安装这个OpenELB时,记得不要存在其他的LB插件哦,例如之前安装测试的MetalLB。
- 要安装使用 OpenELB 非常简单,直接使用下面的命令即可一键安装:
# 注意如果不能获取 k8s.gcr.io 镜像,需要替换其中的镜像
kubectl apply -f https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml
- 这里先来替换下镜像
[root@master1 OpenELB]#wget https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml
[root@master1 OpenELB]#vim openelb.yaml
将
1267 image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
替换为
1267 image: kubespheredev/kube-webhook-certgen:v1.1.1
1300 image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
替换为
1300 image: kubespheredev/kube-webhook-certgen:v1.1.1
#注意:这里要替换2处k8s.gcr.io镜像,是2个admission,webhook。
- 安装OpenELB
[root@master1 OpenELB]#kubectl apply -f openelb.yaml
namespace/openelb-system created
customresourcedefinition.apiextensions.k8s.io/bgpconfs.network.kubesphere.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.network.kubesphere.io created
customresourcedefinition.apiextensions.k8s.io/eips.network.kubesphere.io created
serviceaccount/kube-keepalived-vip created
serviceaccount/openelb-admission created
role.rbac.authorization.k8s.io/leader-election-role created
role.rbac.authorization.k8s.io/openelb-admission created
clusterrole.rbac.authorization.k8s.io/kube-keepalived-vip created
clusterrole.rbac.authorization.k8s.io/openelb-admission created
clusterrole.rbac.authorization.k8s.io/openelb-manager-role created
rolebinding.rbac.authorization.k8s.io/leader-election-rolebinding created
rolebinding.rbac.authorization.k8s.io/openelb-admission created
clusterrolebinding.rbac.authorization.k8s.io/kube-keepalived-vip created
clusterrolebinding.rbac.authorization.k8s.io/openelb-admission created
clusterrolebinding.rbac.authorization.k8s.io/openelb-manager-rolebinding created
service/openelb-admission created
deployment.apps/openelb-manager created
job.batch/openelb-admission-create created
job.batch/openelb-admission-patch created
mutatingwebhookconfiguration.admissionregistration.k8s.io/openelb-admission created
validatingwebhookconfiguration.admissionregistration.k8s.io/openelb-admission created
上面的资源清单会部署一个名为 openelb-manager 的 Deployment 资源对象,openelb-manager 的 Pod 为整个Kubernetes 集群实现了 OpenELB 的功能,为保证高可用,可以将该控制器扩展为两个副本。第一次安装的时候还会为admission webhook 配置 https 证书。
- 安装完成后查看 Pod 的状态是否正常:
root@master1 OpenELB]#kubectl get pods -n openelb-system -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
openelb-admission-create--1-m227m 0/1 Completed 0 3m7s 10.244.2.10 node2 <none> <none>
openelb-admission-patch--1-nj8nn 0/1 Completed 3 3m7s 10.244.1.15 node1 <none> <none>
openelb-keepalive-vip-2cfgk 1/1 Running 0 83s 172.29.9.52 node1 <none> <none>
openelb-keepalive-vip-f9psr 1/1 Running 0 83s 172.29.9.53 node2 <none> <none>
openelb-manager-794999f796-8f8sx 1/1 Running 0 3m7s 172.29.9.53 node2 <none> <none>
[root@master1 OpenELB]#kubectl get validatingwebhookconfiguration
NAME WEBHOOKS AGE
ingress-nginx-admission 1 18m
openelb-admission 1 3m40s
[root@master1 OpenELB]#kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
openelb-admission 1 3m55s
- 此外还会安装几个相关的 CRD 用户 OpenELB 配置:
[root@master1 OpenELB]#kubectl get crd |grep kubesphere
bgpconfs.network.kubesphere.io 2023-03-06T23:14:48Z
bgppeers.network.kubesphere.io 2023-03-06T23:14:48Z
eips.network.kubesphere.io 2023-03-06T23:14:48Z
4、配置
- 部署 Layer2 模式需要把 K8s 集群中的 ipvs 配置打开 strictARP,开启之后 K8s 集群中的 kube-proxy 会停止响应 kube-ipvs0 网卡之外的其他网卡的 arp 请求,而由 OpenELB 接手处理。我们只需要在 K8s 集群中编辑kube-proxy 配置即可:
注意:当前集群为v1.22.2
,默认已经是ipvs模式了。
注意:如果kube-peoxy是iptables,也是可以直接使用MetaLB的;
[root@master1 ~]#kubectl edit configmap -n kube-system kube-proxy
#搜索ipvs
33 ipvs:
34 excludeCIDRs: null
35 minSyncPeriod: 0s
36 scheduler: ""
37 strictARP: true
38 syncPeriod: 0s
39 tcpFinTimeout: 0s
40 tcpTimeout: 0s
41 udpTimeout: 0s
42 kind: KubeProxyConfiguration
43 metricsBindAddress: ""
44 mode: ipvs
- 接下来就可以创建一个 Eip 对象来充当 OpenELB 的 IP 地址池了,创建一个如下所示的资源对象:
#范例
#eip.yaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
name: eip-pool
spec:
address: 172.29.9.60-172.29.9.70
protocol: layer2
disable: false
interface: eth0
#实际代码
#eip.yaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
name: eip-pool
spec:
address: 172.29.9.60-172.29.9.70
protocol: layer2
disable: false
interface: ens33
这里我们通过 address 属性指定了 IP 地址池,可以填写一个或多个 IP 地址(要注意不同 Eip 对象中的 IP 段不能重叠),将被 OpenELB 使用。值格式可以是:
- IP 地址,例如 192.168.0.100
- IP 地址/子网掩码,例如 192.168.0.0/24
- IP 地址 1-IP 地址 2,例如 192.168.0.91-192.168.0.100
protocol 属性用来指定 Eip 对象用于哪种 OpenELB 模式,可以配置为 layer2 、 bgp 或 vip,默认为 bgp 模式,我们这里使用 layer2 模式,所以需要显示指定。interface 是用来指定 OpenELB 监听 ARP 或 NDP 请求的网卡,该字段仅在协议设置为 layer2 时有效,我这里的环境是 eth0 网卡。 disable 表示是否禁用 Eip 对象。
⚠️ 注意:这里说的eth0
是指哪个网卡呢?
自己当下虚机环境:(本次配置应该使用这个ens33
)
- 创建完成 Eip 对象后可以通过 Status 来查看该 IP 池的具体状态:
[root@master1 OpenELB]#kubectl apply -f eip.yaml
eip.network.kubesphere.io/eip-pool created
[root@master1 OpenELB]#kubectl get eip
NAME CIDR USAGE TOTAL
eip-pool 172.29.9.60-172.29.9.70 11
[root@master1 OpenELB]#kubectl get eip eip-pool -oyaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"network.kubesphere.io/v1alpha2","kind":"Eip","metadata":{"annotations":{},"name":"eip-pool"},"spec":{"address":"172.29.9.60-172.29.9.70","disable":false,"interface":"ens33","protocol":"layer2"}}
creationTimestamp: "2023-03-06T23:31:33Z"
finalizers:
- finalizer.ipam.kubesphere.io/v1alpha1
generation: 2
name: eip-pool
resourceVersion: "181393"
uid: 976c1bb9-7a3a-4c7e-a248-0c89c49603c8
spec:
address: 172.29.9.60-172.29.9.70
interface: ens33
protocol: layer2
status:
firstIP: 172.29.9.60
lastIP: 172.29.9.70
poolSize: 11
ready: true
v4: true
Eip 对象创建完成后,会自动填充 status 属性,我们创建 Eip 对象的时候,不需要配置这些字段,但是对于我们排查问题比较有帮助:
- occupied :Eip 对象中的 IP 地址是否已用完。
- usage :指定 Eip 对象中有多少个 IP 地址已经分配给 Services。
- used :指定使用的 IP 地址和使用 IP 地址的服务。服务以命名空间/服务名称格式显示(例如,default/testsvc)。
- poolSize :Eip 对象中的 IP 地址总数。
- firstIP:Eip 对象中的第一个 IP 地址。
- lastIP:Eip 对象中的最后一个 IP 地址。
- v4 :地址族是否为 IPv4,目前,OpenELB 仅支持 IPv4,该值只能为 true。
- ready :用于 BGP/ARP/NDP 路由发布的 Eip 关联程序是否已经初始化。
此时,我们再看下之前部署的ingress-nginx-controller
svc是否已自动配置上VIP地址了呢?
[root@master1 OpenELB]#kubectl get svc -ningress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.97.111.207 <pending> 80:30970/TCP,443:32364/TCP 36m
ingress-nginx-controller-admission ClusterIP 10.100.175.233 <none> 443/TCP 36m
[root@master1 OpenELB]#kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
my-nginx nginx first-ingress.172.29.9.53.nip.io 80 34m
哎,发现OpenELB怎么没有给ingress-nginx-controller
svc自动分配vip地址呢,MetalLB
都是可以自动分配的哦。
客官请往下面看哦:
1.创建svc测试
- 到这里 LB 的地址池就准备好了,接下来我们创建一个简单的服务,通过 LB 来进行暴露,如下所示:
#openelb-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
这里同样我们部署一个简单的 nginx 服务,然后创建一个 LoadBalancer 类型的 Service 来暴露我们的 nginx 服务,注意这里我们为 Service 添加了几个 annotations 注解:
- lb.kubesphere.io/v1alpha1 : openelb 用来指定该 Service 使用 OpenELB
- protocol.openelb.kubesphere.io/v1alpha1 : layer2 表示指定 OpenELB 用于 Layer2 模式
- eip.openelb.kubesphere.io/v1alpha2 : eip-pool 用来指定了 OpenELB 使用的 Eip 对象,如果未配置此注解,OpenELB 会自动使用与协议匹配的第一个可用 Eip 对象。此外也可以删除此注解并添加
spec:loadBalancerIP
字段来将特定 IP 地址分配给 Service。
同样直接应用上面的资源对象即可:
[root@master1 OpenELB]#kubectl apply -f openelb-test.yaml
deployment.apps/nginx created
service/nginx created
[root@master1 OpenELB]#kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.109.69.19 172.29.9.60 80:31500/TCP 14s
[root@master1 OpenELB]#kubectl get eip
NAME CIDR USAGE TOTAL
eip-pool 172.29.9.60-172.29.9.70 1 11
[root@master1 OpenELB]#kubectl get eip eip-pool -oyaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"network.kubesphere.io/v1alpha2","kind":"Eip","metadata":{"annotations":{},"name":"eip-pool"},"spec":{"address":"172.29.9.60-172.29.9.70","disable":false,"interface":"ens33","protocol":"layer2"}}
creationTimestamp: "2023-03-06T23:31:33Z"
finalizers:
- finalizer.ipam.kubesphere.io/v1alpha1
generation: 2
name: eip-pool
resourceVersion: "183351"
uid: 976c1bb9-7a3a-4c7e-a248-0c89c49603c8
spec:
address: 172.29.9.60-172.29.9.70
interface: ens33
protocol: layer2
status:
firstIP: 172.29.9.60
lastIP: 172.29.9.70
poolSize: 11
ready: true
usage: 1
used:
172.29.9.60: default/nginx
v4: true
- 创建完成后可以看到 Service 服务被分配了一个 EXTERNAL-IP,然后我们就可以通过该地址来访问上面的 nginx 服务了:
[root@master1 ~]#curl 172.29.9.60 #3个节点上都是可以访问的
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@node1 ~]#curl 172.29.9.60
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@node2 ~]#curl 172.29.9.60
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
2.创建ingress测试
⚠️ 注意:在做以下测试时,需要先在k8s集群部署好
ingress-nginx
哦。
- 此时,我们再在
ingress-nginx-controller
svc里添加下annotations字段
,我们再次观察下现象
[root@master1 OpenELB]#kubectl get svc -ningress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.97.111.207 <pending> 80:30970/TCP,443:32364/TCP 53m
ingress-nginx-controller-admission ClusterIP 10.100.175.233 <none> 443/TCP 53m
[root@master1 OpenELB]#kubectl edit svc ingress-nginx-controller -ningress-nginx
……
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
……
[root@master1 OpenELB]#kubectl get svc -ningress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.97.111.207 172.29.9.61 80:30970/TCP,443:32364/TCP 56m
ingress-nginx-controller-admission ClusterIP 10.100.175.233 <none> 443/TCP 56m
此时,这里出现了172.29.9.61地址。
- 我们来创建一个ingress测试:
# lb-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: lb-demo
spec:
selector:
matchLabels:
app: lb-demo
template:
metadata:
labels:
app: lb-demo
spec:
containers:
- name: lb-demo
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: lb-demo
annotations:
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
labels:
app: lb-demo
spec:
ports:
- port: 80
protocol: TCP
name: http
selector:
app: lb-demo
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: lb-demo
namespace: default
spec:
ingressClassName: nginx # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
rules:
- host: lb-demo.172.29.9.61.nip.io # 将域名映射到 lb-demo 服务
http:
paths:
- path: /
pathType: Prefix
backend:
service: # 将所有请求发送到 lb-demo 服务的 80 端口
name: lb-demo
port:
number: 80
# 不过需要注意大部分Ingress控制器都不是直接转发到Service,而是只是通过Service来获取后端的Endpoints列表(因此这里的svc只起到了一个服务发现的作用),直接转发到Pod,这样可以减少网络跳转,提高性能!!!
- 部署并测试
[root@master1 ~]#kubectl apply -f lb-demo.yaml
[root@master1 ~]#kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
lb-demo nginx lb-demo.172.29.9.61.nip.io 172.29.9.61 80 10m
[root@master1 ~]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13d
lb-demo ClusterIP 10.105.180.95 <none> 80/TCP 12m
nginx LoadBalancer 10.109.69.19 172.29.9.60 80:31500/TCP 4h44m
[root@master1 ~]#kubectl get po -ningress-nginx -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create--1-q9s87 0/1 Completed 0 5h34m 10.244.1.13 node1 <none> <none>
ingress-nginx-admission-patch--1-kj5v2 0/1 Completed 1 5h34m 10.244.1.12 node1 <none> <none>
ingress-nginx-controller-c66bc7c5c-8gqdw 1/1 Running 0 5h34m 10.244.2.9 node2 <none> <none>
curl测试:
[root@master1 ~]#curl 172.29.9.61
curl: (7) Failed connect to 172.29.9.61:80; Connection refused
[root@node1 ~]#curl 172.29.9.61
curl: (7) Failed connect to 172.29.9.61:80; Connection refused
[root@node2 ~]#curl 172.29.9.61
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@node2 ~]#
⚠️
但是,这里为啥node2上测试,会出现nginx报404错误呢?很奇怪……😥,这里作为遗留吧,之前MetalLB测试都是没问题的。感觉配置上没什么问题的呀……
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码
x2675263825 (舍得), qq:2675263825。
🍀 微信公众号
《云原生架构师实战》
🍀 语雀
https://www.yuque.com/xyy-onlyone
🍀 csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
🍀 知乎
https://www.zhihu.com/people/foryouone
最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!
更多推荐
所有评论(0)