分布式追踪

img

目录

自定义指标

除了 Istio 自带的指标外,我们还可以自定义指标,要自定指标需要用到 Istio 提供的 Telemetry API,该 API 能够灵活地配置指标、访问日志和追踪数据。Telemetry API 现在已经成为 Istio 中的主流 API。

需要注意的是,Telemetry API 无法与 EnvoyFilter 一起使用,请查看此问题 issue

从 Istio 版本 1.18 版本开始,Prometheus 的 EnvoyFilter 默认不会被安装, 而是通过 meshConfig.defaultProviders 来启用它,我们应该使用 Telemetry API 来进一步定制遥测流程,新的 Telemetry API 不但语义更加清晰,功能也一样没少。

对于 Istio 1.18 之前的版本,应该使用以下的 IstioOperator 配置进行安装:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    telemetry:
      enabled: true
      v2:
        enabled: false

Telemetry 资源对象的定义如下所示:

$ kubectl explain Telemetry.spec
GROUP:      telemetry.istio.io
KIND:       Telemetry
VERSION:    v1alpha1

FIELD: spec <Object>

DESCRIPTION:
    Telemetry configuration for workloads. See more details at:
    https://istio.io/docs/reference/config/telemetry.html

FIELDS:
  accessLogging <[]Object>
    Optional.

  metrics       <[]Object>
    Optional.

  selector      <Object>
    Optional.

  tracing       <[]Object>
    Optional.

可以看到 Telemetry 资源对象包含了 accessLoggingmetricsselectortracing 四个字段,其中 accessLoggingtracing 字段用于配置访问日志和追踪数据,而 metrics 字段用于配置指标数据,selector 字段用于配置哪些工作负载需要采集指标数据。

我们这里先来看下 metrics 字段的配置,该字段的定义如下所示:

$ kubectl explain Telemetry.spec.metrics
GROUP:      telemetry.istio.io
KIND:       Telemetry
VERSION:    v1alpha1

FIELD: metrics <[]Object>

DESCRIPTION:
    Optional.

FIELDS:
  overrides     <[]Object>
    Optional.

  providers     <[]Object>
    Optional.

  reportingInterval     <string>
    Optional.

可以看到 metrics 字段包含了 overridesprovidersreportingInterval 三个字段。

  • overrides 字段用于配置指标数据的采集方式。
  • providers 字段用于配置指标数据的提供者,这里一般配置为 prometheus
  • reportingInterval 字段用于配置指标数据的上报间隔,可选的。目前仅支持 TCP 度量,但将来可能会将其用于长时间的 HTTP 流。默认持续时间为 5 秒。

1.删除标签

🚩 实战:自定义指标-删除标签-2023.12.4(测试成功)

实验环境:

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(测试成功)

image-20231105111842627

比如以前需要在 Istio 配置的 meshConfig 部分配置遥测,这种方式不是很方便。比如我们想从 Istio 指标中删除一些标签以减少基数,那么你的配置中可能有这样一个部分:

# istiooperator.yaml
telemetry:
  enabled: true
  v2:
    enabled: true
    prometheus:
      enabled: true
      configOverride:
        outboundSidecar:
          debug: false
          stat_prefix: istio
          metrics:
            - tags_to_remove:
                - destination_canonical_service
                  ...

现在我们可以通过 Telemetry API 来配置,如下所示:

#remove-tags.yaml
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: remove-tags
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus # 指定指标数据的提供者
      overrides:
        - match: # 提供覆盖的范围,可用于选择个别指标,以及生成指标的工作负载模式(服务器和/或客户端)。如果未指定,则overrides 将应用于两种操作模式(客户端和服务器)的所有指标。
            metric: ALL_METRICS # Istio 标准指标之一
            mode: CLIENT_AND_SERVER # 控制选择的指标生成模式:客户端和/或服务端。
          tagOverrides: # 要覆盖的标签列表
            destination_canonical_service:
              operation: REMOVE
          # disabled: true  # 是否禁用指标

在上面的 Telemetry 资源对象中我们指定了一个 metrics 字段,表示用来自定义指标的,然后通过 providers.name 字段指定指标数据的提供者为 prometheus,然后最重要的是 overrides 字段,用于配置指标数据的采集方式。

