k8s 简介

Kubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。

K8S,就是基于容器的集群管理平台,它的全称,是kubernetes。

k8s集群主要包括两个部分:一个Master节点(主节点),一群Node节点(计算节点)

常见用法

Namespace(虚拟集群,一个项目独立部署在一个namespace下)->Deployment资源对象(高级Pod控制器,一个logic进程服务)->Pod(副本,一个进程下分配多少个pod)

k8s实现平滑发布

k8s部署是通过金丝雀发布(灰度发布),可实现平滑发布。

发布新版本时,旧版本的 Pod 继续运行,当新版本的 Pod 全部启动成功,且等待 n 秒后,再 Kill 掉旧版本的 Pod。(此时新旧版本的 Pod 在 n 秒并行运行着,用户可能访问到旧代码的 Pod,也可能访问到新版本的 Pod,这由我们配置的升级策略决定。)

服务重启流程:

服务重启 => 10秒灰度发布,新旧pod共存 => 旧pod移除调度,等待10秒后发送kill -15信号量给程序 => 程序接受信号量并终止,删除旧pod

Kubernetes主要由以下几个核心组件组成

  • etcd保存了整个集群的状态;
  • apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
  • controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
  • scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
  • kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
  • Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
  • kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;

术语

k8s-master-node架构

  • 主节点(Master)或 控制平面(Control Plane)

    用于控制 Kubernetes 节点的计算机。所有任务分配都来自于此。(我们通过 Master 对每个节点 Node 发送命令。简单来说,Master 就是管理者,Node 就是被管理者。)

  • 节点(Node)

    负责执行请求和所分配任务的计算机。由 Kubernetes 主机负责对节点进行控制。【在 Node 上面可以运行多个 Pod】

  • 容器组或副本(Pod/replicas)

    容器组(Pod)是最小、最简单的 Kubernetes 对象

    容器组,是 Kubernetes 最基本的操作单元。一个 Pod 代表着集群中运行的一个进程,它内部封装了一个或多个紧密相关的容器(docker),如filebeat日志容器、主程序容器。

    【可以这样简单理解:一个 go logic 服务就是一个 pod,部署多个 pod 可以实现负载均衡,而每个 pod 包含一个 go logic 服务】

    TIP

    请不要混淆以下两个概念:

    重启 Pod 中的容器 重启 Pod Pod 本身并不会运行,Pod 仅仅是容器运行的一个环境

  • Job

    Job是K8s用来控制批处理型任务的API对象。批处理业务与长期伺服业务的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。Job管理的Pod根据用户的设置把任务成功完成就自动退出了。成功完成的标志根据不同的spec.completions策略而不同:单Pod型任务有一个Pod成功就标志完成;定数成功型任务保证有N个任务全部成功;工作队列型任务根据应用确认的全局成功而标志成功。

  • 服务(Service)

    将工作内容与容器集分离。Kubernetes 服务代理会自动将服务请求分发到正确的容器集——无论这个容器集会移到集群中的哪个位置,甚至可以被替换掉。

  • 命名空间(namespace)

    一个虚拟集群,Kubernetes 通过名称空间(namespace)在同一个物理集群上支持多个虚拟集群。

    命名空间的用途是,为不同团队的用户(或项目)提供虚拟的集群空间,也可以用来区分开发环境/测试环境、准上线环境/生产环境。

Pod定义文件yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
  • apiVersion: 使用哪个版本的Kubernetes API来创建此对象

  • kind:要创建的对象类型,例如Pod,Deployment等

    如果多个容器在同一Pod下他们公用一个IP所以不能出现重复的端口号,比如在一个Pod下运行两个nginx就会有一个容器异常,一个Pod下的多个容器可以使用localhost来访问对方端口

    因为Pod是最小的单元如果在Pod中容器出现异常终止了是不会重启,在实际使用场景下基本不会直接使用 Pod 而是使用 Deployment 部署自己的应用

    Deployment 优点

    • Deployment 拥有更加灵活强大的升级、回滚功能,并且支持滚动更新
    • 使用 Deployment 升级 Pod 只需要定义 Pod 的最终状态,k8s 会为你执行必要的操作(RC要自己定义如何操作)
  • metadata:用于唯一区分对象的元数据,包括:name,UID和namespace

    • labels:是一个个的key/value对,定义这样的label到Pod后,其他控制器对象可以通过这样的label来定位到此Pod,从而对Pod进行管理。(参见Deployment等控制器对象)
  • spec: 其它描述信息,包含Pod中运行的容器,容器中运行的应用等等。不同类型的对象拥有不同的spec定义。

    • minReadySeconds: 最小准备时间
    • replicas: pod 副本数量

滚动升级策略:

  • maxSurge: 允许最多同时创建Pod的数量或比例(pod 超过期望数量则 kill 掉),
  • maxUnavailable: 允许不可用的Pod数量或比例(非 running 状态的 Pod 为不可用)
  • 最大存活的 Pod 数量:replicas+maxSurge,最大 Running Pod 数量:replicas+maxSurge-maxUnavailable

