Yongci

View on GitHub
../ 返回上级目录🔙

基础资源对象和概念

容器编排介绍

  1. 何为容器编排

    将一套复杂系统中的不同服务组件封装成镜像推送至仓库,将这些镜像组织起来按照一定的规则和顺序启动它们;容器与容器之间可以相互调用,对外提供服务。同时,提供统一化的服务管理体验

    你可能听说过docker-compose,它是用python写的一个小工具,通过编写docker-compose.yml配置,我们可以快速的启动一整套系统,但是,它最大的问题是:只能做单机编排,也就是应用全跑在一台机器上;无法做服务高可用以及提供j较好的弹性能力、管理能力。

    近几年来,Kubernetes先后打败了Mesos、Docker-swarm;已成为容器编排的标准,不同的公有云厂商在基于原生Kubernetes的基础上也各自推出了自家的的容器编排产品,如:华为云CCE/CCI、Azure的AKS、阿里的ACK、谷歌自家的GKE等;还有一些基于Kubernetes二次开发的企业级发行版:红帽的OpenShift、VMware的Tanzu Kubernetes产品等。

  2. 发展历史

    略,网上搜索下是有很多介绍的;前身基于Google的borg,谷歌十几年的编排经验

  3. Kubernetes的优势

    • 大规模的容器编排、资源调度

    • 服务自动发现和负载均衡

    • 平台一致性,对于异构项目的支持

    • 服务的极致弹性能力

    • 自动化程度较高,降低了工作量

Kubernetes架构&组件介绍

集群架构

components-of-kubernetes

标准组件

  1. API-Server

    集群组件网关、主要的API实现;与ETCD通信,持久化集群数据

  2. scheduler

    调度器主要负责Pod调度,其行为可受亲和性、资源限制、污点、容忍等因素影响;当你创建了一个要求必须1c1g的Pod(实例)时,调度器会根据集群空闲资源情况,选择一个合适的Node来放置这个Pod

  3. controller-manager

    集群核心组件,各种控制器实现(标准资源对象),准入控制、访问控制等

  4. ETCD

    由Go实现的一个分布式的键值存储,用于共享配置以及服务发现;算是是集群的数据库,所有的资源对象声明最终都存储于此

  5. kubelet

    kubelet主要工作在Node节点上,与不同的容器运行时交互,创建Pod沙箱环境,管理容器的生老病死,同时会聚合所有的运行指标,供监控系统等使用

  6. kube-proxy

    实现k8s中Service的部分概念,负责流量代理;底层通常支持两种模式:iptablets/ipvs

  7. kubectl

    k8s命令行管理工具,可以使用它管理集群中的任何资源对象

扩展组件

入口控制器

入口控制器主要用于处理集群中7层代理以及HTTPS需求,其工作原理如下图所示:

Ingress-controller-diagram

如果要使用Ingress资源,你必须安装一个可用的ingress控制器,常见的入口控制器类有这些:

  1. nginx-ingress-controller
  2. kong-ingress-controller
  3. traefik-ingress-controller

存储

  1. NFS

    通常对于NFS服务端来说具有单点问题,无法做高可用、容灾设计;此外,IO注定不高;所以仅用于开发、测试环境

  2. ceph

    分布式文件系统,块存储类型,此类型只能单实例挂载,独占;高可用、容灾

  3. glusterFS

    分布式文件系统,共享型;可多实例挂载,高可用、容灾

存储类有很多,这里只列出了部分;通常,私有云实现如vSAN;公有云通常实现了不同的存储类声明,这些存储类可以对接其自家平台的不同存储类型的产品,拿华为云来说,其CCE集群的存储组件名为:everest-csi-controller,可供选择的存储类如下:

