第一部分:Pod 亲和性与反亲和性

Pod 调度是 Kubernetes 的核心功能之一。默认情况下,调度器(kube-scheduler)会根据资源请求(Request)进行打分筛选,但生产环境中我们往往需要更精细的控制:哪些 Pod 应该部署在一起(低延迟、高吞吐),哪些 Pod 应该物理隔离(高可用、容灾)。

亲和性(Affinity)与反亲和性(Anti-Affinity)提供了比 nodeSelector 更强大的表达能力。

1.1 核心概念演进

  • NodeSelector(节点选择器):最原始的方式。只能将 Pod 调度到包含特定标签的节点上,逻辑是“硬性必须”,且无法表达“优先”或“互斥”。

  • Node Affinity(节点亲和性):NodeSelector 的升级版。支持 required(硬亲和)和 preferred(软亲和)两种策略。

  • Pod Affinity/Anti-Affinity(Pod 亲和/反亲和):最强大的调度能力。不再基于节点标签,而是根据 已经在节点上运行的 Pod 的标签 来决定调度。解决了“希望两个服务跑在同一台机器上”或“不希望两个副本跑在同一可用区”的问题。

1.2 Node Affinity(节点亲和性)

Node Affinity 允许你告诉调度器:这个 Pod 应该(或必须)被调度到具有特定标签的节点上。

1.2.1 配置语法

在 Pod 的 spec 中,通过 affinity.nodeAffinity 字段定义。

  • requiredDuringSchedulingIgnoredDuringExecution硬亲和。如果节点不满足条件,Pod 不会被调度。相当于增强版的 nodeSelector

  • preferredDuringSchedulingIgnoredDuringExecution软亲和。调度器会尽量满足,但如果找不到,Pod 也会被调度到不满足条件的节点上。

示例:将 Pod 调度到有 GPU 且为 SSD 磁盘的节点

yaml

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  affinity:
    nodeAffinity:
      # 硬性要求:必须包含 gpu=true 标签
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - "true"
      # 软性偏好:最好是 disk-type=ssd
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: cuda-container
    image: nvidia/cuda:latest

Operator 操作符详解:

  • In:标签值在列表中。

  • NotIn:标签值不在列表中。

  • Exists:只要节点存在该标签(无论值是什么)。

  • DoesNotExist:节点不存在该标签。

  • Gt:值大于指定数值(字符串转数值比较)。

  • Lt:值小于指定数值。

1.2.2 调度阶段(DuringScheduling vs DuringExecution)

IgnoredDuringExecution 后缀意味着:如果 Pod 已经成功运行,即使节点上的标签后来发生了变化(不再满足亲和性),Pod 也不会被驱逐。Kubernetes 仅在调度时刻检查此条件。

1.3 Pod Affinity(Pod 亲和性)

Pod 亲和性决定一个 Pod 是否应该与某个 特定的 Pod 集合 运行在同一个“拓扑域”中。这里的“拓扑域”通常指 Node,但也可以是机架、可用区(Zone)等。

1.3.1 核心参数
  • labelSelector:通过标签选择器找到一组参考 Pod。

  • topologyKey:指定拓扑域的键(如 kubernetes.io/hostname 表示节点,topology.kubernetes.io/zone 表示可用区)。

  • namespaceSelector:默认只在当前 namespace 查找,可通过此字段跨命名空间匹配。

场景:将 Web 服务与 Redis 缓存部署在同一台物理机上,以降低网络延迟

yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - redis
            # 拓扑域:同一节点
            topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx

在这个例子中,web-server 的 Pod 只会被调度到 已经运行了带有 app=redis 标签的 Pod 的节点 上。

1.4 Pod Anti-Affinity(Pod 反亲和性)

反亲和性是亲和性的孪生兄弟,用于实现 互斥。它将 Pod 尽量(或必须)避开拥有特定标签的 Pod。

场景:将同一个 Deployment 的副本打散到不同节点,防止单点故障(高可用)

yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ha
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          # 硬性反亲和:同一个 Deployment 的副本绝对不能调度到同一节点
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx

如果集群只有 2 个节点,而 replicas 设为 3,那么第三个 Pod 将处于 Pending 状态,因为找不到没有运行 app=nginx Pod 的节点了(除非节点数足够多)。

1.5 高级用法与性能考量

1.5.1 topologySpreadConstraints(拓扑分布约束)

