0%

K8s Basic I

面对赢不了的挑战有两个选择:要不就投降,要不就让自己变得更强。

Docker或者说容器,实现概念主要基于CGroups(限制每个进程的资源)、NameSpace(隔离不同的容器进程)、rootFS(文件系统,挂载到主机上)。

下面着重讲一下K8s四组基本概念。

K8s四组基本概念

Pod/Pod控制器

Pod

K8s集群中运行的最小单元不是容器,而是Pod。

  • Pod是K8S里能被运行的最小的逻辑单元(原子单元)
  • 1个Pod里面可以运行多个容器,他们共享UTS+NET+IPC名称空间
  • 可以把Pod看成豌豆荚,而同一个Pod内的每个容器是一颗颗豌豆
  • 一个Pod里运行多个容器,又称作:边车(SideCar)模式(类似从动轮)

Pod控制器

  • Pod控制器是Pod启动的一种模板,用来保证在K8S里启动的Pod应始终按照人们的预期运行(副本数、生命周期、健康状态检查…)
  • K8S内提供了众多的Pod控制器,常用的有以下几种:
    • Deployment(真正暴露在外面的)
    • DeamonSet(每个运算节点上都起一份)
    • ReplicaSet(Deployment管理ReplicaSet,ReplicaSet管理Pod,ReplicaSet不直接暴露在外面)
    • StatefulSet(管理有状态应用)
    • Job(管理普通任务)
    • Cronjob(管理定时任务)

Name/Namespace

Name

  • 由于K8S内部,使用”资源”来定义每一种逻辑概念(功能),所以每种”资源”,都应该有自己的”名称”
  • “资源”有api版本(apiVersion)、类别(kind)、元数据(metadata)、定义清单(spec)、状态(status)等配置信息
  • “名称”通常定义在”资源”的”元数据”信息里

Namespace

  • 随着项目增多、人员数量增加、集群规模扩大,需要一种能够隔离K8S内各种”资源”的方法,这就是Namespace命名空间
  • Namespace可以理解为K8S内部的虚拟集群组
  • 不同名称空间内的”资源”,名称可以相同,相同名称空间内的同种”资源”,”名称”不能相同
  • 合理地使用K8S的名称空间,使得集群管理员能够更好地对交付到K8S里的服务进行分类管理和浏览
  • K8S里默认的Namespace有:default、kube-system、kube-public
  • 查询K8S里特定”资源”要带上相应的名称空间(Namespace),不带默认查询default空间

Label/Label选择器

Label

  • 标签是K8S特色的管理方式,便于分类管理资源对象
  • 一个标签可以对应多个资源,一个资源也可以有多个标签,它们是多对多的关系。
  • 一个资源拥有多个标签,可以实现不同维度的管理(一个资源可以打上各种各样的标签,比如可以打上污点、容忍度等)
  • 标签的组成:key=value(键值对)
  • 与标签类似的,还有一种”注解”(annotations)

Label的定义有限制,长度要小于63,必须以字母或数字开头,中间值也有要求。(官网原话:Valid label values must be 63 characters or less nad must be empty or begin and end with an alphanumeric character([a-z0-9A-Z]) with dashes(_),dots(.), and alphanumerics between).

Label选择器

  • 给资源打上Label之后,可以使用标签选择器过滤指定的标签,然后执行我们想要的操作
  • 标签选择器目前有两种:基于等值关系(等于、不等于)和基于集合关系(属于、不属于、存在)
  • 许多资源支持内嵌标签选择器字段
    • matchLabels
    • matchExperssions

Service/Ingress(最重要)

Service(暴露在OSI第四层,暴露IP和端口)

  • 在K8S的”世界”里,虽然每个Pod都会被分配一个单独的IP地址,但这个IP地址会随着Pod的销毁而消失(Pod有生命周期,而且每次重启Pod的IP都会变化,那么IP地址每次都变化,怎么调度流量呢?每次都去修改IP么?不行,想到了巧妙的Service)
  • Service(服务)就是用来解决这个问题的核心概念
  • 一个Service可以看做是一组提供相同服务的Pod的对外访问接口
  • Service作用于哪些Pod是通过标签选择器来定义的

Ingress(暴露在应用层)

  • Ingress是K8S集群里工作在OSI网络参考模型下的应用层(第7层)的应用,对外直接暴露的接口
  • Service只能进行L4流量调度,表现形式是ip + port
  • Ingress则可以调度不同业务域、不同URL访问路径的业务流量

如果用了HTTP和HTTPS协议,那么暴露应用的时候绝大时候都是用Ingress暴露

查找逻辑关系是,Ingress去找Service,然后Service去找Pod。

K8S核心组件与核心附件

