K8S-简单入门
简介
Kubernetes(简称K8S)是开源的容器集群管理系统,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。它既是一款容器编排工具,也是全新的基于容器技术的分布式架构领先方案。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等功能,提高了大规模容器集群管理的便捷性。Kubernetes是容器集群管理工具
架构
Kubernetes集群包含有节点代理kubelet和Master组件(APIs, scheduler, etc),一切都基于分布式的存储系统。下面这张图是Kubernetes的架构图。
kubernetes 集群主要由 master 和 node 节点组成。
分层架构
- 核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
- 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
- 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
- 接口层:kubectl命令行工具、客户端SDK以及集群联邦
- 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
- Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
- Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
Kubernetes 集群组件
核心组件
- etcd保存了整个集群的状态;
- apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
- controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
- scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
- kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
- Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
- kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;
Addons(插件)
- kube-dns负责为整个集群提供DNS服务
- Ingress Controller为服务提供外网入口
- Heapster提供资源监控
- Dashboard提供GUI
- Federation提供跨可用区的集群
- Fluentd-elasticsearch提供集群日志采集、存储与查询
开放接口
- CRI(Container Runtime Interface):容器运行时接口,提供计算资源
- CNI(Container Network Interface):容器网络接口,提供网络资源
- CSI(Container Storage Interface):容器存储接口,提供存储资源
组件
Master 组件
Master组件提供集群的管理控制中心。
Master组件可以在集群中任何节点上运行。但是为了简单起见,通常在一台VM/机器上启动所有Master组件,并且不会在此VM/机器上运行用户容器。请参考 构建高可用群集以来构建multi-master-VM。
kube-apiserver
kube-apiserver用于暴露Kubernetes API。任何的资源请求/调用操作都是通过kube-apiserver提供的接口进行。请参阅构建高可用群集。
ETCD
etcd是Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划。
kube-controller-manager
kube-controller-manager运行管理控制器,它们是集群中处理常规任务的后台线程。逻辑上,每个控制器是一个单独的进程,但为了降低复杂性,它们都被编译成单个二进制文件,并在单个进程中运行。
控制器类型
- 节点控制器(NodeController):kubelet进程在启动时通过API Server注册自身的节点信息,并定时向API Server汇报状态信息,API Server在接收到这些信息后,会将这些信息更新到etcd中。在etcd中存储的节点信息包括节点健康状况、节点资源、节点名称、节点地址信息、操作系统版本、Docker版本、kubelet版本等。节点健康状况包含“就绪”(True)“未就绪”(False)和“未知”(Unknown)三种。
- 工作流程图:Node Controller通过API Server实时获取Node的相关信息,实现管理和监控集群中的各个Node的相关控制功能
- 工作流程图:Node Controller通过API Server实时获取Node的相关信息,实现管理和监控集群中的各个Node的相关控制功能
- 副本控制器(Replication Controller):核心作用是确保在任何时候集群中某个RC关联的Pod副本数量都保持预设值。资源对象Replication Controller简写为RC
- 职责
- 确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量。
- 通过调整RC的spec.replicas属性值来实现系统扩容或者缩容。
- 通过改变RC中的Pod模板(主要是镜像版本)来实现系统的滚动升级。
- 使用场景
- 重新调度(Rescheduling):即使发生节点故障或Pod副本被终止运行等意外状况,副本控制器时刻都能确保集群中指定数量的pod副本
- 弹性伸缩(Scaling):手动或者通过自动扩容代理修改副本控制器的spec.replicas属性值的方式来实现增加或减少副本的数量
- 滚动更新(Rolling Updates):本控制器被设计成通过逐个替换Pod的方式来辅助服务的滚动更新。
- 职责
- 端点控制器(Endpoints Controller):填充Endpoints对象(即连接Services&Pods)
- ServiceAccount Controller和Token Controller:为新的Namespace 创建默认帐户访问API Token
- 资源配额管理(ResourceQuota Controller):资源配额管理确保了指定的资源对象在任何时候都不会超量占用系统物理资源,避免了由于某些业务进程的设计或实现的缺陷导致整个系统运行紊乱甚至意外宕机,对整个集群的平稳运行和稳定性有非常重要的作用。
- 资源管理级别
- 容器级别:可以对CPU和Memory进行限制
- Pod级别:可以对一个Pod内所有容器的可用资源进行限制
- Namespace级别:为Namespace(多租户)级别的资源限制
- Pod数量
- Replication Controller数量
- Service数量
- ResourceQuota数量
- Secret数量
- 可持有的PV数量
- 资源管理级别
-
Namespace Controller:
- Service Controller:
cloud-controller-manager
云控制器管理器负责与底层云提供商的平台交互。云控制器管理器是Kubernetes版本1.6中引入的,目前还是Alpha的功能。
云控制器管理器仅运行云提供商特定的(controller loops)控制器循环。可以通过将--cloud-provider
flag设置为external启动kube-controller-manager ,来禁用控制器循环。
cloud-controller-manager 具体功能:
-
节点(Node)控制器
-
路由(Route)控制器
-
Service控制器
-
卷(Volume)控制器
kube-scheduler
kube-scheduler 监视新创建没有分配到Node的Pod,为Pod选择一个Node。
插件 addons
插件(addon)是实现集群pod和Services功能的 。Pod由Deployments,ReplicationController等进行管理。Namespace 插件对象是在kube-system Namespace中创建。
DNS
虽然不严格要求使用插件,但Kubernetes集群都应该具有集群 DNS。
群集 DNS是一个DNS服务器,能够为 Kubernetes services提供 DNS记录。
由Kubernetes启动的容器自动将这个DNS服务器包含在他们的DNS searches中。
了解更多详情
用户界面
kube-ui提供集群状态基础信息查看。更多详细信息,请参阅使用HTTP代理访问Kubernetes API
容器资源监测
容器资源监控提供一个UI浏览监控数据。
Cluster-level Logging
Cluster-level logging,负责保存容器日志,搜索/查看日志。
Node 组件
节点组件运行在Node,提供Kubernetes运行时环境,以及维护Pod。
kubelet
kubelet是主要的节点代理,它会监视已分配给节点的pod,具体功能:
- 安装Pod所需的volume。
- 下载Pod的Secrets。
- Pod中运行的 docker(或experimentally,rkt)容器。
- 定期执行容器健康检查。
- Reports the status of the pod back to the rest of the system, by creating a mirror pod if necessary.
- Reports the status of the node back to the rest of the system.
kube-proxy
kube-proxy通过在主机上维护网络规则并执行连接转发来实现Kubernetes服务抽象。
docker
docker用于运行容器。
RKT
rkt运行容器,作为docker工具的替代方案。
supervisord
supervisord是一个轻量级的监控系统,用于保障kubelet和docker运行。
fluentd
fluentd是一个守护进程,可提供cluster-level logging.。
名词解释
API 对象
API对象是K8s集群中的管理操作单元。
API 的属性
每个API对象都有3大类属性:元数据(metadata)、规范(spec)和状态(status)。
- metadata: 元数据是用来标识API对象的,每个对象都至少有3个元数据
- namespace
- name
- uid
- 各种各样的标签labels用来标识和匹配不同的对象
- spec: 规范描述了用户期望K8s集群中的分布式系统达到的理想状态(Desired State)
- 如:通过复制控制器Replication Controller设置期望的Pod副本数为3
- status: 描述了系统实际当前达到的状态(Status)
Pod
Pod是在K8s集群中运行部署应用或服务的最小单元。Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
Pods提供两种共享资源:网络和存储
-
网络
- 每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)
-
存储
- Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。
K8S中主要任务类型及其对应的控制器
Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。
任务类型 | 控制器 |
---|---|
长期伺服型(long-running) | 部署(Deployment) |
批处理型(batch) | 任务(Job) |
节点后台支撑型(node-daemon) | 后台支撑服务集(DaemonSet) |
有状态应用型(stateful application) | 有状态服务集(PetSet) |
Namespace
Namespace是对一组资源和对象的抽象集合,通常用于隔离不同的用户。
namespace 的状态
- Active
- Terminating : 在namespace删除过程中,其状态被置为 Terminating
namespace 的管理
# 1. 查询
kubectl get namespaces
# 2. 创建
## 注意:命名空间名称满足正则表达式[a-z0-9]([-a-z0-9]*[a-z0-9])?, 最大长度为63位
## 2.1 命令行直接创建
kubectl create namespace new-namespace
## 2.2 通过文件创建
cat > my-namespace.yaml <<-'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: new-namespace
EOF
kubectl create -f ./my-namespace.yaml
# 3. 删除
## default和kube-system 默认命名空间不可删除
## 删除一个namespace会自动删除所有属于该namespace的资源
kubectl delete namespaces new-namespace
复制控制器(Replication Controller,RC)
通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。RC 只支持基于等式的selector(env=dev或environment!=qa)
副本集(Replica Set,RS)
RS是新一代RC,提供同样的高可用能力,区别主要在于 RS 能支持更多种类的匹配模式,如基于集合的selector(version in (v1.0, v2.0)或env notin (dev, qa))。副本集对象一般不单独使用,而是作为Deployment的理想状态参数使用。
Deployments是一个更高层次的概念,它管理ReplicaSets,并提供对pod的声明性更新以及许多其他的功能。因此,建议您使用Deployments而不是直接使用ReplicaSets。
部署(Deployment)
部署表示对K8s集群的一次更新操作。Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法。
应用场景
- 定义Deployment来创建Pod和ReplicaSet
- 滚动升级和回滚应用
- 扩容和缩容
- 暂停和继续Deployment
服务(Service)
一个Pod只是一个运行服务的实例,它不能以确定的IP和端口号提供服务。Kubernete Service 是一个定义了一组 Pod 的策略的抽象,前端通过 Service 访问 对应的后端 Pod。
要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的的后端服务实例。在K8s集群中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。在K8s集群中微服务的负载均衡是由Kube-proxy实现的。Kube-proxy是K8s集群内部的负载均衡器。它是一个分布式代理服务器。
Virtual IPs and service proxies(虚拟IP和服务代理)
每一个节点上都运行了一个kube-proxy,这个应用监控着Kubermaster增加和删除服务,对于每一个服务,kube-proxy会随机开启一个本机端口,任何发向这个端口的请求都会被转发到一个后台的Pod当中,而如何选择是哪一个后台的pod的是基于SessionAffinity进行的分配。kube-proxy会增加iptables rules来实现捕捉这个服务的Ip和端口来并重定向到前面提到的端口。这样,用户感觉不到请求被转发到后端的Pod上。
Discovering services (服务发现)
Kubernetes 支持两种方式的来发现服务 ,环境变量和 DNS
环境变量
当一个Pod在一个node上运行时,kubelet 会针对运行的服务增加一系列的环境变量,它支持Docker links compatible 和普通环境变量。必须在POD创建之前声明环境变量
DNS
DNS 服务器监控着API SERVER ,当有服务被创建的时候,DNS 服务器会为之创建相应的记录,如果DNS这个服务被添加了,那么Pod应该是可以自动解析服务的。
External services(外部服务)
对于应用程序来说,可能有部分应用是放在Kubernete外部的(如:有单独的物理机来承担数据库的角色)的情况。Kubernetes支持两种调用方式:NodePorts,LoadBalancers
每一个服务都会有一个字段定义了该服务如何被调用(发现),该字段的枚举值:
- ClusterIP:使用一个集群固定IP,这个是默认选项
- NodePort:使用一个集群固定IP,但是额外在每个POD上均暴露这个服务,端口
- LoadBalancer:使用集群固定IP和NODEPord ,额外还会申请申请一个负载均衡器来转发到服务(load balancer )
任务(Job)
Job是K8s用来控制批处理型任务的API对象。批处理业务与长期伺服业务的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。
Job负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。
支持的 job 类型
- 非并行Job:通常创建一个Pod直至其成功结束
- 固定结束次数的Job:设置.spec.completions,创建多个Pod,直到.spec.completions个Pod成功结束
- 带有工作队列的并行Job:设置.spec.Parallelism但不设置.spec.completions,当所有Pod结束并且至少一个成功时,Job就认为是成功
根据.spec.completions和.spec.Parallelism的设置,可以将Job划分为以下几种pattern:
Job类型 | 使用示例 | 行为 | completions | Parallelism |
---|---|---|---|---|
一次性Job | 数据库迁移 | 创建一个Pod直至其成功结束 | 1 | 1 |
固定结束次数的Job | 处理工作队列的Pod | 依次创建一个Pod运行直至completions个成功结束 | 2+ | 1 |
固定结束次数的并行Job | 多个Pod同时处理工作队列 | 依次创建多个Pod运行直至completions个成功结束 | 2+ | 2+ |
并行Job | 多个Pod同时处理工作队列 | 创建一个或多个Pod直至有一个成功结束 | 1 | 2+ |
Job Controller
Job Controller负责根据Job Spec创建Pod,并持续监控Pod的状态,直至其成功结束。如果失败,则根据restartPolicy(只支持OnFailure和Never,不支持Always)决定是否创建新的Pod再次重试任务。
Job Spec 格式
- spec.template格式同 Pod
- RestartPolicy仅支持Never或OnFailure
- 单个Pod时,默认Pod成功运行后Job即结束
- .spec.completions标志Job结束需要成功运行的Pod个数,默认为1
- .spec.parallelism标志并行运行的Pod的个数,默认为1
- spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试
守护进程服务集(DaemonSet)
DaemonSet保证在每个Node上都运行一个容器副本,典型的后台支撑型服务包括 存储,日志和监控等在每个节点上支持K8s集群运行的服务。
有状态服务集(PetSet|StatefulSet)
有状态(stateful)<-> 无状态(stateless)
- RC和RS主要是控制提供无状态服务的,其所控制的Pod的名字是随机设置的,一个Pod出故障了就被丢弃掉,在另一个地方重启一个新的Pod
- PetSet是用来控制有状态服务,PetSet中的每个Pod的名字都是事先确定的,不能更改。
- 适合于PetSet的业务包括数据库服务MySQL和PostgreSQL,集群化管理服务Zookeeper、etcd等有状态服务
- PetSet做的只是将确定的Pod与确定的存储关联起来保证状态的连续性
应用场景
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
- 有序收缩,有序删除(即从N-1到0)
StatefulSet 组成
- 用于定义网络标志(DNS domain)的Headless Service
- 用于创建PersistentVolumes的volumeClaimTemplates
- 定义具体应用的StatefulSet
StatefulSet中每个Pod的DNS格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local
,其中
serviceName
为Headless Service的名字0..N-1
为Pod所在的序号,从0开始到N-1statefulSetName
为StatefulSet的名字namespace
为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace.cluster.local
为Cluster Domain
集群联邦(Federation)
服务的作用距离范围从近到远划分:
- 同主机(Host,Node)
- 跨主机 同可用区(Available Zone)
- 跨可用区 同地区(Region)
- 跨地区 同服务商(Cloud Service Provider)
- 跨云平台
K8s的设计定位是单一集群在同一个地域(Region)内,因为同一个地区的网络性能才能满足K8s的调度和计算存储连接要求。
联合集群(Federation)服务就是为提供跨Region跨服务商K8s集群服务而设计的。
每个K8s Federation有自己的分布式存储、API Server和Controller Manager。用户可以通过Federation的API Server注册该Federation的成员K8s Cluster。当用户通过Federation的API Server创建、更改API对象时,Federation API Server会在自己所有注册的子K8s Cluster都创建一份对应的API对象。在提供业务请求服务时,K8s Federation会先在自己的各个子Cluster之间做负载均衡,而对于发送到某个具体K8s Cluster的业务请求,会依照这个K8s Cluster独立提供服务时一样的调度模式去做K8s Cluster内部的负载均衡。而Cluster之间的负载均衡是通过域名服务的负载均衡来实现的。
存储卷(Volume)
K8s的存储卷的生命周期和作用范围是一个Pod。每个Pod中声明的存储卷由Pod中的所有容器共享。
支持的存储卷类型
- 公有云平台的存储
- AWS
- Azure云
- 分布式存储
- GlusterFS
- Ceph
- 主机本地目录|文件系统
- hostPath
- NFS
- Persistent Volume Claim (PVC) 逻辑存储
持久存储卷(Persistent Volume,PV)和持久存储卷声明(Persistent Volume Claim,PVC)
- PV和Node是资源的提供者,根据集群的基础设施变化而变化,由K8s集群管理员配置
- PVC和Pod是资源的使用者,根据业务服务的需求变化而变化,有K8s集群的使用者即服务的管理员来配置
节点(Node)
K8s集群中的计算能力由Node提供,最初Node称为服务节点Minion,后来改名为Node。K8s集群中的Node也就等同于 Apache Mesos(分布式资源管理框架) 集群中的Slave节点,是所有Pod运行所在的工作主机(物理机|虚拟机)。工作主机的统一特征是上面要运行kubelet管理节点上运行的容器。
为了管理Pod,每个Node节点上至少要运行container runtime(比如docker或者rkt)、kubelet和kube-proxy服务。
Node 管理
Kubernetes只使用 Node Controller 去检查 Node 是否真的存在这个 Node 。
Node Controller 的职责
- 维护Node状态
- 与Cloud Provider同步Node
- 给Node分配容器CIDR
- 删除带有NoExecute taint的Node上的Pods
默认情况下,kubelet在启动时会向master注册自己,并创建Node资源。
Node 的状态
- 地址:包括hostname、外网IP和内网IP
- 条件(Condition):包括OutOfDisk、Ready、MemoryPressure和DiskPressure
- 容量(Capacity):Node上的可用资源,包括CPU、内存和Pod总数
- 基本信息(Info):包括内核版本、容器引擎版本、OS类型等
Node 维护模式
此种模式标志Node不可调度但不影响其上正在运行的Pod
kubectl cordon $NODENAME
Node 调度
Taints和tolerations用于保证Pod不被调度到不合适的Node上。
- Taint 应用于Node上
- toleration 则应用于Pod上(Toleration是可选的)
密钥对象(Secret)
Secret是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。使用Secret的好处是可以避免把敏感信息明文写在配置文件里。在K8s集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问AWS存储的用户名密码。为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个Secret对象,而在配置文件中通过Secret对象引用这些敏感信息。这种方式的好处包括:意图明确,避免重复,减少暴漏机会。
用户帐户(User Account)和服务帐户(Service Account)
顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和K8s集群中运行的Pod提供账户标识。用户帐户和服务帐户的一个区别是作用范围;用户帐户对应的是人的身份,人的身份与服务的namespace无关,所以用户账户是跨namespace的;而服务帐户对应的是一个运行中程序的身份,与特定namespace是相关的。
Service Account
Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。
- User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计;
- User account是跨namespace的,而service account则是仅局限它所在的namespace;
- 每个namespace都会自动创建一个default service account
- Token controller检测service account的创建,并为它们创建 secret
- 开启ServiceAccount Admission Controller后
- 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
- 验证Pod引用的service account已经存在,否则拒绝创建
- 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
- 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/
名字空间(Namespace)
名字空间为K8s集群提供虚拟的隔离作用,K8s集群初始有两个名字空间,分别是默认名字空间default和系统名字空间kube-system,除此以外,管理员可以可以创建新的名字空间满足需要。
RBAC访问授权
K8s在1.3版本中发布了alpha版的基于角色的访问控制(Role-based Access Control,RBAC)的授权模式。相对于基于属性的访问控制(Attribute-based Access Control,ABAC),RBAC主要是引入了角色(Role)和角色绑定(RoleBinding)的抽象概念。在ABAC中,K8s集群中的访问策略只能跟用户直接关联;而在RBAC中,访问策略可以跟某个角色关联,具体的用户在跟一个或多个角色相关联。显然,RBAC像其他新功能一样,每次引入新功能,都会引入新的API对象,从而引入新的概念抽象,而这一新的概念抽象一定会使集群服务管理和使用更容易扩展和重用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)