Kubernetes

官方网站:http://www.kubernetes.io

官方文档:https://kubernetes.io/zh/docs/home/

kubernetes是一种容器边编排机制,用于容器化应用程序的部署,扩展以及管理,目标是让部署容器化应用变得简单高效。

image-20220113081152509

发展经历

  • Infrastucture as a Service,简称IAAS,基础设施即服务,代表阿里云、亚马逊云

  • Platform as a Serivce,简称PAAS,平台即服务,代表有:

    • 新浪sae,派单给运维构建环境

    • Apache Mesos 开源分布式资源管理框架,用于构建资源池

    • DockerSwarm,轻量级容器资源管理器,功能太少

    • Kubernetes,领航者,功能全面、稳定,由Google支持,是由brog系统使用go重新演化出来的

  • Software as a Service,简称SAAS,软件即服务,代表:Office 365、腾讯文档无需安装,直接在浏览器使用

集群架构和组件

Brog(博格)架构

image-20220114142738421
  • BrogMaster(集群部署)主要负责请求分发。客户可以通过brogcfg配置文件、command-line tools命令行、web browser浏览器三种方式对集群进行调度管理

  • Scheduler,调度器,由客户端发起的调度将会交给该组件解析,并将任务存储在Google Paxos键值对数据库中

  • Broglet则会从Paxos中循环读取数据,找到自己需要做的任务进行处理,负责提供计算能力与服务

Kubernetes架构

查看源图像
  • Master

    • 负责请求的分发

    • Scheduler调度器:介绍任务给节点,也就是负责将任务分散到不同的node中,并将结果交给ApiServer,ApiServer再将数据写入到etcd(kv数据库)

    • Controller Manager:处理集群中常规后台任务,一个资源对应一个控制器,而ControllerManager就是负责管理这些控制器的。例如Deployment、Service

    • api server:Kubernetes API,集群的统一入口(kubectl, web UI, scheduler, etcd, replication controller),各组件协调者,以RESTfulAPI提供接口服务,所有对象资源的增删改查和监听操作都交给APIServer处理后再提交给Etcd存储。

  • etcd:分布式键值存储系统,用于保存集群状态数据,比如Pod、Service等对象信息

    • go语言编写的可信赖的分布式的键值存储服务,用于存储关键数据,协助分布式集群正常运转

    • v2版本只能存储在内存中,v3则会持久化。注意,在k8s v1.11版本中,v2版本已被弃用

    • 采用HTTP协议、C/S架构

    • 架构图:架构图

  • Node

    • 真正提供计算能力与服务的组件,负责运行Pod和容器

    • kubelet组件,Master在Node节点上的Agent,管理本机运行容器的生命周期,比如创建容器、Pod挂载数据卷、下载secret、获取容器和节点状态等工作。kubelet将每个Pod转换成一组容器。容器之间的差异通过CRI(容器运行时接口)屏蔽。

    • kubeproxy,负责写入规则到iptables或者IPVS实现映射访问,以实现在Node节点上实现Pod网络代理,维护网络规则和四层负载均衡工作。

    • 容器引擎,比如Docker、Containerd

  • 集群调用者

    • Dashboard:给K8s集群提供一个B/S结构的访问体系

    • kubectl:命令管理k8s集群

  • CoreDNS:可以为集群中的SVC创建一个A记录(域名IP对应关系解析)

  • FEDERATION:提供一个可以跨集群多k8s统一管理的功能

  • PROMETHEUS:普罗米修斯,提供K8s监控能力

  • ELK:提供k8s集群日志统一分析介入平台

部署Kubernetes

生产环境部署k8s主要的两种方式:

  1. kubeadm,Kubeadm是一个工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。

    部署地址:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

    优点:快速、方便

  2. 二进制,从官方下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群。

    下载地址:https://github.com/kubernetes/kubernetes/releases

    优点:可以对部署过程更好的理解

服务器硬件配置推荐:

image-20220113085230251

网络组件的作用

部署网络组件的目的是打通Pod到Pod之间网络、Node与Pod之间网络,从集群中数据包可以任意传输,形成了一个扁平化网络。

目前,主流的网络组件有:

  1. Flannel,几十台

  2. Calico,更大规模

CNI(Container Network Interface,容器网络接口),就是对k8s对接这些三方网络组建的接口。

配置命令补全

连接Kubernetes集群

kubeconfig配置文件

文件位于:~/.kube/config,kubectl 使用kubeconfig认证文件连接到k8s集群,我们可以使用kubectl config指令生成kubeconfig文件;kubeconfig文件主要记录了下面的几个部分的信息:

  • 集群信息:

  • 上下文信息(已经连接的所有集群信息,比如用户,生产环境有可能有多个集群):

  • 当前上下文信息(当前选择的哪个集群)

  • 客户端证书信息

