Kubernetes 集群的两种管理角色: Master 和 Node
Master 只的是集群控制节点,每个集群需要一个检点来负责整个集群的管理和控制。 基本上所有控制命令都发给它,它来负责具体的执行过程。
Master 节点通常会占据一个独立的服务器。
Master 节点上运行着以下一组关键进程
Master 节点上还需要启动一个etcd服务,k8s里的所有资源对象的数据全部保存在etcd中
集群中除了Master其他机器被称为Node节点,Node可以是一台物理机,也可以是一台虚拟机。Node是k8s集群中工作负载节点,没个Node会被Master分配一些工作负载(docker 容器), 当某个Node宕机时,其上的工作负载会被Master自动转移到其他节点上去。
Node 节点运行以下一组关键进程
Node 可以在运行期间动态增加到Kubernetes集群中,前提是节点已正确安装、配置和启动上述关键进程。
默认情况kubelet会向Master注册自己, 这也是Kubernetes推荐的Node管理方式。
一旦Node纳入集群管理范围,kubelet进程就会定时向Master节点回报自身的情报,如操作系统、Docker版本、机器的CPU和内存情况,以及当前有哪些Pod在运行。这样Master可以获知每个Node的资源使用情况,并实现高效均衡的资源调度策略。
某个Node超过指定时间不上报信息,会被Master判定为“失联”,Node的状态被标记为不可用(Not Ready), 随后Master会触发“工作负载转移”的自动流程。
构成
设计 Pause 原因:
Pod IP: k8s 为每个Pod 分配了唯一IP地址,Pod里多个容器共享。
k8s要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,通常采用虚拟二层网络技术来实现,如 Flannel、Open vSwitch.
一个Pod里的容器与另外主机上的Pod容器能够直接通信。
分类:
默认情况Pod里某个容器停止,k8s会自动检测到并重启这个Pod(重启Pod内所有容器),如果Pod所在Node宕机,将会将Node上所有Pod重新调度到其他节点上。
Endpoint: Pod的IP加上容器的端口(containerPort)
Pod Volume: 定义在Pod上, 被各个容器挂载到自己的文件系统中。
查看pod描述信息,定位问题。
kubectl describe pod <pod name>
Event:是一个事件的记录, 记录了时间的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此时间的原因等众多信息。Event 通常会关联到某个具体的资源对象上,是排查故障的重要参考信息。
Pod 对服务器上计算资源设置限额
k8s里通常以千分之一的CPU配额为最小单位。用m来表示。通常一个容器的配额被定义为100~300m,既0.1~0.3个cpu.因为是一个绝对值,所以无论是一个Core还是48个Core的机器 100m所代表的使用量是一样的。
k8s里一个计算资源进行配额限定需要设定两个参数
通常将 Request 设置为一个比较小的值,符合容器平时的工作负载情况下的资源需求,把Limits设置为峰值负载下资源占用的最大值。。
如表明mysql容器最少申请0.25个CPU和64Mib内存,在运行时,mysql锁能使用的资源配额为 0.5个CPU及128Mib内存
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Label 是一个键值对,可以附加到各种资源对象上,如 Node、Pod、Service、RC等。
一个资源可以定义任意数量的Lable, 同一个Lable也可以被添加到任意数量的资源对象上去。
可以在对象定义时确定,也可以在对象创建后动态添加或者删除。
通过制定的资源对象捆绑一个或多个不同的Label 来实现多维度的资源分组管理,方便的进行资源分配、调度、配置、部署等管理工作。
常用标签示例
通过标签选择器(Label Selector) 查询和筛选拥有Label的资源对象。
Label Selector 表达式:
可以使用多个Label Selector 表达式组合实现复杂的条件选择,多个表达式使用,
进行分隔,几个条件是AND关系
name=redis,env!=production
新出现的管理对象Deployment、ReplicaSet、DaemonSet和Job 可以使用Selector中使用基于集合筛选条件定义
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
同时设置了这两组,他们的关系是 AND 关系,所有条件都满足才能完成筛选。
RC: 声明某种Pod的副本数量在任意时刻都符合某种预期值
定义RC提交到k8s集群后,master节点的Controller Manager组件获得通知,定期巡检系统中存活的目标Pod, 并确保Pod实例的数量刚好等于此RC的期望值。 如果过多的Pod运行,就停一些Pod,否则会创建一些Pod.
通过RC实现了用户应用集群的高可用,减少手工运维工作。
在运行时,可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling)。
kubectl scale rc redis-slave --replicas=3
注意 删除RC并不会影响通过该RC已创建好的Pod, 为了删除所有Pod,可以将replicas
的值为0
,然后更新该RC.
还提供了 stop 和 delete 命令来一次性删除RC和RC 控制的所有Pod.
通过RC可以实现 滚动升级(Rolling Update)
Kubernetes v1.2时,升级为新的概念 Replica Set, 下一代RC.
区别:
apiVersion: v1
kind: ReplicaSet # 对比: 这里写的是 ReplicationController
metadata:
name: mysql
spec:
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
当前我们很少单独使用,主要被Deployment这个高层对象所使用。我们在使用Deployment时,无须关心如何创建ReplicaSet.
为了更好解决Pod编排问题,内部使员工ReplicaSet
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
release: dev
template:
metadata:
labels:
app: myapp
release: dev
spec:
containers:
- name: myapp-containers
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
创建:
kubectl create -f deployment.yaml
查看Deployment的信息
➜ k8s kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myapp-deploy 1 1 1 1 1m
查看对应的ReplicaSet, 命名和Deployment有关
➜ k8s kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-deploy-f4bcc4799 1 1 1 10m
查看Pod, 以ReplicaSet的名称为前缀, 清晰表明那些rs创建了哪些Pod,对于滚动升级,可以容易排除错误。
➜ k8s kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-deploy-f4bcc4799-d9pqn 1/1 Running 0 14m
Pod 横向自动扩容, 简称HPA
HPA有两种方式作为Pod负载的度量指标
CPUUtilizationPercentage 是一个算数平均值,既目标Pod所有副本自身的CPU利用率的平均值。
一个Pod自身CPU利用率是该Pod当前CPU的使用量除以它的Pod Request的值。
如:一个Pod的PodRequest为0.4,当前CPU使用率是0.2, 那么它CPU使用率为50% (0.2/0.4=0.5)
这样我们就可以算出一个RC控制的Pod副本的CPU利用率的算数平均值。
如果某个时刻CPUUtilizationPercentage的值超过80%,则意味着当前的Pod副本数量可能不足以支撑接下来更多的请求,需要动态扩容。 当峰值时段过去,CPU利用率又降下来,此时对应的Pod副本应该自动减少到一个合理水平。
CPUUtilizationPercentage计算使用到的Pod的CPU使用量通常在1min内的平均值,目前通过Heapster扩展组件来得到这个值。所以需要安装Heapster.
如果目标没定义Pod Request,则无法使用CPUUtilizationPercentage
提供有状态的服务。
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合,每个定义中要声明它属于哪个Headless Service。
Headless Service 与普通Service的区别在于,它没有Cluster IP, 如解析Headless Service的DNS域名, 则返回的是该Service对应的全部Pod的Endpoint列表。
StatefulSet在Headless Service的基础上又为控制的每个Pod实例创建了一个DNS域名:
$(podname).$(headless service name)
如: 3 个节点的kafka, Headless Service名字kafka,则3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-2.kafka
这些DNS名称可以直接在集群的配置文件中固定下来。
每个Service相当于微服务架构中的 "微服务"
Service与其后端Pod副本集群通过Label Selector 来实现"无缝对接"
RC 保证 Service 的服务能力和服务质量始终处于预期的标准值。
运行在Node上的kube-proxy进程负责负载,把请求转发到后盾Pod实例上, 内部实现负载与会话保持。
Cluster IP : Service不是共用一个负载均衡器的IP地址,而是没个Service分配了一个全局唯一的虚拟IP地址, 这个IP叫Cluster IP
这样每个服务都变成了具有唯一IP地址的"通信节点",服务变成了基础的TCP网络通信问题。
Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新Pod的IP地址和旧的Pod的不同
Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP
, 而且在Service的整个生命周期内, 它的Cluster IP
不会发生改变。
所以服务发现使用Service的Name
与Cluster IP
地址做一个DNS域名映射就解决问题。
创建一个Service: tomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
selector:
tier: frontend
创建
kubectl create -f tomcat-service.yaml
这时候就会去对应一个Pod, 使用下面的命令查看对应情况
kubectl get endpoints
查看Cluster IP
kubectl get svc tomcat-service -o yaml
服务多端口问题,存在多个Endpoint,定义例子如:
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tier: frontend
Service 都有一个唯一的Cluster IP 及唯一的名字。名字由开发者自己定义,部署也不需要改,所以完全可以固定在配置中。
最早使用环境变量(env),在每个Pod的容器在启动时,自动注入。但是不够直观。
通过Add-On增值包的方式引入了DNS系统, 把服务名作为DNS域名, 这样程序就可以直接使用服务名来简历通信连接了。
三种IP
Node IP是 k8s集群中没个节点的物理网卡的IP地址, 这是一个真实存在的物理网络。
这表明k8s集群之外的节点访问k8s集群某个节点或TCP/IP服务,必须通过Node IP 通信。
Pod IP 是每个Pod的IP地址,它是根据Docker Engine 根据 docker0 网桥IP地址段进行分配的,通常是一个虚拟的二层网络。
所以k8s里一个Pod里的容器访问另一个Pod里的容器,就通过Pod IP 所在的虚拟二层网络进行通信,真实流量则是通过Node IP所在的物理网卡流出。
Cluster IP 也是虚拟IP,
Cluster IP 属于 k8s 内部的地址,无法在集群外部直接使用这个地址。
采用NodePort 解决上述问题
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002 # 手动指定NodePort 端口号,不然会自动分配。
selector:
tier: frontend
NodePort还没完全解决外部访问Service的所有问题,如负载均衡。最好使用一个负载均衡器,由负载均衡器负责转发流量到后面某个Node的NodePort.
如: 使用硬件 Load balancer 负载, 活 HAProxy 或者 Nginx.
谷歌GCE公有云上,将 type=NodePort 改为 type=LoadBalancer, k8s会自动创建一个对应的Load balancer 使用。其他公有云实现此驱动,也可使用。
Volume 是 Pod 中能够被多个容器访问的共享目录。
k8s中的 Volume 定义在Pod上, 然后被一个Pod里的多个容器挂载到具体的文件目录下。
k8s中的 Volume 与Pod的生命周期相同, 与容器的生命周期不相关。
当容器终止或者重启时,Volume 中的数据不会丢失。
k8s 支持多种文件类型的 Volume.
使用: 在Pod上声明一个 Volume, 然后在容器里引用Volume并Mount到容器的某个目录上。
如: 给 Pod 增加一个 名字为 datavol 的 Volume, 挂载到 /mydata-data上
template:
metadata:
labels:
app: mysql
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: mysql
image: mysql
volumeMounts:
- mountPath: /mydata-data
name: datavol
一个emptyDir Volume 是在Pod 分配到Node时创建的。它的内容为空,并且无须指定宿主机上对应的文件目录。这是k8s自动分配的一个目录。 Pod 从Node上移除, emptyDir 中的数据也会被永久删除。
用途:
hostPath 为在Pod上挂载宿主机上的文件或目录,
使用注意:
volumes:
- name: datavol
hostPath:
path: "/data"
使用这种类型的Volume 表示使用谷歌公开云提供的永久磁盘(Persisteent Disk, PD)存放Volume的数据,它与emptyDir不同,会永久保存。 当Pod被删除时,PD只会卸载,但不会被删除。你需要先创建一个永久磁盘(PD), 才能使用
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
volumes:
- name: datavol
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
亚马逊公有云提供的EBS Volume存储数据,需要先创建一个ESB Volume才能使用。
一些限制:
aws ec2 create-volume --availability-zone eu-west-1a --size 10 --volume-type gp2
volumes:
- name: datavol
awsElasticBlockStore:
volumeID: aws://<availability-zone>/<volume-id>
fsType: ext4
使用NFS网络文件系统提供的共享目录存储数据时,需要在系统部署一个NFS Server.
volumes:
- name: nfs
nfs:
server: nfs-server.localhost
path: "/"
之前说的Volume是定义在Pod上,属于"计算资源"的一部分
"网络存储" 是相对独立于"计算资源"而存在的一种实体资源
Persistent Volume: 简称 PV 。
Persistent Volume Claim (简称PVC) 。
可以认为是集群中某个网络存储对应的一块存储,与 Volume类似
区别:
例子: NFS类型的PV的yaml, 声明需要5Gi的空间
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 127.17.0.2
PV 的 accessModes 属性
如果Pod 想申请PV, 需要先定义一个PVC.
apiVersion: v1
kind: PersistentVolumeClain
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
然后在Pod 的 Volume 定义上引用上述PVC :
volumes:
- name: datavol
persistentVolumeClain:
claimName: myclaim
namespace 在很多情况下用于多租户资源隔离。通过将集群内部的资源对象分配到不同的namespace 形成逻辑上分组的不同项目、小组、用户组。 便于不同分组在共享使用整个集群的资源的同时还能被分别管理。
k8s 启动后,会创建一个名为default的Namespace 通过
kubectl get namespaces
如果不特别指明,则用户创建的Pod、RC、Service都将被系统创建到这个默认namespace。
创建一个名为 development 的 Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: development
创建后可以指定这个资源对象属于哪个Namespace.
定义一个名为busybox的Pod, 放入上面创建的:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
...
这时使用命令将看不到上面创建的pod, 默认使用的是default
kubectl get pods
需要添加参数--namespace
来查看
kubectl get pods --namespace=development
使用key/value 键值对
Annotation 用来记录的信息如下: