Kubernetes(k8s)控制器(五):有状态应用StatefulSet
一.系统环境
本文主要基于Kubernetes1.21.9和Linux操作系统CentOS7.4。
| 服务器版本 | docker软件版本 | Kubernetes(k8s)集群版本 | CPU架构 |
|---|---|---|---|
| CentOS Linux release 7.4.1708 (Core) | Docker version 20.10.12 | v1.21.9 | x86_64 |
Kubernetes集群架构:k8scloude1作为master节点,k8scloude2,k8scloude3作为worker节点。
| 服务器 | 操作系统版本 | CPU架构 | 进程 | 功能描述 |
|---|---|---|---|---|
| k8scloude1/192.168.110.130 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico | k8s master节点 |
| k8scloude2/192.168.110.129 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
| k8scloude3/192.168.110.128 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
二.前言
在Kubernetes集群中部署和管理有状态应用是一项复杂的任务。有状态应用(Stateful Applications)通常要求持久化存储、唯一网络标识和有序的启动顺序。为了解决这些需求,Kubernetes提供了一个强大的控制器——StatefulSet。
使用StatefulSet的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Centos7 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/16686769.html。
三.StatefulSet简介
StatefulSet是Kubernetes的一个核心控制器,用于管理有状态应用的部署和伸缩。它为每个Pod分配一个唯一的标识符,确保有状态应用的稳定性和可扩展性。StatefulSet还可以自动处理Pod的创建、删除和更新,确保有状态应用的高可用性。
相比于Deployment等控制器,StatefulSet提供了以下特性:
- 稳定的网络标识符:每个Pod都会被分配一个稳定的主机名和网络标识符,使得有状态应用能够方便地进行服务发现和通信。
- 有序的部署和扩展:StatefulSet确保有状态应用的Pod按照指定的顺序进行部署和扩展,保证有序启动和关闭。
- 持久化存储:StatefulSet支持将持久化存储卷(Persistent Volume)与Pod绑定,使得有状态应用能够保留数据。
StatefulSet简写为sts,deployment控制器创建出来的pod都是一样的,更适合用于对外提供服务的pod,但是有一些应用,只适用于公
司内部,并不想对外提供服务,比如一个数据库有1个master节点和3个slave节点,master提供写,slave提供读,master通过日志把数
据拷贝给slave,slave节点有自己的存储,slave1读slave1的存储,slave2读slave2的存储,以此类推,slave节点不共享存储,这种情况
不适合使用deployment来创建pod,这种情况就需要使用statefulset控制器。
StatefulSet常用的属性包括:
- serviceName:指定与StatefulSet关联的Service的名称。这个Service将用于提供稳定的网络标识符,供其他应用访问StatefulSet中的Pod。
- volumeClaimTemplates:定义了每个Pod所需的持久化存储卷(Claim)模板。可以在这里指定存储类、存储大小等参数,以满足有状态应用对持久化存储的需求。
- podManagementPolicy:指定Pod管理策略,可以是OrderedReady或Parallel。OrderedReady表示按顺序启动和终止Pod,保证有序性和稳定性;Parallel表示可以并行启动和终止Pod,适用于无依赖关系的应用场景。
四.有状态应用和无状态应用区别
在Kubernetes中,应用可以分为有状态应用和无状态应用。有状态应用是指需要保留数据或状态的应用,而无状态应用则不需要。
有状态应用与无状态应用之间的主要区别如下:
- 网络标识符:有状态应用需要稳定的网络标识符,使得其他服务能够准确地访问它们。而无状态应用通常使用Service来提供负载均衡和服务发现,不直接依赖于特定的网络标识符。
- 数据持久化:有状态应用需要将数据保存在持久化存储中,并且能够在Pod重新启动时恢复数据。无状态应用通常将数据保存在外部数据库或缓存中,不依赖于Pod的生命周期。
- 有序启动和关闭:有状态应用通常需要按照特定的顺序启动和关闭,以确保数据的一致性。无状态应用则可以并行启动和关闭,不需要保证顺序。
五.StatefulSet
5.1 创建StatefulSet
创建存放yaml文件的目录。
[root@k8scloude1 ~]# mkdir statefulset
[root@k8scloude1 ~]# cd statefulset/
创建命名空间。
[root@k8scloude1 statefulset]# kubectl create ns statefulset
namespace/statefulset created
切换命名空间。
[root@k8scloude1 statefulset]# kubens statefulset
Context "kubernetes-admin@kubernetes" modified.
Active namespace is "statefulset".
现在还没有pod。
[root@k8scloude1 statefulset]# kubectl get pod
No resources found in statefulset namespace.
查看存储类storageclass,managed-nfs-storage是我们前面章节创建的storageclass,关于存储类storageclass的详细信息,请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass》。
[root@k8scloude1 statefulset]# cd sts/
[root@k8scloude1 sts]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage fuseim.pri/ifs Delete Immediate false 71d
查看managed-nfs-storage 的描述信息。
[root@k8scloude1 sts]# kubectl describe sc managed-nfs-storage
Name: managed-nfs-storage
IsDefaultClass: No
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"managed-nfs-storage"},"parameters":{"archiveOnDelete":"false"},"provisioner":"fuseim.pri/ifs"}
Provisioner: fuseim.pri/ifs
Parameters: archiveOnDelete=false
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
StatefulSet的yaml文件如下,功能为:创建一个StatefulSet,其中包含一个Pod运行着Nginx容器,并且有一个挂载了持久化存储卷的文件系统。这个StatefulSet通过Service提供稳定的网络标识符,可以被其他应用访问。
[root@k8scloude1 sts]# vim sts.yaml
[root@k8scloude1 sts]# cat sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
#matchLabels:设置了一个标签选择器,选择具有app: nginx标签的Pod。
matchLabels:
app: nginx
#serviceName: "nginx":指定了与StatefulSet关联的Service的名称为"nginx"。该Service将提供稳定的网络标识符供其他应用访问StatefulSet中的Pod。
serviceName: "nginx"
#replicas: 1:定义了StatefulSet的副本数量为1,即只创建一个Pod。
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
#当需要关闭容器时,立即杀死容器而不等待默认的30秒优雅停机时长。
terminationGracePeriodSeconds: 0
containers:
- name: nginx
image: nginx
#imagePullPolicy: IfNotPresent:表示如果本地已经存在该镜像,则不重新下载;否则从远程 Docker Hub 下载该镜像
imagePullPolicy: IfNotPresent
#把www卷挂载到容器内的路径为"/usr/share/nginx/html"
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
#volumeClaimTemplates:定义了持久化存储卷(Claim)模板,用于创建Pod所需的持久化存储。
volumeClaimTemplates:
- metadata:
#name: www:指定了卷模板的名称为"www"。
name: www
spec:
#accessModes: [ "ReadWriteOnce" ]:设置了卷的访问模式为"ReadWriteOnce",即可以被一个节点以读写方式挂载。
accessModes: [ "ReadWriteOnce" ]
#storageClassName: "managed-nfs-storage":指定了存储类的名称为"managed-nfs-storage",用于创建持久化存储卷。
storageClassName: "managed-nfs-storage"
resources:
requests:
#requests:设置了卷的请求资源,即要求1Gi的存储空间。
storage: 1Gi
现在还没有PVC。关于PV,PVC的详细内容,请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(四):持久卷Persistent Volume》。
[root@k8scloude1 sts]# kubectl get pvc
No resources found in statefulset namespace.
创建statefulset。
[root@k8scloude1 sts]# kubectl apply -f sts.yaml
statefulset.apps/web created
创建statefulset之后,pod和PVC都有了。
[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8s
[root@k8scloude1 sts]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-3b58a17b-acbd-4eb6-a1e4-a2bd2cf331ac 1Gi RWO managed-nfs-storage 37s
查看statefulset。
[root@k8scloude1 sts]# kubectl get statefulset -o wide
NAME READY AGE CONTAINERS IMAGES
web 1/1 111s nginx nginx
[root@k8scloude1 sts]# kubectl get sts -o wide
NAME READY AGE CONTAINERS IMAGES
web 1/1 2m11s nginx nginx
5.2 scale扩展副本数
把statefulset的副本数扩展为2。
[root@k8scloude1 sts]# kubectl scale sts web --replicas=2
statefulset.apps/web scaled
查看statefulset。
[root@k8scloude1 sts]# kubectl get sts -o wide
NAME READY AGE CONTAINERS IMAGES
web 2/2 3m nginx nginx
pod变为2个了。
[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 3m27s 10.244.251.230 k8scloude3 <none> <none>
web-1 1/1 Running 0 37s 10.244.251.219 k8scloude3 <none> <none>
把statefulset的副本数扩展为3。
[root@k8scloude1 sts]# kubectl scale sts web --replicas=3
statefulset.apps/web scaled
pod变为3个了。
[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m43s
web-1 1/1 Running 0 113s
web-2 1/1 Running 0 13s
查看statefulset。
[root@k8scloude1 sts]# kubectl get sts
NAME READY AGE
web 3/3 5m9s
pvc也变为了3个。
[root@k8scloude1 sts]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-3b58a17b-acbd-4eb6-a1e4-a2bd2cf331ac 1Gi RWO managed-nfs-storage 5m54s
www-web-1 Bound pvc-689e6b63-9ca8-413b-b240-5e2fae467a12 1Gi RWO managed-nfs-storage 3m4s
www-web-2 Bound pvc-565f137f-9e4b-44a1-8953-2b71e393a409 1Gi RWO managed-nfs-storage 84s
在每个pod里写入不同的内容。
[root@k8scloude1 sts]# kubectl exec -it web-0 -- sh -c "echo web-0 > /usr/share/nginx/html/index.html"
[root@k8scloude1 sts]# kubectl exec -it web-1 -- sh -c "echo web-1 > /usr/share/nginx/html/index.html"
[root@k8scloude1 sts]# kubectl exec -it web-2 -- sh -c "echo web-2 > /usr/share/nginx/html/index.html"
把statefulset的副本数扩展为0,所有pod都被删除了。
[root@k8scloude1 sts]# kubectl scale sts web --replicas=0
statefulset.apps/web scaled
[root@k8scloude1 sts]# kubectl get pod
No resources found in statefulset namespace.
把statefulset的副本数扩展为3。
[root@k8scloude1 sts]# kubectl scale sts web --replicas=3
statefulset.apps/web scaled
pod变为3个了。
[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3s
web-1 0/1 ContainerCreating 0 1s
[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8s
web-1 1/1 Running 0 6s
web-2 1/1 Running 0 4s
可以发现,把pod删除之后,又创建3个pod,新的3个pod名字没变,并且web-0的存储还是www-web-0,web-1的存储还是www-web-1,web-2的存储还是www-web-2,pod对应的存储不会错乱,每个pod里面的内容也没有变化。
[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 5m41s 10.244.251.225 k8scloude3 <none> <none>
web-1 1/1 Running 0 5m39s 10.244.251.226 k8scloude3 <none> <none>
web-2 1/1 Running 0 5m37s 10.244.251.218 k8scloude3 <none> <none>
[root@k8scloude1 sts]# kubectl exec -it web-0 -- sh -c "cat /usr/share/nginx/html/index.html"
web-0
[root@k8scloude1 sts]# kubectl exec -it web-1 -- sh -c "cat /usr/share/nginx/html/index.html"
web-1
[root@k8scloude1 sts]# kubectl exec -it web-2 -- sh -c "cat /usr/share/nginx/html/index.html"
web-2
手动删除web-1这个pod。
[root@k8scloude1 sts]# kubectl delete pod web-1
pod "web-1" deleted
statefulset自动创建了一个一模一样的web-1。
[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 3h7m 10.244.251.225 k8scloude3 <none> <none>
web-1 1/1 Running 0 3s 10.244.251.221 k8scloude3 <none> <none>
web-2 1/1 Running 0 3h7m 10.244.251.218 k8scloude3 <none> <none>
5.3 创建无头服务headless service
删除其中一个pod,statefulset很快又会生成一个同名的pod,但是IP地址发生变化,不适合直接使用pod的IP地址访问,使用svc访问更
好,但是创建svc有一定问题,svc具有负载均衡的作用,你访问svc,可能给你后端转发到web-0,也可能后端转发到web-1,后端pod又
都有自己专属的存储,所以svc也不合适,这时就需要无头服务(headless service)了。关于svc的详细内容,请查看博客《Kubernetes(k8s)服务service:service的发现和service的发布》。
现在没有svc。
[root@k8scloude1 sts]# kubectl get svc
No resources found in statefulset namespace.
如下是无头服务headless service的yaml文件,功能为:创建一个名为"nginx"的Service,它与具有app: nginx标签的Pod关联。该Service通过暴露在80端口上,并且没有Cluster IP地址。
无头服务headless service和service的区别是:无头服务headless service的 clusterIP为None。
[root@k8scloude1 sts]# cat svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
#设置了一个标签app: nginx,与其他相关资源进行关联。
labels:
app: nginx
spec:
ports:
#port: 80:将Service的端口设置为80。
- port: 80
#name: web:为该端口指定名称为"web"。
name: web
#clusterIP: None:将Service的Cluster IP设置为"None",表示该Service是一个Headless Service,没有集群内部的虚拟IP地址。
clusterIP: None
selector:
#app: nginx:设置了一个标签选择器,选择具有app: nginx标签的Pod。
app: nginx
创建无头服务headless service。
[root@k8scloude1 sts]# kubectl apply -f svc.yaml
service/nginx created
查看无头服务headless service,CLUSTER-IP为None。
[root@k8scloude1 sts]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx ClusterIP None <none> 80/TCP 12s app=nginx
5.4 通过无头服务headless service访问StatefulSet
查看pod的标签。
[root@k8scloude1 sts]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
web-0 1/1 Running 0 3h20m 10.244.251.225 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-0
web-1 1/1 Running 0 13m 10.244.251.221 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-1
web-2 1/1 Running 0 3h20m 10.244.251.218 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-2
因为无头服务headless service的CLUSTER-IP为None,所以访问方式为:pod名.svc名 来访问pod,statefulset创建的pod,pod名字是不变的。
[root@k8scloude1 sts]# curl web-0.nginx
curl: (6) Could not resolve host: web-0.nginx; 未知的名称或服务
容器外无法访问。
[root@k8scloude1 sts]# curl http://web-0.nginx
curl: (6) Could not resolve host: web-0.nginx; 未知的名称或服务
创建一个临时容器podclient作为客户端,来访问pod,--rm表示创建临时容器。
[root@k8scloude1 sts]# kubectl run -it podclient --image=yauritux/busybox-curl:latest --rm --image-pull-policy=IfNotPresent -- sh
If you don't see a command prompt, try pressing enter.
#访问成功
/home # curl web-0.nginx
web-0
/home # curl web-1.nginx
web-1
/home # curl web-2.nginx
web-2
/home # exit
Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running
pod "podclient" deleted
可以发现,StatefulSet只适合内部访问,不适合外部访问。
[root@k8scloude1 sts]# kubectl get sts
NAME READY AGE
web 3/3 3h43m
删除StatefulSet。
[root@k8scloude1 sts]# kubectl delete sts web
statefulset.apps "web" deleted
[root@k8scloude1 sts]# kubectl get sts
No resources found in statefulset namespace.
[root@k8scloude1 sts]# kubectl get pod
No resources found in statefulset namespace.
六.总结
本文介绍了Kubernetes中的一个重要控制器——StatefulSet,用于管理有状态应用的部署和伸缩。通过为每个Pod分配唯一的标识符和稳定的网络标识,StatefulSet确保了有状态应用的稳定性、可扩展性和数据持久性。相比于无状态应用,有状态应用需要更多的关注持久化存储和有序性启动等问题。
使用StatefulSet可以轻松地部署和管理有状态应用,使其能够充分发挥Kubernetes的优势。通过深入理解StatefulSet的概念和特性,我们可以更好地构建和管理复杂的有状态应用。
Kubernetes(k8s)控制器(五):有状态应用StatefulSet的更多相关文章
- Kubernetes(k8s)控制器(四):ReplicaSet
目录 一.系统环境 二.前言 三.ReplicaSet概览 四.ReplicaSet工作原理 五.ReplicaSet使用场景 六.创建ReplicaSet 七.扩展replicaset副本数 一.系 ...
- Kubernetes(k8s)控制器(三):ReplicationController
目录 一.系统环境 二.前言 三.ReplicationController概览 四.ReplicationController工作机制 五.创建ReplicationController 六.扩展r ...
- Kubernetes(k8s)控制器(二):DaemonSet
目录 一.系统环境 二.前言 三.DaemonSet 概览 四.创建DaemonSet 4.1 创建daemonset 让其在k8s集群所有worker节点运行pod 4.2 创建daemonset让 ...
- Kubernetes(k8s)控制器(一):deployment
目录 一.系统环境 二.前言 三.Kubernetes 控制器 四.Deployment概览 五.创建deployment 六.修改deploy副本数 6.1 kubectl edit deploy ...
- k8s控制器资源(五)
Pod pod在之前说过,pod是kubernetes集群中是最小的调度单元,pod中可以运行多个容器,而node又可以包含多个pod,关系如下图: 在对pod的用法进行说明之前,有必要先对docke ...
- Kubernetes K8S之资源控制器RC、RS、Deployment详解
Kubernetes的资源控制器ReplicationController(RC).ReplicaSet(RS).Deployment(Deploy)详解与示例 主机配置规划 服务器名称(hostna ...
- Kubernetes K8S之资源控制器StatefulSets详解
Kubernetes的资源控制器StatefulSet详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...
- Kubernetes K8S之资源控制器Daemonset详解
Kubernetes的资源控制器Daemonset详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2C/ ...
- Kubernetes K8S之资源控制器Job和CronJob详解
Kubernetes的资源控制器Job和CronJob详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...
- Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass
目录 一.系统环境 二.前言 三.静态制备和动态制备 四.存储类StorageClass 4.1 存储类StorageClass概览 4.2 StorageClass 资源 五.创建存储类Storag ...
随机推荐
- 我来泼盆冷水:正面迎击AI的时代千万别被ChatGPT割了韭菜
前言 ChatGPT从出来的时候我就一直密切关注,为此还加了不少群,用了不少套壳的程序,公司还开了专门的培训会,技术团队还为此搭建了接入ChatGPT的服务,帮助全公司的产品.商务.测试.运维.研发一 ...
- RDIFramework.NET代码生成器全新V5.0版本发布
RDIFramework.NET代码生成器介绍 RDIFramework.NET代码生成器,代码.文档一键生成. RDIFramework.NET代码生成器集代码生成.各数据库对象文档生成.数据库常用 ...
- Pytorch实现分类器
本文实现两个分类器: softmax分类器和感知机分类器 Softmax分类器 Softmax分类是一种常用的多类别分类算法,它可以将输入数据映射到一个概率分布上.Softmax分类首先将输入数据通过 ...
- 机器学习(七):梯度下降解决分类问题——perceptron感知机算法与SVM支持向量机算法进行二维点分类
实验2 感知机算法与支持向量机算法 一.预备知识 1.感知机算法 二.实验目的 掌握感知机算法的原理及设计: 掌握利用感知机算法解决分类问题. 三.实验内容 设计感知机算法求解, 设计SVM算法求解( ...
- js中宏任务和微任务
宏任务包括:<script>整体代码.setTimeout.setInterval.setImmediate.Ajax.DOM事件微任务:process.nextTick.Mutation ...
- C# 笔迹擦除8边形
擦除区域与橡皮大小不一致 测试反馈,擦除区域与真实的橡皮大小不一致: 上图中,橡皮显示是圆形的,但擦除效果是一个"8边形"区域. 找了一台8K屏,确实是能复现的: 看到这个诡异的8 ...
- pytest的几种执行方式
1 pytest xxxx 2 python -m pytest xxxx python -m pytest --html=./report/rep2.html test_env_pytest_ini ...
- HMS Core 6.10.0版本发布公告
分析服务 ◆ 事件分析下新增商品订阅分析报告,帮助开发者了解应用内用户付费订阅概况,评估订阅付费价值: ◆ 营销分析.用户质量.转化分析以及过滤器中,新增广告系列/广告任务通过ID进行搜索的功能,通过 ...
- 2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内。 小团在(a,
2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内. 小团在(a, ...
- 2021-04-30:一条直线上有居民点,邮局只能建在居民点上。给定一个有序正数数组arr,每个值表示 居民点的一维坐标,再给定一个正数 num,表示邮局数量。选择num个居民点建立num个 邮局,使
2021-04-30:一条直线上有居民点,邮局只能建在居民点上.给定一个有序正数数组arr,每个值表示 居民点的一维坐标,再给定一个正数 num,表示邮局数量.选择num个居民点建立num个 邮局,使 ...