在 Kubernetes v1.19+ 中,topologySpreadConstraints 是反亲和性的更现代、更精细的替代方案。它允许你定义“在指定拓扑域中,最多允许有多少个同类型的 Pod”。

示例:确保服务在可用区间均匀分布

yaml

spec:
  topologySpreadConstraints:
  - maxSkew: 1          # 最大偏差,即最多允许一个 Zone 比另一个 Zone 多 1 个 Pod
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule  # 如果无法满足,则不调度
    labelSelector:
      matchLabels:
        app: my-app
  • maxSkew: 1 意味着所有可用区之间的 Pod 数量差异不能超过 1。

  • whenUnsatisfiable 可以设为 ScheduleAnyway(软约束)或 DoNotSchedule(硬约束)。

1.5.2 亲和性/反亲和性的开销
  • 计算复杂度:Pod 反亲和性(尤其是跨 Namespace 的)会大幅增加调度器的计算开销。对于大规模集群(数千节点),大量的反亲和性规则可能导致调度延迟。

  • 可用区打散:推荐优先使用 topologySpreadConstraints,它在性能上优于传统的 podAntiAffinity,且逻辑更清晰。


第二部分:污点(Taints)与容忍度(Tolerations)

如果说亲和性是“Pod 选择节点”,那么污点与容忍度则是 “节点拒绝 Pod” 的机制。它允许节点排斥一类特定的 Pod。

2.1 核心概念

  • Taint(污点):应用在 节点 上。如果一个节点被标记了污点,默认情况下,没有任何 容忍度(Toleration) 的 Pod 是不会被调度到该节点上的。

  • Toleration(容忍度):应用在 Pod 上。允许 Pod 调度到具有匹配污点的节点上。

2.2 污点的组成

每个污点由 keyvalue 和 effect 组成。格式:key=value:effect

Effect(效果)有三种:

  1. NoSchedule:核心影响。如果 Pod 不容忍此污点,则 Pod 不会被调度到此节点。已有的 Pod 不受影响

  2. PreferNoSchedule:软版本。系统会尽量不让 Pod 调度到此节点,但如果资源紧张,还是有可能调度上来。

  3. NoExecute:最严厉。如果 Pod 不容忍此污点:

    • 不会被调度到此节点。

    • 该节点上已有的、不容忍的 Pod 将被驱逐

2.2.1 管理污点命令

bash

# 添加污点
kubectl taint nodes node1 key1=value1:NoSchedule

# 移除污点(末尾加 -)
kubectl taint nodes node1 key1=value1:NoSchedule-

# 查看污点
kubectl describe node node1 | grep Taints

2.3 容忍度的配置

在 Pod 的 spec.tolerations 中定义。

2.3.1 匹配规则
  • 完全匹配keyvalueeffect 三个完全一致。

  • 存在性匹配operator: Exists,只要 key 和 effect 相同,忽略 value 是否匹配。

  • 通配 Effect:如果不指定 effect,表示容忍所有 effects。

示例:

yaml

tolerations:
# 1. 完全匹配容忍
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"

# 2. 只要存在 key1,无论 value 是什么,都容忍 NoSchedule
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

# 3. 容忍所有的污点(谨慎使用)
- operator: "Exists"

2.4 典型应用场景

场景一:专用节点

有时候我们希望某些节点只运行特定类型的服务(例如只运行监控组件),不希望其他业务 Pod 占用资源。

bash

# 给节点打污点
kubectl taint nodes node-monitoring dedicated=monitoring:NoSchedule

然后,只有具有以下容忍度的 Pod 才能被调度上去:

yaml

tolerations:
- key: "dedicated"
  operator: "Equal"
  value: "monitoring"
  effect: "NoSchedule"
场景二:有特殊硬件的节点(GPU)

给 GPU 节点打污点,防止普通 Pod 调度上去占用 CPU 资源,只允许需要 GPU 的 Pod 调度。

bash

kubectl taint nodes node-gpu gpu=true:NoSchedule

只有配置了容忍度的 Pod 才能使用 GPU 节点。

场景三:节点故障驱逐(NoExecute)

这是 Kubernetes 节点自我修复的关键机制。当节点失联(NotReady)时,Controller Manager 会自动给节点添加污点:

  • node.kubernetes.io/unreachable:节点失联。

  • node.kubernetes.io/not-ready:节点 NotReady 状态。

此时,如果 Pod 没有容忍这两个污点,且没有配置 tolerationSeconds,它们将被立即驱逐(或在默认的 5 分钟宽限期后驱逐)。