指定kubeconfig执行命令:

不加会默认从家目录读取,可以移动家目录缩短命令:

将配置文件分发给其他机器,就可以连接k8s集群。

连接多个k8s集群

合并配置,context方式,切换context

我的mac上docker-desktop的kubeconfig:

我的windows虚拟机上的kubeconfig:

我想让我的mac上的kubectl可以同时访问这两个集群,将windows虚拟机上的kubeconfig拷贝到mac,并修改名称:

配置 KUBECONFIG 环境变量,是 kubectl 工具支持的变量,变量内容是冒号分隔的 kubernetes config 认证文件路径,以此来合并多个kubeconfig文件:

查看合并后的文件cat ~/.

注意,不要重名


切换context

  1. docker desktop

    docker-desktop
  2. 使用命令更改context:

切换config文件(不推荐)

直接指定配置文件(不推荐)

集群监控

查看资源集群状态

查看master组件状态:

查看node节点状态:

查看集群信息:

查看api资源信息:

查看资源列表:

查看资源的详细信息:

查看集群资源利用率

查看Node资源消耗:

查看Pod资源消耗:

这个过程是:

如果提示: error: Metrics API not available,则需要安装metrics,官方地址:kubernetes-sigs/metrics-server: Scalable and efficient source of container resource metrics for Kubernetes built-in autoscaling pipelines. (github.com)

安装完毕后再执行命令如下:

管理组件日志

  • k8s系统组件日志

    • systemd守护进程管理的组件,比如kubelet

    • 或者查看系统日志

    • Pod部署的组件:

  • k8s集群内部部署的应用程序日志

    • 标准输出,容器的标准输出将会输出在宿主机的/var/lib/docker/containers/<container-id>-json.log

    • 日志文件,比如java log框架打印到某个指定的文件,可以对应用程序内部路径挂载,在宿主机查看;或者使用kubectl exec -it <Pod名称> -- bash进入容器内部直接查看文件。

收集k8s日志的思路

  • 针对标准输出:以DaemonSet方式在每个Node上部署一个日志收集程序,采集/var/lib/docker/containers/目录下的所有日志

    image-20220114192414927
  • 针对容器中的日志文件:在Pod中增加一个容器运行日志采集器,使用emptyDir共享日志目录让日志采集器读取到日志文件

    image-20220114192431691

部署应用

image-20220114233339118

命令方式

  1. 使用Deployment控制器部署镜像

  2. 使用service发布pod

  3. 访问任意NodeIP:31052即可访问Nginx服务

  4. 卸载: kubectl delete delployment webkubectl delete svc web

yaml方式

  1. 编写yaml资源文件

  2. kubectl apply -f 上面的yaml文件

  3. 卸载 kubectl delete -f 文件

快速生成yaml

根据已有资源生成yaml

查看资源的API

查看所有的API资源

查看所有的API的版本

查看某个API的二级字段

查看某个API的所有级别的字段

查看某个API具体的某个字段的下一级字段

资源

资源:在k8s中,所有的内容都被抽象为资源,当资源被实例化后,成为对象。

集群资源的分类

  • 名称空间(namespace)级别的资源

    • 工作负载资源(workload)

      • Pod

      • ReplicationController,v1.11被废弃

      • ReplicaSet

      • Deployment

      • StatefulSet

      • DaemonSet

      • Job

      • CronJob

    • 服务发现/负载均衡资源(ServiceDiscovery LoadBalance)

      • Service

      • Ingress

    • 配置与存储资源

      • Volume

      • CSI(容器存储接口)

    • 特殊类型存储卷

      • ConfigMap(配置中心资源类型)

      • Secret(保存敏感数据)

      • DownwardAPI(将外部环境中的信息输出给容器)

  • 集群级别的资源

    • Namespace

    • Node

    • Role

    • ClusterRole

    • RoleBinding

    • ClusterRoleBinding

  • 元数据型的资源

    • HPA

    • PodTemplate

    • LimitRange

Deployment

Pod和控制器的关系

Deployment控制器与其他控制器的最主要目的就是为了方便管理k8s中的容器,而Deployment是最常见的工作负载控制器,是k8s的一个抽象概念,用于更高层次的对象,负责部署、管理Pod。与之类似的控制器还有DaemonSet、StatefulSet等。

主要功能:

  • 管理Pod和ReplicaSet

  • 具有上线部署、副本设定、滚动升级、回滚等功能

  • 提供声明式更新

应用场景:

  • 网站

  • API

  • 微服务

管理应用生命周期

image-20220115205620781

Deployment可以针对应用生命周期进行管理:

部署

升级

滚动升级:k8s对Pod升级的默认策略,通过使用新版本Pod逐步更新旧版本Pod,实现零停机发布,用户无感知

