Lanyon 记录下日常工作与学习哈~,还有技术分享哦。。🎉

k8s核心组件及pod组件间通信原理

介绍k8s的核心组件如PodControllerStatefulSet等组件以及组件间通信原理ServiceIngress服务。

Docker实例及Pods间的通信原理

在通信协议中“网络栈”包括有:网卡(network interface)、回环设备(loopback device)、路由表(routing table)和iptables规则。在docker中启动一个容器可使用宿主机的网络栈(-net=host),指定-net后默认不开启network namespace空间:

$ docker run –d –net=host --name nginx-host nginx

nginx服务启动后默认监听主机80端口,容器启动后会创建一个docker0的网桥。docker实例通过Veth Pair与宿主机建立连接关系,其中Veth的一端在容器内,另一段插在宿主机的docker0网桥上。

同一台宿主机上的容器实例间的网络是互通的,请求路由是通过宿主机向外转发。ping 172.17.0.3时匹配0.0.0.0的路由网关,意味着这是一条直连规则,匹配该规则的都走主机的eth0网卡。

在容器内ping other-ip时需将other-ip转换为mac地址(通arp地址解析获取硬件地址),容器内无法完成此操作容器通过默认路由在宿主机解析,获取请求mac地址 然后从容器经过docker0Veth Pair另外一端通过宿主机将请求转发出去。

docker的默认配置下,一台宿主机上的docker0网桥,和其他宿主机上的docker0网桥,没有任何关联,它们互相之间也没办法连通。所以,连接在这些网桥上的容器,自然也没办法进行通信了。

1. 容器跨主机网络(Overlay Network)

flannel 项目是coreOS公司主推的容器网络方案,事实上,flannel项目本身只是一个框架,真正为我们提供容器网络功能的,是 flannel的后端实现。有3种方式,基于vxlanhost-gwudp进行实现。flannel UDP模式提供的其实是一个三层的Overlay网络。

node 1上有一个容器container-1,它的IP地址是100.96.1.2,对应的docker0网桥的地址是100.96.1.1/24

node 2上有一个容器container-2,它的IP地址是100.96.2.3,对应的docker0网桥的地址是100.96.2.1/24

$ ip route
default via 10.168.0.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.2

node跨主机通信引入了flannel组件,从node1请求node2在每个组件对请求包进行分发(docker0flannel),flannel包含子网在node2地址在subnet范围内,则对请求包进行分发最终达到node2上的container

flannel项目里一个非常重要的概念子网(subnet),在由flannel管理的容器网络里,一台宿主机上的所有容器,都属于该宿主机被分配的一个“子网”。在我们的例子中,node 1 的子网是100.96.1.0/24container-1IP地址是100.96.1.2node 2的子网是 100.96.2.0/24container-2的 IP地址是100.96.2.3

TUN设备的原理,这正是一个从用户态向内核态的流动方向(Flannel进程向TUN设备发送数据包),所以 Linux内核网络栈就会负责处理这个IP包,具体的处理方法,就是通过本机的路由表来寻找这个IP包的下一步流向。

课后问题:我觉得不合适,mac地址为硬件地址 当与请求节点直连时 可通过mac实现,但当目的node不在subnet时,还是要需要rarp地址逆解析)转换为ip 然后将数据包分发到目的node

VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络虚似化技术。所以说,VXLAN 可以完全在内核态实现上述封装和解封装的工作,从而通过与前面相似的“隧道”机制,构建出覆盖网络(Overlay Network)。

设计思想是在现有的三层网络之上,“覆盖”一层虚拟的、由内核 VXLAN 模块负责维护的二层网络,使得连接在这个 VXLAN 二层网络上的“主机”(虚拟机或者容器都可以)之间,可以像在同一个局域网(LAN)里那样自由通信。

2. kubernetes的网络模型与CNI网络插件

kubernetes使用cni作为pod的容器间通信的网桥(与docker0功能相同),初始化pod网络流程:

创建Infra容器调用cni插件初始化infra容器网络(插件位置:/opt/cni/bin/flannel),开始dockershim设置的一组 CNI环境变量(枚举值ADDDELETE),用于表示将容器的VethPair插入或从cni0网桥移除。 与此同时,cni bridge插件检查cni网桥在宿主机上是否存在,若不存在则进行创建。接着,cni bridge插件在network namespace创建VethPair,将其中一端插入到宿主机的cni0网桥,另一端直接赋予容器实例eth0cni插件把容器ip提供给dockershimkubelet用于添加到podstatus字段。

接下来,cni bridge调用cni ipam插件 从ipam.subnet子网中给容器eth0网卡分配ip地址同时设置default route配置,最后cni bridge插件为cni网桥设置ip地址。