核心组件

  • 配置存储中心:etcd服务(类似ZK,主要用来存储节点元数据信息,比如资源配置,类似MySQL,etcd是非关系型数据库)

  • 主控(master)节点

    • kube-apiserver服务

    详解:apiserver是整个K8S的大脑,提供了集群管理的REST API接口(包括鉴权、数据校验及集群状态变更);负责其他模块之间的数据交互;是资源配额控制的入口;提供完备的集群安全机制;

    • kube-controller-manager服务

    详解:由一系列控制器组成,通过apiserver监控整个集群的状态,并确保集群处于预期的工作状态。

    • kube-scheduler服务

    详解:主要功能是接收调度pod到适合的运算节点上

  • 运算(node)节点

    • kube-kubelet服务

    详解:简单来说,kubelet的功能就是定时从某个地方获取节点上pod的期望状态(运行什么容器、网络状态配置等等),并调用对应的容器平台接口以达到这个状态;kubelet还会定时汇报当前节点状态给apiserver,以供调度的时候使用;镜像和容器的清理工作,保证节点上的镜像不会沾满存储空间。

    可见,kubelet功能很多,很核心。

    • kube-proxy服务

    详解:proxy是K8S每个节点上运行网络的代理,是service资源的代理。说白了,是建立了pod网络和集群网络的关系(clusterip与podip的关系);常用三种流量调度模式:Userspace(已经被废弃)、Iptables(濒临被废弃)、Ipvs(推荐使用)。实际proxy提供的是service的虚拟网络。

CLI客户端

  • kubectl

详解:给Pod提供网络

核心附件

  • CNI网络插件:flannel/calico
  • 服务发现用插件:coredns
  • 服务暴露用插件:traefik
  • GUI管理插件:Dashboard

生产经验(K8S三条网络)

这里说的生产经验,实际上是如何规划K8S的三条网络。如下图所示:

K8S三条网络.png

上图可以很清晰地展现K8S的三个网络:节点网络、Pod网络、Service网络。

宿主机器的网络,或者说Node网络,就是图中的节点网络

Pod通过从宿主机上NAT,得到Pod网络,过程中利用了docker0设备,所有docker容器会从docker通过NAT映射出来。

需要注意,kube-proxy连接了Pod网络和Service网络,其中Service网络是虚的网络

举例:

  • Service网络可以设置为:192.168.0.0/16,Pod网络可以设置为172.7.0.0/16,节点网络可以设置为10.4.7.0/24
  • Node1和Node2分别设置为:10.4.7.2110.4.7.22,然后Node1对应的pod网络是172.7.21.0/24172.7.22.0/24

上述设计经过了严格考虑,三条网络分别对应了三个私有地址,即10段的私有地址、172.7段的私有地址和192.168段的私有地址。(这个设计在很多生产环境上都在使用)

首先,因为Service网络是虚拟的网络,所以用192.168.x.x/16没有问题。

然后,因为pod的网络也经过了规划,所以我们一看到比如172.7.21.4之类的IP,就能知道是Node1节点上的pod,这样的对应关系有助于排错

在规划pod的IP和Node的IP的时候,也是有讲究的。比如我们的Node1设置IP为10.4.7.1,每一位都有自己的含义:10代表公网的私有地址、4代表机房,也就是不同楼层的哪个机房、7代表不同的项目或者环境,比如7代表公共开发部NCE基础研发部,8代表NCE应用研发部,9和10代表生产环境等,也就是第三位用来区分业务和环境,可以用来做物理隔离。此外,pod的网络也要根据Node来变化,比如Node1的第二位是5,即10.5.7.1,那么此时pod网络可以设置第二位为17,即172.17.21.0/24,以此类推。

所以,在规划Node网络和Pod网络的时候,我们最好要达到,一看到某个pod的IP,就知道是哪台机器上的,因为有可能pod挂的时候会挂一批,那么能通过pod的IP找到Node,就很利于排错。

K8S服务发现和负载均衡

可以参考这篇文章

详细的负载均衡可以参考这篇文章

一个基于Service做服务发现和负载均衡的案例

之前我们已经了解到 pod 的网络跟机器不是同一个段的网络,那怎么让 pod 网络暴露出去给外部访问,从而提供给外部的用户去调用的?这时就需要服务发现。而如果pod使用rc创建了多个副本,那么service就能代理多个相同的pod,通过kube-proxy,实现负载均衡

一个模型如图所示:

K8S服务发现和负载均衡.png

在 K8s 里面,服务发现与负载均衡就是 K8s Service。上图就是在 K8s 里 Service 的架构,K8s service 向上提供了外部网络以及 pod 网络的访问,即外部网络可以通过 service 去访问,pod 网络也可以通过 K8s Service 去访问。

向下,K8s 对接了另外一组 pod,即可以通过 K8s Service 的方式去负载均衡到一组 pod 上面去,这样相当于解决了前面所说的复发性问题,或者提供了统一的访问入口去做服务发现,然后又可以给外部网络访问,解决不同的 pod 之间的访问,提供统一的访问地址。