发布策略主要有:蓝绿、灰度(金丝雀、A/B测试、冒烟测试)、滚动。(停机瀑布式升级已经过时)

部署升级的过程

水平扩容缩容

扩容/缩容操作实际就是控制ReplicaSet的副本数

回滚(不常用)

回滚是调用ReplicaSet重新部署某个版本(每个版本都有对应的一个ReplicaSet,可以查看rs的信息确认对应的版本号已经所做的改动):

image-20220115222629354

项目下线

ReplicaSet

用途:

  1. Pod副本数量管理,不断对比当前Pod数量和期望Pod数量

  2. Deployment每次发布都会创建一个ReplicaSet作为记录,用于实现回滚

  3. 滚动升级,创建新的RS进行逐步替换旧的RS

Pod

最小封装集合(豌豆荚,容器荚),一个Pod中会封装多个容器,也是k8s管理的最小单位。有些服务之间关联性较强,需要共享网络环境、存储环境等。如果使用标准容器则很难完成操作,所以k8s在容器外部增加了一个Pod的概念。

pod

特点:

  1. 一个Pod可以理解为一个应用程序示例

  2. Pod中的所有容器始终部署在同一个Node上

  3. Pod中容器共享网络,存储资源

边车模式设计(侧面可以带人的三轮摩托):

  • 通过在Pod中定义专门容器来执行业务容器需要的辅助工作

  • 可以将辅助功能同主业务容器解耦,实现独立发布和能力重用

  • 比如:日志收集、应用监控

Pod对象管理命令

注意:一半没有人直接创建Pod

创建Pod:

  1. kubectl apply -f pod.yaml,kind资源类型为Pod

  2. kubectl run nginx --image=nginx

查看Pod:

  1. kubectl get pods

  2. kubectl get pods -w 实时查看

  3. kubectl describe pod <pod_name> 资源的详细信息

查看日志:

  1. kubectl logs <pod_name> [-c 容器名称]

  2. kubectl logs <pod_name> [-c 容器名称] -f 实时查看

进入容器终端:

  1. kubectl exec <pod_name> [-c 容器名称] -- bash

删除Pod:

  1. kubectl delete pod <pod_name>

Pod的状态

  1. Pending 挂起:Pod已经被k8s系统接受,但是有一个或者多个容器尚未创建。这段时间包括:调度Pod、镜像下载等

  2. Running 运行中:Pod已经被绑定在某个Node上,Pod中的所有容器都已经被创建。至少有一个容器处于运行状态,或者正在处于启动或者重启状态

  3. Succeeded 成功:Pod中的所有容器都被成功终止,并且不会再重启

  4. Failed 失败:Pod中至少有一个容器是失败终止的(容器以非0状态退出或者被系统终止)

  5. Unknown 未知:因为某些原因无法取得Pod状态,通常是因为与Pod所在主机通讯失败

创建Pod的流程

流程-时序图

Kubernetes基于list-watch机制的控制器架构,实现组件间交互的解耦。其他组件监控自己负责的资源,当这些组件方发生变化时,kube-apiserver会通知这些组件,这个过程类似发布与订阅。

  1. 执行命令创建Pod(或是由ControllerManager发送,比如Deployment控制器创建的),命令行将会通过api发送到APIServer,并将创建pod的配置信息提交给ETCD键值对存储系统

  2. Schedule检测到未绑定节点的Pod,当根据自身算法选择一个合适的节点,并给这个pod打一个标记,比如:nodename=node1;然后响应给apiserver,并写入到etcd中

  3. kubelet通过apiserver发现有分配到自己节点的新pod,于是调用CRI创建容器,随后将容器状态上报给apiserver,然后写入etcd

  4. kubectl get 请求apiserver 获取当前命令空间pod列表状态,apiserver从etcd直接读取

Pod的生命周期

image-20220114195532614

创建pod成功后:

  1. 初始化基础容器(pause容器)以完成Pod内网络存储的共享

  2. 由pause容器启动一个或者多个init容器,多个init容器链式执行

    1. 如果前一个执行完毕,切没有错误,就会执行下一个init容器

    2. 如果init容器启动失败,且Pod的重启策略为Always,那么Pod将会不断地重启

    3. 作用:在容器创建前,使用初始化容器完成一些工具以及数据的初始化,以防止MainC不安全或者冗余

    4. 作用:Init容器使用LinuxNamespace,所以相对于应用程序具有不同的文件视图,他们具有访问Secret的权限,MainC不具备

    5. 先于MainC运行,可以用于阻塞启动容器

  3. 并发启动所有MainC(业务容器)

    1. 对每个主容器进行readiness/liveness 就绪/存活检测

    2. 就绪检测之后,容器会变为Ready就绪状态,就绪之后才会开放端口,暴露服务

    3. 存活检测跟随整个容器生命,会不间断的对容器的生存情况进行检测,如果不存活,则会根据Pod的重启策略,对Pod进行重启

  4. start/stop 生命周期动作

