授权
目录
[toc]
本节实战
实战名称 |
---|
🚩 实战:HTTP 请求授权-20223.11.27(测试成功) |
🚩 实战:TCP 请求授权-20223.11.28(测试成功) |
前言
Istio 的授权功能为网格中的工作负载提供网格、命名空间和工作负载级别的访问控制。授权策略是对服务器端 Envoy 代理的入站流量实施访问控制。每个 Envoy 代理都运行一个授权引擎,该引擎在运行时授权请求。当请求到达代理时,授权引擎根据当前授权策略评估请求上下文,并返回授权结果 ALLOW
或 DENY
。
要对工作负载实施访问控制,需要应用授权策略。对于没有应用授权策略的工作负载,Istio 允许所有请求。
授权策略支持 ALLOW
、DENY
和 CUSTOM
操作。我们可以根据需要应用多个策略,每个策略具有不同的操作, 以确保对工作负载的访问安全。
Istio 按以下顺序检查层中的匹配策略:CUSTOM
-> DENY
-> ALLOW
。对于每种类型的操作,Istio 首先检查是否有策略的操作已被应用,然后检查请求是否匹配策略的规则。如果请求与其中一层中的策略不匹配, 则检查将继续到下一层。
当将多个授权策略应用于同一工作负载时,Istio 会累加地应用它们。
要配置授权策略,只需要创建一个 AuthorizationPolicy
自定义资源,其中包括:选择器(selector)、动作(action)和一个规则(rules)列表:
selector
字段指定策略的目标action
字段指定允许还是拒绝请求rules
指定何时触发动作rules
下的from
字段指定请求的来源rules
下的to
字段指定请求的操作rules
下的when
字段指定应用规则所需的条件
比如有一个如下所示的授权策略对象:
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/sleep"]
- source:
namespaces: ["dev"]
to:
- operation:
methods: ["GET"]
when:
- key: request.auth.claims[iss]
values: ["https://accounts.google.com"]
# notValues: ["https://accounts.google.com"]
上面这个授权策略表示允许两个源(服务帐户 cluster.local/ns/default/sa/sleep
和命名空间 dev
),在使用有效的 JWT
令牌发送请求时,可以访问命名空间 foo
中带有标签 app: httpbin
和 version: v1
的工作负载。
在上面的资源对象中配置了一个 when
字段,该字段是可选的,当配置后,指定了请求的附加条件列表,如果未设置,则允许任何条件。其中指定的 key
表示 Istio 属性的名称,目前支持的属性名称如下表所示:
名称 | 描述 | 支持的协议 | 示例 |
---|---|---|---|
request.headers |
HTTP 请求头。头部名称用 [] 包围,不使用任何引号 |
仅 HTTP | key: request.headers[User-Agent]``values: ["Mozilla/*"] |
source.ip |
来源工作负载实例的 IP 地址,支持单个 IP 或 CIDR | HTTP 和 TCP | key: source.ip``values: ["10.1.2.3", "10.2.0.0/16"] |
remote.ip |
根据 X-Forwarded-For 头或代理协议确定的原始客户端 IP 地址,支持单个 IP 或 CIDR | HTTP 和 TCP | key: remote.ip``values: ["10.1.2.3", "10.2.0.0/16"] |
source.namespace |
来源工作负载实例的命名空间,需要启用相互 TLS | HTTP 和 TCP | key: source.namespace``values: ["default"] |
source.principal |
来源工作负载的身份,需要启用相互 TLS | HTTP 和 TCP | key: source.principal``values: ["cluster.local/ns/default/sa/productpage"] |
request.auth.principal |
经过认证的 JWT 令牌的主体,根据 JWT 声明以 / 格式构建,需要应用请求认证策略 |
仅 HTTP | key: request.auth.principal``values: ["issuer.example.com/subject-admin"] |
request.auth.audiences |
经过认证的 JWT 令牌的预期受众,根据 JWT 声明 `` 构建,需要应用请求认证策略 | 仅 HTTP | key: request.auth.audiences``values: ["example.com"] |
request.auth.presenter |
经过认证的 JWT 令牌的授权展示者,根据 JWT 声明 `` 构建,需要应用请求认证策略 | 仅 HTTP | key: request.auth.presenter``values: ["123456789012.example.com"] |
request.auth.claims |
经过认证的 JWT 令牌的原始声明。声明名称用 [] 包围,不使用任何引号,也可以使用嵌套声明,需要应用请求认证策略。注意仅支持字符串类型或字符串列表类型的声明 |
仅 HTTP | key: request.auth.claims[iss]``values: ["*@foo.com"] — key: request.auth.claims[nested1][nested2]``values: ["some-value"] |
destination.ip |
目的地工作负载实例的 IP 地址,支持单个 IP 或 CIDR | HTTP 和 TCP | key: destination.ip``values: ["10.1.2.3", "10.2.0.0/16"] |
destination.port |
目的地工作负载实例端口,必须在 [0, 65535] 范围内。注意这不是服务端口 | HTTP 和 TCP | key: destination.port``values: ["80", "443"] |
connection.sni |
服务器名称指示,需要启用 TLS | HTTP 和 TCP | key: connection.sni``values: ["www.example.com"] |
experimental.envoy.filters.* |
过滤器的实验性元数据匹配,[] 中包裹的值作为列表进行匹配 |
HTTP 和 TCP | key: experimental.envoy.filters.network.mysql_proxy[db.table]``values: ["[update]"] |
我们这里配置的 key: request.auth.claims[iss]
表示请求的 JWT
中的 iss
字段,然后通过 values
字段指定了 iss
字段的值,这里我们指定了 https://accounts.google.com
,表示只有当 iss
字段的值为 https://accounts.google.com
时才允许访问。
除了可以配置 ALLOW
之外也可以配置 DENY
,比如下面的配置:
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: httpbin-deny
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: DENY
rules:
- from:
- source:
notNamespaces: ["foo"]
这个授权策略表示如果请求来源不是命名空间 foo
,请求将被拒绝。
⚠️ 注意 ⚠️:拒绝策略优先于允许策略,如果请求同时匹配上允许策略和拒绝策略,请求将被拒绝。Istio 首先评估拒绝策略,以确保允许策略不能绕过拒绝策略。
除了 values
之外也可以使用 notValues
字段,比如下面的授权策略对象:
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: deny-method-get
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
action: DENY
rules:
- to:
- operation:
methods: ["GET"]
# paths: ["/ip"] # 也可以指定 path
when:
- key: request.headers[x-token]
notValues: ["admin"]
该对象表示只有当 HTTP 头中 x-token
值不是 admin
时才会拒绝 GET 请求。
1、HTTP 请求授权
🚩 实战:HTTP 请求授权-20223.11.27(测试成功)
实验环境:
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
提取码:7yqb2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)
- 了解了授权策略的基本概念后,接下来我们通过一些示例来了解下具体的使用。首先来看一下如何为 HTTP 流量授权。
[root@master1 ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
details-v1-5f4d584748-6fzzm 2/2 Running 0 14d
fortio-deploy-5cd456bbdb-dfdn7 2/2 Running 0 14d
productpage-v1-564d4686f-krxns 2/2 Running 0 14d
ratings-v1-686ccfb5d8-zmvtr 2/2 Running 0 14d
reviews-v1-86896b7648-vclhs 2/2 Running 0 14d
reviews-v2-b7dcd98fb-nzljg 2/2 Running 0 14d
reviews-v3-5c5cc7b6d-dpnlg 2/2 Running 0 14d
[root@master1 ~]#kubectl get gateway
NAME AGE
bookinfo-gateway 14d
[root@master1 istio-1.19.3]#kubectl get vs bookinfo -oyaml
……
spec:
gateways:
- bookinfo-gateway
hosts:
- '*'
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
[root@master1 istio-1.19.3]#
- 这里还是以前面的 Bookinfo 应用为例进行说明:
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
echo $INGRESS_PORT
echo $SECURE_INGRESS_PORT
- 然后获取集群中任意一个节点的 IP 地址:
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
echo $INGRESS_HOST
echo $GATEWAY_URL
- 部署 Bookinfo 应用后通过
http://$GATEWAY_URL/productpage
访问 product 页面,可以看到如下内容:
在左下方有数据信息,包括:图书类型,页数,出版社等。
在页面右下方是数据评论数据。
当刷新页面时,应用会在 product 页面中以轮询的方式显示不同版本的评论:如红色星标,黑色星标,或者没有星标。
在 Istio 中我们可以很轻松地为网格中的工作负载配置访问控制。
我们这里首先配置一个简单的 allow-nothing
的策略,来拒绝工作负载的所有请求,然后我们再逐渐添加更多的访问权限。
- 首先在
default
命名空间中创建一个如下所示的授权策略对象:
#deny-all.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-nothing
namespace: default
spec: {}
可以看到上面的这个 AuthorizationPolicy
策略没有配置任何规则,spec:
字段为空值 {}
,意思是不允许任何流量,拒绝所有请求,它会把该策略应用于 default
命名空间中的每个工作负载。
- 直接应用该策略,然后打开浏览器访问 Bookinfo 的 productpage 页面,我们将会看到
RBAC: access denied
这样的信息,该错误表明配置的deny-all
策略生效了,现在在网格内没有任何规则允许对 default 命名空间中的工作负载进行任何访问。
[root@master1 istio]#kubectl apply -f deny-all.yaml
authorizationpolicy.security.istio.io/allow-nothing created
- 接下来我们配置一个授权策略,允许通过 GET 方法访问
productpage
工作负载,如下所示:
#productpage-viewer.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: productpage-viewer
namespace: default
spec:
selector:
matchLabels:
app: productpage
action: ALLOW
rules:
- to:
- operation:
methods: ["GET"]
可以看到上面的这个授权策略对象中我们并没有在 rules
字段中配置 from
字段,这意味着所有的请求源都被允许访问。
- 同样直接应用该资源对象后,在浏览器里访问 Bookinfo 的 productpage 页面,现在我们可以访问到
Bookinfo Sample
页面了,但会发现页面中有如下的错误:
[root@master1 istio]#vim productpage-viewer.yaml
[root@master1 istio]#kubectl apply -f productpage-viewer.yaml
authorizationpolicy.security.istio.io/productpage-viewer created
[root@master1 istio]#kubectl get AuthorizationPolicy
NAME AGE
allow-nothing 5m55s
productpage-viewer 9s
这些错误是符合预期的,因为我们没有授权 productpage 工作负载去访问 details 和 reviews 工作负载,所以当 productpage 工作负载去访问 details 和 reviews 工作负载时,就会出现错误。
- 接下来,我们再配置一个授权策略来容许访问其他工作负载,比如下面的策略对象:
#details-viewer.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: details-viewer
namespace: default
spec:
selector:
matchLabels:
app: details
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/bookinfo-productpage"]
to:
- operation:
methods: ["GET"]
该策略对象表示允许请求身份为 cluster.local/ns/default/sa/bookinfo-productpage
的工作负载以 GET 方式访问 details
工作负载,这个身份对应的是 productpage
工作负载的 ServiceAccount
,所以应用该资源对象后 productpage 工作负载就可以访问 details 工作负载了。
[root@master1 istio]#kubectl get po productpage-v1-564d4686f-krxns -oyaml|grep serviceAccountName
fieldPath: spec.serviceAccountName
serviceAccountName: bookinfo-productpage
[root@master1 istio]#
- 用同样的方式为
reviews
工作负载也配置一个类似的授权策略,如下所示:
#reviews-viewer
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: reviews-viewer
namespace: default
spec:
selector:
matchLabels:
app: reviews
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/bookinfo-productpage"]
to:
- operation:
methods: ["GET"]
- 应用上面的这两个资源对象后
productpage
服务就可以访问details
和reviews
服务了。现在在浏览器访问 Bookinfo productpage 页面我们就可以看到Bookinfo Sample
页面、Book Details
在左下方、Book Reviews
在右下方。
kubectl apply -f details-viewer.yaml
kubectl appply -f reviews-viewer.yaml
- 但是在
Book Reviews
部分偶尔会有Ratings service currently unavailable
的错误。
这是因为 reviews
工作负载没有权限访问 ratings
工作负载,要修复这个问题我们只需要授权 reviews
工作负载可以访问 ratings
工作负载即可。
- 创建一个如下所示的
AuthorizationPolicy
授权策略对象即可:
#ratings-viewer.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: "ratings-viewer"
namespace: default
spec:
selector:
matchLabels:
app: ratings
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/bookinfo-reviews"]
to:
- operation:
methods: ["GET"]
需要注意这里配置的 principals
身份要是 reviews
的服务身份。
- 应该该资源对象后再次在浏览器访问 Bookinfo productpage 页面就可以得到正常结果了。
[root@master1 istio]#kubectl apply -f ratings-viewer.yaml
- 测试完成后记得清理上面的资源对象:
kubectl delete authorizationpolicy allow-nothing
kubectl delete authorizationpolicy productpage-viewer
kubectl delete authorizationpolicy details-viewer
kubectl delete authorizationpolicy reviews-viewer
kubectl delete authorizationpolicy ratings-viewer
测试结束。😘
2、TCP 请求授权
🚩 实战:TCP 请求授权-20223.11.28(测试成功)
实验环境:
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
提取码:7yqb2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)
上面我们学习了如何为 HTTP 请求添加授权策略,接下来我们来了解下如何为 TCP 流量设置授权策略。
首先在命名空间 foo
中部署两个 TCP 工作负载:sleep
和 tcp-echo
,tcp-echo
工作负载会监听 9000、9001 和 9002 端口,并以 hello
为前缀输出它收到的所有流量。比如我们发送 world
给 tcp-echo
,那么它将会回复 hello world
。tcp-echo
的 Kubernetes Service 对象只声明了 9000 和 9001 端口。
- 使用以下命令部署示例命名空间和工作负载:
kubectl create ns foo
kubectl apply -f <(istioctl kube-inject -f samples/tcp-echo/tcp-echo.yaml) -n foo
kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo
- 我们可以使用以下命令验证
sleep
是否可以成功与tcp-echo
的 9000 和 9001 端口通信:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
hello port 9000
connection succeeded
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
hello port 9001
connection succeeded
配置 ALLOW 授权策略
- 接下来我们为
tcp-echo
应用创建一个授权策略,该策略将允许请求到 9000 和 9001 端口,资源对象如下所示:
#tcp-policy.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: tcp-policy
namespace: foo
spec:
selector:
matchLabels:
app: tcp-echo
action: ALLOW
rules:
- to:
- operation: # 仅允许请求到 9000 和 9001 端口
ports: ["9000", "9001"]
部署上面资源:
kubectl apply -f tcp-policy.yaml
- 使用以下命令验证是否允许请求 9000 端口:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
hello port 9000
connection succeeded
使用以下命令验证是否允许请求 9001 端口:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
hello port 9001
connection succeeded
这里需要注意下:
我们此时测试pod的9002端口,看还会通吗?
[root@master1 istio-author]#kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9002" | nc 10.244.1.23 9002' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
#可以看到,9002端口不通,符合预期。
- 如果现在我们为 9000 端口添加一个名为
methods
的 HTTP-only 字段来更新策略,如下所示:
#tcp-policy2.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: tcp-policy
namespace: foo
spec:
selector:
matchLabels:
app: tcp-echo
action: ALLOW
rules:
- to:
- operation:
methods: ["GET"]
ports: ["9000"]
上面的策略规则在对 TCP 流量使用了 HTTP-only 字段(methods),这会导致规则无效,Istio 会忽略无效的 ALLOW
规则。最终结果是该请求被拒绝,因为它与任何 ALLOW
规则都不匹配,所以我们现在验证 9000 端口会被拒绝:
#部署
[root@master1 istio-author]#kubectl apply -f tcp-policy2.yaml
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
当然同样 9001 端口请求与任何 ALLOW
规则也不匹配,请求也会被拒绝:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
配置 DENY 授权策略
上面我们是配置的 ALLOW
授权策略,接下来我们来看下如何配置 DENY
授权策略。
- 如下所示:
#tcp-policy3.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: tcp-policy
namespace: foo
spec:
selector:
matchLabels:
app: tcp-echo
action: DENY
rules:
- to:
- operation:
methods: ["GET"]
这个授权策略对象为 TCP 端口创建 DENY
规则时会不理解 HTTP-only 字段 methods
,由于这个规则的限制性质,将拒绝所有到 tcp 端口的流量:
#部署
[root@master1 istio-author]#kubectl apply -f tcp-policy3.yaml
authorizationpolicy.security.istio.io/tcp-policy configured
Warning: configured AuthorizationPolicy will deny all traffic to TCP ports under its scope due to the use of only HTTP attributes in a DENY rule; it is recommended to explicitly specify the port
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
同样 9001 端口的请求也会被拒绝:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
- 如果我们明确指定一个 TCP 端口来进行
DENY
,那么会怎样呢?如下所示:
#tcp-policy4.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: tcp-policy
namespace: foo
spec:
selector:
matchLabels:
app: tcp-echo
action: DENY
rules:
- to:
- operation:
methods: ["GET"]
ports: ["9000"]
比如我们请求 9000 端口,请求会被拒绝,这是因为此类请求与上述 DENY
策略中的 ports
是匹配的:
#部署
[root@master1 istio-author]#kubectl apply -f tcp-policy4.yaml
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
connection rejected
但是现在对于 9001 端口的请求,请求会被允许,这是因为此类请求与上述 DENY
策略中的 ports
不匹配:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
-c sleep -n foo -- sh -c \
'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
hello port 9001
connection succeeded
到这里我们就验证了为 TCP 流量进行授权策略配置。
- 回收测试资源
[root@master1 istio-author]#pwd
/root/istio/istio-author
[root@master1 istio-author]#ls
tcp-policy2.yaml tcp-policy3.yaml tcp-policy4.yaml tcp-policy.yaml
[root@master1 istio-author]#kubectl delete -f .
authorizationpolicy.security.istio.io "tcp-policy" deleted
Error from server (NotFound): error when deleting "tcp-policy2.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found
Error from server (NotFound): error when deleting "tcp-policy3.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found
Error from server (NotFound): error when deleting "tcp-policy4.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found
测试结束。😘
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码
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
最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!