k8s阶段04 service资源及用法, Service名称解析和CoreDNS, 应用编排和Deployment, kubeproxy改为ipvs, StatefulSet及有状态应用编排示例
编排一个应用: (1)选择一个合适的工作负载型控制器,创建该控制器下的资源类型对应的资源对象; 应用编排控制器: Deployment StatefulSet DaemonSet Job CronJob (2)创建一个Service对象,为该应用提供一个固定的访问入口,服务发现和负载均衡 客户端流量来源存在不同,Service需要分别为其提供流量入口 External Client --> Nginx Service --> Nginx Pod --> Wowrdpress Service --> Wordpress Pod --> MySQL Service --> MySQL Pod Service: netfilter: iptables/ipvs ipvs集群:4层负载均衡器,port 定义vs:识别并筛选出 vip:port Service: 独占的VIP,独占的Port(建议选择使用应用协议的well-known的端口) ClusterIP, ServiceIP 10.96.0.0/12 向vs上添加rs Service,自动发现各上游端点, 标签选择器: 每个Pod,存在事先添加的标签 规则添加到集群中的哪些节点上? Service的类型: ClusterIP:客户端必须集群内部的地址;Service关键标识是ClusterIP; NodePort:客户端源自于集群外部(访问任一节点都行);Service的关键标识是NodePort; 增强版的CLusterIP,它使用NodePort接入集群外部客户端流量,使用ClusterIP接入集群内部流量 LoadBalancer:客户端源自于集群外部;Service的关键标识是NodePort;但额外需要一个集群外部的负载均衡器;#云服务提供支持Lbaas的负载均衡器可用(会动态生成),如手动搭建自己写比较麻烦,可用下方ExternalIP方式 LoadBalancer Service是增强版的NodePort #非云端采用如下方式,在节点加个ip地址对外暴露,OpenELB或MetalLB实现keepalive高可用 ExternalIP:手动配置;存在一些解决方案,能自动完成该功能,例如OpenELB或MetalLB; Service的模式:取决kube proxy的配置;#如果规模特别大, iptables最好改为nftables或ipvs iptables #默认 nftables #iptables的性能进阶版 ipvs Service:服务发现、负载均衡 服务发现:标签选择器 负载均衡:iptables/nftables/ipvs
Endpoints负责维护由相关Service标签选择器匹配的Pod对象 ◼ Endpoints对象上保存Service匹配到的所有Pod的IP和Port信息(即端点) ◼ 后端每个Pod的每次变动,都需更新整个Endpoints对象,并需要将该对象同步至每个节点的kube-proxy 为什么需要EndpointSlice? ◼ 即便只有一个Pod的IP等信息发生变动,也必须向集群中的每个kube-proxy发送整个endpoints对象 ◼ 存储在etcd中的对象的默认大小限制为1.5MB,这也决定了单个Endpoints对象的存储上限 ◆至多可以存储5000个左右的端点信息 ◼ 极端场景 ◆一个由2000个节点组成的集群中,更新一个有着5000个Pod IP的Endpoints对象,需要发送3GB的数据 ◆若以滚动更新机制,一次替换一个Pod的信息,更新这个Endpoints对象需要发送15T的数据 ◼ EndpointSlice资源通过将Endpoints切分为多片来解决上述问题 ◆自Kubernetes v1.16引入 ◆每个端点信息的变动,仅需要更新和发送一个EndpointSlice,而非整个Endpoints ◆每个EndpointSlice默认存储100个端点信息,不会触达etcd对单个对象的存储限制 ◆可在kube-controller-manager程序上使用“--max-endpoints-per-slice”选项进行配置 EndpointSlice并未取代Endpoints,二者同时存在
创建Service资源
由Service表示的负载均衡器,主要定义如下内容
◼ 负载均衡器入口:ClusterIP及相关的Service Port、NodePort(每个节点的Node IP都可用)
◆ 根据通信需求,确定选择的类型
◼ 标签选择器:用于筛选Pod,并基于筛选出的Pod的IP生成后端端点列表(被调度的上游端点)
◼ Service类型的专有配置
标签:附加在资源对象上的键值型元数据 ◼ 键标识:由“键前缀(可选)”和“键名”组成,格式为“key_prefix/key_name” ◆键前缀必须使用DNS域名格式 ◆键名的命名格式:支持字母、数字、连接号、下划线和点号,且只能以字母或数字开头;最长63个字符; ◼ “kubectl label”命令可管理对象的标签 标签选择器:基于标签筛选对象的过滤条件,支持两种类型 ◼ 基于等值关系的选择器 ◆操作符:=或==、!= ◼ 基于集合关系的选择器 ◆操作符:in、notin和exists ◆使用格式:KEY in (VALUE1, VALUE2, …)、 KEY notin (VALUE1, VALUE2, …)、KEY 和 !KEY 标签: 定义的方式1: metadata: labels: key1: value1 key2: value2 ... 定义方式2: 命令:kubectl label kubectl label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N 增加: kubectl label TYPE NAME KEY_1=VAL_1 ... KEY_N=VAL_N 修改:#overwrite进行覆盖 kubectl label --overwrite TYPE NAME KEY_1=VAL_1 ... KEY_N=VAL_N 删除:#名称后面加- kubectl label TYPE NAME KEY_1- #例: #查看现有标签 ]#kubectl get pods --show-labels #给一个pod添加标签 ]#kubectl label pods demoapp-7c58cd6bb-f5zvg version="v1.0" #修改标签 ]#kubectl label --overwrite pods demoapp-7c58cd6bb-f5zvg version="v1.1" #删除标签 ]#kubectl label pods demoapp-7c58cd6bb-f5zvg version- #标签选择器示例 ]#kubectl label pods demoapp-7c58cd6bb-f5zvg version="v1.0" ]#kubectl label pods demoapp-7c58cd6bb-f5zvg tier="backend" ]#kubectl label pods demoapp-7c58cd6bb-ptjcw version="v1.0" #过滤出标签值为version=v1.0 ]#kubectl get pods -l "version=v1.0" #过滤出标签值不为version=v1.0 (没有version标签的也可以) ]#kubectl get pods -l "version!=v1.0" #过滤出标签值在(frontend,backend,db)范围内 ]#kubectl get pods -l "tiel in (frontend,backend,db)" --show-labels #过滤出标签 (包含不存在tiel标签) ]#kubectl get pods -l "tiel notin (frontend,backend,db)" --show-labels #过滤出存在version键的 ]#kubectl get pods -l "version" #过滤出不存在version键 ]#kubectl get pods -l '!version' #或逻辑 ]#kubectl get pods -l "tier" -l "version=v1.0" #与逻辑 ]#kubectl get pods -l "tier,version=v1.0"
#Kubernetes上标准的API资源类型 apiVersion: v1 kind: Service metadata: name: … namespace: … spec: type <string> # Service类型,默认为ClusterIP selector <map[string]string> # 等值类型的标签选择器,内含“与”逻辑 ports: # Service的端口对象列表 - name <string> # 端口名称 protocol <string> # 协议,目前仅支持TCP、UDP和SCTP,默认为TCP port <integer> # Service的端口号 targetPort <string> # 后端目标进程的端口号或名称,名称需由Pod规范定义 nodePort <integer> # 节点端口号,仅适用于NodePort和LoadBalancer类型(尽量不要自己指) clusterIP <string> # Service的集群IP,建议由系统自动分配 externalTrafficPolicy <string> # 外部流量策略处理方式,Local表示由当前节点处理,Cluster表示向集群范围调度 loadBalancerIP <string> # 外部负载均衡器使用的IP地址,仅适用于LoadBlancer externalName <string> # 外部服务名称,该名称将作为Service的DNS CNAME值 #Service资源示例 #ClusterIP Service kind: Service apiVersion: v1 metadata: name: demoapp spec: type: ClusterIP # 类型标识,默认即为ClusterIP; selector: app: demoapp ports: - name: http # 端口名称标识 protocol: TCP # 协议,支持TCP、UDP和SCTP port: 80 # Service的端口号 targetPort: 80 # 目标端口号,即后端端点提供服务的监听端口号 #实际操作示例 #先创建pod ]#kubectl create deployment demoapp --image=ikubernetes/demoapp:v1.0 --replicas=3 #编辑service.yaml ]#vim demoapp-clusterip-service.yaml apiVersion: v1 kind: Service metadata: name: demoapp-clusterip namespace: default spec: selector: #选择标签 app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 80 #后端项目监听的80 #创建service ]#kubectl apply -f demoapp-clusterip-service.yaml #查看service [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-clusterip ClusterIP 10.96.150.225 <none> 80/TCP 26s #自动创建endpoint,同名 [root@master01 ~]#kubectl get ep NAME ENDPOINTS AGE demoapp-clusterip 10.244.1.59:80,10.244.2.48:80,10.244.3.44:80 2m37s #此时集群内部任意节点访问service,都会自动分配到其中的pod上 #让外部也能访问service,要用nodeport类型 #注意:nodeport是ClusterIP增强版,可以接受内部流量和外部流量,有nodeport就不需要ClusterIP了 ]#cp demoapp-clusterip-service.yaml demoapp-nodeport-service.yaml ]#vim demoapp-nodeport-service.yaml apiVersion: v1 kind: Service metadata: name: demoapp-nodeport namespace: default spec: type: NodePort #指定类型 selector: #选择标签 app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 80 #后端项目监听的80 #创建时既会分配ClusterIP,又会分配nodeport ]#kubectl apply -f demoapp-nodeport-service.yaml #查看service,80是service端口,32725是nodeport端口 [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-clusterip NodePort 10.111.228.210 <none> 80:32725/TCP 13s #集群内部任一节点访问 [root@node01 ~]#curl 10.111.228.210 #集群外部访问,这里用windows的cmd访问,通过任一节点,nodeport端口号进行访问 C:\Users\ldc>curl 10.0.0.151:32725 iKubernetes demoapp v1.0 !! ClientIP: 10.244.0.0, ServerName: demoapp-8699b9895b-8b7w5, ServerIP: 10.244.2.48!
流量策略一:Cluster,表示在整个Kubernetes集群范围内调度;
◼ 该流量策略下,请求报文从某个节点上的NodePort进入,该节点上的Service会将其调度至任何一个可用后端
Pod之上,而不关心Pod运行于哪个节点;
流量策略二:Local,表示仅将请求调度至当前节点上运行的可用后端端点;
◼ 该流量策略下,请求报文从某节点NodePort进入后,该节点上的Service仅会将请求调度至当前节点上适配到该Service的后端端点
◼ 仅应该从运行有目标Service对象后端Pod对象的节点的NodePort发起访问
需要注意, local策略需要配合外部的LB才可以, 因为必须访问有pod存在的节点才行, 需要外部的LB来控制
NodePort Service的外部流量策略: Local:结合外部的LB使用,外部LB要能理解Service Pod与Node的映射关系 Cluster:默认值,存在跃点 #LoadBalancer 如何获得LB:#阿里云ACK,腾讯云,华为云CCE,国外的云都有相应解决方案 MetalLB或OpenELB #也能实现LB功能(可能openstack不支持,云环境不一定支持)
如果实在没有上面的LB使用时,可以尝试使用 ExternalIP 方案
在Service上使用ExternalIP
◼ Service可通过使用节点上配置的辅助IP地址接入集群外部客户端流量
◼ 流量入口仅能是配置有该IP地址的节点,其它节点无效,因而此时在节点间无负载均衡的效果
◼ external IP所在的节点故障后,该流量入口失效,除非将该IP地址转移配置到其它节点
◼ 是除了LoadBalancer和NodePort外,接入外部流量的又一种方式
#示例 #在节点1上加个ip地址,该地址要能被路由,加在设备ens33上(这里为临时添加,重启就没了) [root@node01 ~]#ip addr add 10.0.0.200 dev ens33 #进行查看 [root@node01 ~]#ip a #把10.0.0.200关联到service进行使用,ClusterIp或者NodePort类型都行 #主节点上编辑(这里直接在线编辑,也可以编辑yaml再apply) spec下追加externalIPs [root@master01 ~]#kubectl edit svc demoapp-clusterip spec: ... externalIPs: - 10.0.0.200 #必须某个节点上有,并且从外部可路由过去的 #此时查看service出现EXTERNAL-IP [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-clusterip ClusterIP 10.111.228.210 10.0.0.200 80/TCP 86m kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9d #外部访问测试 C:\Users\ldc>curl 10.0.0.200 iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.0, ServerName: demoapp-8699b9895b-8b7w5, ServerIP: 10.244.2.48! #注意,如果节点宕机了,就无法访问了,可通过keepalive漂移到另一节点(keepalive会自动解决缓存问题,手动切需要解决) #当然非云端,MetalLB和OpenELB项目能直接解决外部访问,不用想上面通过ip绑定service
非云环境中的LoadBalancer ◼ 部署于裸机或类似裸机环境中的Kubernetes集群,缺少可用的LBaaS服务 ◼ MetalLB和OpenELB等项目为此提供了解决方案 MetalLB #K8s上部署MetalLB,不用手动在节点上添加ip地址,给MetalLB一个地址池,每当创建一个service,会模拟像LB给该service中一个pod ip。如果那个节点挂了,MetalLB会自动发现并把ip迁移到其他节点上,并通知整个网络更新缓存 #某个service的节点成为唯一入口,该节点会成为性能瓶颈。如果前端网络设备支持BGP协议,地址不配置在任何节点上,流量通过路由,路由通过BGP分发给每一个节点,任一节点都可以作为服务负载均衡器使用 ◼ A network load-balancer implementation for Kubernetes using standard routing protocols ◼ MetalLB核心功能的实现依赖于两种机制 ◆Address Allocation(地址分配):基于用户配置的地址池,为用户创建的LoadBalancer分配IP地址,并配置在节点上 ◆External Announcement(对外公告):让集群外部的网络了解新分配的IP地址,MetalLB使用ARP、NDP或BGP实现 ◼ MetalLB 可配置为在二层模式或BGP 模式下运行 ◼ 二层模式(ARP/NDP) ◆LoadBalancer IP地址配置在某一个节点上,并使用ARP(IPv4)或NDP(IPv6)对外公告 ◆拥有LoadBalancer IP地址的节点将成为Service流量的惟一入口,并在节点故障时自动进行故障转移 ◆并未真正实现负载均衡,存在性能瓶颈,且故障转移存在秒级的延迟 ◼ BGP模式 ◆集群中的所有节点与本地网络中的BGP Router建立BGP对等会话,通告LoadBalancer IP,从而告知Router如何进行流量路由 ◆可以实现跨多个节点的真正意义上的负载均衡
#官网 https://metallb.universe.tf/ #安装MetalLB(这里采用上面的二层模式,某个节点成为唯一入口) #主节点 [root@master01 ~]#kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml #会运行几个pod(每个主机上都有speaker,做ARP公告的) [root@master01 ~]#kubectl get pods -n metallb-system NAME READY STATUS RESTARTS AGE controller-8694df9d9b-ks75k 0/1 Running 0 71s speaker-2lx2s 0/1 Running 0 71s speaker-fbwd6 0/1 Running 0 71s speaker-tbsg9 0/1 Running 0 71s speaker-wgrg4 0/1 Running 0 71s #地址池里必须是集群节点地址范围,并且是其他节点没有使用的地址
[root@master01 ~]#cd learning-k8s/MetalLB/ [root@master01 MetalLB]#vim metallb-ipaddresspool.yaml apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: localip-pool namespace: metallb-system #metallb自己的名称空间 spec: addresses: #确保地址没人用 - 10.0.0.51-10.0.0.80 autoAssign: true #自动配置在某个节点上 avoidBuggyIPs: true #避免使用全0或全1的ip #创建地址池 [root@master01 MetalLB]#kubectl apply -f metallb-ipaddresspool.yaml #做公告模式 [root@master01 MetalLB]#vim metallb-l2advertisement.yaml apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: localip-pool-l2a namespace: metallb-system spec: ipAddressPools: - localip-pool interfaces: #通过这个接口对外发ARP(自问自答) - ens33 #网卡地址(如果多个节点网卡名不同,那都列在这) #创建对应资源 [root@master01 MetalLB]#kubectl apply -f metallb-l2advertisement.yaml #使用loadbalance service(通过MetalLB实现) [root@master01 ~]#cp demoapp-nodeport-service.yaml demoapp-lb-service.yaml [root@master01 ~]#vim demoapp-lb-service.yaml apiVersion: v1 kind: Service metadata: name: demoapp-lb namespace: default spec: type: LoadBalancer #externalTrafficPolicy: Local #外部流量策略,默认是 Cluster selector: app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 80 #应用 [root@master01 ~]#kubectl apply -f demoapp-lb-service.yaml #查看LoadBalancer也有nodeport,其实就是增强版NodePort,就算没有EXTERNAL-IP地址,也可以nodeport访问 [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-clusterip NodePort 10.111.228.210 10.0.0.200 80:32725/TCP 18h demoapp-lb LoadBalancer 10.107.254.160 10.0.0.51 80:32194/TCP 15s #外部访问测试(流量分发到不同pod上) C:\Users\ldc>curl 10.0.0.51 iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.1, ServerName: demoapp-8699b9895b-j6rrh, ServerIP: 10.244.1.64! C:\Users\ldc>curl 10.0.0.51 iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.0, ServerName: demoapp-8699b9895b-8b7w5, ServerIP: 10.244.2.54! C:\Users\ldc>curl 10.0.0.51 iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.0, ServerName: demoapp-8699b9895b-fn4k9, ServerIP: 10.244.3.48! #修改lb service的流量策略为local(会检查后端节点状态,能响应就作为后端端点使用,没响应流量不会转发过去) [root@master01 ~]#vim demoapp-lb-service.yaml apiVersion: v1 kind: Service metadata: name: demoapp-lb namespace: default spec: type: LoadBalancer externalTrafficPolicy: Local #外部流量策略,默认是 Cluster selector: app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 80 #应用 [root@master01 ~]#kubectl apply -f demoapp-lb-service.yaml #外部访问测试,请求都打在了10.244.1.64上(也可能会变) C:\Users\ldc>curl 10.0.0.51 iKubernetes demoapp v1.0 !! ClientIP: 10.0.0.1, ServerName: demoapp-8699b9895b-j6rrh, ServerIP: 10.244.1.64! C:\Users\ldc>curl 10.0.0.51 iKubernetes demoapp v1.0 !! ClientIP: 10.0.0.1, ServerName: demoapp-8699b9895b-j6rrh, ServerIP: 10.244.1.64! #可能ip地址被配在1.64所在主机上,EXTERNAL-IP不能实现真正的负载均衡;如果碰巧配的ip地址上没有pod,可能依然无法响应 #要解决这个问题要么使用BGP模式,要么使用云端的LBaaS服务
ClusterIP Service: CoreDNS: API Server的客户端,注册监视每个Service的变动 自动为每个Service生成FQDN格式的名称 <service_name>.<namespace>.svc.<zone> <zone>: cluster.local 例如:default/demoapp: demoapp.default.svc.cluster.local dev/pod --> kube-system/openebs dev.svc.cluster.local svc.cluster.local cluster.local openebs.kube-system 自动生资源记录
Cluster DNS(CoreDNS)是Kubernetes集群的必备附件,负责为Kubernetes提供名称解析和服务发现 ◼ 每个Service资源对象,在CoreDNS上都会自动生成一个遵循“<service>.<ns>.svc.<zone>”格式的名称 ◆<service>:当前Service对象的名称 ◆<ns>:当前Service对象所属的名称空间 ◆<zone>:当前Kubernetes集群使用的域名后缀,默认为“cluster.local” ◼ 围绕该名称会生成一些DNS格式的资源记录 CoreDNS会持续监视API Server上的Service资源对象的变动,并实时反映到相关的DNS资源记录中 Pod中各容器默认会在/etc/resolv.conf中,将nameserver指向CoreDNS相关的Service的ClusterIP ◼ 由kubelet创建Pod时根据指定的配置自动注入
每个Service,在CoreDNS上都会有A/AAAA、SRV和PTR资源记录 ◼ A/AAAA资源记录 ◆<service>.<ns>.svc.<zone>. <ttl> IN A <cluster-ip> ◆<service>.<ns>.svc.<zone>. <ttl> IN AAAA <cluster-ip> ◼ 为每个定义了名称的端口生成一个SRV记录,以支持服务发现 ◆_<port>._<proto>.<service>.<ns>.svc.<zone>. <ttl> IN SRV <weight> <priority> <port-number> <service>.<ns>.svc.<zone>. ◼ 为每个A记录(例如a.b.c.d)或AAAA记录生成对应的PTR记录,例如 ◆<d>.<c>.<b>.<a>.in-addr.arpa. <ttl> IN PTR <service>.<ns>.svc.<zone>. ◆h4.h3.h2.h1.g4.g3.g2.g1.f4.f3.f2.f1.e4.e3.e2.e1.d4.d3.d2.d1.c4.c3.c2.c1.b4.b3.b2.b1.a4.a3.a2.a1.ip6.arpa <ttl> IN PTR <service>.<ns>.svc.<zone>. Pod可基于Service的DNS名称向其发起服务访问请求 #示例: #查看当前服务 [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-clusterip NodePort 10.111.228.210 10.0.0.200 80:32725/TCP 24h demoapp-lb LoadBalancer 10.107.254.160 10.0.0.51 80:32194/TCP 5h30m #进入pod [root@master01 ~]#kubectl exec -it demoapp-8699b9895b-8b7w5 -- /bin/sh #注:这里名称空间default是当前所在的名称空间,如果属于其他名称空间,这里会变为对应名称空间 [root@demoapp-8699b9895b-8b7w5 /]# cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 #限制路径最多5个. #解析域名(名称后缀会根据resolv.conf选项自动补全) [root@demoapp-8699b9895b-8b7w5 /]# host -t A demoapp-clusterip demoapp-clusterip.default.svc.cluster.local has address 10.111.228.210 [root@demoapp-8699b9895b-8b7w5 /]# host -t A demoapp-clusterip.default demoapp-clusterip.default.svc.cluster.local has address 10.111.228.210 Pod中的每个容器:#pod内部是可以互相互相通讯的,外部请求没办法解析service地址 /etc/resolv.conf nameserver 10.96.0.10 #请求调用 External Client --> Nginx Service --> Nginx Pod --> Wowrdpress Service --> Wordpress Pod --> MySQL Service --> MySQL Pod #内部请求,可以直接通过名称进行调用service External Client --> Nginx Service --> Nginx Pod --> Wowrdpress Service name --> Wordpress Pod --> MySQL Service name --> MySQL Pod #外部访问nginx不能使用域名解析 Nginx Service: NodeIP:NodePort blog.magedu.com:NodePort LoadBalancer --> Nginx Service (NodeIP:NodePort) #改进 blog.magedu.com --> LB_IP
Kubernetes支持在单个Pod资源规范上自定义DNS解析策略和配置,并组合生效 ◼ spec.dnsPolicy:解析策略 ◼ spec.dnsConfig:名称解析机制 DNS解析策略 #dnsPolicy ◼ Default:从运行在的节点继承DNS名称解析相关的配置 ◼ ClusterFirst:于集群DNS服务上解析集群域内的名称,其他域名的解析则交由从节点继承而来的上游名称服务器 #hostNetwork:pod使用宿主机的网络 ◼ ClusterFirstWithHostNet:专用于在设置了hostNetwork的Pod对象上使用的ClusterFirst策略 ◼ None:用于忽略Kubernetes集群的默认设定,而仅使用由dnsConfig自定义的配置 DNS解析机制 #dnsConfig类配置 ◼ nameservers <[]string>:DNS名称服务器列表,附加于由dnsPolicy生成的DNS名称服务器之后 ◼ searches <[]string>:DNS名称解析时的搜索域,附加由于dnsPolicy生成的搜索域之后 ◼ options <[]Object>:DNS解析选项列表,同dnsPolicy生成的解析选项合并成最终生效的定义 #示例(自定义dns解析策略) [root@master01 services]#vim pod-with-dnspolicy.yaml apiVersion: v1 kind: Pod metadata: name: pod-with-dnspolicy namespace: default spec: containers: - name: demo image: ikubernetes/demoapp:v1.0 imagePullPolicy: IfNotPresent dnsPolicy: None dnsConfig: #自定义 nameservers: #指明dns服务器 - 10.96.0.10 #本地dns - 223.5.5.5 - 223.6.6.6 searches: - svc.cluster.local - cluster.local - ilinux.io #自定义域名 options: - name: ndots value: "5" [root@master01 services]#kubectl apply -f pod-with-dnspolicy.yaml [root@master01 services]#kubectl exec -it pod-with-dnspolicy -- cat /etc/resolv.conf search svc.cluster.local cluster.local ilinux.io nameserver 10.96.0.10 nameserver 223.5.5.5 nameserver 223.6.6.6 options ndots:5
类似无头服务
Service的特殊类型: ExternalName: #本质上就是pod去连接一个服务直接跳转(CName)到外部真正的服务 Internal Client --> Service Name --> External Name --> 外部的某服务的IP地址; #使得访问集群外部就像访问集群内部服务一样 wordpress Pod --> mysql.blog.svc.cluster.local --> mysql.magedu.com --> 172.29.5.101/172.29.5.102 #示例: [root@master01 services]#vim externalname-redis-svc.yaml kind: Service apiVersion: v1 metadata: name: externalname-redis-svc namespace: default spec: type: ExternalName externalName: redis.ik8s.io #CName解析为这个名字 ports: - protocol: TCP port: 6379 targetPort: 6379 nodePort: 0 selector: {} #不选择任何pod [root@master01 services]#kubectl apply -f externalname-redis-svc.yaml [root@master01 services]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE externalname-redis-svc ExternalName <none> redis.ik8s.io 6379/TCP 17s #随便进一个pod,看看能不能解析 [root@master01 services]#kubectl exec -it demoapp-8699b9895b-8b7w5 -- /bin/sh [root@demoapp-8699b9895b-8b7w5 /]# host -t A externalname-redis-svc externalname-redis-svc.default.svc.cluster.local is an alias for redis.ik8s.io. redis.ik8s.io has address 1.2.3.4 #这个域名是互联网上的
Headless Service: 创建一个常规的ClusterIP类型的Service,但却要求不分配ClusterIP; 被StatefulSet所依赖; #示例 [root@master01 ~]#vim demoapp-headless-service.yaml apiVersion: v1 kind: Service metadata: name: demoapp-headless namespace: default spec: type: ClusterIP #指定类型 clusterIP: None #不要分配任何ClusterIP selector: app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 80 [root@master01 ~]#kubectl apply -f demoapp-headless-service.yaml [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp-headless ClusterIP None <none> 80/TCP 20s #查看endpoint [root@master01 ~]#kubectl get ep NAME ENDPOINTS AGE demoapp-headless 10.244.1.64:80,10.244.2.54:80,10.244.3.48:80 119s #进入任意pod,解析名称 [root@master01 services]#kubectl exec -it demoapp-8699b9895b-8b7w5 -- /bin/sh #直接解析到各个pod,service负载均衡器不发挥作用,通过dns服务器默认轮询调度 [root@demoapp-8699b9895b-8b7w5 /]# host -t A demoapp-headless demoapp-headless.default.svc.cluster.local has address 10.244.2.54 demoapp-headless.default.svc.cluster.local has address 10.244.1.64 demoapp-headless.default.svc.cluster.local has address 10.244.3.48 #Headless Service这种类型会被StatefulSet工作负载控制器所依赖
[root@master01 ~]#kubectl get cm -n kube-system NAME DATA AGE coredns 1 10d [root@master01 ~]#kubectl get cm coredns -n kube-system -o yaml apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . /etc/resolv.conf { #转给/etc/resolv.conf解析 max_concurrent 1000 } #如果有个内网地址想要被解析,可以后面再加个forward(给dns服务器) cache 30 loop reload loadbalance } kind: ConfigMap metadata: creationTimestamp: "2024-11-16T04:10:04Z" name: coredns namespace: kube-system resourceVersion: "225" uid: b97caacd-6428-451e-861e-aaad19b522d7
工作负载编排: 专用编排以Pod形式承载的应用程序的控制器 Deployment Controller --> 管理一切Deployment资源类型下的资源对象 控制器模式: Spec: 期望状态,由用户定义,并提交给API Server Status: 实际状态,由控制器负责通过运行代码生成
控制器模型(控制回路)
◼ 初始,Controller负责根据Input(目标状态)控制System,并生成Output(结果状态)
◼ Feedback根据Output生成Feedback Signal,而后由Error Detector基于Feedback Signal和Input来判定是否存在错误,并在有错误时生成Error Signal
◼ Error Signal将驱动Controller生成Actuating Signal,并控制System的行为与Input要求相同
Kubernetes Controller的控制回路
◼ Controller根据spec,控制System生成Status
◼ Controller借助于Sensor持续监视System的Spec和Status,在每一次控制回路中都会对二者进行比较,并确保System的Status不断逼近或完全等同Status
服务类应用: Deployment --> ReplicaSet --> Pod:无状态应用 #Deployment通过ReplicaSet来编排控制pod Nginx --> 3 Pods StatefulSet:编排有状态应用 Redis Cluster --> 3 Pods RabbitMQ Cluster --> 3 Pods Operator:operatorhub.io #增强版StatefulSet 结合专用的自定义资源类型(CRD, Custom Resource Defination)来完成; ElasticSearch DaemonSet:编排系统级应用 #通常集群中每个节点部署一个pod(一个就够了) 作业类应用 Job: 一次性作业 CronJob:周期性作业 应用编排的基本配置框架 replicas: 副本数 selector: 标签选择器 template:Pod模板 Deployment: ReplicaSet: #示例 #查看ReplicaSet资源,简写rs [root@master01 ~]#kubectl explain rs GROUP: apps KIND: ReplicaSet VERSION: v1 ... #定义replicaset [root@master01 ~]#vim replicaset-demoapp.yaml apiVersion: apps/v1 kind: ReplicaSet metadata: name: rs-demo namespace: dev #保证dev名称空间存在 spec: replicas: 2 #要创建几个pod selector: matchLabels: #service没有该字段,replicaset必须有 app: demoapp template: metadata: #名字自动生成,(上面name后缀加随机字符);名称空间会和上面相同 labels: #保证上面的标签选择器能选中 app: demoapp #tier: backend #其他标签随意定义 spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.0 #创建 [root@master01 ~]#kubectl create namespace dev [root@master01 ~]#kubectl apply -f replicaset-demoapp.yaml #查看replicaset,rs为简写 [root@master01 ~]#kubectl get rs -n dev NAME DESIRED CURRENT READY AGE rs-demo 2 2 2 45s #显示更多字段 [root@master01 ~]#kubectl get rs -n dev -o wide NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR rs-demo 2 2 2 2m4s demoapp ikubernetes/demoapp:v1.0 app=demoapp #扩容 [root@master01 ~]#vim replicaset-demoapp.yaml ... spec: replicas: 5 #修改为5 ... [root@master01 ~]#kubectl apply -f replicaset-demoapp.yaml [root@master01 ~]#kubectl get rs -n dev NAME DESIRED CURRENT READY AGE rs-demo 5 5 5 9m37s #缩容 #除了修改yaml文件,通过命令行命令也能实现 [root@master01 ~]#kubectl scale replicaset rs-demo --replicas=4 -n dev #更新 [root@master01 ~]#vim replicaset-demoapp.yaml ... spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.1 #修改镜像 [root@master01 ~]#kubectl apply -f replicaset-demoapp.yaml #查看replicaset变为1.1,但是去访问里面的服务,依然是1.0 [root@master01 ~]#kubectl get rs -n dev -o wide NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR rs-demo 5 5 5 22m demoapp ikubernetes/demoapp:v1.1 app=demoapp #注意,这里只有删一个pod,它自动创建一个才会用新模板变1.1 #replicaset没有办法完成高级的更新功能 #Deployment建立在replicaset基础之上,提供滚动更新并保存历史的能力
负责编排无状态应用的基础控制器是ReplicaSet,相应的资源类型通过三个关键组件定义如何编排一个无状态应用
◼ replicas:期望运行的Pod副本数
◼ selector:标签选择器
◼ podTemplate:Pod模板
Deployment是建立在ReplicaSet控制器上层的更高级的控制器
◼ 借助于ReplicaSet完成无状态应用的基本编排任务
◼ 它位于ReplicaSet更上面一层,基于ReplicaSet提供了滚动更新、回滚等更为强大的应用编排功能
◼ 是ReplicaSet资源的编排工具
◆Deployment编排ReplicaSet
◆ReplicaSet编排Pod
◼ 但是,应该直接定义Deployment资源来编排Pod应用,ReplicaSet无须显式给出
#示例: [root@master01 ~]#cp replicaset-demoapp.yaml deployment-demoapp.yaml [root@master01 ~]#vim deployment-demoapp.yaml apiVersion: apps/v1 kind: Deployment #修改类型 metadata: name: deploy-demo #修改名称 namespace: dev spec: replicas: 4 selector: matchLabels: app: demoapp template: metadata: labels: app: demoapp spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.1 #删掉前面的replicaset [root@master01 ~]#kubectl delete -f replicaset-demoapp.yaml #创建 [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml #查看所有资源 [root@master01 ~]#kubectl get all -n dev NAME READY STATUS RESTARTS AGE pod/deploy-demo-5fbd75c9c-5bt7g 1/1 Running 0 101s pod/deploy-demo-5fbd75c9c-hqzhs 1/1 Running 0 101s pod/deploy-demo-5fbd75c9c-lkd5x 1/1 Running 0 101s pod/deploy-demo-5fbd75c9c-qh8gp 1/1 Running 0 101s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/deploy-demo 4/4 4 4 101s NAME DESIRED CURRENT READY AGE replicaset.apps/deploy-demo-5fbd75c9c 4 4 4 101s #后缀5fbd75c9c对应yaml中的template内容hash码,template内容有变更,这个hash码一定会变更 [root@master01 ~]#kubectl get deploy -n dev -o wide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR deploy-demo 4/4 4 4 4m7s demoapp ikubernetes/demoapp:v1.1 app=demoa #更新 [root@master01 ~]#vim deployment-demoapp.yaml ... spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.2 #修改版本 #apply应用会自动创建ReplicaSet,并替换旧版本,默认一次更新25% #这里应用的同时,查看deployment的状态(非必须) [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml && kubectl rollout status -f deployment-demoapp.yaml [root@master01 ~]#kubectl get all -n dev NAME READY STATUS RESTARTS AGE pod/deploy-demo-fdccdbb9d-6m5s8 1/1 Running 0 72s pod/deploy-demo-fdccdbb9d-88fjl 1/1 Running 0 82s pod/deploy-demo-fdccdbb9d-8jd56 1/1 Running 0 82s pod/deploy-demo-fdccdbb9d-vdm2z 1/1 Running 0 71s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/deploy-demo 4/4 4 4 12m NAME DESIRED CURRENT READY AGE replicaset.apps/deploy-demo-5fbd75c9c 0 0 0 12m replicaset.apps/deploy-demo-fdccdbb9d 4 4 4 82s #查看历史 [root@master01 ~]#kubectl rollout history -f deployment-demoapp.yaml deployment.apps/deploy-demo REVISION CHANGE-CAUSE 1 <none> #创建 2 <none> #更新版本 #回顾,直接指或者用配置文件加载也行,不指定版本就回滚当前版本前一个版本 [root@master01 ~]#kubectl rollout undo -f deployment-demoapp.yaml deployment.apps/deploy-demo rolled back #查看历史(因为1和3相同内容,历史中相同内容的版本号会被取消) [root@master01 ~]#kubectl rollout history -f deployment-demoapp.yaml deployment.apps/deploy-demo REVISION CHANGE-CAUSE 2 <none> 3 <none> #只要修改yaml下的template都会触发更新 [root@master01 ~]#vim deployment-demoapp.yaml ... spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.1 env: #追加内容 - name: PORT value: "8080" [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml #查看历史 [root@master01 ~]#kubectl rollout history -f deployment-demoapp.yaml deployment.apps/deploy-demo REVISION CHANGE-CAUSE 2 <none> 3 <none> 4 <none> [root@master01 ~]#kubectl get -f deployment-demoapp.yaml -o yaml ... spec: ... revisionHistoryLimit: 10 #历史保留最近的10个 strategy: rollingUpdate: maxSurge: 25% #允许新加后超出的数量不超过,可以设置为个数 maxUnavailable: 25% #允许减掉后缺少的数量不能超过,可以设置为个数 type: RollingUpdate #更新策略:滚动更新 #第一步:加1个新的,干掉2个旧的 #第二步:加2个新的,干掉2个旧的 #第三步:加1个新的
Deployment的滚动更新支持使用如下两个字段来配置相关的策略
◼ maxSurge:指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是相对于期望值的一个百分比
◼ maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望值的个数,其值可以是0或正数,也可以是相对于期望值的一个百分比,默认值为1
存在的问题:必须以Pod为原子单位切割规模比例,且无法控制流量路由比例;
Deployment控制器支持两种更新策略 ◼ 滚动式更新(rolling updates) ◆逐批次更新Pod的方式,支持按百分比或具体的数量定义批次规模,默认策略 ◆触发条件:podTemplate的hash码变动 ⚫ 仅podTemplate的配置变动才会导致hash码改变 ⚫ replicas和selector的变更不会导致podTemplate的hash变动 ◼ 重建式更新(recreate) ◆在Pod资源被删除时,使用新的模板定义被足缺失的Pod数量,完成更新 ◆触发条件:现有Pod被删除 命令:kubectl rollout 子命令: history #查看历史 status #查看状态 pause #暂停 resume #继续 undo #回滚 restart #重启 #示例:金丝雀发布(发布一个,暂停在那) #定义service [root@master01 ~]#vim demoapp-dev.yaml apiVersion: v1 kind: Service metadata: name: demoapp namespace: dev spec: type: LoadBalancer #有MetalLB了,可以用LB类型,集群外部访问 selector: app: demoapp ports: - name: http protocol: TCP port: 80 targetPort: 8080 [root@master01 ~]#kubectl apply -f demoapp-dev.yaml [root@master01 ~]#kubectl get svc -n dev NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp LoadBalancer 10.111.169.250 10.0.0.52 80:32002/TCP 30s #测试,在多个pod之间进行调度 [root@node01 ~]#curl 10.0.0.52 #改下更新策略(要求先加一个,不允许先删除) [root@master01 ~]#vim deployment-demoapp.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deploy-demo namespace: dev spec: strategy: #追加 type: RollingUpdate #更新策略:滚动更新 rollingUpdate: maxSurge: 1 #允许新加后超出的数量不超过,可以设置为个数 maxUnavailable: 0 #允许减掉后缺少的数量不能超过,可以设置为个数 replicas: 4 selector: matchLabels: app: demoapp template: metadata: labels: app: demoapp spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.1 env: - name: PORT value: "8080" [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml #更新template,来触发更新 [root@master01 ~]#vim deployment-demoapp.yaml ... spec: containers: - name: demoapp image: ikubernetes/demoapp:v1.2 #修改 #应用来更新,使用rollout pause来暂停(加入一个新版本节点就会暂停) [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml && kubectl rollout pause -f deployment-demoapp.yaml #也可以用deployments资源名称 [root@master01 ~]#kubectl apply -f deployment-demoapp.yaml && kubectl rollout pause deployments deploy-demo -n dev #查看,会有5个pod,1个新版本,4个老版本(此时请求service,会有20%请求打在新版本节点上) [root@master01 ~]#kubectl get pods -n dev #继续更新 [root@master01 ~]#kubectl rollout resume -f deployment-demoapp.yaml
kubeproxy改为ipvs
#把kube-proxy配置记录到文件中 [root@master01 ~]#kubectl get cm kube-proxy -o yaml -n kube-system > kube-proxy.yaml [root@master01 ~]#vim kube-proxy.yaml ... ipvs: ... strictARP: true #如果部署了matellb,这里要改成true ... mode: "ipvs" #修改 #下方metadata把没用的信息删了,保留如下 metadata: #删除annotations: #删除creationTimestamp: labels: app: kube-proxy name: kube-proxy namespace: kube-system #删除resourceVersion: #删除uid: #应用,当时kube-proxy的pod没有重启,依然没有生效(要重启,最好一个个重启) [root@master01 ~]#kubectl apply -f kube-proxy.yaml #重启daemonset资源类型 [root@master01 ~]#kubectl rollout restart daemonset kube-proxy -n kube-system #查看kube-proxy的pod,会发现pod被依次更新了 [root@master01 ~]#kubectl get pods -n kube-system
这里假设wordpress是无状态应用(实际wordpress有登录信息)
#编辑Deployment,pvc,service(用于对外访问) [root@master01 ~]#vim deployment-wordpress.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wordpress-pvc namespace: blog spec: accessModes: ["ReadWriteMany"] volumeMode: Filesystem resources: requests: storage: 5Gi storageClassName: nfs-csi --- apiVersion: apps/v1 kind: Deployment metadata: name: wordpress namespace: blog spec: replicas: 2 selector: matchLabel: app: wordpress strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: wordpress spec: volumes: - name: pagestor persistentVolumeClaim: claimName: wordpress-pvc containers: - name: wordpress image: wordpress:6.4-apache volumeMounts: - name: pagestor mountPath: /var/www/html/ env: - name: WORDPRESS_DB_HOST #value: mysql #服务名就叫mysql,同一个名称空间下 #mysql这个服务中的mysql-0这个pod(这里只连mysql主节点) value: mysql-0.mysql #后缀自动加.blog.svc.cluster.local - name: WORDPRESS_DB_USER value: wpuser - name: WORDPRESS_DB_PASSWORD value: wpP@ss - name: WORDPRESS_DB_NAME value: wpdb ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: wordpress #不同的资源类型同名没关系 name: blog spec: type: LoadBalancer selector: app: wordpress ports: - name: http port: 80 targetPort: 80
StatefulSet的编排机制
功能:负责编排有状态(Stateful Application)应用
◼ 有状态应用会在其会话中保存客户端的数据,并且有可能会在客户端下一次的请求中使用这些数据
◼ 应用上常见的状态类型:会话状态、连接状态、配置状态、集群状态、持久性状态等
◼ 大型应用通常具有众多功能模块,这些模块通常会被设计为有状态模块和无状态模块两部分
◆业务逻辑模块一般会被设计为无状态,这些模块需要将其状态数据保存在有状态的中间件服务上,如消息队列、数
据库或缓存系统等
◆无状态的业务逻辑模块易于横向扩展,有状态的后端则存在不同的难题
StatefulSet控制器自Kubernetes v1.9版本才正式引入,为实现有状态应用编排,它依赖于几个特殊设计
◼ 各Pod副本分别具有唯一的名称标识,这依赖于一个专用的Headless Service实现
◼ 基于Pod管理策略(Pod Management Policy),定义创建、删除及扩缩容等管理操作期间,施加在Pod副本上的操作方式
◆OrderedReady:创建或扩容时,顺次完成各Pod副本的创建,且要求只有前一个Pod转为Ready状态后,才能进行后一
个Pod副本的创建;删除或缩容时,逆序、依次完成相关Pod副本的终止
◆Parallel:各Pod副本的创建或删除操作不存在顺序方面的要求,可同时进行
◼ 各Pod副本存储的状态数据并不相同,因而需要专用且稳定的Volume
◆基于podTemplate定义Pod模板
◆在podTemplate上使用volumeTemplate为各Pod副本动态置备PersistentVolume
Pod副本的专用名称标识 ◼ 每个StatefulSet对象强依赖于一个专用的Headless Service对象 ◼ StatefulSet中的各Pod副本分别拥有唯一的名称标识 ◆前缀格式为“$(statefulset_name)-$(ordinal)” ◆后缀格式为“$(service_name).$(namespace).svc.cluster.local” ◼ 各Pod的名称标识可由ClustrDNS直接解析为Pod IP #例: StatefulSet:编排有状态应用 每个Pod的标识: #名称空间为blog statefulset:mysql headless service: mysql #pod名字.service名字.名称空间.svc.cluster.local 能被独立解析为该pod的ip mysql-0.mysql.blog.svc.cluster.local --> Pod IP volumeTemplateClaim ◼ 在创建Pod副本时绑定至专有的PVC ◼ PVC的名称遵循特定的格式,从而能够与StatefulSet控制器对象的Pod副本建立关联关系 ◼ 支持从静态置备或动态置备的PV中完成绑定 ◼ 删除Pod(例如缩容),并不会一并删除相关的PVC StatefulSet存在的问题 ◼ 各有状态、分布式应用在启动、扩容、缩容等运维操作上的步骤存在差异,甚至完全不同,因而StatefulSet只能提供一个基础的编排框架 ◼ 有状态应用所需要的管理操作,需要由用户自行编写代码完成
除了标签选择器和Pod模板,StatefulSet必须要配置一个专用的Headless Service,而且还可能要根据需要,编写代码 完成扩容、缩容等功能所依赖的必要操作步骤; #例 apiVersion: apps/v1 # API群组及版本; kind: StatefulSet # 资源类型特有标识; metadata: name <string> # 资源名称,在作用域中要惟一; namespace <string> # 名称空间;DaemonSet资源隶属名称空间级别; spec: minReadySeconds <integer> # Pod就绪后多少秒内任一容器无crash方可视为“就绪”; serviceName <string> # 关联的Headless Service的名称,需要事先存在; selector <object> # 标签选择器,必须匹配template字段中Pod模板中的标签; replicas <integer> # Pod的副本数量; template <object> # Pod模板对象; volumeClaimTemplates <[]Object> # 卷请求模板; podManagementPolicy <string> #Pod管理策略,OrderedReady有主次关系,Parallel无主次,默认为前者; revisionHistoryLimit <integer> # 滚动更新历史记录数量,默认为10; updateStrategy <Object> # 滚动更新策略 type <string> # 滚动更新类型,可用值有OnDelete和RollingUpdate; rollingUpdate <Object> # 滚动更新参数,专用于RollingUpdate类型; maxUnavailable <string> #更新期间可比期望的Pod数量缺少的数量或比例;(有状态只能先减后加) partition <integer> #更新策略中的partition号码,默认为0;(大于等于该编号都要被更新,更新为逆序) #若Pod管理策略是OrderedReady,创建为顺序来,删除和缩容为逆序来 #示例 apiVersion: apps/v1 kind: StatefulSet metadata: name: demodb namespace: default spec: selector: matchLabels: app: demodb serviceName: "demodb" #依赖的headless service(事先创建好,否则StatefulSet无法运行) replicas: 2 template: metadata: labels: app: demodb spec: containers: - name: demodb-shard image: ikubernetes/demodb:v0.1 ports: - containerPort: 9907 #监听端口 name: db env: - name: DEMODB_DATADIR #数据保存路径 value: "/demodb/data" volumeMounts: - name: data mountPath: /demodb/data volumeClaimTemplates: #为每一个pod做pvc模板 - metadata: name: data #每一个pod卷pvc名字为 卷模板名字(这里为data) + pod名字 spec: accessModes: [ "ReadWriteOnce" ] storageClassName: “nfs-csi" resources: requests: storage: 2Gi #实际操作示例 [root@master01 statefulsets]#vim demodb.yaml apiVersion: v1 kind: Service metadata: name: demodb namespace: default labels: app: demodb spec: clusterIP: None #headless service ports: - port: 9907 selector: app: demodb --- apiVersion: apps/v1 kind: StatefulSet metadata: name: demodb namespace: default spec: selector: matchLabels: app: demodb serviceName: "demodb" replicas: 2 template: metadata: labels: app: demodb spec: containers: - name: demodb-shard image: ikubernetes/demodb:v0.1 ports: - containerPort: 9907 name: db env: - name: DEMODB_DATADIR value: "/demodb/data" livenessProbe: initialDelaySeconds: 2 periodSeconds: 10 httpGet: path: /status port: db readinessProbe: initialDelaySeconds: 15 periodSeconds: 30 httpGet: path: /status?level=full port: db volumeMounts: - name: data mountPath: /demodb/data volumeClaimTemplates: - metadata: name: data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "nfs-csi" resources: requests: storage: 2Gi [root@master01 statefulsets]#kubectl apply -f demodb.yaml #pod会一个一个创建 [root@master01 statefulsets]#kubectl get pods NAME READY STATUS RESTARTS AGE demodb-0 1/1 Running 0 112s demodb-1 1/1 Running 0 49s #查看pvc,也是一个一个创建(kubectl get pv查看pv也一样) [root@master01 ~]#kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE data-demodb-0 Bound pvc-4d9b41a1-46ee-4af5-b3ec-44c256c74815 2Gi RWO nfs-csi <unset> 79s data-demodb-1 Bound pvc-f8c6b026-bcdb-4402-8a3c-3af40ee347fd 2Gi RWO nfs-csi <unset> 16s #扩容 [root@master01 statefulsets]#vim demodb.yaml ... spec: selector: matchLabels: app: demodb serviceName: "demodb" replicas: 4 #修改 ... [root@master01 statefulsets]#kubectl apply -f demodb.yaml #当前是默认Pod管理策略,OrderedReady,创建是顺序,删除或缩容是逆序进行的 #先创建demodb-2,创建pvc:data-demodb-2;再创建demodb-3,创建pvc:data-demodb-3 # -w持续监控资源变化 [root@master01 statefulsets]#kubectl get pods -w #更新(先更新一个,可以的话,剩下的全部更新) [root@master01 statefulsets]#vim demodb.yaml ... spec: selector: matchLabels: app: demodb serviceName: "demodb" replicas: 4 updateStrategy: #追加更新策略 type: RollingUpdate rollingUpdate: maxUnavailable: 1 #一次更新一个 partition: 3 #从编号3开始,也就是第4个 template: ... #应用更新策略,pod没有变化不会更新 [root@master01 statefulsets]#kubectl apply -f demodb.yaml #修改更新内容 [root@master01 statefulsets]#vim demodb.yaml ... template: metadata: labels: app: demodb spec: containers: - name: demodb-shard image: ikubernetes/demodb:v0.2 #修改版本 #进行更新(因上面更新策略只更新最后一个),监视下 [root@master01 statefulsets]#kubectl apply -f demodb.yaml && kubectl get pods -w #没问题,就把所有pod都更新,修改更新策略中的编号为0 [root@master01 statefulsets]#vim demodb.yaml ... spec: selector: matchLabels: app: demodb serviceName: "demodb" replicas: 4 updateStrategy: #追加更新策略 type: RollingUpdate rollingUpdate: maxUnavailable: 1 #一次更新一个 partition: 0 #从编号0开始,也就是全部更新 template: ... #进行更新(3已经更新过了,不会再更新,从2开始依次往前更新) [root@master01 statefulsets]#kubectl apply -f demodb.yaml