其中 overrides.match.metric 字段用来指定要覆盖的 Istio 标准指标,支持指标如下所示:

名称描述
ALL_METRICS使用这个枚举表示应将覆盖应用于所有 Istio 默认指标。
REQUEST_COUNT对应用程序的请求计数器,适用于 HTTP、HTTP/2 和 GRPC 流量。Prometheus 提供商将此指标导出为:istio_requests_total。Stackdriver 提供商将此指标导出为:istio.io/service/server/request_count(服务器模式)istio.io/service/client/request_count(客户端模式)
REQUEST_DURATION请求持续时间的直方图,适用于 HTTP、HTTP/2 和 GRPC 流量。Prometheus 提供商将此指标导出为:istio_request_duration_milliseconds。Stackdriver 提供商将此指标导出为:istio.io/service/server/response_latencies(服务器模式)istio.io/service/client/roundtrip_latencies(客户端模式)
REQUEST_SIZE请求体大小的直方图,适用于 HTTP、HTTP/2 和 GRPC 流量。Prometheus 提供商将此指标导出为:istio_request_bytes。Stackdriver 提供商将此指标导出为:istio.io/service/server/request_bytes(服务器模式)istio.io/service/client/request_bytes(客户端模式)
RESPONSE_SIZE响应体大小的直方图,适用于 HTTP、HTTP/2 和 GRPC 流量。Prometheus 提供商将此指标导出为:istio_response_bytes。Stackdriver 提供商将此指标导出为:istio.io/service/server/response_bytes(服务器模式)istio.io/service/client/response_bytes(客户端模式)
TCP_OPENED_CONNECTIONS工作负载生命周期中打开的 TCP 连接计数器。Prometheus 提供商将此指标导出为:istio_tcp_connections_opened_total。Stackdriver 提供商将此指标导出为:istio.io/service/server/connection_open_count(服务器模式)istio.io/service/client/connection_open_count(客户端模式)
TCP_CLOSED_CONNECTIONS工作负载生命周期中关闭的 TCP 连接计数器。Prometheus 提供商将此指标导出为:istio_tcp_connections_closed_total。Stackdriver 提供商将此指标导出为:istio.io/service/server/connection_close_count(服务器模式)istio.io/service/client/connection_close_count(客户端模式)
TCP_SENT_BYTESTCP 连接期间发送的响应字节计数器。Prometheus 提供商将此指标导出为:istio_tcp_sent_bytes_total。Stackdriver 提供商将此指标导出为:istio.io/service/server/sent_bytes_count(服务器模式)istio.io/service/client/sent_bytes_count(客户端模式)
TCP_RECEIVED_BYTESTCP 连接期间接收的请求字节计数器。Prometheus 提供商将此指标导出为:istio_tcp_received_bytes_total。Stackdriver 提供商将此指标导出为:istio.io/service/server/received_bytes_count(服务器模式)istio.io/service/client/received_bytes_count(客户端模式)
GRPC_REQUEST_MESSAGES每发送一个 gRPC 消息时递增的客户端计数器。Prometheus 提供商将此指标导出为:istio_request_messages_total
GRPC_RESPONSE_MESSAGES每发送一个 gRPC 消息时递增的服务器计数器。Prometheus 提供商将此指标导出为:istio_response_messages_total

比如我们这里配置的指标为 ALL_METRICS 则表示要覆盖所有的 Istio 标准指标。

overrides.match.mode 则表示选择网络流量中底层负载的角色,如果负载是流量的目标(从负载的角度看,流量方向是入站),则将其视为作为 SERVER 运行。如果负载是网络流量的源头,则被视为处于 CLIENT 模式(流量从负载出站)。

名称描述
CLIENT_AND_SERVER选择适用于工作负载既是网络流量的源头,又是目标的场景。
CLIENT选择适用于工作负载是网络流量的源头的场景。
SERVER选择适用于工作负载是网络流量的目标的场景。