service:服务,是一个虚拟概念,逻辑上代理后端pod。众所周知,pod生命周期短,状态不稳定,pod异常后新生成的pod ip会发生变化,之前pod的访问方式均不可达。通过service对pod做代理,service有固定的ip和port,ip:port组合自动关联后端pod,即使pod发生改变,kubernetes内部更新这组关联关系,使得service能够匹配到新的pod。这样,通过service提供的固定ip,用户再也不用关心需要访问哪个pod,以及pod是否发生改变,大大提高了服务质量。

每个节点都有一个组件kube-proxy,实际上是为service服务的,通过kube-proxy,实现流量从service到pod的转发,kube-proxy也可以实现简单的负载均衡功能

kube-proxy代理模式:userspace方式:kube-proxy 在节点上为每一个服务创建一个临时端口,service的IP:port 过来的流量转发到这个临时端口上,kube-proxy会用内部的负载均衡机制(轮询),选择一个后端pod,然后建立iptables,把流量导入这个pod里面。

K8S服务发现

服务发现在微服务架构里,服务之间经常进行通信,服务发现就是解决不同服务之间通信的问题。比如一个nginx的pod,要访问一个mysql服务,就需要知道mysql服务的ip和port,获取ip和port的过程就是服务发现。

kubernetes 支持两种服务发现模式:

  1. 环境变量

Pod创建的时候,服务的ip和port会以环境变量的形式注入到pod里,比如pod创建时有一个redis-master服务,服务ip地址是10.0.0.11,port是6379,则会把下面一系列环境变量注入到pod里,通过这些环境变量访问redis-master服务。 REDIS_MASTER_SERVICE_HOST=10.0.0.11REDIS_MASTER_SERVICE_PORT=6379REDIS_MASTER_PORT=t

  1. dns

K8s集群会内置一个dns服务器,service创建成功后,会在dns服务器里导入一些记录,想要访问某个服务,通过dns服务器解析出对应的ip和port,从而实现服务访问

service 是微服务架构中的微服务。service 定义了一个服务的访问入口地址,前端的应用(pod)通过这个入口访问其背后的一组由pod副本组成的集群实例,service与其后端pod副本集群之间是通过label seletor 实现无缝对接的。而rc的功能实际上是保证servic的服务能力和服务质量处于预期的标准

通常我们的系统是由多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过tcp/ip进行通信,从而形成了强大而又灵活的弹性网络,拥有了强大的分布式能力,弹性扩展能力,容错能力。

K8S的Service如何进行服务发现和负载均衡

service可以将pod ip封装起来,即使pod发生重建,依然可以通过service来访问pod提供的服务,service还小程度地解决了负载均衡的问题。

运行在每个node上的kube-proxy进程其实就是一个智能的软件负载均衡器,他负责把service的请求转发到后端的某个pod实例Kube-proxy 的默认模式是 iptables,它支持相当复杂的基于规则的 IP 管理。iptables 模式下,负载分配的本地方法是随机选择——由一个传入的请求去随机选择一个服务中的 pod。早先版本(以及原来的默认模式)的 kube-proxy 模式是 userspace,它使用循环的负载分配,在 IP 列表上来分配下一个可以使用的 pod,然后更换(或置换)该列表。

kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。

真正的负载均衡:Ingress

上面讲到了支持K8S负载均衡的两种方式,即kube-proxy和service,但是这些并不是真正的负载均衡。为了实现真正的负载均衡,当前最流行、最灵活、应用于很多领域的方法是Ingress,它通过在专门的 Kubernetes pod 中的Controller(控制器)进行操作。

控制器包括一个 Ingress 资源——一组管理流量的规则和一个应用这些规则的守护进程。 控制器有自己内置的负载均衡特性,具备一些相当复杂的功能。你还可以让 Ingress 资源包含更复杂的负载均衡规则,来满足对具体系统或供应商的负载均衡功能和需求。

除了 Ingress,还可以使用负载均衡器类型的服务来替代它。该服务使用基于云服务的外部负载均衡器。负载均衡器只能与 AWS、Azure、OpenStack、CloudStack 和 Google Compute Engine 等特定的云服务提供商一起使用,且均衡器的功能根据提供者而定。除此之外其他的负载均衡方法可以从服务提供商以及第三方获得。

但是总的来说,还是推荐Ingress。当前 Ingress 是首选的负载均衡方法。因为它是作为一个基于 pod 的控制器在 Kubernetes 内部执行,因此对 Kubernetes 功能的访问相对不受限制(不同于外部负载均衡器,它们中的一些可能无法在 pod 层面访问)。Ingress 资源中包含的可配置规则支持非常详细和高度细化的负载均衡,可以根据应用程序的功能要求极其运行条件进行定制。