[root@localhost ~]# kubectl get sc
NAME                PROVISIONER                     AGE
csi-disk            everest-csi-provisioner         436d  # 云硬盘,不能跨AZ挂载,独占
csi-disk-topology   everest-csi-provisioner         436d  # 云硬盘,可跨AZ挂载,独占
csi-nas             everest-csi-provisioner         436d  # 弹性文件存储卷,共享型
csi-obs             everest-csi-provisioner         436d  # 对象存储,共享型
csi-sfsturbo        everest-csi-provisioner         436d  # 极速文件存储,共享型
# flex系列在逐步淘汰,应使用遵循CSI规范的存储类
efs-performance     flexvolume-huawei.com/fuxiefs   436d
efs-standard        flexvolume-huawei.com/fuxiefs   436d
nfs-rw              flexvolume-huawei.com/fuxinfs   436d
obs-standard        flexvolume-huawei.com/fuxiobs   436d
obs-standard-ia     flexvolume-huawei.com/fuxiobs   436d
sas                 flexvolume-huawei.com/fuxivol   436d
sata                flexvolume-huawei.com/fuxivol   436d
ssd                 flexvolume-huawei.com/fuxivol   436d

观测

  1. Metrics-Server: 集群指标聚合,启用HPA弹性伸缩规则时依赖此服务
  2. Prometheus-Operator:监控全家桶,后期有机会的话可以讲讲它的架构;集群监控和业务监控的主要实现
  3. filebeat:日志采集agent,公有云服务有实现自己的日志、指标采集agent,比如华为云是icagent
  4. kube-dashboard:官方的Kubernetes集群可视化管理界面
  5. Lens:桌面Kubernetes集群可视化管理界面,也可以作为Kubernetes IDE

服务发现

CoreDNS

​ 集群内部DNS解析服务,结合Service资源对象实现集群的服务自动发现;现已成为一个正常集群不可或缺的一部分。kubelet在为容器创建运行环境时,会自动为容器配置CoreDNS的地址,使其可以正确的访问集群服务域名和外部站点域名。

递归查询:

cluster-dns

资源对象介绍

常见标准资源对象

Pod

Pod是Kubernetes中最小的资源对象,每个Pod都算一个具体的实例;我们通常挂在嘴边的几个副本这种概念,实际上讲的是究竟创建了多少Pod;一般情况下,在1个Pod中最少存在两个容器,Pause容器和业务容器;Pause容器只有登到节点上才可看到,它实际上叫做infra容器(基础容器、基座容器),kubelet会先创建该容器;然后该Pod下其余的业务容器均共享该容器的网络namespace(使用–net=container方式实现),它与业务容器的关系如下:

Kubernetes-pod-pause

ReplicaSet

副本集下有多个Pod,副本集中也定义了具体要创建一个什么样的Pod,这些Pod是完全一致的;每个副本集都是独一无二的;在Deployment对象创建rs对象时,会为该rs打上一个独一无二的标签(pod-template-hash),在滚动升级时,会根据升级策略的先后顺序来调整新旧rs内的Pod的数量,实现服务的滚动升级

Kubernetes-replicaset

Deployment

该资源中文翻译叫部署,听起来有些别扭,所以很多情况下,我们也称它为无状态副本集;因为ReplicaSet对象本身无法做到服务的滚动升级,对于Deployment来说,每次改动都会创建一个全新的ReplicaSet,对应一个特定的历史版本;回滚技术主要由此实现;对于Deployment来说,在回退时,它将历史版本的ReplicaSet的副本数量慢慢调整上去,然后将最新版本的ReplicaSet副本数量慢慢降下来;在滚动升级中,会出现短暂的多个版本的代码同时存在

Kubernetes-deployment

目前如无特殊要求,新起的业务模块均为Deployment类型。

StatefulSet

有状态副本集是为某些有状态应用设计的一种资源对象,对于某些应用(如Eureka、Prometheus等),实例间通常要共享状态以及实例间的相互注册发现,有状态态副本集可提供固定的主机名、独立的存储空间、更稳定的网络访问等;有状态副本集通常升级时从最后一个Pod开始升级,创建时从第0个Pod开始;无论是创建还是升级操作,任意一个失败升级都将终止

Kubernetes-statefulset

DaemonSet

该资源被翻译为守护进程集,该资源对象会创建Pod,它创建的Pod将会在每个Node节点运行一个;通常存储插件、日志采集插件等以此种方式部署;还有一个比较典型的是Prometheus中,负责采集服务器各项运行指标的组件node-exporter也以DaemonSet方式部署运行;此外,使用DaemonSet方式部署的应用,其直接使用宿主机的网络命名空间(宿主机网卡地址,docker的–net=host模式),这也表明了,如果目标应用监听了8080端口的话,你将无法在该节点上起第二个监听了8080的DaemonSet类型的服务