Prometheus(系统监控和告警工具包)

Prometheus 中文文档

随着容器技术的迅速发展,Kubernetes 已然成为大家追捧的容器集群管理系统。Prometheus 作为生态圈 Cloud Native Computing Foundation(简称:CNCF)中的重要一员,其活跃度仅次于 Kubernetes, 现已广泛用于 Kubernetes 集群的监控系统中。

使用场景

Prometheus 适用于记录文本格式的时间序列,它既适用于以机器为中心的监控,也适用于高度动态的面向服务架构的监控。在微服务的世界中,它对多维数据收集和查询的支持有特殊优势。Prometheus 是专为提高系统可靠性而设计的,它可以在断电期间快速诊断问题,每个 Prometheus Server 都是相互独立的,不依赖于网络存储或其他远程服务。当基础架构出现故障时,你可以通过 Prometheus 快速定位故障点,而且不会消耗大量的基础架构资源。

SNAT和DNAT

SNAT 是源地址转换

DNAT 是目标地址转换

都是地址转换的功能,将私有地址转换为公网地址

SNAT:内部地址要访问公网上的服务时(如web访问),内部地址会主动发起连接,由路由器或者防火墙上的网关对内部地址做个地址转换,将内部地址的私有IP转换为公网的公有IP,网关的这个地址转换称为SNAT,主要用于内部共享IP访问外部。

DNAT:当内部需要提供对外服务时(如对外发布web网站),外部地址发起主动连接,由路由器或者防火墙上的网关接收这个连接,然后将连接转换到内部,此过程是由带有公网IP的网关替代内部服务来接收外部的连接,然后在内部做地址转换,此转换称为DNAT,主要用于内部服务对外发布。

为什么需要 LB 直通 Pod

LB 直接绑 NodePort 来实现云上的 Ingress 或 LoadBalancer 类型 Service 是最简单通用的方法,那为什么有了这种实现还不够,还要搞个 LB 直通 Pod 的模式?

首先,我们分析下传统 NodePort 实现方式存在的一些问题:

  1. 流量从 LB 转发到 NodePort 之后还需要进行 SNAT,再转发到 Pod,会带来一些额外的性能损耗。
  2. 如果流量过于集中到某几个 NodePort 时(比如使用 nodeSelector 部署网关到固定几台节点上),可能导致源端口耗尽,或者 conntrack 插入冲突。
  3. NodePort 本身也充当负载均衡器,LB 绑定过多节点 NodePort 可能导致负载均衡状态过于分散,导致全局负载不均。

如果使用 LB 直通 Pod 的方式,以上问题都将消失,并且还有一些其它好处:

  1. 由于没有 SNAT,获取源 IP 不再需要 externalTrafficPolicy: Local 。
  2. 实现会话保持更简单,只需要让 CLB 开启会话保持即可,不需要设置 Service 的 sessionAffinity。

所以使用 LB 直通 Pod 的场景通常有:

  1. 在四层获取客户端真实源 IP,但又不希望通过使用 externalTrafficPolicy: Local 的方式。
  2. 希望进一步提升网络性能。
  3. 让会话保持更容易。
  4. 解决全局连接调度的负载不均。

常见案例

部署在 k8s 上的项目可能出现的问题

  1. 接口响应慢,容易出现超时
  2. go fasthttp是建立tcp连接,会报 “dialing to the given TCP address timed out”,3秒没建立连接超时就异常了

问题定位:

k8s集群的节点是默认通过 SNAT 将流量转发出去的;运维测试通过 SNAT 的节点访问非 k8s 项目的接口很慢。但试了下一台节点绑定了公网IP,不走SNAT 网关,访问出去就很快。

解决方案:

扩容 SNAT

Kubernetes 网络疑难杂症排查分享

点击查看文章

requests 与 limits 简介

为了实现 K8s 集群中资源的有效调度和充分利用, K8s 采用requests和limits两种限制类型来对资源进行容器粒度的分配。每一个容器都可以独立地设定相应的requests和limits。这 2 个参数是通过每个容器 containerSpec 的 resources 字段进行设置的。一般来说,在调度的时候requests比较重要,在运行时limits比较重要。

requests定义了对应容器需要的最小资源量。这句话的含义是,举例来讲,比如对于一个 Spring Boot 业务容器,这里的requests必须是容器镜像中 JVM 虚拟机需要占用的最少资源。如果这里把 pod 的内存requests指定为 10Mi ,显然是不合理的,JVM 实际占用的内存 Xms 超出了 K8s 分配给 pod 的内存,导致 pod 内存溢出,从而 K8s 不断重启 pod 。

limits定义了这个容器最大可以消耗的资源上限,防止过量消耗资源导致资源短缺甚至宕机。特别的,设置为 0 表示对使用的资源不做限制。值得一提的是,当设置limits而没有设置requests时,Kubernetes 默认令requests等于limits。