https://onedayxyy.cn/docs/GatewayAPI
实战名称 |
---|
🚩 实战:Gateway API在istio里的安装及测试-2023.12.23(测试失败) |
Gateway API
是由 SIG-NETWORK 社区管理的开源项目,项目地址:https://gateway-api.sigs.k8s.io/。
主要原因是 Ingress 资源对象不能很好的满足网络需求,很多场景下 Ingress 控制器都需要通过定义 annotations
或者 crd 来进行功能扩展,这对于使用标准和支持是非常不利的,新推出的 Gateway API 旨在通过可扩展的面向角色的接口来增强服务网络。
Gateway API 是 Kubernetes 中的一个 API 资源集合,包括 GatewayClass
、Gateway
、HTTPRoute
、TCPRoute
、Service
等,这些资源共同为各种网络用例构建模型。
Gateway API 最初设计用于管理从集群外部客户端到集群内部服务的流量(入口或北/南情况)。随着时间的推移,服务网格用户的兴趣促使 GAMMA(Gateway API for Service Mesh)计划的创建,以定义 Gateway API 如何用于同一集群内的服务间或东/西流量。
Gateway API 的改进比当前的 Ingress 资源对象有很多更好的设计:
还有一些其他值得关注的功能:
GatewayClasses
- GatewayClasses
将负载均衡实现的类型形式化,这些类使用户可以很容易了解到通过 Kubernetes 资源可以获得什么样的能力。无论是道路、电力、数据中心还是 Kubernetes 集群,基础设施都是为了共享而建的,然而共享基础设施提供了一个共同的挑战,那就是如何为基础设施用户提供灵活性的同时还能被所有者控制。
Gateway API 通过对 Kubernetes 服务网络进行面向角色的设计来实现这一目标,平衡了灵活性和集中控制。它允许共享的网络基础设施(硬件负载均衡器、云网络、集群托管的代理等)被许多不同的团队使用,所有这些都受到集群运维设置的各种策略和约束。
一个集群运维人员创建了一个基于 GatewayClass
的 Gateway
资源,这个 Gateway
配置了它所代表的基础网络资源,集群运维和特定的团队必须沟通什么可以附加到这个 Gateway 上来暴露他们的应用。 集中的策略,如 TLS,可以由集群运维在 Gateway 上强制执行,同时,Store 和 Site 应用在他们自己的命名空间中运行,但将他们的路由附加到相同的共享网关上,允许他们独立控制他们的路由逻辑。
这种关注点分离的设计可以使不同的团队能够管理他们自己的流量,同时将集中的策略和控制留给集群运维。
在整个 Gateway API 中涉及到 3 个角色:基础设施提供商、集群管理员、应用开发人员,在某些场景下可能还会涉及到应用管理员等角色。Gateway API 中定义了 3 种主要的资源模型:GatewayClass
、Gateway
、Route
。
GatewayClass
定义了一组共享相同配置和动作的网关。每个 GatewayClass
由一个控制器处理,是一个集群范围的资源,必须至少有一个 GatewayClass
被定义。
这与 Ingress 的 IngressClass 类似,在 Ingress v1beta1 版本中,与 GatewayClass 类似的是
ingress-class
注解,而在 Ingress V1 版本中,最接近的就是IngressClass
资源对象。
Gateway 网关描述了如何将流量转化为集群内的服务,也就是说,它定义了一个请求,要求将流量从不了解 Kubernetes 的地方转换到集群内的服务。例如,由云端负载均衡器、集群内代理或外部硬件负载均衡器发送到 Kubernetes 服务的流量。
它定义了对特定负载均衡器配置的请求,该配置实现了 GatewayClass
的配置和行为规范,该资源可以由管理员直接创建,也可以由处理 GatewayClass
的控制器创建。
Gateway 可以附加到一个或多个路由引用上,这些路由引用的作用是将流量的一个子集导向特定的服务。
路由资源定义了特定的规则,用于将请求从网关映射到 Kubernetes 服务。从 v1alpha2
版本开始,API 中包含四种 Route 路由资源类型。
HTTPRoute
HTTPRoute
是用于 HTTP 或 HTTPS 连接,适用于我们想要检查 HTTP 请求并使用 HTTP 请求进行路由或修改的场景,比如使用 HTTP Headers 头进行路由,或在请求过程中对它们进行修改。
TLSRoute
TLSRoute
用于 TLS 连接,通过 SNI 进行区分,它适用于希望使用 SNI 作为主要路由方法的地方,并且对 HTTP 等更高级别协议的属性不感兴趣,连接的字节流不经任何检查就被代理到后端。
TCPRoute 和 UDPRoute
TCPRoute
(和 UDPRoute
)旨在用于将一个或多个端口映射到单个后端。在这种情况下,没有可以用来选择同一端口的不同后端的判别器,所以每个 TCPRoute 在监听器上需要一个不同的端口。你可以使用 TLS,在这种情况下,未加密的字节流会被传递到后端,当然也可以不使用 TLS,这样加密的字节流将传递到后端。
GRPCRoute
GRPCRoute
用于路由 gRPC 流量,支持 GRPCRoute
的网关必须支持 HTTP/2,无需从 HTTP/1 进行初始升级,因此可以确保 gRPC 流量正常进行。
GatewayClass
、Gateway
、xRoute
和 Service
的组合定义了一个可实施的负载均衡器,下图说明了不同资源之间的关系:
使用反向代理实现的网关的典型客户端/网关 API 请求流程如下所示:
客户端向 http://foo.example.com
发出请求
DNS 将域名解析为 Gateway
网关地址
反向代理在监听器上接收请求,并使用 Host Header 来匹配 HTTPRoute
(可选)反向代理可以根据 HTTPRoute
的匹配规则进行路由
(可选)反向代理可以根据 HTTPRoute
的过滤规则修改请求,即添加或删除 headers
最后,反向代理根据 HTTPRoute
的 forwardTo
规则,将请求转发给集群中的一个或多个对象,即服务。
我们这里主要是讲解 Gateway API 在服务网格中的使用,首先我们先了解下 Gateway API 与 Istio API 的区别。
Gateway API 与 Istio API(如 Gateway
和 VirtualService
)有很多相似之处。主资源使用相同的 Gateway
名称,并且这些资源服务于相类似的目标。
新的 Gateway API 致力于从 Kubernetes 的各种 Ingress 实现(包括 Istio)中吸取经验,以构建标准化的,独立于供应商的 API。这些 API 通常与 Istio Gateway 和 VirtualService 具有相同的用途,但依然有一些不同的地方:
HTTPRoute
和 TCPRoute
。接下来我们就来了解下如何在 Istio z 中使用 Gateway API。默认情况下 Kubernetes 集群中不会安装 Gateway API。
🚩 实战:Gateway API在istio里的安装及测试-2023.12.23(测试失败)
k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
istio v1.19.3(--set profile=demo)
实验软件:
链接:https://pan.baidu.com/s/1pMnJxgL63oTlGFlhrfnXsA?pwd=7yqb
提取码:7yqb
2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)
experimental-install.yaml
文件:
链接:https://pan.baidu.com/s/1pcuEn2DHsqmDTPX_rtNtLQ?pwd=wwhj
提取码:wwhj
实战:Gateway API在istio里的安装及测试-2023.12.23(测试失败)
[root@master1 GatewayAPI]#kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml
customresourcedefinition.apiextensions.k8s.io/backendtlspolicies.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/tcproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/tlsroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/udproutes.gateway.networking.k8s.io created
standard-install.yaml
包括所有已升级为 GA 或 Beta 的资源,包括 GatewayClass
、Gateway
、HTTPRoute
和 ReferenceGrant
,由于 Istio 已经对 Gateway API 提供了支持,所以现在我们就可以直接使用了。
experimental.yaml
包括其他的路由对象,比如 TLSRoute、TCPRoute、UDPRoute 等。
istio
的 GatewayClass
资源对象,如下所示(另外还有一个名为 istio-remote
):[root@master1 GatewayAPI]#kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
istio istio.io/gateway-controller True 13m
istio-remote istio.io/unmanaged-gateway True 13m
[root@master1 GatewayAPI]#kubectl get gatewayclass istio -oyaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
……
spec:
controllerName: istio.io/gateway-controller
description: The default Istio GatewayClass
……
[root@master1 GatewayAPI]#
因为大部分场景下在一个 Kubernetes 集群中只会有一个 Istio 集群,所以我们可以直接使用默认的 istio
这个 GatewayClass
,如果你有多个 Istio 集群,那么你可以创建多个 GatewayClass 来区分不同的集群。
比如接下来我们来尝试将 httpbin 应用使用 Gateway API 暴露到外部。
kubectl apply -f samples/httpbin/httpbin.yaml
Gateway
资源对象Gateway
资源对象,用于将流量从外部负载均衡器转发到集群内的服务,如下所示:# default-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress # 网关资源对象所在的命名空间
spec:
gatewayClassName: istio # 使用默认的 istio GatewayClass
listeners: # 监听器
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes: # 允许的路由
namespaces:
from: All # 允许所有命名空间
Gateway
代表了逻辑负载均衡器的实例化,它是根据一个 istio 这个 GatewayClass
进行模板化的,网关在 80 端口上监听 HTTP 流量,这个特定的 GatewayClass
在部署后会自动分配一个 IP 地址,该地址会显示在 Gateway.status
中。
🔱 注意:
需要注意的是这里我们声明使用的命名空间为
istio-ingress
, 这是因为 Istio Gateway 可以直接使用 istio ingressgateway 的 Deployment,而这个 Deployment 默认是部署在istio-system
命名空间中的,我们这里单独将Gateway
资源对象创建在istio-ingress
命名空间中,那么就会自动在这个命名空间中部署一个网关控制器,用于区分默认的 istio ingressgateway。
istio-ingress
命名空间中创建这个 Gateway
资源对象,这样才能让 istio ingressgateway 通过 Gateway
资源对象来获取配置。kubectl create namespace istio-ingress
$ kubectl apply -f default-gateway.yaml
##查看
$ kubectl get gateway -n istio-ingress
NAME CLASS ADDRESS PROGRAMMED AGE
gateway istio False 8m23s
$ kubectl get deploy -n istio-ingress
NAME READY UP-TO-DATE AVAILABLE AGE
gateway-istio 1/1 1 1 8m35s
$ kubectl get svc -n istio-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway-istio LoadBalancer 10.100.103.86 <pending> 15021:31509/TCP,80:32530/TCP 9m38s
我们也可以去对比下上面生成的 gateway-istio
和 Istio 默认的 istio-ingressgateway
的 Deployment,他们的配置几乎是一样的。
这里的排查记录仅作为记录,稍后再排查这个问题!
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m23s default-scheduler Successfully assigned istio-ingress/gateway-istio-7474cd4d9b-pkmsr to node2
Warning FailedCreatePodSandBox 6s (x20 over 4m21s) kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: open /proc/sys/net/ipv4/ip_unprivileged_port_start: no such file or directory: unknown
[root@master1 GatewayAPI]#
[root@master1 GatewayAPI]#kubectl get gateway -nistio-ingress
NAME CLASS ADDRESS PROGRAMMED AGE
gateway istio False 5m50s
[root@master1 GatewayAPI]#kubectl get po -nistio-ingress
NAME READY STATUS RESTARTS AGE
gateway-istio-7474cd4d9b-pkmsr 0/1 ContainerCreating 0 5m52s
[root@master1 GatewayAPI]#kubectl get svc -nistio-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway-istio LoadBalancer 10.100.204.5 <pending> 15021:32204/TCP,80:31157/TCP 5m54s
[root@master1 GatewayAPI]#
感觉像是底层pod sandbox
–pause??问题??;
可能和这个有关系:。。。。例如 containerd 或 runc 的正确版本。
我删除下flannel看下把。。。
[root@master1 ~]#kubectl get po -nkube-flannel -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel-ds-2ndhl 1/1 Running 1 (11d ago) 44d 172.29.9.62 node1 <none> <none>
kube-flannel-ds-m44dc 1/1 Running 1 (11d ago) 44d 172.29.9.61 master1 <none> <none>
kube-flannel-ds-s4787 1/1 Running 3 (5m10s ago) 44d 172.29.9.63 node2 <none> <none>
[root@master1 ~]#kubectl delete po kube-flannel-ds-s4787 -n kube-flannel
pod "kube-flannel-ds-s4787" deleted
##已重建
[root@master1 ~]#kubectl delete po kube-flannel-ds-s4787 -n kube-flannel
pod "kube-flannel-ds-s4787" deleted
[root@master1 ~]#kubectl get po -nkube-flannel -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel-ds-2ndhl 1/1 Running 1 (11d ago) 44d 172.29.9.62 node1 <none> <none>
kube-flannel-ds-m44dc 1/1 Running 1 (11d ago) 44d 172.29.9.61 master1 <none> <none>
kube-flannel-ds-mkh5z 1/1 Running 0 14s 172.29.9.63 node2 <none> <none>
[root@master1 ~]#
还是不行。。。
Warning FailedCreatePodSandBox 12m (x3 over 14m) kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to get sandbox image "registry.k8s.io/pause:3.6": failed to pull image "registry.k8s.io/pause:3.6": failed to pull and unpack image "registry.k8s.io/pause:3.6": failed to resolve reference "registry.k8s.io/pause:3.6": failed to do request: Head "https://us-west2-docker.pkg.dev/v2/k8s-artifacts-prod/images/pause/manifests/3.6": dial tcp 173.194.174.82:443: i/o timeout
sed -i 's/registry.k8s.io\/pause:3.6/registry.aliyuncs.com\/google_containers\/pause:3.9/g' /etc/containerd/config.toml
重启node2后,一直卡在这里。。。
这里记录下就好,这个问题有点难搞……
[root@master1 ~]#ssh node2
Last login: Sat Dec 23 08:48:26 2023 from master1
[root@node2 ~]#ctr -n k8s.io i ls -q|grep pause
registry.aliyuncs.com/k8sxio/pause:3.8
registry.aliyuncs.com/k8sxio/pause@sha256:67cc096c2abe1f29ece08439b509738a5cd11f8ff87851a06d092772d52c090e
[root@node2 ~]#
HTTPRoute
资源对象来定义路由规则HTTPRoute
资源对象来定义这个路由规则,如下所示:# httpbin-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: default
spec:
parentRefs: # 引用定义的 Gateway 对象
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # 域名
rules: # 具体的路由规则
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的请求
backendRefs: # 引用的后端服务
- name: httpbin
port: 8000
在上面的 HTTPRoute
对象中我们通过 parentRefs
字段指定要连接到的网关,只要网关允许这种连接,这将允许路由接收来自父网关的流量,在 backendRefs
中定义将要发送流量的后端。但是需要注意我们这里只定义了匹配 /get
这个路径的请求,然后将要访问的域名通过 hostnames
来定义。
$ kubectl apply -f httpbin-route.yaml
$ kubectl get httproute
NAME HOSTNAMES AGE
httpbin ["httpbin.example.com"] 5s
httpbin.example.com
来访问 httpbin 应用了:# $ export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io gateway -n istio-ingress -ojsonpath='{.status.addresses[0].value}')
export GATEWAY_URL=$(kubectl get po -l istio.io/gateway-name=gateway -n istio-ingress -o 'jsonpath={.items[0].status.hostIP}'):$(kubectl get svc gateway-istio -n istio-ingress -o 'jsonpath={.spec.ports[?(@.name=="default")].nodePort}')
如果你的集群可以正常使用 LoadBalancer,那么 Gateway
控制器在部署后会自动分配一个 IP 地址,该地址会显示在 Gateway.status
中。我们这里暂不支持,所以还是可以通过 NodePort 方式来进行访问。
curl
访问 httpbin 服务:$ curl -s -HHost:httpbin.example.com "http://$GATEWAY_URL/get"
HTTP/1.1 200 OK
server: istio-envoy
date: Mon, 18 Dec 2023 07:29:11 GMT
content-type: application/json
content-length: 494
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2
请注意,使用 -H
标志可以将 Host HTTP 标头设置为 httpbin.example.com
。这一步是必需的,因为 HTTPRoute
已配置为处理 httpbin.example.com
的请求,但是在测试环境中,该主机没有 DNS 绑定,只是将请求发送到入口 IP。
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
HTTP/1.1 404 Not Found
date: Mon, 18 Dec 2023 07:31:51 GMT
server: istio-envoy
transfer-encoding: chunked
或者:
gateway-istio
的日志,可以看到类似于下面的日志:$ kubectl -n istio-ingress logs -f gateway-istio-7474cd4d9b-8dw2k
# ......
2023-12-18T07:04:33.164968Z info cache returned workload trust anchor from cache ttl=23h59m59.835033638s
2023-12-18T07:04:33.621616Z info Readiness succeeded in 813.201909ms
2023-12-18T07:04:33.621946Z info Envoy proxy is ready
[2023-12-18T07:29:11.177Z] "HEAD /get HTTP/1.1" 200 - via_upstream - "-" 0 0 2 2 "10.244.1.1" "curl/7.29.0" "68f51e89-8125-4d9e-be36-e0cdc6e6ead8" "httpbin.example.com" "10.244.1.131:80" outbound|8000||httpbin.default.svc.cluster.local 10.244.1.132:46066 10.244.1.132:80 10.244.1.1:55626 - default.httpbin.0
[2023-12-18T07:31:52.051Z] "HEAD /headers HTTP/1.1" 404 NR route_not_found - "-" 0 0 0 - "10.244.1.1" "curl/7.29.0" "a9a1002f-d65c-40d8-861d-ec99d4a4a442" "httpbin.example.com" "-" - - 10.244.1.132:80 10.244.1.1:53583 - -
证明我们的路由规则已经生效了。
/headers
路由,那么我们可以更新下 HTTPRoute
对象:# httpbin-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: default
spec:
parentRefs: # 引用定义的 Gateway 对象
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # 域名
rules: # 具体的路由规则
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的请求
- path:
type: PathPrefix
value: /headers # 匹配 /headers 的请求
filters:
- type: RequestHeaderModifier # 添加一个修改请求头的过滤器
requestHeaderModifier:
add: # 添加一个标头
- name: my-added-header
value: added-value
backendRefs: # 引用的后端服务
- name: httpbin
port: 8000
我们除了在 rules
中添加了一个匹配 /headers
的规则外,还添加了一个 RequestHeaderModifier
过滤器,用于添加一个 Header 头信息。
/headers
,注意到 My-Added-Header
标头已被添加到请求中了:$ curl -s -HHost:httpbin.example.com "http://$GATEWAY_URL/headers"
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.example.com",
"My-Added-Header": "added-value", # 添加了一个Header头
"User-Agent": "curl/7.29.0",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=11fc66a1c6c9fc44e65ec67f7f8b16d06fbaa73d9729e141e0bd91134dc59db3;Subject=\"\";URI=spiffe://cluster.local/ns/istio-ingress/sa/gateway-istio"
}
}
在上面的示例中,在配置网关之前,我们并没有去安装 Ingress 网关的 Deployment,因为在默认配置中会根据 Gateway
配置自动分发网关 Deployment
和 Service
。但是对于高级别的场景可能还是需要去手动部署。
1、自动部署
默认情况下,每个 Gateway 将自动提供相同名称的 Service 和 Deployment。如果 Gateway 发生变化(例如添加了一个新端口),这些配置将会自动更新。这些资源可以通过以下几种方式进行定义:
将 Gateway
上的注解和标签复制到 Service 和 Deployment。这就允许配置从上述字段中读取到的内容,如配置内部负载均衡器等。Istio 提供了一个额外的注解来配置生成的资源:
networking.istio.io/service-type
:控制 Service.spec.type
字段。例如设置 ClusterIP 为不对外暴露服务,将会默认为 LoadBalancer。通过配置 addresses
字段可以显式设置 Service.spec.loadBalancerIP
字段:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: 192.0.2.0 # 仅能指定一个地址
type: IPAddress
2、手动部署
如果您不希望使用自动部署,可以进行手动配置 Deployment 和 Service。完成此选项后,您将需要手动将 Gateway 链接到 Service,并保持它们的端口配置同步。
要将 Gateway 链接到 Service,需要将 addresses
字段配置为指向单个 Hostname。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: ingress.istio-gateways.svc.cluster.local
type: Hostname
当然我们这里只是一个最简单的示例,我们将在后面的课程中继续介绍 Gateway API 的更多功能。
我的博客主旨:
🍀 微信二维码
x2675263825 (舍得), qq:2675263825。
🍀 微信公众号
《云原生架构师实战》
🍀 个人博客站点
http://onedayxyy.cn/
🍀 语雀
https://www.yuque.com/xyy-onlyone
🍀 csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
🍀 知乎
https://www.zhihu.com/people/foryouone
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!
304699014)]
🍀 微信公众号
《云原生架构师实战》
[外链图片转存中…(img-zntHMkMo-1703304699015)]
🍀 个人博客站点
http://onedayxyy.cn/
[外链图片转存中…(img-dyfE0ixN-1703304699015)]
[外链图片转存中…(img-A9No82cT-1703304699016)]
🍀 语雀
https://www.yuque.com/xyy-onlyone
[外链图片转存中…(img-FAK6bbRe-1703304699016)]
🍀 csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
[外链图片转存中…(img-IiWTivCF-1703304699017)]
🍀 知乎
https://www.zhihu.com/people/foryouone
[外链图片转存中…(img-UdDqYg5I-1703304699017)]
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!