验证:Pod内多容器网络、资源共享机制

修改yaml资源清单如下:

执行:

验证网络以及资源共享:

验证:每个Pod都会初始化一个pause容器

名称为nginx的容器共有两个,其中一个为pause容器。

环境变量

创建Pod时,可以为其下的容器设置环境变量。

应用场景:

  1. 容器内通过环境变量获取Pod信息

  2. 容器内应用程序通过用户自定义变量改变应用程序默认行为

环境变量定义方式:

  1. 自定义变量值

  2. 变量值从Pod属性获取

  3. 变量值从Secret、ConfigMap获取

测试:

创建上述pod:

查看环境变量配置是否生效:

进入容器输出环境变量:

init容器/初始化容器

初始化容器用于初始化工作,执行完毕就结束,可以理解为一次性任务

  1. 支持大部分应用容器配置,但是不支持健康检查

  2. 优先于应用容器执行

应用场景:

  1. 环境检查:确保应用容器以来的服务启动后再启动应用容器

  2. 初始化配置:例如给应用容器准备配置文件

示例:下载并初始化配置

重启策略 restartPolicy

共有三中那个重启策略,分别用在不同的场景:

  1. Always:当容器终止退出后,总是重新启动容器,是Pod的默认策略。适合需要持续运行提供服务的程序,比如nginx、redis、javaapp

  2. OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。适合需要周期性运行的程序,比如数据库备份、巡检

  3. Never:当容器退出后,从不重启容器。适合一次性运行的程序,比如计算程序、数据离线处理程序

设置Pod的重启策略:

健康检查

健康检查分为三个阶段,位于Init容器运行成功之后:

  1. startupProbe:启动检查,检查成功才由存活检查接手,用于保护慢启动容器(某些容器启动过程时间长,通过启动检查可以排除环境问题,防止长时间启动最后因环境失败的情况)

  2. livenessPobe:存活检查,将杀死容器,根据Pod的restartPolicy来操作

  3. redinessProbe:就绪检查,检查服务是否正常运行,比如项目是否启动成功。如果检查失败,Kubernetes会把Pod从service endpoints中剔除

其中每种检查都支持一下三种检查方法:

  1. httGet:发送HTTP请求,返回200-400范围状态码为成功

  2. exec:执行shell命令返回状态码是0成功

  3. tcpSocket:发起TCP Socket建立成功

示例:健康检查-端口探测(http)

部署一个deployment,他的资源清单nginx.yaml如下:

查看部署情况:

查看pod内容器的详细信息,确认livenessProbe和redinessProbe配置是否成功

验证存活检查与就绪检查是否每段时间发送一次请求

验证存活检查失败,是否将杀死容器,并根据Pod的restartPolicy来操作

再次查看pod信息:

重启次数:

验证Kubernetes是否会把Pod从service endpoints中剔除

在执行上述命令之前,使用如下命令持续监测endpoint的状态,可以看到如下的结果:

静态Pod

特点:

  1. Pod由特定节点上的kubelet管理

  2. 不能使用控制器

  3. Pod名称标志当前节点名称

应用场景:

  1. k8s搭建就是这种机制,用于启动kube系统组件

  2. 工作中不会用

在kubelet配置文件中启用静态Pod参数:

将部署的pod yaml放在该目录由kubelet自动创建,从这个目录移除就会自动移除静态pod。

DaemonSet

功能:

  1. 在每个Node上都运行一个Pod

  2. 新加入的Node也同样会自动运行一个Pod

应用场景:

  1. 网络插件

  2. 监控Agent

  3. 日志Agent

查看调度失败原因,kubectl describe pod <NAME>

  1. 节点CPU/内存不足

  2. 有污点,没容忍

  3. 没有匹配到节点标签

Service

Service的引入主要解决Pod的动态变化(IP每次部署都不同),并提供统一的访问入口:

  1. **服务发现:**防止Pod失联,找到提供同一个服务的Pod

  2. 负载均衡:定义一组Pod的访问策略,并可以避免将流量发送到不可达的Pod上

是集群内服务的代理节点。

Pod和Service的关系

  1. Service 通过标签关联一组Pod

  2. Service通过iptables或者ipvs为一组Pod提供负载均衡的能力

定义与创建Service

创建svc:

查看已经创建的svc:

type字段