另外的 tagOverrides 字段表示要覆盖选定的指标中的标签名称和标签表达式的集合,该字段中的 key 是标签的名称,value 是对标签执行的操作,可以添加、删除标签,或覆盖其默认值。

字段类型描述是否必需
operationOperation操作控制是否更新/添加一个标签,或者移除它。
valuestring当操作为 UPSERT时才考虑值。值是基于属性的 CEL 表达式。例如:string(destination.port)request.host。Istio 暴露所有标准的 Envoy 属性。此外,Istio 也将节点元数据作为属性暴露出来。更多信息请参见 自定义指标文档

对应的操作 Operator 可以配置 UPSERTREMOVE 两个操作:

名称描述
UPSERT使用提供的值表达式插入或更新标签。如果使用 UPSERT操作,则必须指定 value字段。
REMOVE指定标签在生成时不应包含在指标中。
  • 现在我们直接应用上面的这个资源对象,然后我们再去访问下 productpage 应用,再次验证下指标数据中是否包含我们移除的 destination_canonical_service 标签。
istioctl dashboard prometheus --address 0.0.0.0

看下当前指标:(是含有这个destination_canonical_service 标签的)

部署资源,再次验证:

[root@master1 istio]#kubectl apply -f remove-tags.yaml 
telemetry.telemetry.istio.io/remove-tags created
[root@master1 istio]#kubectl get telemetries.telemetry.istio.io -nistio-system
NAME          AGE
remove-tags   21s

#重新去访问一次
istioctl dashboard prometheus --address 0.0.0.0

img

从上面的结果可以看到,我们已经成功删除了 destination_canonical_service 标签,这样就可以减少指标数据的基数了,可以用同样的方法再去删除一些不需要的标签。

另外需要注意在 Telemetry 对象中我们还可以通过 selector 字段来配置哪些工作负载应用这个遥测策略,如果未设置,遥测策略将应用于与遥测策略相同的命名空间中的所有工作负载,当然如果是在 istio-system 命名空间中则会应用于所有命名空间中的工作负载。

测试结束。😘

2.添加指标

🚩 实战:自定义指标-删除标签-2023.12.4

实验环境:

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(测试成功)

image-20231105111842627

  • 上面我们已经介绍了如何删除指标中的标签,那么我们也可以通过 Telemetry API 来添加指标中的标签,如下所示:
#add-tags.yaml
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: add-tags
spec:
  metrics:
    - overrides:
        - match:
            metric: REQUEST_COUNT
            mode: CLIENT
          tagOverrides:
            destination_x:
              operation: UPSERT
              value: "upstream_peer.labels['app'].value" # 必须加上双引号
        - match:
            metric: REQUEST_COUNT
          tagOverrides:
            destination_port:
              value: "string(destination.port)"
            request_host:
              value: "request.host"
      providers:
        - name: prometheus

在上面的这个资源对象中我们在 tagOverrides 中首先添加了如下的配置:

destination_x:
  operation: UPSERT
  value: "upstream_peer.labels['app'].value"

表示我们要添加一个名为 destination_x 的标签,然后通过 value 字段指定标签的值为 upstream_peer.labels['app'].value,这个值是一个 CEL 表达式(common expression)(必须在 JSON 中用双引号引用字符串)。Istio 暴露了所有标准的 Envoy 属性,对于出站请求,对等方元数据作为上游对等方(upstream_peer)的属性可用;对于入站请求,对等方元数据作为下游对等方(downstream_peer)的属性可用,包含以下字段:

属性类型
namestringPod 名
namespacestringPod 所在命名空间
labelsmap工作负载标签
ownerstring工作负载 owner
workload_namestring工作负载名称
platform_metadatamap平台元数据
istio_versionstring代理的版本标识
mesh_idstring网格唯一 ID
app_containerslist<string>应用容器的名称列表
cluster_idstring工作负载所属的集群标识

例如,用于出站配置中的对等应用标签的表达式是 upstream_peer.labels['app'].value,所以上面我们最终添加的 destination_x 这个标签的值为上游对等方的 app 标签的值。

另外添加的两个标签 destination_portrequest_host 的值分别为 string(destination.port)request.host,这两个值就来源于暴露的 Envoy 属性。