配置宽限期:

yaml

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 300   # 容忍 300 秒,如果 300 秒后节点仍未恢复,则 Pod 被驱逐

2.5 污点与亲和性的协作

在生产环境中,污点通常与亲和性结合使用。例如:

  1. 给节点打污点防止普通 Pod 进入。

  2. 给特定 Pod 加容忍度允许进入。

  3. 给该 Pod 加 Node Affinity,确保它只调度到这些打了污点的节点(防止调度到其他节点)。


第三部分:Pod 生命周期(Lifecycle)

理解 Pod 从创建到终止的全过程,对于排查故障和编写优雅的应用至关重要。

3.1 Pod 的阶段(Phase)

Pod 的 status.phase 是一个简单的状态机,汇总了 Pod 的生命周期阶段:

Phase 描述
Pending Pod 已被 Kubernetes 系统接受,但有一个或多个容器镜像尚未创建,或者尚未调度到节点。
Running Pod 已绑定到某个节点,且所有容器已创建,至少有一个容器正在运行,或正在启动/重启中。
Succeeded Pod 中所有容器均已成功终止,并且不会重启。常见于 Job 或 CronJob 完成任务后。
Failed Pod 中所有容器均已终止,且至少有一个容器以失败状态终止(非 0 退出码)。
Unknown 无法获取 Pod 状态,通常是由于与 Pod 所在节点通信失败。

3.2 容器状态(Container States)

Pod 内部的每个容器都有更细粒度的状态:

  • Waiting:容器正在等待(比如拉取镜像、等待依赖)。

  • Running:正常运行。

  • Terminated:运行结束(成功或失败)。包含 exit codereason 和 signal

3.3 容器生命周期钩子(Lifecycle Hooks)

Kubernetes 提供了两个钩子函数,让容器可以在生命周期中的关键点执行代码,实现优雅启动和优雅终止。

3.3.1 PostStart

在容器 创建后 立即执行。注意:PostStart 的执行与容器的 ENTRYPOINT 是异步的,Kubernetes 不会等待 PostStart 完成才标记容器为 Running。如果 PostStart 失败,容器会被终止。

3.3.2 PreStop

在容器 终止前 执行。这是实现优雅下线最关键的钩子。当 Pod 被删除时,Kubernetes 会先调用 PreStop 钩子,等待钩子执行完毕(或超过宽限期),再发送 SIGTERM 信号给容器。

典型用法:在 PreStop 中让应用从负载均衡器摘除、等待现有连接处理完毕。

yaml

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: nginx
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from postStart > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 15 && nginx -s quit"]

在这个例子中,当 Pod 被删除时,会先执行 sleep 15,给应用 15 秒的时间处理完现有请求,然后再优雅关闭 Nginx。

3.4 Pod 的终止流程(Termination Flow)

当执行 kubectl delete pod 或 Deployment 缩容时,会发生一系列有序的事件:

  1. Pod 标记为 Terminating:API Server 更新 Pod 状态,设置 deletionTimestamp

  2. kubelet 执行 PreStop 钩子:如果定义了 preStop,kubelet 会在本地执行该命令。

  3. kubelet 发送 SIGTERM 信号:如果 PreStop 执行完毕,或者超过了 terminationGracePeriodSeconds(默认 30s)的限制,kubelet 会向容器主进程发送 SIGTERM 信号。

  4. Endpoint 更新:几乎在 Pod 标记为 Terminating 的同时,控制平面会将该 Pod 从所有 Service 的 Endpoints 列表中移除。新的流量不再转发给该 Pod。

  5. 宽限期倒计时:kubelet 等待容器优雅退出(默认 30 秒)。

  6. 强制终止:如果容器在宽限期后仍未退出,kubelet 发送 SIGKILL 强制杀死容器。

  7. Pod 移除:kubelet 通知 API Server 移除该 Pod 对象。

关键参数 terminationGracePeriodSeconds
在 Pod 的 spec 中定义,默认为 30 秒。如果应用需要更长的关闭时间(如处理长连接),应调大此值。

yaml

spec:
  terminationGracePeriodSeconds: 60

3.5 重启策略(RestartPolicy)

Pod 的 spec.restartPolicy 决定了容器退出后的行为:

  • Always(默认):无论容器退出码是什么,都会重启。

  • OnFailure:只有容器以非 0 状态退出时才重启。

  • Never:从不重启。