常见的Service类型有三种:

  1. ClusterIP,默认值,分配一个IP地址,即VIP,只能在集群内部访问

  2. NodePort,在每个节点上启用一个端口来暴露服务,可以让其通过任意node+端口来进行外部访问;同时也像ClusterIP一样分配一个集群内部IP供集群内部访问

    image-20220119151136177
  3. LoadBalance,与NodePort完全一致。除此以外,k8s会请求底层云平台(比如阿里云、腾讯云、AWS等)上的负载均衡,将每个Node([NodeIp]:[NodePort]) 作为后端添加进去

    image-20220119152054063

service负载均衡实现机制

image-20220119153337811

Service 底层实现主要有iptables和 ipvs两种网络模式,决定你如何转发流量。

service DNS名称解析

CoreDNS是一个DNS服务器,k8s默认采用pod的方式部署在集群中。CoreDNS服务监视KubernetesAPI,为每一个Service创建DNS A记录用于域名解析。其格式为 <service-name>.<namespace-name>.svc.cluster.local

CoreDNS Yaml文件可以参考: https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/coredns

验证:当创建service时会自动添加一个DNS记录:

创建Deployment以及service,进入容器,测试DNS:

Ingress

既然后又NodePort,为什么还需要Ingress?

  1. NodePort 是基于iptables/ipvs实现的负载均衡器,他是四层转发

  2. 四层转发是指在传输层基于IP和Port的转发方式,这种方式的转发不能满足类似域名分流、重定向之类的需求

  3. 所以引入的Ingress七层转发(应用层),他可以针对HTTP等应用层协议的内容转发,可以满足的场景更多

image-20220121144403957
  1. Ingress:k8s中的一个抽象资源,用于给管理员提供一个暴露应用的入口定义方法

  2. Ingress Controller:负责流量路由,根据Ingress生成具体的路由规则,并对Pod进行负载均衡

  3. 外部用户通过Ingress Controller访问服务,由Ingress规则决定访问哪个Service

  4. IngressController内包含一个Service,也可以通过NodePort暴露端口,让用户访问

  5. 然后将流量直接转发到对应的Pod上(注意:只通过Service找到对应的Pod,实际发送并不经过Service,这样更高效)

  6. IngressController是社区提供的一种接口,其下面有很多具体的实现,比如 Nginx、Kong等

  7. 最主流的实现为kubernetes/ingress-nginx

部署ingress-nginx

  1. 从github中下载yaml配置

  2. ingress-nginx相关镜像位于google镜像仓库中,国内网络无法访问;可以从docker hub上寻找相关镜像,修改yaml中的相关镜像地址

  3. 修改用于暴露Ingress-Nginx-Controller的Service的端口暴露方式(ingress controller是pod,负责动态生成nginx配置):

  4. 执行部署

kubernetes里命名空间删不掉的问题

如果某个命名空间(此例里是ingress-nginx)迟迟删除不掉,状态一直是Terminating,然后在此命名空间里重新创建资源时报如下错误:

解决方案:

  1. 在第一个终端里执行: kubectl proxy

  2. 在第二个终端里:kubectl get namespace ingress-nginx -o json > xx.json

  3. 更改json文件:

    file

    改为

    file
  4. 最后执行:

创建ingress规则(HTTP)

查看ingress规则:

配置hosts,访问这些地址即可。

创建ingress规则(HTTPS)

  1. 准备域名证书文件(使用阿里云免费证书,或者使用openssl/cfssl创建自签证书)

  2. 将证书文件保存到k8s Secret中

  3. 使用Ingress规则配置tls

  4. 配置hosts文件,访问: https://ingress.yangsx95.com:30443/

工作原理

IngressController通过与k8s API交互,动态感知集群中Ingress规则变化,然后读取他,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置,应用到管理的nginx服务,然后加载生效。以此来达到负载均衡配置即热更新的效果。

工作流程:

StatefulSet(部署有状态应用)

有状态和无状态

Deployment控制器的设计原则:管理的所有Pod一模一样,提供同一个服务,也不考虑在哪台Node运行,可随意扩容缩容。这种应用成为无状态应用。比如web应用程序就是无状态应用。

在实际场景中,并不能满足所有的应用,尤其是分布式应用程序,一般会部署多个实例,不同于无状态应用比如web服务,这些实例之间往往有依赖关系,例如:主从关系、主备关系,这种应用成为有状态应用,比如Mysql集群,ETCD集群。

StatefulSet就是为了解决部署有状态应用而出现的控制器:

  1. 给Pod分配一个唯一稳定的网络标志符(主机名、唯一域名):使用Headless Service来维护网络的身份

  2. 稳定唯一的持久的存储(唯一的PV和PVC):StatefulSet的存储卷使用VolumeClaimTemplate(卷申请模板),当StatefulSet使用VolumeClaimTemplate创建一个PersisentVolume时,同样也会为每个Pod分配并创建一个对应的PVC

StatefulSet三要素:

  1. 域名

  2. 主机名

  3. 存储(PVC)

部署StatefulSet

调度