Kubernetes-daemonset

Job

在某些场景下,我们需要执行那种一次性的任务,比如初始化数据库表结构;此时可以创建这种一次性任务,任务执行成功后即销毁实例,释放计算资源。

CronJob

定时任务可以创建Job,它的定时格式以Linux上标准定时任务格式一致(分时日月周);定时任务的基准时间是Controller-Manager所在节点的时间,目前我们有使用它周期性的检查某些服务或ETL工具定时同步数据用

一个比较奇特的想法,比如加油同步数据的场景,加油服务模块提供触发接口;由CronJob任务定时触发,每次触发随机调度到任意实例,任务执行失败是会触发Prometheus告警规则;此方法可减少一个服务模块,简化任务调度设计中的复杂度。

ConfigMap

cm被设计用来存放应用的配置,这些配置以键值对形式存储,这些数据被API-Server存储在ETCD中;可以通过环境变量引用或是Volumes挂卷形式使用,cm中通常不存放敏感信息

Secret

secret被用来放一些相对敏感的信息,主要用来存放密码、SSL证书、Token等;随为secret,但实际上拥有集群中secret读权限的人是可以比较轻松的拿到其中的内容,它里面存储的数据均以base64格式编码,解码比较容易

Service

svc资源为kubernetse中实现服务自动发现负载均衡的核心资源对象,该资源对象会通过标签关联Pod;这些Pod将会被自动关联到该svc的对应的Endpoints中,对于服务之间调用可以使用可以使用内部域名解析,如下图:

Kubernetes-service

svc资源目前主要有以下几种类型:

  1. clusterIP: 仅集群内部调用,使用如上图中的内部域名进行相互访问
  2. NodePort: 节点端口,nodeport类型的svc会下发kube-proxy端口映射规则,将集群的一个端口映射到宿主机某个端口
  3. LoadBalancer: 负载均衡器,在公有云场景下,集群会申请一个公有云的L4负载均衡器并绑定到svc上,如果要实现HTTPS访问,证书需要封装到容器内(所以我们用Ingress解决该问题)
PV

持久化卷(Persistent Volumes),全局资源,不属于任何Namespace之下;PV主要对接不同的存储服务(NFS/glusterFS等),通过对接不同的存储服务,抽象成一种新的资源对象

k8s-pv-2021-06-14-172957

PVC

持久化卷声明Persistent Volume Claims,从PV中划分而来;PVC资源对象通常位于某一个Namespace下,该Namespace下的Pod可以挂载它;因为容器中的数据不能持久化,在容器重启后都会被销毁,如果需要永久性存储某些数据文件,则必须为其挂载PVC;PVC的存储空间通常从PV中分配而来

k8s-pvc-2021-06-14-173124

StorageClass

存储类用于解决PV对接不同基础存储服务时的复杂度,简单来说;如果你要使用PVC,则必须先声明好PV;StorageClass简化了整个过程,你只需在资源对象中声明StorageClass的注解,即可实现自动创建PV、PVC的过程

Namespace

全局资源,主要用于区分项目,在红帽的OpenShift平台中,Namespace被称为Project;在上面所介绍的诸多资源中对象中,除去全局资源外,其他资源对象通常都位于某一具体的Namespace中

k8s-namespaces

Node

对应物理机资源,集群中计算资源的多少跟Node的硬件配置、数量有直接关系;一个正常的集群,通常由大量的Node节点组成一个庞大的计算资源池,这些资源会根据具体资源对象的需求来调度分配。

扩展资源对象

Ingress

Ingress主要用于处理集群中7层代理以及HTTPS需求,需要使用该资源,你需要一个先安装好一个可用的Ingress-controller组件,下图简单展示了用户请求是如何到达服务的:

Kubernetes-ingress-to-svc

其它资源对象(CRD)

CRD全称: Custom Resource Definition

当标准资源对象无法满足业务需求时,也可以创建CRD资源,提供更加定制化的体验;如上文中的kong-ingress-controller控制器类,其本身兼容标准Ingress对象定义,但同时提供KongPlugin、KongConsumer、KongClusterPlugin、KongIngress等CRD对象,提供更细粒度的路由和访问控制。

