目录
为什么用
- 当一个容器发生故障了,可以让另外一个容器立刻启动去替补发生故障的容器。
- 当并发量突然变大的时候,可以做到横向扩展容器的数量。
- 当并发量突然变小的时候,也可以做到横向减少容器的数量。
- Kubernetes的这种特性也就是动态伸缩。
是什么
Kubernetes就是一组服务器的集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。
主要功能
- 自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器。
- 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整。
- 服务发现:服务可以通过自动发现的形式找到它所依赖的服务。
- 负载均衡:如果一个服务启动了多个容器,能够自动实现请求的负载均衡。
- 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本。
- 存储编排:可以根据容器自身的需求自动创建存储卷。
概念
- Master:集群控制节点,每个集群需要至少一个master节点负责集群的管控。
- Node:工作负载节点,由master分配容器到这些node工作节点上,然后node节点上的docker负责 容器运行。
- Pod:K8S的最小控制单元,容器都是运行在pod中,一个pod可以有1个或者多个容器。
- Controller:控制器,通过它来实现对pod的管理,比如启动pod,停止pod,伸缩pod的数量等等。
- Service:pod对外服务的统一入口,service可以维护同一类的多个pod。
- Label:标签,用于对pod进行分类,同一类pod会拥有相同的标签。
- NameSpace:命名空间,用来隔离pod的运行环境。
组件
一个K8S集群主要是由控制节点(master),工作节点(node)构成,每个节点上都会安装不同的组件。
- master:集群的控制平面,负责集群的决策(管理)
- ApiServer:资源操作的唯一入口,接受用户输入的命令,提供认证,授权,API注册和发现。
- Scheduler:负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上。
- ControllerManager:负责维护集群的状态,比如程序部署安排,故障检测,自动扩展,滚动更新。
- Etcd:负责存储集群中各种资源对象的信息。
- node:集群的数据平面,负责为容器提供运行环境(干活)
- Kubelet:负责维护容器的生命周期,通过控制docker来创建,更新,销毁容器。
- KubeProxy:负责提供集群内部的服务发现和负载均衡。
- Docker:负责节点上容器的各种操作。
各组件调用关系
- Kubernetes的环境启动以后,Master节点和Node节点都会将自己的信息存储到etcd数据库中。
- 比如安装一个Nginx服务,安装的请求首先会被发送到Master节点上面的ApiServer组件。
- ApiServer组件会调用Scheduler组件来决定到底应该把这个服务安装到哪个Node节点上。
- 然后Scheduler组件会从etcd数据库中读取各个Node节点的信息,按照自己的算法选择一个Node节 点,然后将选择的结果告诉给ApiServer组件。
- ApiServer组件调用Controller-Manager组件去调度Node节点来安装Nginx服务。
- Node节点在接收到指令以后,需要先通过Docker启动一个服务的Pod,然后在通过Docker在当前的 Pod中启动一个Nginx服务的容器实例。
- Pod是Kubernetes的最小操作单元,所有的容器都必须运行在Pod中。
- 到这里一个Nginx服务就运行了,如果需要访问Nginx服务,就需要通过kube-proxy(趴克塞)来对Pod 产生访问的代理。
集群
- 一主多从
- 多主多从
资源管理器
什么是资源管理器
在K8S中,所有的内容都抽象为资源,用户需要通过操作资源来管理K8S。
- K8S的本质就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,就是在K8S集群 中运行一个个容器,并将指定的程序跑在容器中。
- K8S的最小管理单元是pod而不是容器,只能将容器放在pod中,而K8S一般也不会直接管理pod, 而是通过pod管理器来管理Pod。
- pod可以提供服务之后,就要考虑如何访问pod中服务,K8S提供了Service资源实现这个功能。
资源管理方式
命令式对象管理
直接使用命令去操作K8S资源 kubectl run nginx-pod --image=nginx:1.17.1 --port=80
- 查看所有pod:kubectl get pod
- 查看某个pod:kubectl get pod pod_name
- 查看某个pod,以yaml格式显示结果:kubectl get pod pod_name -o yaml
- 查看K8S所有抽象资源:kubectl api-resources
- K8S帮助命令:kubectl --help
- 创建一个namespace:kubectl create ns dev
- 获取namespace:kubectl get ns
- 在此namespace下创建并运行一个nginx的pod:kubectl run pod --image=nginx:latest -n dev
- 查看新创建的pod:kubectl get pod -n dev
- 删除指定的pod:kubectl delete pod pod-name
- 删除指定的namespace:kubectl delete ns dev
命令式对象配置
通过命令配置和配置文件去操作K8S资源 kubectl create/patch -f nginx-pod.yaml
- create创建资源:kubectl create -f nginxpod.yaml
- get查看资源:kubectl get -f nginxpod.yaml
- delete删除资源:kubectl delete -f nginxpod.yaml
声明式对象配置
通过apply命令和配置文件去操作K8S资源 kubectl apply -f nginx-pod.yaml 创建/更新
- 声明式对象配置就是使用apply描述一个资源最终的状态(在yaml中定义状态)
- 使用apply操作资源:
- 如果资源不存在,就创建,相当于kubectl create
- 如果资源已存在,就更新,相当于kubectl path
kubectl可以在node节点上运行吗?
kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行此命令,需要将master上的.kube文件复制到node节点上,即在master节点上执行下面操作:
scp -r HOME/.kube node1:HOME/
三种方式应该怎么使用?
- 创建/更新资源,使用声明式对象配置kubectl apply -f XXX.yaml
- 删除资源,使用命令式对象配置kubectl delete -f XXX.yaml
- 查询资源,使用命令式对象管理kubectl get(describe)资源名称
Namespace
什么是Namespace
多套环境的资源隔离或者多租户的资源隔离
为什么使用Namespace
- 默认情况下,K8S集群中的所有Pod都是可以相互访问的。但是实际中,可能不想让两个Pod之间进 行互相的访问,那此时就可以将两个Pod划分到不同的namespace下。
- K8S通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的组,以方便不同的组的资 源进行隔离使用和管理。
命令
- 查看所有的ns:kubectl get ns
- 查看指定的ns:kubectl get ns default
- 指定输出格式:kubectl get ns default -o wide/json/yaml
- 查看ns详情:kubectl describe ns default
- 创建ns:kubectl create ns dev
- 删除ns:kubectl delete ns dev
Pod
什么是Pod
- Pod是K8S集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
- Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。
Pod中容器分类
- 用户程序所在的容器,数量可多可少
- Pause容器,每个Pod都会有一个根容器,作用有两个:
- 可以以它为依据,苹果整个Pod的健康状态
- 可以在跟容器上设置IP地址,其它容器都是此IP,以实现Pod内部的网络通信。Pod之间的通讯 采用虚拟二层网络技术来实现,当前环境用户的是Flannel。
命令
- 查询K8S集群组件:kubectl get pod -n kube-system
- 创建并运行nginx:kubectl run nginx --image-nginx:lastest --port=80 --namespace dev
- 查看Pod基本信息:kubectl get pods -n dev
- 查看Pod的详细信息:kubectl describe pod nginx -n dev
- 获取Pod的IP:kubectl get pods -n dev -o wide
- 访问Pod:curl http://10.244.1.23:80
- 删除指定Pod:kubectl delete pod nginx
- Pod是由Pod控制器创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建。此时要想删 除Pod,必须删除Pod控制器。
- 查询当前namespace下的Pod控制器:kubectl get deploy -n dev
- 删除次Pod控制器:kubectl delete deploy nginx -n dev
Pod生命周期
Pod生命周期过程
- pod创建过程
- 运行初始化容器(init container)过程
- 运行主容器(main container)
- 容器启动后钩子(post start),容器终止前钩子(pre stop)
- pod终止过程。
Pod生命周期5种状态(相位)
- 挂起(Pending):apiServer已经创建了pod资源对象,但尚未被调度完成或者仍处于下载镜像的过程中。
- 运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成。
- 成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启。
- 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态。
- 未知(Unknown):apiServer无法正常获取到Pod对象的状态信息,通常由网络通信失败所导致。
创建和终止
Pod的创建过程
- 用户通过kubectl或其他api客户端提交需要创建的pod信息给apiServer。
- apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端。
- apiServer开始反映etcd中的pod对象的变化,其它组件使用watch机制来跟踪检查apiServer上的变 动。
- Scheduler发现由新的pod对象要创建,开始为pod分配主机并将结果信息更新至apiServer。
- node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果回送至apiServer。
- apiServer将接收到的pod状态信息存入etcd中。
Pod的终止过程
- 用户向apiServer发送删除pod对象的命令
- apiServer中的pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
- 将pod标记为terminating状态
- kubelet在监控到pod对象转为terminating状态时同时启动pod关闭过程
- 端点控制器监控到pod对象的关闭行为时将其从所匹配到此端点的service资源的端点列表中移除
- 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后级会以同步的方式启动执 行。
- pod对象中的容器进程收到停止信号
- 宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号
- kubelet请求apiServer将此pod资源的宽限设置为0从而完成删除操作,此时pod对于用户已不可见
初始化容器
什么是初始化容器
初始化容器是在pod的主容器启动之前要运行的容器,主要时做一些主容器的前置工作。
初始化容器特征
- 初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么K8S需要重启直到成功完成。
- 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行
初始化容器应用场景
- 提供主容器镜像中不具备的工具程序或自定义代码
- 初始化容器要先应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得 到满足。
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码
K8S在主容器的启动之后和停止之前提供了两个钩子函数:
- post start:容器创建之后执行,如果失败了会重启容器
- pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
容器探测
什么是容器探测
- 容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。
- 如果经过探测,实例的状态不符合预期,那么K8S就会把该问题实例”摘除”,不承担业务流量
探针
K8S提供了两种探针来实现容器探测:
- liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,K8S会重启 容器。
- readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,K8S不会转发 流量。
- livenessProbe决定是否重启容器,readinessProbe决定是否将请求转发给容器。
探测方式
- Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常
- TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正 常
- HTTPGet:调用容器内Web应用的URL,如果返回状态码在200和399之间,则认为程序正常,否则 不正常
重启策略
Pod的重启策略有3种:
- Always:容器失效时,自动重启该容器,这也是默认值。
- OnFailure:容器终止运行且退出码不为0时重启。
- Never:不论状态为何,都不重启该容器。
Pod调度
什么是Pod调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程不受人工控制。但是在实际使用中,这并不满足需求,因为很多情况下,我们想控制某些Pod到达某些节点上,这就要K8S对Pod的调度来实现。
调度规则
- 自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出。
- 定向调度:NodeName,NodeSelector。
- 亲和性调度:NodeAffinity,PodAffinity,PodAntiAffinity。
- 污点(容忍)调度:Taints,Toleration。
定向调度
什么是定向调度
利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。这里的调度是强制的,即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。
NodeName
NodeName用于强制约束将Pod调度到指定的Name的Node节点上。这种范式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过K8S的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。
亲和性调度
什么是亲和性调度
它在NodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
亲和性调度分类
- nodeAffinity(node亲和性):以node为目标,解决pod可以调度到哪些node的问题
- podAffinity(pod亲和性):以pod为目标,解决Pod可以和哪些已存在的pod部署在同一个拓扑域中的 问题
- podAntiAffinity(pod反亲和性):以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域 中的问题
亲和性/反亲和性使用场景
- 亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能的靠近,这样可以减少因 网络通信而带来的性能损耗。
- 反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上, 这样可以提高服务的高可用性。
污点和容忍
污点
什么是污点
- 通过在Node上添加污点属性,来决定是否允许Pod调度过来
- Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度过来,甚至可以将已 经存在的Pod驱逐出去
污点设置
污点格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
- PreferNoSchedule:K8S将尽量避免把Pod调度到具有该污点的Node上,除非没有其它节点可调度。
- NoSchedule:K8S将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod。
- NoExecute:K8S将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离。
容忍
- 我们可以在node上添加污点用于拒绝pod调度上来,但是如果就是想将一个pod调度到一个有污点 的node上去,这就要使用到容忍。
- 污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,Pod通过容忍忽略拒绝。
Pod控制器
什么是Pod控制器
Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod。
Pod控制器种类
- ReplicationController:比较原始的pod控制器,已经被废弃,由ReplicaSet替代。
- ReplicaSet:保证副本数量一直维持在期望值,并支持pod数量扩缩容,镜像版本升级。
- Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级,回退版本
- Horizontal Pod Autoscaler(HPA):可以根据集群负载自动水平调整Pod的数量,实现削峰填谷。
- DaemonSet:在集群中的指定Node上运行且仅运行一个副本,一般用于守护进程类的任务。
- Job:它创建出来的Pod只要完成任务就立即退出,不需要重启或重建,用于执行一次性任务。
- Cronjob:它创建的Pod负责周期性任务控制,不需要持续后台运行
- StatefulSet:管理有状态应用。
ReplicaSet
保证一定数量的pod正常运行,它会持续监听这些pod的运行状态,一旦pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级。
Deployment
什么是Deployment
Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod。
即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更强大。
Deployment主要功能
- 支持所有ReplicaSet的所有功能
- 支持发布的停止,继续
- 支持滚动升级和回滚版本
Deployment更新策略
- 重建更新:Recreate,在创建出新的Pod之前会先杀掉所有已存在的Pod。
- 滚动更新:RollingUpdate,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod。
Deployment版本回退
Kubectl rollout
- status:显示当前升级状态
- history:显示升级历史记录
- pause:暂停版本升级过程
- resume:继续已经暂停的版本升级过程
- restart:重启版本升级过程
- undo:回滚到上一级版本(可以使用--to-revison回滚到指定版本)
金丝雀发布
有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按照期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。
HPA
K8S期望可以实现通过监测Pod的使用情况,实现pod数量的自动调整,于是就产生了HPA这种控制器。
HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种K8S资源对象,它通过追踪分析RC控制的所有目标Pod的负责变化情况,来确定是否需要针对性地调整目标Pod的副本数,这是HPA的实现原理。
DaemonSet
什么是DaemonSet
DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本。一般适用于日志收集,节点监控等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。
DaemonSet特点
- 每当向集群中添加一个节点时,指定的Pod副本也将添加到该节点上。
- 当节点从集群中移除时,Pod也就被垃圾回收了。
Job
什么是Job
主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务。
Job特点
- 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量。
- 当成功结束的pod达到指定的数量时,Job将完成执行。
CronJob
ConJob控制器以Job控制器资源为其管控对象, 并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。
Label
什么是Label
在资源上添加标识,用来对它们进行区分和选择。
Label的特点
- 一个Label会以key/value键值对的形式附加到各种对象上,如Node,Pod,Service等等
- 一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。
- Labe通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除。
Label Selector
用于查询和筛选拥有某些标签的资源对象
- 基于等式的Label Selector
- name=slave:选择所有包含Lable中key=”name”且value=”slave”的对象。
- env!=producation:选择所有包括Label中的key=”env”且value不等于”producation”的对象。
- 基于集合的Label Selector
- name in(master,slave):选择所有包含Label中的key=”name”且value=”master”或”slave”的对象
- name not in(fronted):选择所有包含Label中的key=”name”且value不等于”frontend”的对象
- 标签的选择条件可以使用多个,将多个Label Selector进行组合
- name=slave,env!=production
- name not in (frontend),env!=producation
命令
- 为Pod资源打标签:kubectl label pod nginx-pod version=1.0 -n dev
- 为Pod资源更新标签:kubectl label pod nginx-pod version=2.0 -n dev --overwrite
- 查看标签:kubectl get pod nginx-pod -n dev --show-labels
- 筛选标签:kubectl get pod -n dev -l version=2.0 --show-labels
- 删除标签:kubectl label pod nginx-pod version- -n dev
Service
什么是Service
在K8S中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问。
K8S提供了Service资源,Service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务。
kube-proxy
什么是kube-proxy
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。
三种工作模式
userspace模式
kube-proxy会为每一个Service创建一个监听端口,发送Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据负载均衡算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。
iptables模式
Kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的负载均衡策略,当后端Pod不可用时也无法进行重试。
ipvs模式
ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此之外,ipvs支持更多负载均衡算法。
Service类型
- ClusterIP:默认值,它是K8S系统自动分配的虚拟IP,只能在集群内部访问
- NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,可以在集群外部访问服务。
- LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
- ExternalName:把集群外部的服务引入集群内部,直接使用
Endpoint
什么是Endpoint
Endpoint是K8S中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。
一个Service由一组Pod组成,这些Pod通过Endpoints暴露处理,Endpoints是实现实际服务的端点集合。换句话说,service和pod之间的联系是通过endpoints实现的。
负载分发策略
- 如果不定义,默认使用kube-proxy的策略,比如随机,轮询
- 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上。
HeadLiness类型的Service
在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,K8S提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。
NodePort类型的Service
如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过NodeIp:NodePort来访问service了。
LoadBalancer类型的Service
LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
ExternalName类型的Service
ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。
Ingress
为什么使用Ingress
- NodePort的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。
- LoadBalancer的缺点是每个Service需要一个LB,浪费,麻烦,并且需要K8S之外设备的支持
- 基于这种现状,K8S提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴 露多个Service的需求。
Ingress工作机制
Ingress相当于一个7层的负载均衡器,是K8S对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置,然后对外部提供服务。
- ingress:K8S中的一个对象,作用是定义请求如何转发到Service的规则
- ingress controller:具体实现反向代理及负载均衡程序,对ingress定义的规则进行解析,根据配置的规 则来实现请求转发,实现方式有很多,比如Nginx,Contour,Haproxy等等。
Ingress工作原理
- 用户编写Ingress规则,说明哪个域名对应K8S集群中的哪个Service
- Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置
- Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态解析
- 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则。
数据存储
Volume
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,K8S通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。
基本存储
EmptyDir
EmptyDir是最基础的Volumn类型,一个EmptyDir就是Host上的一个空目录。
EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为K8S会自动分配一个目录,当Pod销毁时,EmptyDir中的数据也会被永久删除。
EmptyDir用途:
-
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
- 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
HostPath
HostPath就是将Node主机中一个实际目录挂载到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依旧可以存在于Node主机上。
NFS
HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS,CIFS。
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问。
高级存储
PV
持久卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由K8S管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
PVC
持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC其实就是用户向K8S系统发出的一种资源需求申请。
生命周期
PV和PVC之间的相互作用遵循以下生命周期:
- 资源供应:管理员手动创建底层存储和PV
- 资源绑定:用户创建PVC,K8S负责根据PVC的声明去寻找PV,并绑定。
在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的
- 一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了。
- 如果找不到,PVC则会无限期处于Pending状态,直到等到系统管理员创建一个符合其要求的PV
- PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其它PVC进行绑定了
- 资源使用:用户可在pod中像volume一样使用pvc,Pod使用Volume的定义,将PVC挂载到容器内 的某个路径进行使用。
- 资源释放:用户删除pvc来释放pv。当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV 会被标记为”已释放”,但还不能立刻与其它PVC进行绑定。通过之前PVC写入的数据可能还被留在存 储设备上,只有在清除之后改PV才能再次使用。
- 资源回收:K8S根据pv设置的回收策略进行资源回收。对于PV,管理员可以设定回收策略,用于设置 与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能提供新的 PVC绑定和使用。
配置存储
ConfigMap
ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。
Secret
主要用于存储敏感信息,例如密码,密钥,证书等等。
安全认证
访问控制概述
安全性其实就是保证对K8S的各种客户端进行认证和鉴权操作。
客户端:
- User Account:一般是独立于K8S之外的其它服务管理的用户账号。
- Service Account:K8S管理的账号,用于为Pod中的服务进程在访问K8S时提供身份标识。
ApiServer是访问及管理资源对象的唯一入口。任何一个请求访问ApiServer,都有经过下面三个流程:
- Authentication(认证):身份鉴别,只有正确的账号才能够通过认证。
- Authorization(授权):判断用户是否有权限对访问的资源执行特定的动作
- Admission Control(准入控制):用户补充授权机制以实现更加精细的访问控制功能。
认证管理
- HTTP Base认证:通过用户名+密码的方式认证
这种认证方式是把”用户名:密码”用base64算法进行编码后的字符串放在HTTP请求中的Header Authorization域里发送给服务器。服务端收到后进行编码,获取用户名及密码,然后进行用户身份认 证的过程。
- HTTP Token认证:通过一个Token来识别合法用户。
- HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式。
- HTTPS通信双方的服务器向CA机构申请证书,CA机构下发根证书、服务端证书及私钥给申请
- 客户端向服务器端发起请求,服务端下发自己的证书给客户端,
- 客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥,
- 客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器
- 客户端发送自己的证书给服务器端,服务端接收到证书后,通过私钥解密证书,
- 在证书中获得客户端的公钥,并用该公钥认证证书信息,确认客户端是否合法
- 服务器端和客户端协商好加密方案后,客户端会产生一个随机的秘钥并加密,发送到服务器端。
- 服务器端接收这个秘钥后,双方接下来通信的所有内容都通过该随机秘钥加密
授权管理
授权发生在认证成功之后,通过认证就可以知道请求用户是谁, 然后K8S会根据事先定义的授权策略来决定用户是否有权限访问,这个过程就称为授权。
API Server目前支持以下几种授权策略:
- AlwaysDeny:表示拒绝所有请求,一般用于测试
- AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程(Kubernetes默认的策略)
- ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
- Webhook:通过调用外部REST服务对用户进行授权
- Node:是一种专用模式,用于对kubelet发出的请求进行访问控制
- RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)
RBAC:给哪些对象授予了哪些权限
- 对象:User,Groups,ServiceAccount。
- 角色:代表着一组定义在资源上的可操作动作(权限)集合。
- 绑定:将定义好的角色跟用户绑定在一起。
RBACy引入了4个顶级资源对象:
- Role,ClusterRole:角色,用于指定一组权限。
- RoleBinding,ClusterRoleBinding:角色绑定,用于将角色(权限)赋予给对象。
准入控制
通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver才会处理这个请求。
只有当所有的准入控制器都检查通过之后,apiserver才执行该请求,否则返回拒绝。
当前可配置的Admission Control准入控制如下:
- AlwaysAdmit:允许所有请求。
- AlwaysDeny:禁止所有请求,一般用于测试。
- AlwaysPullImages:在启动容器之前总去下载镜像。
- DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求。
- ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
- Service Account:实现ServiceAccount实现了自动化。
- SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效。
- ResourceQuota:用于资源配额管理目的,观察所有请求,确保在namespace上的配额不会超标。
- LimitRanger:用于资源限制管理,作用于namespace上,确保对Pod进行资源限制。
- InitialResources:为未设置资源请求与限制的Pod,根据其镜像的历史资源的使用情况进行设置。
- NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒绝。 当删除一个namespace时,系统将会删除该namespace中所有对象。
- DefaultStorageClass:为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认。 的StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节。
- DefaultTolerationSeconds:这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute 和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min。
- PodSecurityPolicy:这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的 PodSecurityPolicy对Pod的安全策略进行控制。