K8S特点
- 轻量级 :消耗资源小
- 开源
- 弹性伸缩
- 负载均衡
K8S架构
borg架构图
对于高可用集群来说,他的调度器(高可用节点)最好设置为3以上的奇数个。这样的好处就是防止投票的时候出现平票。
k8s架构图
Kubernetes是一个管理系统。从上面这张图可以看出,其本身的架构为主从分离架构。这个也很正常,Master节点负责对整个系统的调整和监控,而Node节点负责执行具体的任务。
Node,工作节点,用来从Master接受任务并执行,并且适当的调整自己的状态或者删除过期的负载。
Kubelet 是工作节点主要的程序,其会监视已分配给节点的Pod,具体功能包括:
- 创建Pod 所需的数据卷
- 创建Pod 所需的网络
- 下载Pod 所需的Secrets
- 启动Pod 之中运行的容器
- 定期执行容器健康检查
- 上报节点状态
Kube-proxy:通过主机上维护网络规则并执行连接转发来实现Kubernetes 服务抽象
Docker/Rkt:用于运行容器
重要组件:
- api server:所有服务访问统一入口
- Replication controller:维持副本期望数目
- scheduler:负责接收任务,选择合适的节点进行分配任务
- etcd:键值对数据库,持久化k8s集群的所有重要信息(部分不重要的信息可能不会储存)
- Kubelet:直接跟容器引擎交互,实现容器的生命周期管理
- Kube-proxy:负责写入规则至 iptables,ipvs。实现服务映射访问
次要组件:
- coreDNS:可以为集群中的SVC创建一个域名IP的对应关系解析
- DashBoard:可以为k8s集群提供一个B/S结构的GUI
- Ingress controller:官方只能实现4层代理,ingress可以实现七层代理。一般为服务提供外网入口。
- fedetation:提供一个可以跨集群中心多k8s统一管理功能
- prometheus:提供k8s集群的监控能力
- Elk:提供k8s集群日志统一分析接入平台
组件通讯
Kubernetes 多组件之间的通信原理:
- apiserver 负责 etcd 存储的所有操作,且只有 apiserver 才直接操作 etcd 集群
- apiserver 对内(集群中的其他组件)和对外(用户)提供统一的 REST API,其他组件均通过 apiserver 进行通信
- controller manager、scheduler、kube-proxy 和 kubelet 等均通过 apiserver watch API 监测资源变化情况,并对资源作相应的操作
- 所有需要更新资源状态的操作均通过 apiserver 的 REST API 进行
- apiserver 也会直接调用 kubelet API(如 logs, exec, attach 等),默认不校验 kubelet 证书,但可以通过
--kubelet-certificate-authority
开启(而 GKE 通过 SSH 隧道保护它们之间的通信) - 用户通过 REST API 创建一个 Pod
- apiserver 将其写入 etcd
- scheduluer 检测到未绑定 Node 的 Pod,开始调度并更新 Pod 的 Node 绑定
- kubelet 检测到有新的 Pod 调度过来,通过 container runtime 运行该 Pod
- kubelet 通过 container runtime 取到 Pod 状态,并更新到 apiserver 中
关键组件简介
Etcd
用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。
Etcd主要功能:
- 基本的key-value存储
- 监听机制
- key的过期及续约机制,用于监控和服务发现
- 原子CAS和CAD,用于分布式锁和leader选举
kube-apiserver
kube-apiserver 是 Kubernetes 最重要的核心组件之一,主要提供以下的功能:
- 提供集群管理的 REST API 接口,包括认证授权、数据校验以及集群状态变更等
- 提供其他模块之间的数据交互和通信的枢纽(其他模块通过 API Server 查询或修改数据,只有 API Server 才直接操作 etcd)
Controller Manager
Controller Manager由kube-controller-manager和cloud-controller-manager组成,是Kubernetes的大脑,它通过apiserver监控整个集群的状态,并确保集群处于预期的工作状态。
kube-controller-manager
kube-controller-manager由一系列的控制器组成
- Replication Controller
- Node Controller
- CronJob Controller
- Daemon Controller
- Deployment Controller
- Endpoint Controller
- Garbage Collector
- Namespace Controller
- Job Controller
- Pod AutoScaler
- RelicaSet
- Service Controller
- ServiceAccount Controller
- StatefulSet Controller
- Volume Controller
- Resource quota Controller
cloud-controller-manager
在Kubernetes启用Cloud Provider的时候才需要,用来配合云服务提供商的控制,也包括一系列的控制器,如:
- Node Controller
- Route Controller
- Service Controller
kube-scheduler
kube-scheduler 负责分配调度 Pod 到集群内的节点上,它监听 kube-apiserver,查询还未分配 Node 的 Pod,然后根据调度策略为这些 Pod 分配节点(更新 Pod的 NodeName 字段)。
调度器需要充分考虑诸多的因素:
- 公平调度
- 资源高效利用
- QoS
- affinity 和 anti-affinity
- 数据本地化(data locality)
- 内部负载干扰(inter-workload interference)
- deadlines
Kubelet
每个节点上都运行一个 kubelet 服务进程,默认监听 10250 端口,接收并执行 master 发来的指令,管理 Pod 及 Pod 中的容器。每个 kubelet 进程会在 API Server 上注册节点自身信息,定期向 master 节点汇报节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。
Container runtime
容器运行时(Container Runtime)是 Kubernetes 最重要的组件之一,负责真正管理镜像和容器的生命周期。Kubelet 通过 Container Runtime Interface (CRI) 与容器运行时交互,以管理镜像和容器。
kube-proxy
每台机器上都运行一个 kube-proxy 服务,它监听 API server 中 service 和 endpoint 的变化情况,并通过 iptables 等来为服务配置负载均衡(仅支持 TCP 和 UDP)。
kube-proxy 可以直接运行在物理机上,也可以以 static pod 或者 daemonset 的方式运行。
kube-proxy 当前支持一下几种实现:
- userspace:最早的负载均衡方案,它在用户空间监听一个端口,所有服务通过 iptables 转发到这个端口,然后在其内部负载均衡到实际的 Pod。该方式最主要的问题是效率低,有明显的性能瓶颈。
- iptables:目前推荐的方案,完全以 iptables 规则的方式来实现 service 负载均衡。该方式最主要的问题是在服务多的时候产生太多的 iptables 规则,非增量式更新会引入一定的时延,大规模情况下有明显的性能问题
- ipvs:为解决 iptables 模式的性能问题,v1.8 新增了 ipvs 模式,采用增量式更新,并可以保证 service 更新期间连接保持不断开
- winuserspace:同 userspace,但仅工作在 windows 上。
Pod
Pod是K8S的基本运行单元。
在Pod里的容器,既共享网络
,又共享存储卷
回忆一下docker容器
如果要在两个容器之间共享网络,我们会将两个容器加入相同的network。
1
2
3
4
5
6# 创建bridge网络
docker network create -d bridge my-bridge
# 将busybox1容器,busybox2容器 连接到 my-bridge 这个网络上
docker network connect my-bridge busybox1
docker network connect my-bridge busybox2如果要在两个容器之间共享储存,我们会将两个容器加入相同的
数据卷容器
1
2
3
4
5
6
7
8
9# 创建一个数据卷挂载到/dbdata
docker run -it -v /dbdata --name dbdata ubuntu
# 其他容器中使用--volumes-from来挂载dbdata容器中的数据卷.
# 例如创建db1和db2两个容器,并从dbdata容器挂载数据卷
docker run -it --volumes-from dbdata --name db1 ubuntu
docker run -it --volumes-from dbdata --name db2 ubuntu
# 此时,容器db1和db2都挂载同一个数据卷到相同的/dbdata目录。三个容器任何一方在该目录下的写入,其他容器都可以看到。
在一个K8S的Pod中,会默认会有一个pause容器,然后用户自定义的容器都会连接到pause容器中。
也就谁说,在Pod中,相当于将docker容器都加入到了pause这个数据卷容器中,同时又创建一个network,并且将这些容器都加入到这个network中。这就使得pod中的容器既共享网络
,又共享存储卷
。
Pod分类
- 自主式Pod(不被控制器管理的Pod)
- 被控制器管理的Pod
Pod的控制器
全部的控制器可回顾:[kube-controller-manager](# kube-controller-manager),重点讲一下下面几种控制器:
ReplicationController
:用来确保容器应用的副本数始终保持在用户定义的副本数。即如果有容器异常退出,会自动创建新的Pod来代替,如果异常多出来的容器也会自动回收。新版本的K8S中,建议使用ReplicaSet来取代ReplicationControlle。ReplicaSet
:跟ReplicationController没有本质不同,只是名字不一样。并且ReplicaSet支持集合式的seelctor。Deployment
:虽然ReplicaSet可以独立使用,但是还是建议使用Deployment来自动管理ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持滚动更新(rolling-update),但Deployment支持)deployment还支持
滚动更新
的回滚
操作。deployment的滚动更新流程:
- 当滚动更新的时候,会创建一个RS-1,
- 然后在RS-1里创建一个Pod后,同时就会退出RS里的一个Pod。直到RS-1的Pod全部创建完毕,RS的Pod全部退出。
- 但是RS并不会被删除,只是被停用。所以当要执行回滚操作的时候,重新启用RS,之后就可以逆向执行上面的操作。
StatefulSet Controller
:是为了解决有状态服务的问题。(对应Deployements和ReplicaSets是为了无状态服务而设计的),其应用场景包括:- 稳定的持久化存储。即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。简单来说就是:如果一个Pod死亡以后,再调度一个新的Pod取代之前的Pod的时候,新的Pod用到的存储还是之前的Pod的存储,并且存储里面的数据也不会丢失。
- 稳定的网络标志。即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现。
- 有序部署。即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod都需都是Running和Ready状态),基于init containers来实现。简单来说就是:只有前面的Pod都处于Running和Ready状态之后,新的Pod才能被创建。
- 有序收缩,有序删除(即从N-1到0)
什么是
有状态服务
?简单来说,如果一个容器从集群中移出去,之后再移进来,却无法正常工作的服务,就是有状态服务。
为什么需要有序部署?
一个网络服务,必须先启动mysql,再启动Apache,再启动Nginx。既然网络服务有启动顺序,那么我们部署服务的时候也一样要有序部署。删除的时候,一样需要逆序删除。
DaemonSet
:确保全部(或者部分)Node上运行同一份 Pod 副本。当有的Node加入集群的时候,也会为他们新增一个Pod。当有的Node从集群移除的时候,这些Pod也会被回收。删除DaemonSet将会级联删除它创建的所有Pod。简单来说,DaemonSet 的作用就像是计算机中的守护进程,它能够运行集群存储、日志收集和监控等『守护进程』,这些服务一般是集群中必备的基础服务。
- 守护进程是在后台运行不受终端控制的进程(如输入、输出等),一般的网络服务都是以守护进程的方式运行。
- 守护进程脱离终端的主要原因有两点:
- 用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。
- (如其他用户登录该终端后,以前的守护进程的错误信息不应出现)由终端上的一些键所产生的信号(如中断信号),不应该对以前从该终端上启动的任何守护进程造成影响。
- 简单理解,守护进程和普通进程区别是指:将后台程序变成一种服务,比如说,用命令行输入启动程序,如果不是守护进程的话,一旦命令行窗口关闭,程序就终止了;而如果启动守护进程,则退出命令行窗口之后,服务一直处于运行状态。
- 后台进程和守护进程的区别:
- 守护进程已经完全脱离终端控制台了,而后台程序并未完全脱离终端(在终端未关闭前还是会往终端输出结果),守护进程在关闭终端控制台时不会受影响,而后台程序会随用户退出而停止。
- 守护进程的会话组和当前目录,文件描述符都是独立的。后台运行只是终端进行了一次fork,让程序在后台执行,这些都没改变。
使用DaemonSet的一些典型用法:
- 运行集群存储daemon,例如在每个Node上运行glistered,ceph。
- 在每个Node上运行日志收集daemon,例如fluent,logstash。
- 在每个Node上运行监控daemon,例如Prometheus Node Exporter
Job:负责批处理任务,即仅执行一次的任务,他保证批处理任务的一个或多个Pod成功结束。
Cron Job管理给予时间的Job,即:
- 在给定时间点只运行一次
- 周期性的在给定时间点运行
网络通讯方式
通讯方式:
- 同一个Pod内的多个容器之间:localhost
- 各个Pod之间的通讯:Overlay Netwrok
- Pod与Service之间的通讯:各个节点的Iptables规则
Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中,这在GCE(Google Compute Engine)里面是现成的网络模型,Kubernetes假定这个网络已经存在。而在私有云里搭建Kubernetes集群,就不能假定这个网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间的互相访问先打通,然后运行Kubernetes。
所谓的
扁平的网络空间
指的是:所有的Pod都可以通过对方的IP来直接访问。
所谓的扁平的网络空间
,只是表现是如此,但是实际上并不是这样的,底层有一堆的转换机制在。运用的机制就是Flannel网络规划服务。
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。而且它还能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内。
web app1-3和backend分别是四个Pod。
web app1里的Apache和mysql要进行通讯,因为两个容器在同一个Pod内,所以使用的是localhost
web app2和web app1要进行通讯,因为两个Pod在同一个物理主机上,所以通过docker网桥进行沟通。也就是:
1
2
3
4
5web app2
-> 10.1.15.2/24
-> Docker0 10.1.15.1/24
-> 10.1.15.3/24
web app1Web app2要和backend进行通讯,因为跨了物理主机,所以使用的Overlay Netwrok
在上面流程中,ETCD和Flannel的关系:
- ETCD存储管理Flannel可分配的IP地址段资源(也就是说,在启动之后Flannel会向ETCD插入可以分配的网段)
- Flannel监控ETCD中每个Pod的实际地址,并在内存中建立维护Pod节点路由表