配置Pod的资源限制

  1. cpu的单位为毫核(m)或者为浮点数字,比如500m = 0.51000m = 1

  2. 内存的单位为Mb,GB等,例如500Mi1Gi

  3. request代表应用程序启动时需要的资源数量,调度器会寻找符合request要求的节点,如果没有Pod就会一直处于pending状态

  4. limit代表应用程序运行时最多占用的资源数量,这个值对调度机制并不起决定性的作用,如果request的值满足,那么就会部署容器

  5. limit可以防止应用程序假死或者超负荷运行导致主机崩溃的情况,可以更合理控制资源

  6. request的值设置的过大会造成资源浪费,被request分配的资源,不管应用程序有没有使用,其他容器都无法再分配使用他们了

如何配置这几个值得大小:

  1. request的值根据应用程序启动并正常提供服务时,大约占用的资源量决定

  2. limit的值不建议超过宿主机实际物理配置的20%,剩余空间用来保证物理机的正常运行

  3. limit的值可以根据request配置:不能大于request、request要小于limit 20%~30%

  4. limit的值也可以根据应用根据实际压测估算

查看pod的资源限制:

查看Node信息,看Node上运行的容器的资源限制情况与Node本身的资源情况

将Pod分配给指定节点

nodeName

指定节点名称,用于将Pod调度到指定的Node上,不经过调度器。所有污点、节点亲和都将会失效。

nodeSelector

用于将Pod调度到匹配Label的Node上,如果没有匹配的标签,调度会失败

作用:

  1. 约束Pod到特定的节点上运行

  2. 完全匹配节点标签

应用场景:

  1. 专用节点:根据业务线将Node分组管理

  2. 配备特殊硬件:部分Node配有SSD硬盘、GPU

示例,去报pod被分配具有ssd硬盘的节点上:

  1. 给含有ssd的node,设置一个标签:

  2. 查看node的标签信息

  3. 创建含有nodeSelect的Pod

  4. 验证,确实在node1上

  5. 如果不需要标签栏,可以移除标签:

nodeAffinity

节点亲和类似于nodeSelector,可以根据节点上的标签来约束Pod可以调度在哪些节点上。相比于nodeSelector:

  • 匹配有更多的逻辑组合,不只是字符串的完全相等,支持的操作有: In、NotIn、Exist、DoesNotExist、Gt、Lt

  • 调度分为软策略与应策略:

    • 硬(required):必须满足,如果不满足则调度失败

    • 软(preferred):尽量满足,如果不满足也继续调度,满足则调度到目标

参考官方文档:将 Pod 分配给节点 | Kubernetes

示例:

其中,权重值weight的范围为 1~100,权重值越大,这条亲和规则优先级就越高,调度器就会优先选择

污点和污点容忍

Taints:污点,避免Pod调度到特定的Node

Tolerations:污点容忍,允许Pod调度到持有Taints的Node上

应用场景:

  1. 保证master节点安全,在master节点含有污点,防止pod在master节点运行

  2. 专用节点:根据业务将Node分组管理,希望在默认情况下不调度该节点,只有配置了污点容忍才允许分配

  3. 配备特殊硬件:部分Node配有SSD硬盘、CPU,希望在默认情况下不调度该节点,只有配置了污点容忍才允许分配

  4. 基于Taint的驱逐

查看master节点的污点:

使用污点和污点容忍

  1. 给节点添加污点:

  2. 验证是否正常添加:

  3. 配置污点容忍(pod可以容忍有gpu的节点):

  4. 删除污点(在后面增加一个减号):

存储

容器中的文件是在磁盘中临时存放的,这给容器中运行比较重要的应用程序带来如下问题:

  1. 当容器升级或者崩溃,kubelet会重建容器,容器内的文件会丢失

  2. 一个pod中运行多个容器需要共享文件

所以Kubernetes需要数据卷(Volume),常用的数据卷有:

  1. 节点本地卷(hostPath,emptyDir)

  2. 网络卷(NFS,Ceph,GlusterFS)

  3. 公有云(AWS,EBS)

  4. K8s资源(configMap,secret)

所有支持的卷类型,可以参考:卷 | Kubernetes

emptyDir 临时数据卷

是一个临时的存储卷,与Pod的生命周期绑定在一起,如果Pod删除了卷也会被删除。主要用于Pod中的多个容器之间数据共享。

empty实际上是位于宿主机上的一个文件夹,容器都是共享的这个宿主机文件夹。他的位置在:/var/lib/kubelet/pod/podid/volumes/kubernetes.io~empty-dir/data中。

hostPath 节点数据卷

挂载node的文件系统,也就是pod所在的节点上的文件或者目录到pod中的容器。主要应用在Pod中的容器需要访问宿主机的文件的情况,比如DaemonSet。

注意:当因为某些情况pod被调度到其他节点上时,节点数据卷是不会被迁移过去的。

不安全,不建议使用,建议使用共享存储

NFS 网络数据卷

使用nfs网络数据卷共享存储:

image-20220202001448943

NFS服务端一半是集群外的一台主机,而NFS客户端一般是需要使用共享存储的节点。

部署NFS

centos下准备NFS环境:

ubuntu下准备NFS环境:

测试NFS

在任意一个节点上执行:

使用NFS网络数据卷

进入容器查看nfs情况:

PV和PVC

  • PersistentVolume(PV):对存储资源创建和使用的抽象,使得存储作为集群中的资源管理

  • PersistentVolumeClaim(PVC):让用户不需要关心具体的Volume实现细节

Pod申请PVC作为卷来使用,Kubernetes通过PVC查找绑定的PV,并Mount给Pod。

  1. pvc与pv是一对一的关系,一块存储只能给一个pvc使用

  2. pvc会向上匹配第一个符合要求的pv,如果满足不了,pod处于pending

  3. 存储容量并不能做到有效的限制,他只是一个标志

使用pv和pvc:静态供给

  1. 定义需求卷:

  2. 定义卷需求,Pod 使用 PersistentVolumeClaim 来请求物理存储

  3. 容器应用使用卷需求

pv的访问模式

AccessMode是用来对PV进行访问模式的设置, 用于描述用户应用对存储资源的访问权限,包含以下几种:

  1. ReadWriteOnce:拥有读写权限,但是只能被单个节点挂载

  2. ReadOnlyMany:只读权限,可以被多个节点挂载

  3. ReadWriteMany:读写权限,可以被多个节点挂载