三层网络特点:通过ip route得到数据传输路由,跨节点传输ip包时会将routegetewaymac地址作为ip包的请求头用于数据包传输,到达目标node时 进行拆包,然后根据ip包去除dest地址并根据当前noderoute列表,将数据包转发到相应container中。 优缺点:避免了额外的封包、拆包操作 性能较好,但要求集群宿主机间是二层连通的; 隧道模式:隧道模式通过BGP维护路由关系,其会将集群节点的ip 对应gateway 保存在当前节点的路由中,在请求发包时数据包mac头地址指定为路由gateway地址。 优缺点:需维护集群中所有container的连接信息,当集群中容器数量较大时BGP会爆炸增长,此时可切换至集群中某几个节点维护网络关系,剩余的节点从主要节点同步路由信息。

k8s使用NetworkPolicy定义pod的隔离机制,使用ingressegress定义访问策略(限制可请求的podnamespaceport端口),其本质上是k8s网络插件在宿主机上生成了iptables路由规则;

容器编排和Kubernetes作业管理

随笔写一下,K8Spod的概念,其本质是用来解决一系列容器的进程组问题。生产环境中,往往部署的多个docker实例间具有亲密性关系,类似于操作系统中进程组的概念。

PodK8s中最小编排单位,将这个设计落实到API对象上,Pod 扮演的是传统部署环境里“虚拟机”的角色,把容器看作是运行在这个“机器”里的“用户程序”。比如,凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的。

Pod的实现需要使用一个中间容器,这个容器叫作Infra容器。而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。

Pod的进阶使用中有一些高级组件,SecretConfigMapDownward APIServiceAccountToken组件,Secret的作用,是帮你把Pod想要访问的加密数据,存放到Etcd中。然后,你就可以通过在Pod的容器里挂载Volume的方式,访问到这些Secret里保存的信息了。

ConfigMap保存的是不需要加密的、应用所需的配置信息。你可以使用kubectl create configmap从文件或者目录创建ConfigMap,也可以直接编写ConfigMap对象的YAML文件。

Deployment是控制器组件,其定义编排比较简单,确保携带了app=nginx标签的pod的个数,永远等于spec.replicas指定的个数。它实现了Kubernetes 项目中一个非常重要的功能:Pod 的“水平扩展 / 收缩”(horizontal scaling out/in)。这个功能,是从PaaS时代开始,一个平台级项目就必须具备的编排能力。

Deployment并不是直接操作Pod的,而是通过ReplicaSet进行管理。一个ReplicaSet 对象,其实就是由副本数目的定义和一个 Pod模板组成的。不难发现,它的定义其实是Deployment的一个子集。

$ kubectl scale deployment nginx-deployment --replicas=4deployment.apps/nginx-deployment scaled
$ kubectl create -f nginx-deployment.yaml --record

通过kubectl edit指令可进行滚动更新,保存退出,Kubernetes 就会立刻触发“滚动更新”的过程。你还可以通过 kubectl rollout status 指令查看 nginx-deployment 的状态变化,将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。

$ kubectl rollout status deployment/nginx-deploymentWaiting for rollout to finish: 2 out of 3 new replicas have been updated...deployment.extensions/nginx-deployment successfully rolled out

深入理解StatefulSet有状态应用

StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。StatefulSet这个控制器的主要作用之一,就是使用Pod 模板创建 Pod 的时候,对它们进行编号,并且按照编号顺序逐一完成创建工作。

StatefulSet 的“控制循环”发现 Pod 的“实际状态”与“期望状态”不一致,需要新建或者删除 Pod 进行“调谐”的时候,它会严格按照这些 Pod 编号的顺序,逐一完成这些操作。

DaemonSet 的主要作用,是让你在 Kubernetes 集群里,运行一个Daemon Pod。 所以,这个 Pod 有如下三个特征:这个Pod运行在Kubernetes 集群里的每一个节点(Node)上;每个节点上只有一个这样的 Pod 实例;当有新的节点加入 Kubernetes 集群后,该 Pod 会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的 Pod 也相应地会被回收掉。

场景比如各种监控组件和日志组件、各种存储插件的 ` Agent ` 组件、各种网络插件的 Agent 组件都必须在每个节点上部署一个实例。

K8SjObcronJob的使用频率不多,DeploymentStatefulSet,以及 DaemonSet 这三个编排概念主要编排“在线业务”,即:Long Running Task(长作业)。

Operator 的工作原理,实际上是利用了 Kubernetes 的自定义 API 资源(CRD),来描述我们想要部署的“有状态应用”;然后在自定义控制器里,根据自定义 API 对象的变化,来完成具体的部署和运维工作。