另外这个资源对象我们指定的是 default 命名空间,则只会对 default 命名空间中的工作负载应用这个遥测策略。

  • 同样应用这个资源对象后,再次访问 productpage 应用产生指标,现在我们可以看到指标中已经包含了我们添加的标签了。
[root@master1 istio]#kubectl apply -f add-tags.yaml 
telemetry.telemetry.istio.io/add-tags created
[root@master1 istio]#kubectl get telemetries.telemetry.istio.io 
NAME       AGE
add-tags   20s

#istioctl dashboard prometheus --address 0.0.0.0

img

测试结束。😘

  • 奇怪:我这里没有现象……(prometheus都已经删除重建过了的…… 先搁置吧。)

3.禁用指标

对于禁用指标则相对更简单了。

比如我们通过以下配置禁用所有指标:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: remove-all-metrics
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - disabled: true
          match:
            mode: CLIENT_AND_SERVER
            metric: ALL_METRICS

通过以下配置禁用 REQUEST_COUNT 指标:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: remove-request-count
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - disabled: true
          match:
            mode: CLIENT_AND_SERVER
            metric: REQUEST_COUNT

通过以下配置禁用客户端的 REQUEST_COUNT 指标:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: remove-client
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - disabled: true
          match:
            mode: CLIENT
            metric: REQUEST_COUNT

通过以下配置禁用服务端的 REQUEST_COUNT 指标:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: remove-server
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - disabled: true
          match:
            mode: SERVER
            metric: REQUEST_COUNT

到这里我们就了解了如何通过 Telemetry API 来自定义指标了,这样我们就可以根据自身的需求来定制了。

分布式追踪

分布式追踪可以让用户对跨多个分布式服务网格的请求进行追踪分析,可以通过可视化的方式更加深入地了解请求的延迟,序列化和并行度。Istio 利用 Envoy 的分布式追踪功能提供了开箱即用的追踪集成,Istio 提供了安装各种追踪后端服务的选项,并且通过配置代理来自动发送追踪 Span 到分布式追踪系统服务,比如 Zipkin、Jaeger、Lightstep、Skywalking 等后端服务。

img

一次完整的链路是由多个 span 组成的,每个 span 代表了一次请求的一部分,每个 span 都有一个唯一的 ID,这个 ID 用来标识这个 span,同时还有一个父 span 的 ID,用来标识这个 span 的父 span,这样就可以将多个 span 组成一个链路了。将不同的 span 关联到一起的方式是通过将父 span 的 ID 传递给子 span,这样就可以将多个 span 关联起来了,也就是上下文传递

上下文传递

尽管 Istio 代理能够自动发送 Span,但需要一些附加信息才能将这些 Span 加到同一个调用链,所以当代理发送 Span 信息的时候,应用程序需要附加适当的 HTTP 请求头信息,这样才能够把多个 Span 加到同一个调用链。

要做到这一点,每个应用程序必须从每个传入的请求中收集请求头(Header),并将这些请求头转发到传入请求所触发的所有传出请求。 具体选择转发哪些请求头取决于所配置的跟踪后端。

img

虽然 Istio 代理能够自动发送 span 信息,但它们需要一些提示来将整个跟踪关联起来。应用程序需要传播适当的 HTTP 头,以便当代理发送 span 信息时,span 能够正确地关联到单个跟踪中。为了实现这一点,应用程序需要从传入的请求中收集和传播头信息到所有的外发请求。要传播的头信息的选择取决于所使用的跟踪配置。

首先所有应用程序必须转发以下请求头:

  • x-request-id:这是 Envoy 专用的请求头,用于对日志和追踪进行一致的采样。

对于 Zipkin、Jaeger、Stackdriver 和 OpenCensus Agent,应转发 B3 请求头格式:

  • x-b3-traceid
  • x-b3-spanid
  • x-b3-parentspanid
  • x-b3-sampled
  • x-b3-flags

这些是 Zipkin、Jaeger、OpenCensus 和许多其他工具支持的请求头。

