Kubernetes里的所有资源对象都可以采用yaml或者JSON格式的文件来定义或描述,下面是一个简单的Pod资源定义文件:
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
name: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app: v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
kind为pod表明这是一个Pod的定义,metadata里的name属性为Pod的名字,metadata里还能定义资源对象的标签(Label),这里声明myweb拥有一个name=myweb的标签(Label)。Pod里包含的容器组的定义则在spec一节中声明,这里定义了一个名字为myweb,对应镜像为kubeguide/tomcat-app: v1的容器,该容器注入了名为MYSQL_SERVICE_HOST='mysql'和MYSQL_SERVICE_PORT='3306'的环境变量(env关键字),并且在8080端口(containerPort)上启动容器进程。Pod的IP加上这里的容器端口,就组成了一个新的概念——Endpoint,它代表着此Pod里的一个服务进程的对外通信地址。一个Pod也存在着具有多个Endpoint的情况,比如我们把Tomcat定义为一个Pod时,可以对外暴露管理端口与服务端口这两个Endpoint。 Docker里的Volume在Kubernetes里也有对应的概念——Pod Volume,Pod Volume有一些扩展,比如可以用分布式文件系统GlusterFS等实现后端存储功能;Pod Volume是定义在Pod之上,然后被各个容器挂载到自己的文件系统中的。对于Pod Volume的定义我们后面会讲到。 这里顺便提一下Event概念,Event是一个事件的记录,记录了事件的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此事件的原因等众多信息。Event通常会关联到某个具体的资源对象上,是排查故障的重要参考信息,当我们发现某个Pod迟迟无法创建时,可以用kubectl describe pod xxx来查看它的描述信息,用来定位问题的原因。 每个Pod都可以对其能使用的服务器上的计算资源设置限额,当前可以设置限额的计算资源有CPU和Memory两种,其中CPU的资源单位为CPU(Core)的数量,是一个绝对值。 对于容器来说一个CPU的配额已经是相当大的资源配额了,所以在Kubernetes里,通常以千分之一的CPU配额为最小单位,用m来表示。通常一个容器的CPU配额被定义为100-300m,即占用0.1-0.3个CPU。与CPU配额类似,Memory配额也是一个绝对值,它的单位是内存字节数。 对计算资源进行配额限定需要设定以下两个参数:
- Requests:该资源的最小申请量,系统必须满足要求。
- Limits:该资源最大允许使用的量,不能超过这个使用限制,当容器试图使用超过这个量的资源时,可能会被Kubernetes Kill并重启。
通常我们应该把Requests设置为一个比较小的数值,满足容器平时的工作负载情况下的资源需求,而把Limits设置为峰值负载情况下资源占用的最大量。下面是一个资源配额的简单定义:
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
最小0.25个CPU及64MB内存,最大0.5个CPU及128MB内存。
2、Label(标签)
Label相当于我们熟悉的“标签”,给某个资源对象定义一个Label,就相当于给它打了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。 Label Selector相当于SQL语句中的where查询条件,例如,name=redis-slave这个Label Selector作用于Pod时,相当于select * from pod where pod’s name = ‘redis-slave’这样的语句。Label Selector的表达式有两种:基于等式的(Equality-based)和基于集合的(Set-based)。下面是基于等式的匹配例子。 name=redis-slave:匹配所有标签为name=redis-slave的资源对象。 env != production:匹配所有标签env不等于production的资源对象。 下面是基于集合的匹配例子
- name in (redis-master, redis-slave):匹配所有标签为name=redis-master或者name=redis-slave的资源对象。
- name not in (php-frontend):匹配所有标签name不等于php-frontend的资源对象。
还可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是“AND”的关系,即同时满足多个条件,例如:
name=redis-slave, env!=production
name not in (php-frontend), env!=production
以Pod为例,Label定义在metadata中:
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
app: myweb
RC和Service在spec中定义Selector与Pod进行关联:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 1
selector:
app: myweb
template:
…………
Deployment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件:
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
matchLabels用于定义一组Label,与直接写在Selector中作用相同;matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算符包括:In、NotIn、Exists和DoesNotExist。 如果同时设置了matchLabels和matchExpressions,则两组条件为“AND”关系,即所有条件需要同时满足才能完成Selector的筛选。 Label Selector在Kubernetes中的重要使用场景如下:
- Kube-controller进程通过资源对象RC上定义的Label Selector来筛选要监控的Pod副本的数量,从而实现Pod副本的数量始终符合预期设定的全自动控制流程。
- Kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立起每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。
- 通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod“定向调度”的特性。
下面举个复杂点的例子,假设我们为Pod定义了3个Label:release、env和role,不同的Pod定义了不同的Label。如下图所示,如果我们设置了“role=frontend”的Label Selector,则会选取到Node 1和Node 2上的Pod。
如果我们设置“release=beta”的Label Selector,则会选取到Node 2和Node 3上的Pod,如下图所示。
总结:使用Label可以给对象创建多组标签,Label和Label Selector共同构成了Kubernetes系统中最核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。
3、Replication Controller
RC的作用是声明Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分。
- Pod期待的副本数量(replicas)。
- 用于筛选目标Pod的Label Selector。
- 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
下面是一个完整的RC定义的例子,即确保拥有tier=frontend标签的这个Pod(运行Tomcat容器)在整个Kubernetes集群中始终有三个副本:
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 3
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
当我们定义了一个RC并提交到Kubernetes集群中后,Master节点上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望值。如果有过多的Pod副本在运行,系统就会停掉多余的Pod;如果运行的Pod副本少于期望值,即如果某个Pod挂掉,系统就会自动创建新的Pod以保证数量等于期望值。
通过RC,Kubernetes实现了用户应用集群的高可用性,并且大大减少了运维人员在传统IT环境中需要完成的许多手工运维工作(如主机监控脚本、应用监控脚本、故障恢复脚本等)。
下面我们来看下Kubernetes如何通过RC来实现Pod副本数量自动控制的机制,假如我们有3个Node节点,在RC里定义了redis-slave这个Pod需要保持两个副本,系统将会在其中的两个Node上创建副本,如下图所示。
假如Node2上的Pod2意外终止,根据RC定义的replicas数量2,Kubernetes将会自动创建并启动一个新的Pod,以保证整个集群中始终有两个redis-slave Pod在运行。
系统可能选择Node1或者Node3来创建一个新的Pod,如下图。
通过修改RC的副本数量,可以实现Pod的动态缩放(Scaling)功能。
kubectl scale rc redis-slave --replicas=3
此时Kubernetes会在3个Node中选取一个Node创建并运行一个新的Pod3,使redis-slave Pod副本数量始终保持3个。