k8s技术分享
背景
容器:隔离不同服务,避免依赖冲突;为服务提供一致性的环境;(docker, rkt......)
微服务化:大型单体应用被拆分成很多小的、可独立部署的组件,以便于单组件快速迭代升级、降低单模块的物理资源;
带来的问题:部署、运维的人力成本大幅提升(组件间依赖关系、扩缩容、故障检测和处理、负载均衡、自恢复);
解决方案:自动编排技术(Google k8s、Docker Swarm、Apache Mesos)
简单来说,就是docker规模增长带来了如何自动化编排的问题,k8s就是答案之一。
什么是k8s
关键词:2014 google Borg Omega 部署平台 go
k8s有什么优势
屏蔽底层细节:通过基础设施抽象,提供一个统一的部署平台给开发者,开发者无需关心底层硬件、操作系统细节;
高利用率:自动调度,提高资源利用率;
丰富的运维功能:提供服务发现、扩缩容、负载均衡、故障检测和处理、选主等功能;
市场份额:77%(2019)
google搜索热度:
k8s概念和架构
资源概述
k8s中的操作对象都被看作是资源,如节点、pod、Deployment、LimitRequest,可以类比java中的类
资源名称 | 概念 | |
---|---|---|
node | 工作节点 | |
pod(po) | pod是对容器的封装,是k8s管理的最小单元,一个pod里可以有多个应用容器,也可以有多个init containers;
| |
service(svc) | service是对pod 封装,对pod的访问可以通过service,实现负载均衡,避免pod扩缩容、重新部署带来的ip、端口变更;
| |
endpoint | service和pod之间不是直连,service通过selector选择器,筛选pod构造出包含pod 的ip、端口的endpoint资源。可以创建一个没有selector的service,手动构造endpoint。 | |
ReplicationCotroller(rc) | k8s用于管理集群内pod的资源,通过在rc内指定selector来关联pod。rc内需要指定pod 副本数量。如果pod修改了标签,会成为孤儿节点,脱离rc管控,rc也会立刻新建一个pod。 | |
ReplicaSet(rs) | rc的升级版,支持更高级语法的selector,例如多标签选择、In、NotIn等语法。 | |
Deployment | 封装了rs,支持滚动升级
| |
StatefulSet | 类比ReplicaSet,用于部署有状态服务的pod | |
DaemonSet(ds) | ds能够在每个节点上都部署pod,而不是指定数量,一般用于系统初始化等全局设置。ds也可以指定selector,这样就在指定的节点上部署pod。 | |
Job和CronJob | 不同于pod的持续运行,Job/CronJob只运行一次/定时执行,也可以指定执行次数和并发度。 | |
PersistentVolume(pv)和PersistentVolumeClaim(pvc) | 用于持久化存储 local模式:指定HostPath type的volume,然后通过volumeMount挂载到容器 非local模式:运维人员事先创建好一系列不同类型的pv(如gcePersistentDisk,awsElasticBlockStore,azureDisk,cinder,cephfs),在pvc中定义pv的大小和读写模式,然后pod中就可以直接引用,再通过volumeMount挂载到容器,好处是解耦了实际存储方案。 | |
Horizontal Pod Autoscaler(hpa) | 关联到某个deployment,检测pod的运行资源占用情况(cpu、内存,也可以自定义),实现动态扩缩容,最终尽量接近pod的资源申请值。 | |
ConfigMap(cm) | 本质上是一种卷(volume),用于应用配置抽离,通过卷挂载或者环境变量的方式注入到容器中 | |
ServiceAccount | k8s集群内身份,每个命名空间如果没有手动创建,则会默认创建一个名为‘default’的sa。 授权基于RBAC,通过roleBinding绑定到role,或通过clusterRoleBinding绑定到clusterRole来赋予权限。role和clusterRole的区别是,role是某个命名空间下的资源,clusterRole是集群维度的。 |
k8s集群架构
控制平面组件
控制平面组件包括Kubernetes API server、etcd分布式持久存储、调度器、控制其管理器
组件 | 知识点 |
---|---|
Kubernetes API server |
|
etcd |
|
调度器 |
|
控制器管理器 |
|
工作节点组件
组件 | 知识点 |
---|---|
kubelet |
|
kube-proxy |
|
一次deploy的过程
一个pod 的部署流程
(1)客户端发起请求创建Deployment(如kubectl),API server 创建一个Deployment资源
(2)监听Deployment事件的Deployment Controller收到消息,请求API server创建ReplicaSet
(3)监听ReplicaSet事件的ReplicaController收到消息,请求API server创建pod
(4)监听pod创建事件的调度器收到消息,选择出合适的node发给API server
(5)监听node资源的kubelet收到消息,在本node上创建pod
k8s计算资源管理
问题一:pod如何描述自身所需的计算资源量?
在pod中可以指定requests和limits,
资源requests是指定pod申请的系统资源,如CPU/内存量,跟pod实际使用量无关,即pod实际使用量可能大于、等于或小于requests,是给调度器使用的。
资源limits是资源实际使用量,用于避免某个pod占用过多系统资源。k8s调度时可能超售。当pod使用比limits更多的CPU时不会被kill,但使用比limits更多的内存时会被kill。
limits >= requests
|
问题二:系统资源不够时,k8s按什么机制kill pod?
QoS:按照requests和limits的关系,pod被分成了三档,没有指定requests和limits为BestEffort,指定了requests和limits且相等为Guaranteed,其他为Burstable。
kill顺序为BestEffort -> Burstable -> Guaranteed
如果QoS level一样,则根据使用率来kill,即实际使用量/requests量,较大的会先被kill(为什么是这样的策略呢?我理解requests是调度时分配的额度,比例越高意味着pod可用资源越接近枯竭,就更应该尽早重新调度);
问题三:是否有集群维度的统一限制?
有,通过创建LimitRange资源,可以做如下限制:
(1)pod可申请的最小资源
(2)pod可申请的最大资源
(3)container可申请的最大资源
(4)container未指定时分配的默认requests和limits
(5)request/limits比例的最大值
(6)container可申请的PVC最大值
|
问题四:是否可以在namespace维度限制quota?是否可以限制pod创建数量?
可以,通过创建ResourceQuota资源,可以限制命名空间所有pod可使用的cpu、内存最大值。还可以限制各种资源的创建量,如pod、ReplicationController等。
|
pod的安全机制
问题一:pod内运行的container是相互隔离的,那pod之间有绝对的网络、权限安全性吗?
并没有,需要限制。
从几个方面考虑这个问题,首先,pod可以直接使用宿主机的命名空间的(详见问题二),意味着有能力在pod内部看到宿主机的进程、网络流量,乃至直接与非本pod内的container进行进程间通信。其次,pod默认启动用户是root,结合上一条可知,如果不加限制,是有能力以root身份操作宿主机的。
问题二:pod如何使用宿主机的命名空间?
网络命名空间:pod描述文件中指定hostNetwork:true
绑定宿主机端口:pod描述文件中指定hostPort(网络namespace还是跟宿主机隔离的)
PID命名空间:pod描述文件中指定hostPID: true
IPC命名空间:pod描述文件中指定hostIPC: true
|
问题三:如何在pod描述中增加限制,避免镜像被篡改时可能出现越权操作?
解决方案是securityContext资源:securityContext可以在pod级别对所有container生效,也可以在container级别指定。在securityContext里可以做这些约束:
(1)指定容器运行进程的用户ID:runAsUser(指定UID)
(2)阻止以root运行:runAsNonRoot(不care是哪个用户运行)
(3)指定特权模式:privileged(kube-proxy就是一个需要使用privileged=true的例子,因为要修改iptables等)
(4)单独指定权限:capabilities里指定add/drop内核功能(例如drop SYS_TIME禁止容器修改系统时间)
(5)阻止根文件系统写入:readOnlyRootFileSystem(对具体要写的卷再单独指定readOnly=false)
|
问题四:上面的方案都是依赖pod自己遵守规定,有没有集群维度的统一管理?
有,通过创建PodSecurityPolicy资源,它可以指定securityContext里能做到的一切。
首先创建PodSecurityPolicy(psp),然后创建role,关联此psp,然后再创建roleBinding资源,将此policy赋权给具体的serviceAccount。
|
k8s与juju对比
对juju了解不多,仅限于使用,因此只在使用层面上进行对比。
| k8s | juju |
---|---|---|
| k8s | juju |
描述文件 | 一组yaml文件 | charm包 |
部署 | kubectl create pod/replicaset/deployment -f xxx.yaml 无需关心底层节点,在描述文件里指定tag和副本数量 | juju deploy ${appDir} -n 3 --to 7,8,9 需要了解节点编号 |
升级 | deployment有变更时会自动执行,能够记录历史变更记录,kubectl rollout就可以快速回滚(还可以指定回滚目标版本) | 需要手动执行juju upgrade,无历史记录 |
扩容 | 修改deployment replica数量,就能够自动扩容 | 手动执行juju add-unit docker-nginx -n 3 --to 7,8,9 |
探活 | 支持自定义livenessProbe(Exec / HTTP GET / TCP socket) | 没有探活机制,只监测docker存活状态。 |
组件间依赖 | 在initContainer里检测依赖服务是否正常,每次部署都会检测; | 在metadata.yaml里指定requires服务,只有首次部署会自动添加,后续修改依赖不会自动添加,需要手动执行juju add-relation/juju remove-relation |
hook | 支持Post-start hook(跟容器启动程序是异步的)和pre-stop hook(在容器终止之前执行) | charm包里也可以指定多种stage hook |
进入容器 | 底层抽象的更加彻底,直接在集群内任意机器上执行kubectl exec -it ${podName} /bin/sh | 需要登陆对应宿主机,再docker exec -it ${dockerName} /bin/sh |
配置文件 | 通过init container挂载;通过configMap注入; | 卷挂载方式注入容器 |
相关资料
https://www.kubernetes.org.cn/k8s
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands