• K8S学习笔记01


    1.3 K8S简介

    1.3.1 Service与标签、标签选择器

    在K8S中,Service是分布式集群架构的核心,一个Service对象拥有如下关键特征:

    • 拥有一个唯一指定的名字
    • 拥有一个虚拟IP(Cluster IP,Service IP或者VIP)和端口号。
    • 能够提供某种远程服务的能力。
    • 被映射到了提供这种服务能力的一组容器应用上。

    Service的服务进程目前都基于socket通信方式对外提供服务,一个Service可能有多个相关服务进程,每个服务进程都有一个独立的Endpoint(IP+Port)访问点,K8S能够通过Service(虚拟Cluster IP+Service Port)连接到指定Service上。这样,即使服务进程的IP改变,通过Service一样能够找到对应服务进程。

    容器提供了强大的隔离功能,因此有必要将为Service提供服务的这组进程放入容器中进行隔离。为此,K8S设计了Pod对象,将每个服务进程“包装”到相应的Pod中,使其成为Pod中运行的一个容器(Container)。为了建立Service和Pod之间的关联关系,K8S首先给每个Pod贴上一个标签(Label),然后给相应的Service定义标签选择器(Label Selector),比如MySQL Service的标签选择器的选择条件为name=MySQL,意味着该Service要作用于所有包含name=MySQLLabel的Pod上。这样,就巧妙的解决了Service与Pod的关联问题。

    1.3.2 集群节点

    在集群管理方面,K8S将集群中的机器划分为一个Master节点和一群工作节点(Node)。其中,在master节点上运行着集群管理相关的一组进程:kube-apiserver、kube- controller- manager和kube-scheduleer,这些进程实现了自动对这个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错管理功能。

    Node作为集群中的工作节点,运行真正的应用程序,Node上K8S管理的最小运行单元是Pod。Node上运行着K8S的kubelet、kube-proxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁以及实现软件模式的负载均衡器。

    服务扩容与升级

    在K8S中,只需要为需要扩容的Service关联的Pod创建一个Replication Controller(RC)即可解决扩容及Service升级等问题。

    一个RC文件中包含三个关键信息:

    • 目标Pod的定义
    • 目标Pod需要运行的副本数量(Eplicas)
    • 要监控的目标Pod的标签(Label)

    在创建好RC后,K8S会通过RC中定义的Lable筛选出对应的Pod实例并实时监控器状态和数量,如果实例数量少于定义的副本数量(Replicas),则会根据RC中定义的Pod模板来创建一个新的Pod,然后将此Pod调度到合适的Node上启动运行,知道Pod实例数量达到预定目标。这个过程完全是自动化的,无需人工干预。

    为什么选择K8S

    在K8S的架构方案中,底层网络的细节完全被屏蔽,甚至无需修改基于服务的Cluster IP,就能将系统从物理机运行环境无缝迁移到公有云中。且K8S系统架构具备了超强的动态扩容能力。

    1.4 K8S基本概念和术语

    在K8S中,Node、Pod、Replication Controller、Service等概念都可以看作一种资源对象,通过K8S提供的kubectl工具或者API调用进行操作,并保存在etcd中。

    1.4.1 Node(节点)

    Node信息如下:

    • Node地址:主机的IP地址、或者Node ID
    • Node运行状态:包括Pending、Running、Terminated三种状态。
    • Node Condition:描述Running状态Node的运行条件,目前只有一种条件——Ready。Ready表示Node处于健康状态,可以接收从Master发来的创建Pod的指令。
    • Node系统容量:描述Node可用的系统资源,包括CPU、内存数量、最大可调度Pod数量等。
    • 其他:Node其他信息,包括实例的内核版本号、K8S版本号、Docker版本号、操作系统名称等。

    可以通过命令查看node详细信息:

    kubectl describe node <node_name>
    
    [root@hdss7-21 ~]# kubectl get node
    NAME                STATUS   ROLES         AGE   VERSION
    hdss7-21.host.com   Ready    master,node   14d   v1.17.2
    hdss7-22.host.com   Ready    master,node   14d   v1.17.2
    
    [root@hdss7-21 ~]# kubectl describe node hdss7-21.host.com
    Name:               hdss7-21.host.com
    Roles:              master,node
    Labels:             beta.kubernetes.io/arch=amd64
                        beta.kubernetes.io/os=linux
                        kubernetes.io/arch=amd64
                        kubernetes.io/hostname=hdss7-21.host.com
                        kubernetes.io/os=linux
                        node-role.kubernetes.io/master=
                        node-role.kubernetes.io/node=
    Annotations:        node.alpha.kubernetes.io/ttl: 0
                        volumes.kubernetes.io/controller-managed-attach-detach: true
    ..............................
    此处省略
    ..............................
    [root@hdss7-21 ~]#
    

    1. Node管理

    Node通常是物理机、虚拟机或云主机提供的资源,并不是由K8S创建的。使用K8S创建一个Node,只是表示K8S在系统内部创建了一个Node对象,创建后会对其进行一系列健康检查,如果检查不通过,则该Node将会在集群中被标记为不可用。

    2. Node Controller

    Node Controller是K8S Master中的一个组件,用于管理Node对象。功能:

    • 集群范围内的Node信息同步
    • 单个Node的生命周期管理

    Node信息同步可以通过kube-controller-manager的启动参数--node-sync-period设置同步的时间周期。

    3. Node的自注册

    Kubelet的--register-node默认为true,kubelet会向apiserver注册自己。这也是K8S推荐的Node管理方式。Kubelet进行自注册的启动参数如下:

    • --apiservers=:apiserver的地址
    • --kubeconfig=:登录apiserver所需凭据/证书的目录
    • -- cloud_provider=:云服务商地址,用于获取自身的metadata

    4. 手动管理Node

    K8S也可以收工创建和修改Node对象,需要将--register-node设为false。

    1.4.2 Pod

    Pod是K8S最基本的操作单元,包括一个或多个紧密相关的容器,一个Pod中的多个容器应用通常是紧耦合的,是在Node上被创建、启动或者销毁。

    使用Pod而不直接使用Docker,很重要的一个原因是Docker容器之间的通信受到Docker网络机制的限制。

    一个Pod中的应用容器共享同一组资源,如下:

    • PID命名空间:Pod中国的不同应用程序可以看到其他应用程序的进程ID。
    • 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
    • IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。
    • UTS命名空间:Pod中的多个容器共享同一个主机名。
    • Volumes(共享存储卷):Pod中的各个容器可以访问在Pod级别定义的Volumes。

    1. 对Pod的定义

    对Pod的定义通过YAML或Json格式的配置文件来完成。Pod的生命周期是通过Replication Controller来管理的。Pod生命周期过程包括:

    • Pending:Pod定义正确,提交到Master,但其所包含的容器镜像还未完全创建。通常Master对Pod进行调度需要一些时间,之后Node对镜像进行下载也需要一些时间。
    • Running:Pod已被分配到某个Node上,且包含的所有容器镜像都已经创建完成,并成功运行起来。
    • Successed:Pod中所有同期都成功结束,并且不会被重启,这是Pod的一种最终状态。
    • Failed:Pod中所有容器都结束了,但至少一个容器是以失败状态结束的,这也是Pod的一种最终状态。

    1.4.3 Label(标签)

    Label是K8S系统的一个核心概念。Label以key/value键值对的形式附加到各种对象上。如Pod、Service、RC、Node等。Label定义了这些对象的可识别属性,用来对它们进行管理和选择。Label可以在创建对象时附加到对象上,也可以在对象创建后通过API进行管理。

    在为对象定义好Label后,其他对象就可以使用Label Selector(选择器)来定义起作用的对象了。

    Label Selector的定义由多个逗号分隔的条件组成。

    "labels" : {
    	"key1": "value1",
    	"key2": "value2"
    }
    

    当前有两种Label Selector:基于等式的(Equality-based)和基于集合的(Set-based),在使用时可以将多个Label进行组合来选择。

    一些常用Label示例:

    • keys: ["release", "environment", "tier", "partiton", "track"]
    • values: ["stable", "canary", "dev", "qa", "pro", "frontend", "backend", "middleware", "customerA", "customerB", "daily", "weekly"]

    Replication Controller通过Label Selector来选择要管理的Pod。

    看一下redis-slave RC的yaml配置文件,在RC的定义中,Pod部分的template.metadata.labels定义了Pod的Label,即name=redis-slave。然后在spec.selector中指定name=redis-slave,表示将对所有包含该Label的Pod进行管理。

    apiVersion: v1
    kind: ReplicationController
    metadata:
      name: redis-slave
      labels:
        name: redis-slave
    spec:
      replicas: 2
      selector:
        name: redis-slave
      template:
        metadata:
          labels:
            name: redis-slave
        spec:
          containers:
          - name: slave
            image: kubeguide/guestbook-redis-slave
            ports:
            - containerPort: 6379
            env:
            - name: GET_HOSTS_FROM
              value: env
    

    同样,在Service的定义中,也通过定义spec.selector为name=redis-slave来选择将哪些具有该Label的Pod加入其Load Balance的后端列表中去。

    apiVersion: v1
    kind: Service
    metadata:
      name: redis-slave
      labels:
        name: redis-slave
    spec:
      selector:
        name: redis-slave
      ports:
      - port: 6379
    

    使用Label可以给对象创建多组标签,Service、RC等组件则通过Label Selector来选择对象范围,Label和Label Selector共同构成了K8S系统中最核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。

    1.4.4 Replication Controller(RC)

    Replication Controller是K8S系统中的核心概念,用于定义Pod副本的数量,保证集群中运行着用户期望的副本数量。在Master中,RC进程通过RC的定义来完成Pod的创建、监控、启停等操作。

    对RC的定义使用YAML或JSON格式的配置文件来完成。在K8S中,可以使用kubectl scale命令来一键完成。

    注意:删除RC并不会影响通过该RC已创建好的Pod。为了删除所有Pod,可以设置replicas值为0,然后更新该RC。也可以用stop和delete命令来一次性删除RC和RC控制的全部Pod。

    1.4.5 Service

    1. Service的定义

    Endpoint对象

    Endpoint对象主要由Pod和IP地址和容器需要监听的端口号组成,使用命令可以查看:

    [root@hdss7-21 ~]# kubectl get endpoints
    NAME           ENDPOINTS                                   AGE
    frontend       172.7.21.8:80,172.7.22.7:80,172.7.22.8:80   5h23m
    redis-master   172.7.21.6:6379,172.7.22.5:6379             5h40m
    redis-slave    172.7.21.7:6379,172.7.22.6:6379             5h40m
    

    一个Service可以看作一组提供相同服务的Pod的对外访问接口。Service作用于哪些Pod是通过Label Selector来定义的。

    可以继续查看redis-slave的yaml文件,K8S将会创建一个名为“redis-slave”的服务,并在6479端口上监听。spec.selector的定义表示该Service将包含所有具有“name=redis-slave” Label的Pod。

    2. IP相关

    Pod的IP地址是Docker Daemon根据docker0网桥的IP地址段进行分配的,但Service的Cluster IP地址是K8S系统中的虚拟IP地址,由系统动态分配。Service的Cluster IP地址相对于稳定,被创建时就会分配一个IP地址,在销毁该Service之前,这个IP地址都不会再变化的。而Pod生命周期相对较短,新创建的Pod会分配一个新的IP地址。

    3. 外部访问Service

    由于Service对象在Cluster IP Range池中分配到的IP职能在内部访问,所以其他Pod都可以无障碍的访问到它,但如果这个Service作为前端服务,准备为集群外的客户端提供服务,这时候就需要为这个服务提供公共IP。

    K8S支持两种对外提供服务的Service的type定义:NodePort和LoadBalancer。

    1. NodePort

    在定义Service时指定spec.type=NodePort,并指定spec.ports.nodePort的值,系统就会在K8S集群中的每个Node上打开一个主机上的真实端口号。这样,能够访问Node的客户端就都能通过这个端口号访问到内部的Service了。

    apiVersion: v1
    kind: Service
    metadata:
      name: frontend
      labels:
        name: frontend
    spec:
      type: NodePort
      ports: 
      - port: 80
        nodePort: 3001
      selector:
        name: frontend
    

    2. LoadBalancer

    如果云服务商支持外接负载均衡器,则可以通过spec.type=LoadBalance定义Service,同时,需要指定负载均衡器的IP地址。使用这种类型需要指定Service的nodePort和clusterIP。

    apiVersion: v1
    kind: Service
    metadata: {
    	"kind": "Service",
    	"apiVersion": "v1",
    	metadata: {
    		"name": "myService"
    	},
    	"spec": {
    		"type": "LoadBalancer",
    		"clusterIP": "xxx.xxx.xxx.xxx",
    		"selector": {
    			"myApp"
    		},
    		"ports": [
    			{
    				"protocol": "TCP",
    				"port": 80,
    				"targetPort": 9376,
    				"nodePort": 3002
    			}
    		],
    	},
    	"status": {
    		"loadBalancer": {
    			"ingress": [
    				# 下面这个IP就是云服务商提供的负载均衡器的IP地址
    				"ip": "yyy.yyy.yyy.yyy"
    			]
    		}
    	}
    }
    

    之后,对该Service的访问请求将会通过LoadBalancer转发到后端Pod上去,负载分发的实现方式则依赖于云服务商提供的LoadBalancer的实现机制。

    4. 多端口服务

    可以通过端口进行命名来实现一个服务需要对外暴露多个端口,使各Endpoint不会因重名而产生歧义。

    apiVersion: v1
    kind: Service
    metadata: {
    		"name": "my-service"
    	},
    	"spec": {
    		"selector": {
    			"myApp"
    		},
    		"ports": [
    			{
    				"name": "http"
    				"protocol": "TCP",
    				"port": 80,
    				"targetPort": 9376,
    			},
    						{
    				"name": "https"
    				"protocol": "TCP",
    				"port": 443,
    				"targetPort": 9377,
    			}
    		]
    	}
    }
    

    1.4.6 Volume(存储卷)

    Volume是Pod中能够被多个容器访问的共享目录。K8S的Volume概念与Docker的Volume比较类似,但并不完全相同。K8S中的Volume与Pod生命周期相同,但与容器的生命周期不相关。当容器终止或重启时,Volume中的数据也不会丢失。K8S支持多种类型的Volume,并且一个Pod可以同时使用任意多个Volume。

    Volume类型:

    1. EmptyDir:在Pod分配到Node时创建,在同一个Pod中所有容器可以读写EmptyDir重的相同的文件,当Pod从Node上被移除时,EmptyDir中的数据也会永久删除。用途:

      • 临时空间,无需永久保留数据
      • 长时间任务的中间过程CheckPoint临时保存目录
      • 多容器共享目录
    2. hostPath:在Pod上挂载宿主机上的文件或目录。用途:

      • 容器应用程序生成的日志文件需要永久保存,可以使用宿主机的高速文件系统进行存储。
      • 需要访问宿主机上Docker引擎哪步数据结构的容器应用,可以通过定义hostPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。

    在使用这种类型的Volume时,需要注意:

    • 在不同的Node上具有相同配置的Pod可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致。
    • 如果使用了资源配额管理,则K8S无法将hostPath在宿主机上使用的资源纳入管理。
    # ......
    		spec:
          volumes:
          - name: "persistent-storage"
            hostPath:
              path: "/data"
            volumeMounts:
            - name: "persistent-storage"
              mountPath: "/data"
    # ......
    
    1. gcePersistentDisk:使用这种类型的Volume表示使用谷歌计算引擎(Google Compute Engine,GCE)上永久磁盘(Persistent Disk,PD)上的文件。与EmptyDir不同,PD上的内容永久保存,当Pod被删除时,PD只是被卸载(Unmount),但不会被删除。需要注意的是,需要先创建一个永久磁盘(PD)才能使用gcePersistentDisk。

    使用gcePersistentDisk的限制条件:

    • Node节点需要时GCE虚拟机
    • 这些虚拟机需要与PD存在于相同的GCE项目和Zone中。
    1. awsPersistentDisk:同上。
    2. nfs:使用NFS(网络文件系统)提供的共享目录挂载到Pod中。在系统中需要一个运行中的NFS系统。

    在Pod定义中使用nfs示例:

    apiVersion: v1
    kind: Pod
    metadata: 
    	name: nfs-web
    spec:
    	containers:
    		- name: web
    			image: nginx
    			ports:
    				- name: web
    					containerPort: 80
    			volumeMounts:
    				# name must match the volume name below
    				- name: nfs
    					mountPath: "/usr/share/nginx/html"
    	volumes:
    		- name: nfs
    			nfs:
    				# server是实际的nfs服务器地址
    				server: nfs-server
    				path: "/"
    
    1. 其他挂载方法:iscsi、glusterfs、rbd、gitRepo、secret、persistent VolumeClaim

    1.4.7 Namespace(命名空间)

    Namespace通过将系统内部的对象分配到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户,便于不同的分组在共享使用整个集群的资源的同时,还能被分别管理。K8S集群在启动后,会创建一个名为“default”的Namespace,如果用户不特别指明Namespace,则用户创建的Pod、RC、Service都将被系统创建到“default”的Namespace中。

    根据需求创建新的Namespace,可以通过yaml文件来创建:

    apiVersion: v1
    kind: Namespace
    metadata: 
    	name: development
    

    然后在创建Pod时,可以指定Pod所属的Namespace:

    apiVersion: v1
    kind: Pod
    metadata: 
    	name: xxx
    	namespace: devlopment
    	# ......
    

    查询Namespace,如果不指定参数,则默认查询的是“default”名称空间的对象,要查询其他名称空间的对象,需要指定-n参数:

    kubectl get pods -n devlopment
    

    1.4.8 Annotation

    Annotation与Label相似,也是使用key/value键值对的形式进行定义。Label具有严格的命名规则,它定义的是K8S对象的元数据,并且勇于Label Selector。Annotation则是用户任意定义的“附加”信息,以便于外部工具进行查找。

    Annotation记录的信息包括:

    • build、release、Docker镜像等信息,例如:时间戳、镜像hash值、docker registry地址等
    • 日志库、监控库、分析库等资源库的地址信息
    • 程序调试工具信息,例如:工具名称、版本号等
    • 其他信息,例如:团队成员姓名、联系电话等

    1.4.9 小结

    除了以上核心组件,K8S系统中还有其他可配置的资源对象和内部使用的对象,可以查阅K8S的API文档。

    1.5 K8S总体架构

    etcd是高可用的key/value存储系统,用于持久化存储集群中所有的资源对象,例如集群中的Node、Service、Pod、RC、Namespace等。API Server则提供了操作etcd的封装接口API,以REST方式提供服务,这些API基本上都是集群中资源对象的增删改查及监听资源变化的接口。API Server是连接其他所有服务组件的枢纽。

    1.5.1 K8S相关生命周期

    1. 创建RC生命周期

    kubectl创建一个RC,通过API Server写入到etcd,Controller Manager监听API Server,监听到创建RC的事件,分析当前集群,如果没有该RC对应的Pod实例,则生成对应的Pod对象,并通过API Server写入etcd中,该事件被Scheduler发现,通过一系列调度流程,分配到一个Node节点上,这个过程可称为绑定(Pod Binding),然后又通过API Server将这结果写入到etcd中,随后,目标Node上运行的Kubelet进程通过API Server监测到这个“新生的”Pod并且按照它的定义,启动Pod,直至销毁。

    2. Pod请求生命周期

    kubectl提交一个映射到Pod的Service的创建请求,Controller Manager会通过Label查询到相关联的Pod实例,然后生成Service的Endpoints信息并通过API Server写入到etcd中。所有Node上运行的Proxy进程通过API Server查询并监听Service对象与其对应的Endpoints信息,建立一个软件方式的负载均衡器来实现Service访问到后端Pod的流量转发功能。

    1.5. 2 K8S组件功能总结

    API Server:提供资源对象的唯一操作入口。“全量查询,监听变化”资源数据。API Server内部有一套完备的安全机制,API Server在收到一个REST请求后,会首先执行认证、授权和准入控制的相关逻辑,过滤非法请求,再进行发送。

    Controller Manager:集群内部管理控制中心,实现K8S集群的故障检测和恢复的自动化工作。

    Scheduler:集群中的调度器,负责Pod在集群(Node)节点中的调度分配。

    • 预选策略
    • 优选策略

    Kubelet:负责本Node节点上的Pod增删改查、监控等全生命周期管理,同时定时上报本Node的状态信息到API Server中。

    Kube Proxy:实现了Service的代理及软件模式的负载均衡器。通过API Server查询并监听Service对象与其对应的Endpoints信息,建立软件方式的负载均衡器。

    kubectl:K8S客户端命令行管理工具。

  • 相关阅读:
    2019年1月26日训练日记
    2019年1月25日训练日记
    2019年1月24日训练日记
    2019年1月23日训练日记
    2019年1月22日训练日记
    2019年1月21日训练日记
    2019年1月20日训练日记
    2019年1月19日训练日记
    2019年1月18日训练日记
    C语言学习小结
  • 原文地址:https://www.cnblogs.com/liuhuan086/p/15713993.html
Copyright © 2020-2023  润新知