B3 是一个跟踪上下文传播的格式,它起源于 Zipkin 项目,但后来被其他许多分布式跟踪工具所采用。B3 的名字来源于 BigBrotherBird,它的主要目的是为了在服务之间传播跟踪上下文。它是由一组特定的 HTTP 头部组成的,可以传递跟踪信息,如 trace ID、span ID、采样决策等。

对于 Datadog,应转发以下请求头,对于许多语言和框架而言,这些转发由 Datadog 客户端库自动处理。

  • x-datadog-trace-id
  • x-datadog-parent-id
  • x-datadog-sampling-priority

对于 Lightstep,应转发 OpenTracing span 上下文请求头:

  • x-ot-span-context

对于 Stackdriver 和 OpenCensus Agent,可以使用以下任一请求头来替代 B3 多请求头格式。

  • grpc-trace-bin:标准的 gRPC 追踪头。
  • traceparent:追踪所用的 W3C 追踪上下文标准,受所有 OpenCensus、OpenTelemetry 和 Jaeger 客户端库所支持。
  • x-cloud-trace-context:由 Google Cloud 产品 API 使用。

W3C Trace Context 规范为追踪上下文传播数据的交换定义了一种普遍认同的格式 - 称为追踪上下文Trace Context 是一个在分布式追踪中用于跨服务和跨进程传递 trace 数据的标准。它定义了如何在 HTTP headers 中编码 trace 数据,以便在不同的服务间传递这些数据。具体来说,Trace Context 包含两个部分:traceparenttracestate

  • traceparent 以便携、固定长度的格式描述了传入请求在其追踪链路中的位置。它的设计重点是快速解析,每个跟踪工具都必须正确设置 traceparent,即使它仅依赖于 tracestate 中的供应商特定信息。
  • tracestate 通过一组 name/value 键值对表示来扩展带有供应商特定数据的 traceparent。将信息存储在 tracestate 中是可选的。

使用 W3C 跟踪上下文上下文传播接收 HTTP 请求可能如下所示:

GET /my-service HTTP/1.1
Host: myhost.com
traceparent: 00–0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331–01
tracestate: abc=00f067aa0ba902b7,xyz=99f067aa0ba902b7

traceparent 字段

Traceparent HTTP Header 字段标识跟踪系统中的传入请求,这是一个必要的部分,它由 4 个部分组成,所有字段都使用 16 进制编码:

  • version:系统适配的追踪上下文版本,当前版本是 00
  • trace-id:一个全局唯一的 ID,用于标识整个 trace,它是一个 32 个字符的十六进制字符串。
  • parent-id:一个服务内唯一的 ID,用于标识调用链中的当前操作(通常是一个函数或者请求处理器)。它是一个 16 个字符的十六进制字符串。
  • trace-flags:用于控制 trace 的行为,例如是否需要被采样等。

img

比如我们有一个前端应用中的一个接口中添加了 Trace Context,它的 traceparent 就是这样的:

img

比如我们这里一次 HTTP 请求中通过 Header 传递的 Traceparent 值为 00-a237a2ca46023ce3e1d214ad2866c9c0-d00a29e113663fed-01,对应到 Jaeger UI 中 trace id 为 a237a2ca46023ce3e1d214ad2866c9c0,parent id(也就是父级的 span id)为 d00a29e113663fed,trace flags 为 01

img

tracestate 字段

tracestate 这是一个可选的部分,用于跨多个服务传递额外的 trace 信息。它是一个键值对列表,列表中的每一项都由一个服务添加。服务可以在 tracestate 中添加一些自定义的数据,例如服务的版本、部署环境等。

Jaeger

接下来我们来看下如何在 Istio 中集成 Jaeger,Jaeger 是一个开源的分布式追踪系统,它由 Uber 开源,用于监视和故障排除复杂的分布式系统。同样我们这里还是以 Bookinfo 为例进行说明。

  • 首先要安装 Jaeger,我们这里只是演示,可以直接使用下面的方式进行安装:
