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客户端命令行管理工具。

原文地址:https://www.cnblogs.com/liuhuan086/p/15713993.html