pv的回收策略

  1. Retain:当将pvc删除时,pv进入Released状态,这个状态下保留数据,需要管理员手动清理数据,默认策略,推荐使用

  2. Recycle:清除pv中的数据,效果相同于执行命令rm -rf /共享目录/*

  3. Delete:与pv相连的后端存储也一并删除

pv的状态

一个pv的生命周期中,可能会处于四种不同的状态:

  1. Avaliable:可用状态,还未被任何PVC绑定

  2. Bound:表示PVC已经被PVC绑定

  3. Released:已释放,表示PVC被删除,但是资源还未被集群重新声明

  4. Faild:失败状态,表示PV的自动回收失败

Storage Class

StorageClass是存储类,对一类存储资源的分类,不同的StorageClass可能代表值不同的存储服务的质量等级或者备份策略,比如固态硬盘与机械硬盘,定时备份与不做备份。

官方文档:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

创建一个StorageClass:

pv动态供给

允许按需创建PV,不需要运维人员每次手动添加,大大降低了维护成本。pv的动态供给主要由StorageClass对象实现。

动态卷供应 | Kubernetes

image-20220202114401828

PVC存储请求被创建,将会由对应的StorageClass自动创建一个PV。StorageClass是存储类,对一类存储志愿的分类与抽象。

NFS

Kubernetes 不包含内部 NFS 驱动。你需要使用外部驱动为 NFS 创建 StorageClass。 这里有些例子:

以subdir为例:

  1. 下载三个主要的yaml文件:nfs-subdir-external-provisioner/deploy at master · kubernetes-sigs/nfs-subdir-external-provisioner (github.com)

    1. rabc.yaml:存储供给程序需要创建PV,需要调用K8s API,需要RABC授权

    2. deployment.yaml:存储供给程序

    3. class.yaml:StorageClass 对象,指定nfs存储供给程序

  2. 默认的pv的删除策略为delete,可以在class.yaml文件中进行更改

  3. 修改deployment.yaml,更改镜像地址,否则无法下载镜像

  4. 修改deployment.yaml,更改NFS服务端信息,IP以及PATH(注意有两个地方)

  5. 依次部署这三个yaml文件

查看已经创建的StorageClass:


使用NFS动态供给:

创建上述PVC,并查看PV,应该有自动创建的PV:

ConfigMap

image-20220202223227362

ConfigMap用于应用程序的配置存储,Secret则用于存储敏感数据。ConfigMap共有两种创建方式:

  1. 键值对类型键

  2. 文件类型键

创建后,其数据将会存储在ETCD中。相应的,Pod也可以通过两种不同的方式获取ConfigMap中的数据到应用程序中:

  1. 变量注入

  2. 数据卷挂载

创建ConfigMap

使用ConfigMap

Secret

与ConfigMap类似,区别在于Secret主要存储敏感数据,所有数据都要经过base64编码(不加密)。

Secret的创建命令kubectl create secret支持存储创建三种数据类型的Secret:

  1. docker-registry:存储镜像仓库认证信息

  2. generic:密码

  3. tls:存储证书

generic

创建secret:

使用secret(环境变量方式):

使用secret(volume挂载):

安全

k8s安全框架主要由下面3个阶段进行控制,每个阶段都支持插件方式,通过API Server配置来启用插件:

  1. Authentication 鉴权:

  2. Authorization 授权

  3. Admission Control 准入控制

kubectl 发送指令到API Server依次经过这三个步骤进行安全控制,通过后才后继续进行后续的操作。

image-20220202230537467

Authentication 鉴权

k8s API Server提供三种客户端身份认证:

  1. HTTPS证书认证:基于CA证书签名的数字证书认证(kubeconfig,kubectl就是使用这种方式)

  2. HTTP Token认证:通过一个Token来识别用户(ServiceAccount,一般提供给程序使用,但也可以提供给kubectl)

  3. HTTP Basic认证:用户名 + 密码认证(1.19版本废弃)

Authorization 授权

基于RABC完成授权工作。RABC根据API请求属性,决定允许还是拒绝。

image-20220203160733497
  • 主体(subject)

    • User:用户

    • Group:用户组

    • ServiceAccount:服务账号

  • 角色

    • Role:授权特定命名空间的访问权限

    • ClusterRole:授权所有命名空间的(也就是整个集群)访问权限

  • 角色绑定

    • RoleBinding:将角色绑定到主体

    • ClusterRoleBinding:将集群角色绑定到主体

上图描述了这几个概念之间的关系。

Admission Control 准入控制

Admission Control实际上是一个准入控制器插件列表,发送到 API Server的请求都要经过这个列表中每个准入控制插件的检查,检查不通过则拒绝请求。

启用一个准入控制器:

关闭一个准入控制器:

查看默认启用:

示例:配置一个新的kubectl集群客户端

大致步骤:

  1. 用k8s CA(根证书)签发客户端证书

  2. 生成kubeconfig配置文件

  3. 创建RABC权限策略

  4. 指定kubeconfig文件测试权限

证书链的意思是有一个证书机构A,A生成证书B,B也可以生成证书C,那么A是根证书。操作系统预先安装的一些根证书,都是国际上很有权威的证书机构,比如 verisign 、 ENTRUST 这些公司。

这里k8s集群的根证书位于/etc/kubernetes/pki/ca.crt,可以根据根证书下发子证书。

证书名
描述

ca.cer

中间证书和根证书

nginx.cn.cer

你申请的ssl证书

fullchain.cer

包括了 ca.cer 和 nginx.cn.cer 的全链证书

nginx.cn.key

证书的私钥

创建脚本cert.sh,用于生成证书:

执行脚本将会生成:

再创建脚本kubeconfig.sh,使用此脚本创建kubeconfig:

执行完毕后将会生成yangsx.kubeconfig配置文件,然后将文件下发给某个用户,配置给kubectl即可使用。

在未给yangsx这个用户授权之前,做任何操作都无法通过API Server鉴权的:

我们需要通过创建rbac资源,给指定的用户赋予权限,创建rbac.yaml

使用kube-admin用户创建上述资源清单中的资源:

这样就给用户yangsx分配了权限:

可以查看role、rolebinding的创建情况:

示例:为一个ServiceAccount分配一个只能创建deployment、daemonset、statefulset的权限

ServiceAccount一般提供给程序使用,但也可以给kubectl使用。

实现方式一,通过命令创建:

实现方式二,通过yaml创建:

网络策略

默认情况下,Kubernetes 集群网络没任何网络限制,Pod 可以与任何其他Pod 通信,在某些场景下就需要进行网络控制,减少网络攻击面,提高安全性,这就会用到网络策略。网络策略(Network Policy):是一个K8s资源,用于限制Pod出入流量,提供Pod级别和Namespace级别网络访问控制。

网络策略的应用场景(偏重多租户下):

  • 应用程序间的访问控制,例如项目A不能访问项目B的Pod

  • 开发环境命名空间不能访问测试环境命名空间Pod

  • 当Pod暴露到外部时,需要做Pod白名单

网络策略的工作流程:

image-20220204105532677
  1. 创建Network Policy资源

  2. Policy Controller监控网路策略,同步并通知节点上的程序

  3. 节点上DaemonSet运行的程序从etcd获取Policy,调用本地Iptables规则

案例:拒绝其他命名空间Pod访问

需求:test命名空间下所有pod可以互相访问,也可以访问其他命名空间Pod,但其他命名空间不能访问test命名空间Pod

测试:

案例:同一个命名空间下应用之间限制访问

需求:将test命名空间携带run=web标签的Pod隔离,只允许携带run=client1标签的Pod访问80端口。

测试:

案例:只允许指定命名空间中的应用访问

需求:只允许dev命名空间中的Pod访问test命名空间中的pod 80端口

测试:

最后更新于

这有帮助吗?