注意:重启策略是针对 Pod 内的 容器,且由 kubelet 在本节点上执行。对于 Job 类工作负载,通常设为 OnFailure 或 Never;对于长期运行的 Service,使用 Always


第四部分:健康探测(Probes)

生产环境中,Pod 成功启动并进入 Running 状态,并不意味着它可以对外提供服务(例如可能处于死锁状态、内存泄露但进程未挂)。健康探测是保障服务 自愈 和 流量安全 的核心机制。

Kubernetes 提供了三种探针(Probe),由 kubelet 在节点上执行:

  1. livenessProbe(存活探针):判断容器是否 活着。如果失败,kubelet 会 杀死容器,并根据 restartPolicy 决定是否重启。用于修复死锁。

  2. readinessProbe(就绪探针):判断容器是否 准备好接收流量。如果失败,Pod 的 IP 会从 Service 的 Endpoints 中移除。不会重启容器。用于实现滚动更新和流量切换。

  3. startupProbe(启动探针):v1.16+ 引入。针对启动较慢的容器(如 Java 应用)。如果定义了 startupProbe,在它成功之前,livenessProbe 和 readinessProbe 将被禁用。一旦 startupProbe 成功,其余探针接管。如果 startupProbe 在 failureThreshold * periodSeconds 时间内一直失败,容器会被杀死。

4.1 探测方式

三种探针均支持以下三种执行方式:

4.1.1 exec

在容器内执行命令,退出码为 0 表示成功,非 0 表示失败。

yaml

livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5
4.1.2 httpGet

向容器发送 HTTP GET 请求,状态码在 200-400 之间(含 200,不含 400)视为成功。

yaml

readinessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: Custom-Header
      value: Awesome
  initialDelaySeconds: 3
  periodSeconds: 5
4.1.3 tcpSocket

尝试与容器的指定端口建立 TCP 连接,能连接则成功。

yaml

livenessProbe:
  tcpSocket:
    port: 3306
  initialDelaySeconds: 15
  periodSeconds: 10

4.2 探针参数调优

合理配置探针参数是 K8s 生产环境最佳实践的关键。配置不当可能导致“误杀”或“流量丢失”。

参数 含义 默认值 生产建议
initialDelaySeconds 容器启动后多久开始探测 0 必须设置,尤其是对于启动慢的应用(如 Java),建议 10-30s。
periodSeconds 探测频率 10 默认即可。高频探测(如 1s)会增加 kubelet 负载。
timeoutSeconds 探测超时时间 1 对于网络波动或复杂脚本,建议调至 5-10s。
successThreshold 成功阈值(失败->成功所需连续成功次数) 1 通常保持 1。对于 readinessProbe,可以设为 2 防止抖动。
failureThreshold 失败阈值(成功->失败所需连续失败次数) 3 默认 3 次。对于关键服务或启动慢的服务,建议调高(如 5-10)防止瞬时空闲毛刺导致重启。

公式:容器被重启的最短时间 ≈ initialDelaySeconds + (failureThreshold * periodSeconds)

4.3 常见场景配置

场景一:Java Spring Boot 应用

Spring Boot 应用启动慢(加载类、连接数据库),且通常有 /actuator/health 端点。

yaml

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 60   # 给 JVM 足够启动时间
  periodSeconds: 10
  failureThreshold: 3       # 连续 3 次失败才重启(约 30 秒容忍)

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 5
  failureThreshold: 2
场景二:大型单体应用(启动极慢)

如果应用启动需要 2-3 分钟,使用 startupProbe 防止存活探针在启动期间误杀。

yaml

startupProbe:
  httpGet:
    path: /health
    port: 8080
  failureThreshold: 30      # 允许探测 30 次
  periodSeconds: 10         # 每 10 秒一次,总共允许 300 秒启动
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  periodSeconds: 10
  initialDelaySeconds: 0    # 注意:startupProbe 成功前,livenessProbe 不会生效

4.4 探针的最佳实践

  1. Readiness 与 Liveness 端点分离:永远不要将业务逻辑的“繁忙”状态(如高 CPU)作为 Liveness 失败的依据。Liveness 应该只检测“死锁”或“彻底崩溃”。如果 Liveness 因为 CPU 高而失败,会导致滚动更新中断。

  2. Readiness 可以包含依赖检查:如果应用依赖数据库或 Redis,Readiness 探针可以检测这些依赖是否可用。如果依赖不可用,就绪状态变为 False,流量被切断,但容器不会重启,直到依赖恢复。

  3. 避免在 Liveness 中做过于复杂的校验:如果 Liveness 探针需要执行复杂的 SQL 查询,一旦数据库响应慢,Liveness 超时,容器会被重启,形成“雪崩效应”。


第五部分:实战结合——生产级 Deployment 配置

将以上四个概念融合到一个生产级的 Deployment 示例中,展示它们如何协同工作。

yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: awesome-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: awesome-app
  template:
    metadata:
      labels:
        app: awesome-app
        version: v2
    spec:
      # 1. 优雅终止时间
      terminationGracePeriodSeconds: 60

      # 2. 容器定义
      containers:
      - name: app
        image: myregistry/awesome-app:2.0
        ports:
        - containerPort: 8080

        # 3. 资源限制
        resources:
          requests:
            memory: "256Mi"
            cpu: "500m"
          limits:
            memory: "512Mi"
            cpu: "1000m"

        # 4. 健康探测配置
        startupProbe:
          httpGet:
            path: /actuator/info
            port: 8080
          failureThreshold: 12      # 12 * 5 = 60秒启动时间
          periodSeconds: 5

        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          periodSeconds: 10
          failureThreshold: 3
          timeoutSeconds: 5

        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          periodSeconds: 5
          failureThreshold: 2
          successThreshold: 1
          timeoutSeconds: 3

        # 5. 生命周期钩子(优雅下线)
        lifecycle:
          preStop:
            exec:
              command:
              - /bin/sh
              - -c
              - "sleep 15 && /usr/local/bin/app-graceful-stop"

      # 6. 调度策略:反亲和性(打散到不同节点) + 容忍度(若节点特殊)
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - awesome-app
              topologyKey: kubernetes.io/hostname
      tolerations:
      - key: "dedicated"
        operator: "Equal"
        value: "app-tier"
        effect: "NoSchedule"

第六部分:故障排查与调试

在实际运维中,经常会遇到 Pod 无法调度、频繁重启等问题。以下是一些排查思路。

6.1 调度失败(Pending)

  • 命令kubectl describe pod <pod-name> 查看 Events。

  • 常见原因

    • 资源不足:节点资源(CPU/Memory)不足,或节点有污点未容忍。

    • 节点选择器不匹配nodeSelector 或 Node Affinity 无法匹配任何节点。

    • Pod 反亲和性:由于反亲和性规则,找不到符合要求的节点(例如副本数大于可用节点数)。

6.2 容器不断重启(CrashLoopBackOff)

  • 命令kubectl logs <pod-name> --previous 查看上一次崩溃的日志。

  • 常见原因

    • Liveness 探针配置太严格initialDelaySeconds 太小或 failureThreshold 太小,导致启动慢的应用被杀死。

    • 应用内部错误:应用启动后立即退出(如连接数据库失败)。

    • 内存限制(OOMKilled)kubectl describe pod 查看 Last State 是否为 OOMKilled

6.3 服务不可用(但 Pod 状态为 Running)

  • 原因readinessProbe 失败,Pod 未进入 Endpoints。

  • 排查kubectl describe pod 查看 Readiness 探针的失败日志。检查 /healthz 端点是否返回了非 2xx 状态码。


第七部分:总结对比

特性 核心目的 作用对象 调度阶段 失败后果
Node Affinity Pod 选择特定属性的节点 Node Scheduling 无法调度 (Pending)
Pod Affinity Pod 与参考 Pod 聚集 Node/Zone Scheduling 无法调度 (Pending)
Pod Anti-Affinity Pod 与参考 Pod 分散 Node/Zone Scheduling 无法调度 (Pending)
Taints 节点排斥 Pod Node Scheduling + Execution 无法调度或驱逐
Tolerations Pod 允许被调度到有污点的节点 Pod Scheduling + Execution 无(允许)
livenessProbe 检测容器是否存活,修复死锁 Container Runtime 重启容器
readinessProbe 检测容器是否可接收流量 Container Runtime 摘除 Service 流量
startupProbe 保护启动慢的容器 Container Runtime 重启容器(仅在启动阶段)
Lifecycle Hooks 自定义启动和终止动作 Container Runtime 影响容器启动或终止流程

核心思想

  1. 调度(Affinity/Taints) 解决的是“Pod 放在哪”的问题,主要影响 集群资源利用率 和 可用性拓扑

  2. 生命周期与探针(Probes/Lifecycle) 解决的是“Pod 运行得怎么样”的问题,主要影响 服务自愈 和 流量无损变更

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