Kubernetes有状态应用管理——PetSet
目录贴:Kubernetes学习系列
1、介绍
在Kubernetes中,大多数的Pod管理都是基于无状态、一次性的理念。例如Replication Controller,它只是简单的保证可提供服务的Pod数量。如果一个Pod被认定为不健康的,Kubernetes就会以对待牲畜的态度对待这个Pod——删掉、重建。相比于牲畜应用,PetSet(宠物应用),是由一组有状态的Pod组成,每个Pod有自己特殊且不可改变的ID,且每个Pod中都有自己独一无二、不能删除的数据。
众所周知,相比于无状态应用的管理,有状态应用的管理是非常困难的。有状态的应用需要固定的ID、有自己内部可不见的通信逻辑、特别容器出现剧烈波动等。传统意义上,对有状态应用的管理一般思路都是:固定机器、静态IP、持久化存储等。Kubernetes利用PetSet这个资源,弱化有状态Pet与具体物理设施之间的关联。一个PetSet能够保证在任意时刻,都有固定数量的Pet在运行,且每个Pet都有自己唯一的身份。一个“有身份”的Pet指的是该Pet中的Pod包含如下特性:
1) 静态存储;
2) 有固定的主机名,且DNS可寻址(稳定的网络身份,这是通过一种叫 Headless Service 的特殊Service来实现的。 和普通Service相比,Headless Service没有Cluster IP,用于为一个集群内部的每个成员提供一个唯一的DNS名字,用于集群内部成员之间通信 。);
3) 一个有序的index(比如PetSet的名字叫mysql,那么第一个启起来的Pet就叫mysql-0,第二个叫mysql-1,如此下去。当一个Pet down掉后,新创建的Pet会被赋予跟原来Pet一样的名字,通过这个名字就能匹配到原来的存储,实现状态保存。)
应用举例:
1) 数据库应用,如Mysql、PostgreSQL,需要一个固定的ID(用于数据同步)以及外挂一块NFS Volume(持久化存储)。
2) 集群软件,如zookeeper、Etcd,需要固定的成员关系。
2、使用限制
1) 1.4新加功能,1.3及之前版本不可用;
2) DNS,要求使用1.4或1.4之后的DNS插件,1.4之前的插件只能解析Service对应的IP,无法解析Pod(HostName)对应的域名;
3) 日常运维,对于PetSet,唯一能够更改的就是replicas;
4) 需要持久化数据卷(PV,若为nfs这种无法通过调用API来创建存储的网络存储,数据卷要在创建PetSet之前静态创建;若为aws-ebs、vSphere、openstack Cinder这种可以通过API调用来动态创建存储的虚拟存储,数据卷除了可以通过静态的方式创建以外,还可以通过StorageClass进行动态创建。需要注意的是,动态创建出来的PV,默认的回收策略是delete,及在删除数据的同时,还会把虚拟存储卷删除);
5) 删除或缩容PetSet不会删除对应的持久化数据卷,这么做是处于数据安全性的考虑;
6) 只能通过手动的方式升级PetSet。
3、PetSet示例
以下示例演示了创建两个PV,创建两个PetSet的过程。在PetSet创建完成之后,验证了其hostName及直接Pod访问。
3.1 创建PV
创建PV的时候,我两个PV选择了同一个NFS server,创建也是成功的。Kubernetes在创建PV的时候并不会校验NFS server是否存在,是否能够连接成功,也不会校验storage设置的大小是否真实。所以说,PV的创建需要管理员在一开始就设置好,依赖人为的校验。
[root@k8s-master pv]# cat nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0001
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: "/data/disk1"
server: 192.168.20.47
readOnly: false
[root@k8s-master pv]# cat nfs-pv2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0002
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: "/data/disk1"
server: 192.168.20.47
readOnly: false
[root@k8s-master pv]# kubectl create -f nfs-pv.yaml
persistentvolume "pv0001" created
[root@k8s-master pv]# kubectl create -f nfs-pv2.yaml
persistentvolume "pv0002" created
[root@k8s-master pv]# kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pv0001 5Gi RWX Recycle Available 8s
pv0002 5Gi RWX Recycle Available 5s
3.2 创建PetSet
创建PetSet的时候需要先创建一个“headless”的Service,即service显示的将ClusterIP设置为none。而用户可以通过直接访问PetSet中的Pod的IP(通过Pod的HostName解析得到),来访问后端的服务的。Kubernetes在1.4之后的dns插件之上才支持这种类型的DNS解析
[root@k8s-master pv]# cd ../petset/
[root@k8s-master petset]# cat test-petset.yaml
# A headless service to create DNS records
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port:
name: web
# *.nginx.default.svc.cluster.local
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1alpha1
kind: PetSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas:
template:
metadata:
labels:
app: nginx
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
terminationGracePeriodSeconds:
containers:
- name: nginx
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort:
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
[root@k8s-master petset]# kubectl create -f test-petset.yaml
service "nginx" created
petset "web" created
[root@k8s-master petset]# kubectl get petset
NAME DESIRED CURRENT AGE
web 11s
[root@k8s-master petset]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web- / Running 16s
web- / Running 12s
[root@k8s-master petset]# kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pv0001 5Gi RWX Recycle Bound default/www-web- 1m
pv0002 5Gi RWX Recycle Bound default/www-web- 1m
[root@k8s-master petset]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
www-web- Bound pv0001 5Gi RWX 22s
www-web- Bound pv0002 5Gi RWX 22s
[root@k8s-master petset]# kubectl exec -ti web- /bin/bash
root@web-:/# cd /usr/share/nginx/html
root@web-:/usr/share/nginx/html# ls
root@web-:/usr/share/nginx/html# touch .out
root@web-:/usr/share/nginx/html# exit
exit
[root@k8s-master petset]# ssh 192.168.20.47 #登录到NFS主机
root@192.168.20.47's password:
Last login: Tue Mar :: from 10.0.251.145
[root@localhost ~]# echo "">> /data/disk1/.out
[root@localhost ~]# exit
登出
Connection to 192.168.20.47 closed.
[root@k8s-master petset]# kubectl exec -ti web- /bin/bash
root@web-:/# cat /usr/share/nginx/html/.out root@web-:/# exit
exit
[root@k8s-master petset]# kubectl exec -ti web- /bin/bash
root@web-:/# cat /usr/share/nginx/html/.out root@web-:/# exit
exit
[root@k8s-master petset]#
3.3 验证域名解析
检查PetSet中的Pod的hostName
[root@k8s-master ~]# for i in ; do kubectl exec web-$i -- sh -c 'hostname'; done
web-
web-
检查通过hostName解析IP
[root@k8s-master dns14]# kubectl get svc nginx
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx None <none> /TCP 5h
[root@k8s-master dns14]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Selector: app=nginx
Type: ClusterIP
IP: None
Port: web /TCP
Endpoints: 10.0.28.3:,10.0.82.6:
Session Affinity: None
No events.
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web- / Running 42m 10.0.28.3 k8s-node-
web- / Running 42m 10.0.82.6 k8s-node-
[root@k8s-master ~]# kubectl exec -i -t frontend-service--xuysd /bin/bash #进入集群中的一个pod
[root@frontend-service--xuysd /]# nslookup web-.nginx
Server: 10.254.10.2
Address: 10.254.10.2# Name: web-.nginx.default.svc.cluster.local
Address: 10.0.28.3 [root@frontend-service--xuysd /]# nslookup web-.nginx
Server: 10.254.10.2
Address: 10.254.10.2# Name: web-.nginx.default.svc.cluster.local
Address: 10.0.82.6 <-xuysd /]# nslookup web-.nginx.default.svc.cluster.local #通常情况下,直接解析web-.nginx即可得到对应的IP,但再一些容器内发现只有解析全部的名称“web-.nginx.default.svc.cluster.local”才能得到IP,这个地方需要研究下区别在哪。
Server: 10.254.10.2
Address: 10.254.10.2# Non-authoritative answer:
Name: web-.nginx.default.svc.cluster.local
Address: 10.0.82.6
3.4 验证Pet重建
#为web-0添加一个index页面,内容为它自己的HostName,注意该目录我们将其外挂到了PV之上,是一个NFS路径
[root@k8s-master dns14]# kubectl exec web- -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'
#在其它Pod上获取web-0的欢迎页
[root@k8s-master dns14]# kubectl exec -it web- -- curl web-.nginx
web-
[root@k8s-master dns14]# kubectl exec -it web- -- curl web-.nginx
web-
#查询web-0的存活时间
[root@k8s-master dns14]# kubectl get pod | grep web
web- / Running 5h
web- / Running 5h
[root@k8s-master dns14]# kubectl delete pod web- #删除web-
pod "web-0" deleted
[root@k8s-master dns14]# kubectl get pod | grep web #可以看到,web-0被删除后几乎立即又被重建了
web- / Running 3s
web- / Running 5h
[root@k8s-master dns14]# kubectl get pod | grep web
web- / Running 5s
web- / Running 5h
#查看web-0中的欢迎页是否还在
[root@k8s-master dns14]# kubectl exec -it web- -- curl web-.nginx
web-
[root@k8s-master dns14]# kubectl exec -it web- -- curl web-.nginx
web-
4、运维
4.1 Pet互相发现
通常,Pets需要互相知道对方的存在,在之前的示例中,我们演示了操作员“告诉”一个Pet,它有多少个同伴,但这显然是不够的。一种方法是,在Pod内部调用Kubectl的api来获取该Pet对应的PetSet中的其他成员,但并不推荐这么做。这样做的话,就会使得你的Pod可以反过来操控外部的组件。另一种方法是通过DNS解析,利用工具nslookup工具可以将nginx(上文定义的headless service)中的所有endPoint都查找出来。具体见下:
# apt-get update && apt-get install -y dnsutils
...
# nslookup -type=srv nginx
Server: 10.254.10.2
Address: 10.254.10.2# nginx.default.svc.cluster.local service = web-.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local service = web-.nginx.default.svc.cluster.local.
# nslookup web-.nginx.default.svc.cluster.local
Server: 10.254.10.2
Address: 10.254.10.2# Name: web-.nginx.default.svc.cluster.local
Address: 10.0.82.6 # nslookup web-.nginx.default.svc.cluster.local
Server: 10.254.10.2
Address: 10.254.10.2# Name: web-.nginx.default.svc.cluster.local
Address: 10.0.28.3
4.2 更新及扩缩容
如之前使用限制(3)中讲的,对于PetSet,Kubernetes能帮我们自动做的仅有“replicas”,即副本数量。对于扩充副本数量来说,Kubernetes每次会按照顺序,一个个的创建Pod,且在前一个没有running或ready之前,不会创建下一个;对于缩减副本数量来说,Kubernetes每次会按照顺序,一个个的删除Pod。且在前一个没有真正的shutdown之前,不会删除下一个。
需要注意的是,缩容时,虽然删除了一些Pod,但Pod对于的持久化存储PVC—PV是不会被删除的。例如,我们一开始创建了3个Pet,pod-0、pod-1、pod-2,挂载了pvc-0——pv-0、pvc-1——pv-1、pvc-2——pv-2,在缩容到2个副本的时候,最后一个pod-2会被删除,但pvc-2——pv-2则不会被删除,里面的数据还是安全的。PV的最终删除就像它一开始创建一样,是由管理员统一管理的。
4.3 镜像更新
有时需要更新镜像到新的版本,那该如何操作呢?虽然Kubernetes没有给我们提供一些自动更新整个Pet集群的功能,但通过它提供的edit和set image功能也基本上够我们用的了。更新Pet中的镜像功能示例如下:
[root@k8s-master ~]# kubectl get po web- --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.8
[root@k8s-master ~]# kubectl exec -i -t frontend-service--xuysd /bin/bash #进入集群中的一个pod
[root@frontend-service--xuysd /]# nslookup web-.nginx
Server: 10.254.10.2
Address: 10.254.10.2# Name: web-.nginx.default.svc.cluster.local
Address: 10.0.28.3 [root@k8s-master ~]# kubectl edit petset/web #执行之后就像打开一个vi界面,修改对应的镜像名称或版本,保存退出
petset "web" edited
#也可以用set image的方式进行更改
[root@k8s-master ~]# kubectl set image petset/web nginx=gcr.io/google_containers/nginx-slim:0.7
petset "web" image updated
[root@k8s-master ~]# kubectl delete po web- #手动删除第一个Pod,PetSet会自动给我们再起一个
pod "web-0" deleted
[root@k8s-master ~]# kubectl get po web- -o wide #查看Pod的IP已经更改了
NAME READY STATUS RESTARTS AGE IP NODE
web- / Running 34s 10.0.62.4 k8s-node-
[root@k8s-master ~]# kubectl get po web- --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.7 #查看镜像版本信息,版本也已经更改了
之后可以一个个的将PetSet中的Pod删除,PetSet会自动按照新版本的镜像帮我们启动起来。
5、后续新功能
官方给出后续将会逐步推出的新功能如下:
1) 数据安全与本地数据存储(目前仅支持网络共享存储卷,在IO高的情况下网络存储卷可能会出现性能瓶颈,后续Kubernetes将会推出支持本地存储的PetSet)
2) 丰富通知实践
3) 公网的网络身份
4) 广域网集群发布(跨多个可用区、region、云服务提供商)
5) 更多的自动运维手段(包括镜像升级等)
Kubernetes有状态应用管理——PetSet的更多相关文章
- Kubernetes基础:查看状态、管理服务
目标 了解Kubernetes Pod 了解Kubernetes Node 学习如何调试部署问题 了解如何通过Service暴露应用 Kubernetes Pods 在Kubernetes中创建一个D ...
- Kubernetes容器集群管理环境 - Prometheus监控篇
一.Prometheus介绍之前已经详细介绍了Kubernetes集群部署篇,今天这里重点说下Kubernetes监控方案-Prometheus+Grafana.Prometheus(普罗米修斯)是一 ...
- Kubernetes容器集群管理环境 - 完整部署(中篇)
接着Kubernetes容器集群管理环境 - 完整部署(上篇)继续往下部署: 八.部署master节点master节点的kube-apiserver.kube-scheduler 和 kube-con ...
- 阿里云上万个 Kubernetes 集群大规模管理实践
点击下载<不一样的 双11 技术:阿里巴巴经济体云原生实践> 本文节选自<不一样的 双11 技术:阿里巴巴经济体云原生实践>一书,点击上方图片即可下载! 作者 | 汤志敏,阿里 ...
- Kubernetes容器集群管理环境 - 完整部署(下篇)
在前一篇文章中详细介绍了Kubernetes容器集群管理环境 - 完整部署(中篇),这里继续记录下Kubernetes集群插件等部署过程: 十一.Kubernetes集群插件 插件是Kubernete ...
- Kubernetes 使用 Kubevirt 运行管理 Windows 10 操作系统
原文链接:https://fuckcloudnative.io/posts/use-kubevirt-to-manage-windows-on-kubernetes/ 最近我发现我的 Kubernet ...
- Terraform状态State管理,让变更有记录
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 简介 最近工作中用到了Terraform,权当学习记录一下,希望能帮助到其它人. Terraform系列文章如下: Ter ...
- Java并发框架——同步状态的管理
整个AQS框架核心功能都是围绕着其32位整型属性state进行,一般可以说它表示锁的数量,对同步状态的控制可以实现不同的同步工具,例如闭锁.信号量.栅栏等等.为了保证可见性此变量被声明为volatil ...
- Kubernetes 有状态与无状态介绍
Kubernetes 有状态与无状态介绍 无状态:deployment - 认为所有pod都是一样的,不具备与其他实例有不同的关系. - 没有顺序的要求. - 不用考虑再哪个Node运行. - 随意扩 ...
随机推荐
- react项目使用bootstrap
曾经对于react项目怎么使用bootstrap纠结了很久,网上也查了好多的资料,有的用react-bootstrap,只要npm install 以后,import就可以使用里面的css了.但是这个 ...
- oracle多个单引号的处理
Oracle多个单引号的处理 在ORACLE中,单引号有两个作用,一是字符串是由单引号引用,二是转义.单引号的使用是就近配对,即就近原则.而在单引号充当转义角色时相对不好理解. 下面转载 1.从第二个 ...
- 新Windows本地提权漏洞学习(CVE-2019-0841)
1.这是一个啥漏洞? 睁眼一看,妈呀本地提权,快加入本地提权漏洞利用包里,速度加入.github连接我就不发了.担心被认为是传播黑客工具,咱们在这里单纯学习一下漏洞的原理和部分源代码. 2.文件读写权 ...
- java中jdbc源码解读
在jdbc中一个重要的接口类就是java.sql.Driver,其中有一个重要的方法:Connection connect(String url, java.util.Propeties info); ...
- Oracle tablespace 创建表空间
定义: 表空间是一个逻辑概念,它的所有数据和结构信息都存储在一个或多个数据文件中,表空间属于数据库的一部分.数据库自带有几个表空间,如system,temp.一般系统将创建几个私用或业务的表空间. 模 ...
- 用js实现二维数组的旋转
我最近因为做了几个小游戏,用到了二维数组,其中有需求将这个二维数组正翻转 90°,-90°,180°. 本人是笨人,写下了存起来. 定义的基本二位数组渲染出来是这种效果. 现在想实现的结果是下面的效果 ...
- CAutolock
顾名思义CAutolock就是自动锁的意思,它可以把它之下的代码区锁住一直到其自身被释放掉 后这块代码区中的公共资源才会被其他线程使用.当然这个代码区能尽量少就尽量少,毕竟不能让其他线 程 ...
- ruby离线安装整理
参考官方文档: https://rvm.io/rvm/offline 参考博客:https://blog.csdn.net/topswim/article/details/79260369 一.前提 ...
- 怎样理解JAVA的“构造方法”和“主方法”
在类中除了成员方法之外,还存在一种特殊类型的方法,那就是构造方法.主方法是类的入口点,它定义了程序从何处开始: 主方法提供对程序流向的控制,Java编译器通过主方法来执行程序.那么,下面一起来看一下关 ...
- vlan之间的通信-单臂路由与三层交换之间的互通
注:本试验基于单臂路由通信,三层交换通信,请完成以上两个实验,并保证能够通信 熟练掌握单臂路由的配置 熟练掌握三层交换的配置 三层交换与单臂路由的互通 实验原理 三层交换机在原有二层交换机的基础之上增 ...