使用下面的命令可以查询集群所有的支持的API-Resources对象:

kubectl api-resources

使用下面的命令可以查询已创建的CRD有哪些,CRD也是集群级别的资源对象

kubectl get crd

一个示例

以部署httpbin服务为为例,将这个服务跑到K8s集群中去;我们使用dev环境集群凭据,将下面的内容保存为用户家目录下的.kube/config文件,然后下载Windows下的kubectl命令行工具

Win-AMD64可执行文件下载链接:https://oss.zhuyongci.com/bookimg/b3f628145cb41ef8dd32c23f425f61fa.exe

将该文件保存为kubectl.exe然后放到系统PATH环境变量的搜索路径中。

  1. 将以下内容保存为httpbin.yaml

    # 创建一个名为httpbin-ns的namespace对象
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
      name: httpbin-ns
       
    # 创建一个名为httpbin-ing的ingress对象
    ---
    kind: Ingress
    apiVersion: extensions/v1beta1
    metadata:
      name: httpbing-ing
      # 放到哪个Namespace里面
      namespace: httpbin-ns
      annotations:
        # 该集群共安装了两个ingress-controller
        # 这里使用kong这个控制器类
        kubernetes.io/ingress.class: kong
    spec:
      rules:
        - host: httpbin.zhuyongci.com
          http:
            paths:
              - backend:
                  # 指向服务绑定的Service Name: httpbin-svc
                  serviceName: httpbin-svc
                  # 可以取变量
                  servicePort: http
       
    # 创建一个集群Service对象, Servcie对象会关联Pod
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin-svc
      namespace: httpbin-ns
    spec:
      ports:
        # svc对外暴露的端口,svc name+port
        - port: 80
          # Pod暴露的端口
          targetPort: 80
          name: http
      type: ClusterIP
      selector:
        # 关联具有该标签的Pod
        app: httpbin
       
    # 创建一个无状态副本集
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin-deployment
      namespace: httpbin-ns
    spec:
      # 副本数量
      replicas: 1
      selector:
        matchLabels:
          # 匹配具有该标签的Pod,这些Pod归我所管
          app: httpbin
      template:
        metadata:
          # 为Pod打上唯一标签,供Deployment和Service来关联
          labels:
            app: httpbin
        spec:
          containers:
            # name字段为Pod中容器的名字,一个Pod可以起多个容器,这些容器以name定义区分彼此
            - name: httpbin
              ports:
                - containerPort: 80
                  protocol: TCP
              # 镜像地址
              image: httpbin:latest
              # 拉取叫镜像的策略, 每次都拉取还是如果本地已经存在就不再重新拉取镜像
              imagePullPolicy:  Always
              resources:
                limits:
                  cpu: 1000m
                  memory: 1Gi
                requests:
                  cpu: 1000m
                  memory: 1Gi
              # 探测服务是否准备就绪,如果就绪,将Pod ip加入Service的Endpoints列表(也就是服务如果准备好了,把流量往这个Pod上转)
              readinessProbe:
                tcpSocket:
                  port: 80
                periodSeconds: 10
                initialDelaySeconds: 30
              # 探测到服务没有存活, 会重启服务(重建pod)
              livenessProbe:
                httpGet:
                  port: 80
                  path: /get
                periodSeconds: 11
                initialDelaySeconds: 35
          # 容器运行失败是执行的动作,是执行重启还是失败一定次数后不在有动作
          restartPolicy: Always
    
  2. 使用kubectl创建它,打开CMD或PowerShell,进入该文件所在目录,然后执行下面的命令

    kubectl apply -f httpbin.yaml
    
  3. 添加本地hosts解析,在host文件加入如下记录,如果你有可用域名;也可以直接加公网解析

    # 开发环境集群统一入口LB地址
    172.16.5.20 httpbin.zhuyongci.com
    
  4. 浏览器访问这个域名,用Postman等工具发请求,测试能否返回结果

  5. 清除创建的资源对象,这里我直接delete掉了namespace,该namespace下的所有对象均会被删除掉

    kubectl delete ns/httpbin-ns
    

扩展阅读

  1. 接口规范:CNI、CRI、CSI、CDI
  2. 容器网络、Overlay