$ kubectl apply -f samples/addons/jaeger.yaml
$ kubectl get pods -n istio-system -l app=jaeger
NAME                     READY   STATUS    RESTARTS         AGE
jaeger-db6bdfcb4-qpmmv   1/1     Running   20 (5h40m ago)   29d
$ kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
jaeger-collector       ClusterIP      10.107.60.208    <none>        14268/TCP,14250/TCP,9411/TCP,4317/TCP,4318/TCP                               29d
tracing                ClusterIP      10.98.235.44     <none>        80/TCP,16685/TCP                                                             29d
zipkin                 ClusterIP      10.98.118.194    <none>        9411/TCP                                                                     29d
# ......

如果是要在生产环境中使用 Jaeger,则需要参考官方文档进行部署。

  • 安装 Jaeger 完毕后,需要指定 Istio Envoy Proxy 代理向 Deployment 发送流量,可以使用 --set meshConfig.defaultConfig.tracing.zipkin.address=jaeger-collector:9411 进行配置,此外默认的采样率为 1%,可以通过 --set meshConfig.defaultConfig.tracing.sampling=100 来修改采样率。
istioctl install --set profile=demo --set meshConfig.defaultConfig.tracing.zipkin.address=jaeger-collector:9411 --set meshConfig.defaultConfig.tracing.sampling=100 -y
  • 现在我们就可以通过下面的命令来查看 Jaeger UI 了:
istioctl dashboard jaeger --address 0.0.0.0

[root@master1 ~]#kubectl get istiooperators.install.istio.io -nistio-system
NAME              REVISION   STATUS   AGE
installed-state                       26d

[root@master1 ~]#kubectl get istiooperators.install.istio.io -nistio-system -oyaml
……
    meshConfig:
      accessLogFile: /dev/stdout
      defaultConfig:
        proxyMetadata: {}
        tracing:
          sampling: 100
          zipkin:
            address: jaeger-collector:9411

http://172.29.9.61:16686/

img

  • 接下来我们只需要访问下 Bookinfo 应用,然后在 Jaeger UI 中就可以看到追踪数据了。要查看追踪数据,必须向服务发送请求。请求的数量取决于 Istio 的采样率,采样率在安装 Istio 时设置,默认采样速率为 1%。在第一个跟踪可见之前,我们需要发送至少 100 个请求。使用以下命令向 productpage 服务发送 100 个请求:
for i in $(seq 1 100); do curl -s -o /dev/null "http://$GATEWAY_URL/productpage"; done


for i in $(seq 1 100); do curl -s -o /dev/null "http://172.29.9.61:31666/productpage"; done
  • 从仪表盘左边面板的 Service 下拉列表中选择 productpage.default 并点击 Find Traces:

img

奇怪哇:我这里只有2个服务哦。。。

  • 然后我们可以点击访问 /productpage 的链路详细信息:

img

追踪信息由一组 Span 组成,每个 Span 对应一个 Bookinfo Service。这些 Service 在执行 /productpage 请求时被调用,或是 Istio 内部组件,例如:istio-ingressgateway

  • 在系统架构页面也可以看到对应的 DAG 图:

img

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码
x2675263825 (舍得), qq:2675263825。

image-20230107215114763

🍀 微信公众号
《云原生架构师实战》

image-20230107215126971

🍀 个人博客站点

http://onedayxyy.cn/

🍀 语雀

https://www.yuque.com/xyy-onlyone

🍀 csdn

https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20230107215149885

🍀 知乎

https://www.zhihu.com/people/foryouone

image-20230107215203185

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

690384132)]

🍀 微信公众号
《云原生架构师实战》

[外链图片转存中…(img-6x2RM7AH-1701690384133)]

🍀 个人博客站点

http://onedayxyy.cn/

[外链图片转存中…(img-oZnYMCzV-1701690384133)]

[外链图片转存中…(img-SrUrEOAh-1701690384134)]

🍀 语雀

https://www.yuque.com/xyy-onlyone

[外链图片转存中…(img-tkH0lDiV-1701690384134)]

🍀 csdn

https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

[外链图片转存中…(img-VSRnaRiY-1701690384134)]

🍀 知乎

https://www.zhihu.com/people/foryouone

[外链图片转存中…(img-z6nY6FJF-1701690384135)]

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

[外链图片转存中…(img-3dUX2kKM-1701690384136)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值