第1章 Kubernetes入门
1.1 Kubernetes是什么 首先,它是一个全新的基于容器技术的分布式架构领先方案。其次,如果我们的系统设计遵循了k8s的设计思想,那么传统架构中那些和业务无关的底层代码和模块,都可以从我们的视线消失。我们不必再费心于负载均衡器的选项和部署实施
的问题,不必再考虑引入或者自己开发一个复杂的服务治理框架,不必再头疼于服务监控和故障处理模块的开发。然后,k8s是一个开放平台,任何语言编写的服务,都可以被映射为k8s的Service(服务),并通过TCP通信协议进行交互。此外,k8s平台对现有的编程语言、编程框架、中间件
没有任何入侵性,因此现有的系统也很容易升级到k8s平台上。最后,k8s是一个完备的分布式系统支持平台。k8s具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支持能力、透明的服务注册和服务发现机制、内建
的职能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制,以及多粒度的资源配额管理能力。同时,k8s提供了完善的管理工具,
这些工具包含开发、部署测试、运维监控在内的各个环节。因此,k8s是一个基于容器技术的分布式架构解决方案,并且是一个一站式的完备的分布式系统开发和支持平台。在k8s中,Service是分布式集群架构中的核心,一个Service对象拥有如下关键特征:1.拥有唯一指定的名称(比如mysql-server);2.拥有一个虚拟IP(ClusterIP、Service IP 或者 VIP)和端口号;3.能够提供某种远程服务能力;4.被映射到提供这种服务能力的一组容器应用上Service的服务进程目前都是基于socket通信方式对外提供服务,比如,redis,mamcache,mysql,web server,或者是实现了某个具体业务的特定的TCP Server进程。
虽然一个Service通常由多个相关的服务进程提供服务,每个服务进程都有一个独立的Endpoint(IP+Port)访问点,但k8s能够让我们通过Service(虚拟Cluster IP + Service
Port)连接到指定的Service。有了k8s内建的透明负载均衡和故障恢复机制,不管后端有多少服务进程,也不管某个服务进程是否发生故障而被重新部署到其他机器,都不会影响对服务
的正常调用。更重要的是,这个Service本事一旦创建就不再变化,这意味着我们再也不用为k8s集群中服务的ip地址变化而头疼了。容器提供了强大的隔离功能,所有有必要把为Service提供服务的这组进程放入容器中进行隔离。为此,k8s设计了Pod对象,将每个服务进程都包装到相应的Pod中,使其成为
在Pod中运行的一个容器(Container)。为了建立Service和Pod之间的关联关系,k8s首先给每个Pod都贴上一个标签(Label),给运行的mysql的Pod贴上name=mysql标签,
给运行PHP的Pod贴上name=php的标签,然后给相应的Service定义标签选择器(Label Selector),比如MySQL Service的标签选择器的选择条件为 name=mysql,意为
该Service要作用于包含 name=mysql Label的Pod。这样就解决了Service和Pod的关联问题。这里介绍Pod的概念。首先,Pod运行在一个被称为节点(Node)的环境中,这个节点既可以是物理机,也可以是私有云或者公有云的一个虚拟机,通常在一个节点上运行几百个Pod。
其次,在每个Pod中都运行着一个特殊的被称为Pause的容器,其他容器则被称为 业务容器。这些业务容器共享Pause容器的网络栈和Volume挂载卷,因此它们之间的通信和数据交换
都更为高效,在设计时我们可以充分利用这一特性将一组密切相关的服务进程放在容一个Pod中;最后,需要注意的是,并不是每个Pod和它里面运行的容器都能被映射到一个Service
上,只有提供服务的那组Pod才会被映射为一个服务。在集群管理方面,k8s将集群中的机器划分为一个Master和一些Node。在master上运行着集群管理相关的一组进程 kube-apiserver、kube-controller-manager和
kube-scheduler,这些进程实现了整个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错等管理功能,并且都是自动完成的。Node作为集群中的工作节点,
运行真正的应用程序,在Node上k8s管理的最小运行单位是Pod。在Node上运行着k8s的 kubelet,kube-proxy 服务进程,这些服务进程负责Pod的创建、启动、监控、重启、
销毁,以及实现软件模式的负载均衡器。最后,看看传统的IT系统中服务扩容和升级两个难题。在k8s集群中,只需为需要扩容的Service关联的Pod创建一个RC(Replication Controller),在一个RC定义文件
中包含以下3个关键信息:a) 目标Pod的定义; b) 目标Pod需要运行的副本数量(Replicas)c) 要监控的目标Pod的标签在创建好RC(系统将自动创建好Pod)后,k8s会通过在RC中定义的Label筛选出对应的Pod实例并实时监控其状态和数量,如果实例数量少于定义的副本数量,则会根据在RC
中定义的Pod模板创建一个新的Pod,然后将此Pod调度到合适的Node上启动运行,直到Pod实例的数量达到预定义的目标。这个过程完全是自动化的。有了RC,服务扩容就变成一个
简单的数字游戏了,只需要修改RC中的副本数量即可。后续的服务升级也将通过RC来自动完成。1.2 为什么要用Kubernetes 1.首先,可以轻装上阵的开发复杂系统;2.其次,可以全面拥有微服务架构;3.再次,可以随时随地的将系统整体搬到公有云上;4.然后,k8s内在的服务弹性扩容机制可以让我们轻松应对突发流量;5.最后,k8s系统架构超强的横向扩容能力可以让我们的竞争力大大提升。1.3 从一个简单的例子开始 1.3.1 环境准备 通过 kubeadm 快速安装一个 k8s。1.3.2 启动MySQL服务 首先,为mysql服务创建一个RC定义文件 mysql-rc.yaml,如下:
apiVersion:v1 #副本控制器RC
kind:ReplicationController
metadata:name:mysql #RC的名称,全局唯一
spec:replicas:1 #Pod副本的期待数量selector:app:mysql #符合目标的Pod拥有此标签template: #根据此模板创建Pod的副本(实例)metadata:labels:app:mysql  #Pod副本拥有的标签,对应RC的Selectorsepc:containers: #Pod内容器的定义部分- name:mysql #容器的名称image:mysql #容器对应的Docker Imageports:- containerPort:3306 #容器应用监听的端口号env: #注入容器内的环境变量- name:MYSQL_ROOT_PASSWORDvalue:"123456"以上YAML定义文件中的kind属性来表明此资源对象的类型,比如这里的值为 ReplicationController,表示这是一个RC;在spec一节中是RC相关属性定义,比如spec.selector是RC的Pod标签选择器,即监控和管理拥有这些标签的Pod实例,确保在当前集群中始终有且仅有 replicas 个Pod实例在运行,这里设置 replicas=1,表示只能运行一个mysql Pod实例。当前集群中运行的Pod数量少于 replicas时,RC会根据在 spec.template 一节中定义的Pod模板来生成一个新的Pod实例,spec.template.metadata.labels 指定了该Pod的标签,需要特别注意的是:这里的labels必须匹配之前的spec.selector,否则此RC每创建一个无法匹配Label的Pod,就会不停的尝试创建新的Pod,陷入恶性循环中。在创建好 mysql-rc.yaml文件后,为了将它发布到k8s集群中,我们在Master上执行命令:kubeclt create -f mysql-rc.yaml接下来,用kubectl命令查看刚刚创建的RC:kubectl get rc查看Pod的创建情况时,可以运行下面的命令:kubectl get pods我们通过 docker ps 指令查看正在运行的容器,发现提供mysql服务的Pod容器已经创建并正常运行了,此外会发现mysql Pod对应的容器还多创建了一个来自谷歌的pause容器,这就是Pod的"根容器"。最后,创建一个与之关联的 k8s Service --- mysql 的定义文件(文件名为 mysql-src.yaml)
apiVersion:v1
kind:Service #表明是 k8s Service
metadata:name:mysql #Service的全局唯一名称
spec:ports:- port:3306 #Service 提供服务的端口号selector:app:mysql #Service对应的Pod拥有这里定义的标签其中,metadata.name 是 Service的服务名(ServiceName);port属性则定义了 Service 的虚端口;spec.selector 确定了哪些Pod副本(实例)对应本服务。类似的,我们通过kubectl create 命令创建Service对象。kubectl create -f mysql-src.yaml查看刚刚创建的Service:kubectl get svc可以发现,mysql服务被分配了一个值为 169.169.253.143 的Cluster IP地址。随后,k8s集群中其他新创建的Pod就可以通过 Service的Cluster IP+端口号 3306来连接和访问它了。   通常,Cluster IP 是在 Service 创建后由 k8s 系统自动分配的,其他Pod无法预先知道某个 Service 的Cluster IP地址,因此需要一个服务发现机制来找到这个服务。为此,最初时,k8s巧妙的使用了Linux的环境变量来解决这个问题,后面还有其他机制。现在只需要知道,根据Service的唯一名称,容器可以从环境变量中获取Service对应的Cluster IP 地址和端口,从而发起tcp/ip连接请求。1.3.3 启动Tomcat应用 1.3.4 通过浏览器访问网页 1.4 Kubernetes的基本概念和术语 k8s 中的大部分概念如Node,Pod,Replication Controller,Service等都可以被看做是一种资源,几乎所有的资源对象都可以通过k8s提供的kubectl(或者API来调用)执行增删查改等操作并将其保存在etcd中持久化存储。从这个角度看,k8s其实是一个高度自动化的资源控制系统,它通过跟踪对比etcd库里保存的"资源期望状态"与当前环境中"实际资源状态"的差异来实现自动控制和自动纠错的高级功能。在声明一个k8s资源对象的时候,需要注意一个关键属性:apiVersion。k8s采用了"核心+外围扩展"的设计思路,在保持平台核心稳定的同时具备持续演进升级的优势。k8s大部分的核心资源对象都归属于v1这个核心API,比如Node,Pod,Service,Endpoints,Namespace,RC,PersistentVolume等。在版本迭代过程中,k8s先后扩展了extensions/v1leta1,apps/v1beta1,apps/v1beta2等API组,而在1.9版本之后引入了apps/v1这个正式的扩展API组,正式淘汰了extensions/v1beta1,apps/v1beta1,apps/v1beta2 这3个API组。我们可以采用yaml或者json格式声明(定义或者创建)一个k8s资源对象,每个资源对象都有自己的特定语法格式(可以理解为数据库中的一张表),但随着k8s版本的持续升级,一些资源对象会不断引入新的属性。为了在不影响当前功能的情况下引入新特性的支持,我们通常会采用如下2种方法:方法1:在设计数据库的时候,在每个表中都添加一个很长的备注字段,之后扩展的数据以某种格式(如xml,json,字符串拼接)放入备注字段。因为数据库表的结构没有变化,所以此时程序的改动范围是最小的,但看起来不太美观。方法2:直接修改数据库,增加一个或者多个新的列,此时程序的改动范围比较大。但看起来比较美观。虽然,2种方法都不完美。更优雅的做法是,先采用方法1实现这个新的特性,经过几个版本的迭代后,等新特性稳定成熟之后,可以在后续的版本中采用方法2升级到正式版。为此,k8s为每个资源对象添加了类似数据库表里备注字段的通用属性 Annotations,实现方法1的升级。1.4.1 Master k8s里的Master是指集群控制节点,在每个k8s集群里都需要有一个Master来负责整个集群的管理和控制,基本上k8s的所有控制命令都发给它,它负责具体的执行过程,我们后面执行的所有命令基本都是在Master上运行的。Master通常会占据一个独立的服务器(高可用部署建议用3台),主要是因为它太重要了,是整个集群的"首脑"。在Master上运行着以下关键的进程:1.k8s API Server(kube-apiserver)提供了 HTTP Rest 接口的关键服务进程,是k8s里所有资源的增删查改等操作的唯一入口,也是集群控制的入口进程。2.k8s Controller Manager(kube-controller-manager)k8s里所有的资源对象的自动化控制中心,可以将其理解为资源对象的"大总管"。3.k8s Scheduler(kube-scheduler)负责资源调度(Pod调度)的进程,相当于公交公司的"调度室"。另外,在Master上通常还需要部署etcd服务,因为k8s里的所有资源对象的数据都被保存在etcd中。1.4.2 Node 除了Master,k8s集群中的其他机器被称为Node,在较早的版本中也被称为 Minion。与Master一样,Node可以是一台物理机,也可以是一台虚拟机。Node是k8s集群中的工作负载节点,每个Node都会被Master分配一些工作负载(Docker 容器),当某个Node宕机时,其他的工作负载会被Master自动转移到其他节点上。在每个Node上都运行着以下关键进程:1.kubelet负责Pod对应的容器的创建、启停等任务,同时与Master密切协作,实现集群管理的基本功能。2.kube-proxy实现k8s Service的通信与负载均衡机制的重要组件。3.Docker EngineDocker引擎,负责本机的容器创建和管理工作。Node可以在运行期间动态增加到k8s集群中,前提是这个节点已经正确的安装、配置和启动了上述关键进程,在默认情况下kubelet会向Master注册自己,这也是k8s推荐的Node管理方式。一旦Node被纳入集群管理范围,kubelet进程就会定时向Master汇报自身的情况,如操作系统、Docker版本、机器的cpu和内存情况,以及当前有哪些Pod在运行等,这样Master就可以获知每个Node的资源使用情况,并实现高效的负载均衡调度策略。而某个Node在超过指定时间不上报信息时,会被Master判定为"失联",Node的状态被标记为不可用(Not Ready),随后Master会触发"工作负载大转移"的自动流程。查看集群有多少个nodekubelet get nodes查看某个node的详细信息kubelet describe node <node_name>上述命令展示了Node的如下关键信息:1.Node的基本信息名称、标签、创建时间等2.Node的当前运行状态Node启动后会做一系列的自检工作,比如磁盘空间是否不足(DiskPressure)、内存是否不足(MemoryPressure)、网络是否正常(NetworkUnavailable)、PID资源是否充足(PIDPressure)。在一切正常时设置Node为Ready状态(Ready=true),该状态表示Node处于健康状态,Master将可以在其上调度新任务了。3.Node的主机地址与主机名4.Node上的资源数量,描述node可用的系统资源,包括cpu,内存数量,最大可调度的Pod数量等5.Node可分配的资源量描述Node当前可用于分配的资源量6.主机系统信息包括主机ID,系统UUID,Linux kernel版本号,操作系统类型与版本,Docker版本号,kubelet与kube-proxy的版本号等。7.当前运行的Pod列表概要信息8.已分配的资源使用概要信息,例如资源申请的最低、最大允许使用量占用系统总量的百分比。9.Node相关的Event信息1.4.3 Pod 每个Pod都有一个特殊的被称为"根容器"的Pause容器,Pause容器对应的镜像属于k8s平台的一部分,除了Pause容器,每个Pod还包含一个或者多个紧密相关的用户业务容器。为什么k8s会设计出一个全新的Pod概念并且Pod有这样特殊的组成结构?原因1:在一组容器作为一个单元的情况下,我们很难简单的对"整体"进行判断以及有效行动。比如,一个容器死亡了,此时算是整体死亡了吗?还是n/m的死亡率?引入与业务无关并且不容易死亡的Pause容器作为Pod的根容器,以它的状态代表整个容器组的状态,就简单、巧妙多了。原因二:Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,这样就简化了密切关联的业务容器之间的通信问题,也很好的解决了它们之间的文件共享问题。k8s为每个Pod都分配了唯一的ip地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。k8s要求底层网络支持集群内任意两个Pod之间的tcp/ip直接通信,这通常采用虚拟二层网络技术来实现,例如 Flannel,Open vSwitch等,因此我们需要牢记一点:在k8s里,一个Pod里的容器与另外主机上的Pod容器是能够直接通信的。Pod其实有2种类型:普通的Pod和静态Pod(Static Pod)。后者比较特殊,它并没有被存放在etcd里面,而是被存放在某个具体的node上的一个具体文件中,并且只在此Node上启动、运行。而普通的Pod一旦被创建,就会被存放如etcd中,随后会被k8s Master调度到某个具体的Node上进行绑定(Binding),随后该Pod对应的Node上的kubelet进程实例化成一组相关的docker容器启动。在默认情况下,当Pod里的某个容器停止时,k8s自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,就会将这个Node上的所有Pod调度到其他节点上。k8s的所有资源对象都可以采用yaml或者json格式的文件来定义或者描述,如下:
apiVersion:v1
kind:Pod
metadata:name:myweblables:name:myweb
spec:containers:- name:mywebimage:kubeguide/tomcat-app:v1ports:- containerPort:8080env:- name:MYSQL_SERVICE_HOSTvalue:'mysql'- name:MYSQL_SERVICE_PORTvalue:'3306'Kind为Pod表明这是一个Pod的定义,metadata里的name属性为Pod的名称,在metadata里还能定义资源对象的标签,这里声明myweb拥有一个name=myweb的标签,在Pod里所包含的容器组的定义则在spec一节中声明,这里定义了一个名为myweb、对应镜像为 kubeguide/tomcat-app:v1 的容器,该容器注入了名为 MYSQL_SERVICE_HOST='mysql'和MYSQL_SERVICE_PORT='3306'的环境变量(env关键字),并且在8080端口(containerPort)启动容器进程。Pod的IP加上这里的容器端口(containerPort),组成了一个新的概念---Endport,它代表此Pod例的一个服务进程的对外通信地址。一个Pod也存在多个Endpoint的情况。我们所熟悉的docker Volume在k8s里也有对应的概念 --- Pod Volume,后者有一些扩展,比如可以用分布式文件系统GlusterFS实现后端存储功能;Pod Volume是被定义在Pod上,然后被各个容器挂载到自己的文件系统中的。这里提下Event概念,它是一个事件的记录。记录了事件的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此次事件的原因等众多信息。Event通常会被关联到某个具体的资源对象上,是排查故障的重要参考信息,之前我们看到Node的描述信息有Event,而Pod同样有Event记录,当我们发现某个Pod迟迟无法创建的时候,可以用kubelet describe pod xxx 来查看它的描述信息。每个Pod都可以对其能使用的服务器上的计算资源设置限额,当前可以设置限额的计算资源有cpu和memory两种,其中cpu的资源单位为cpu(Core)的数量,是一个绝对值而非相对值。对于绝大多数容器来说,一个cpu的资源配额相当大,所以在k8s里面通常以千分之一的cpu配额为最小单位,用m来表示。通常一个容器的cpu配额被定义为100~300m,即0.1~0.3个cpu,由于cpu配额是一个绝对值,所以无论是在拥有一个Core的机器上,还是在拥有48个Core的机器上,100m这个配额所代表的的cpu的使用量都是一样的。与cpu配额类似,Memory配额也是一个绝对值,它的单位是内存字节数。在k8s里,一个计算资源进行配额限制时需要设定以下2个参数:1.Requests:该资源的最小申请量,系统必须满足;2.Limits:该资源最大允许使用的量,不能被突破。当容器试图使用超过这个量的资源时,可能会被杀掉重启。通常,我们会把Requests设置为一个较小的数值,符合容器平时的工作负载的情况下的资源需求,而把Limits设置为峰值负载情况下资源占用的最大值。下面的定义表明,mysql容器申请最少0.25个cpu以及64MiB内存,在运行过程中mysql容器所能使用的资源配额为0.5个cpu以及128MiB内存。
spec:containers:- name:dbimage:mysqlresources:requests:memory:"64Mi"cpu:"250m"limits:memory:"128Mi"cpu:"500m"1.4.4 Label Label 是k8s系统中另外一个核心的概念。一个Label是一个key=value的键值对,其中key与value由用户自己指定。Label可以被附加到各种资源对象上,例如Node,Pod,Service,RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意的资源对象上。Label通知在资源对象定义时确定,也可以在对象创建后动态添加或者删除。我们可以通过给指定资源对象捆绑一个或者多个不同的Label来实现多维度的资源分组管理功能,以便灵活、方便的进行资源分配、调度、配置、部署等管理工作。一些常用的Label 如下:1.版本标签:"release":"stable"、"release":"canary"2.环境标签:"environment":"dev"、"environment":"qa"、"environment":"production"3.架构标签:"tier":"frontend"、"tier":"backend"、"tier":"middleware"4.分区标签:"partition":"customerA"、"partition":"customerB"、"partition":"customerC"5.质量管控标签:"track":"daily"、"track":"weekly"Label相当于我们熟悉的"标签"。给某个资源对象定义的一个Label,就相当于给它打了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有这些Label的资源对象,k8s通过这种方式实现了类似sql的简单又通用的对象查询机制。Label Selector 可以被类比为sql语句的where查询条件,例如,name=redis-slave这个Label Selector作用域Pod,可以被类比为select * from pod wherepod's name = 'redis-slave' 这样的语句。当前有两种Label Selector表达式:基于等式的(Equality-based)和基于集合的(Set-based),前者采用等式表达式匹配标签,如下:a) name=redis-slave:匹配所有具有标签 name=redis-slave 的资源对象;b) env != production:匹配所有不具有标签 env=production的资源对象后者是使用集合操作类表达式匹配标签,如下:a) name in (redis-master,redis-slave):匹配具有标签 name=redis-master或者 name=redis-slave 的资源对象;b) name not in (php-frontend):匹配不具有标签 name=php-frontedn 的资源对象。可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式用","进行分割,几个条件之间是"AND"的关系,如下:name=redis-master,env!=production以 myweb Pod 为例,Label被定义在其中meatadata中:
apiVersioin:v1
kind:Pod
metadata:name:myweblabels:app:myweb管理对象RC和Service则通过Selector字段设置需要关联的Pod的Label:
apiVersion:v1
kind:ReplicationController
metadata:name:myweb
spec:replicas:1selector:app:myweb...其他管理对象如Deploment,ReplicaSet,DaemonSet和Job则可以在Selector中使用基于集合的筛选条件定义,例如:
selector:matchLabels:app:mywebmatchExpressions:- {key:tier, operator:In, values:[frontend]}- {key:enviroment, operator:NotIn, values:[dev]}matchLabels 用于定义一组Labels,与直接写在Selector中的作用相同;matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算包括In,NotIn,Exists,DoesNotExist。如果同时设置了 matchLabels和matchExpressions,则两组条件为AND关系,即需要同时满足所有条件才能完成Selector筛选。Label Selector 在k8s中的重要场景如下:1.kube-controller 进程通过在资源对象RC上定义的Label Selector来筛选要监控的Pod副本数量,使Pod副本数量始终符合预期设定的全自动流程控制流程。2.kube-proxy 进程通过Service的Label Selector来选择对应的Pod,自动建立每个Service到对应的Pod的请求转发路由表,从而实现Service的智能负载均衡机制。3.通过某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod定向调度的特性。总之,使用Label可以给对象创建多组标签,Label和Label Selector共同构成了k8s系统中核心的应用模型,使得被管理对象能够被精细的分组管理,同时实现了整个集群的高可用性。1.4.5 Replication Controller RC 是k8s系统中核心的概念之一,它定义的一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分:1.Pod期待的副本数量;2.用于筛选目标的Pod的Label Selector;3.当Pod的副本数量小于预期数量时,用于创建新的Pod的Pod模板(template)在我们定义了一个RC并将其提交到k8s集群后,Master上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保Pod实例数量刚好等于此RC的期望值。在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行 kubectl scale 命令来完成:kubectl scale rc redis-slave --replicas=3需要注意的是,删除RC并不会影响通过该RC已经创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC。此外,kubectl提供了stop和delete命令来一次性删除RC和RC控制的Pod。Replication Controller 由于与k8s 代码中的模块 Replication Controller 同名,同时"Replication Controller"无法准确的表达它的本意,所以在k8s 1.2中,升级为另外一个新概念 --- Replica Set,官方解释为"下一代的RC"。Replica Set与 RC当前唯一的区别是,Replica Set支持基于集合的Label Selector,而RC只支持基于等式的Label Selector,这使得Replica Set的功能更强。举例:
apiVersion:extensions/v1beta1
kind:ReplicaSet
medadata:name:frontend
spec:selector:matchLabels:tier:frontendmatExpressions:- {key:tier, operator:In, values:[frontend]template:...kubectl 命令工具适用于 RC的绝大部分命令同样适用于Replica Set。此外,我们当前很少单独使用Replica Set,它主要被 Deployment 这个更高层的资源对象所使用,从而形成一套Pod创建,删除,更新的编排机制。我们在使用Deployment时,无需关心它是如何创建和维护的Replica Set,这一切都是自动发生的。最后总结下,RC的一些特性与作用:1.在大多数情况下,我们通过定义一个RC实现Pod的创建及副本数量的自动控制;2.在RC里包括完整的Pod定义模板;3.RC通过Label Selector机制实现对Pod副本的自动控制;4.通过改变RC里的Pod副本数量,可以实现Pod的扩容或者缩容;5.通过改变RC里Pod模板的镜像版本,可以实现Pod的滚动升级。1.4.6 Deployment Deployment 是 k8s 在1.2版本中引入的新概念,用于更好的解决Pod的编排问题。为此,Deployment 在内部使用了Replica Set 来实现目的,我们可以把它看做是RC的一次升级。Deployment相对于RC的一个最大升级是我们可以随时知道当前Pod的"部署"的进程。实际上由于一个Pod的创建、调度、绑定节点以及目标Node上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动N个Pod的副本的目标状态,实际上是一个连续变化的"部署过程"导致的最终状态。Deployment 的典型使用场景有以下几个:1.创建一个Deployment 对象来生成对应的Replica Set 并完成 Pod的副本的创建;2.检查Deployment 的状态来看部署动作是否完成(Pod副本数量是否达到预期的值);3.更新Deployment 以创建新的Pod(比如镜像的升级);4.如果当前Deployment 不稳定,则回滚到一个早先的Deployment 版本;5.暂停Deployment 以便于一次性修改多个PodTemplateSpec的配置项,之后再恢复Deployment ,进行新的发布;6.扩展Deployment 以应对高负载;7.查看Deployment 的状态,以此作为发布是否成功的指标8.清理不再需要的旧版本的ReplicaSets。除了API声明与Kind类型等有所区别,Deployment 的定义域Replica Set的定义很类似:
apiVersion:extensions/v1beta1
kind:Deployment
metadata:name:nginx-deployment运行下面命令创建Deployment :kubectl create -f tomcat-deployment.yaml查看Deployment 的信息:kubectl get deployments对上述输出涉及的数量解释如下:1.DESIRED:Pod副本数量的期望值,即在Deployment 里定义的Replica2.CURRENT:当前Replica的值,实际上是 Deployment 创建的 Replica Set里的 Replica 的值,这个值不断的增加,直到达到 DESIRED 为止,表明整个部署过程完成。3.UP-TO-DATE:最新版本的Pod的副本数量,用于指示在滚动升级的过程中,有多少个Pod副本以及成功升级。4.AVAILABLE:当前集群中可用的Pod副本数量,即集群中当前存活的Pod数量。运行下面命令查看对应的Replica Set:kubectl get rs运行下面命令查看创建的Podkubelctl get pods1.4.7 Horizontal Pod Autoscaler 通过手工执行 kubectl scale 命令,我们可以实现Pod扩容和缩容。在k8s 1.1发布了新特性 --- Horizontal Pod Autoscaling(Pod横向自动扩容,HPA)。在k8s 1.2中HPA被升级为稳定版本(apiVersion:autoscaling/v1),但仍然保留了旧版本(apiVersion:extension/v1beta1)。k8s从1.6版本开始,增加了根据应用自定义的指标进行自动扩容和缩容的功能,API版本为 autoscaling/v2alpha1。HPA与之前的RC,Deployment一样,也属于一种k8s资源对象。通过追踪分析指定RC控制的所有目标Pod的负载变化情况,来确定是否需要有针对性的调整目标Pod的数量副本,这是HPA的实现原理。当前,HPA有以下两种方式作为Pod的负载的度量指标:a) CPUUtilizationPercentageb) 应用程序自定义的度量指标,比如服务在每秒内的相应请求数(TPS或者QPS)CPUUtilizationPercentage 是一个算术平均值,即目标Pod所有副本自身的cpu利用率的平均值。一个Pod自身的cpu利用率是该Pod当前cpu使用量除以它的PodRequest的值,比如定义一个pod的Pod Request 为0.4,而当前Pod的cpu使用率为0.2,则它的cpu使用率为50%,这样就可以算出一个RC控制的所有Pod副本的cpu利用率的算术平均值了。如果某个时刻的CPUUtilizationPercentage 超过80%,则意味着当前pod副本数量很可能不足以支撑接下里更多的请求,需要进行动态扩容。如果目标pod没有定义Pod Request的值,则无法使用 CPUUtilizationPercentage 实现Pod的横向扩容。除了使用CPUUtilizationPercentage,k8s 1.2版本开始也尝试支持应用程序自定义的度量指标。在CPUUtilizationPercentage计算过程中Pod的cpu使用量通常是 1min内的平均值,通常通过查询 Heapster 监控子系统来得到这个值,所有需要安装部署Heapster,这样便增加了系统的复杂度和实施HAP特性的复杂度。因此,从1.7开始,k8s自身孵化了一个基础特性数据采集监控框架 --- k8s Monitoring Architecture,从而更好的支持HPA和其他需要用到的基础性能数据的功能模块。在k8s Monitoring Architecture中,k8s定义了一套标准化API的接口 Resource Metrics API,以方便客户端程序(如HPA)从Metrics Server中获取目标资源对象的性能数据。下面是一个HPA定义的具体例子:
apiVersion:autoscaling/v1
kind:HorizontalPodAutoscaler
metadata:name:php-apachenamespace:default
spec:maxReplicas:10minReplicas:1scaleTargetRef:kind:Deploymentname:php-apachetargetCPUUtilizationPercentage:90根据上面定义,可以知道HPA控制的目标对象为一个名为php-apache的Deployment里的Pod副本,当这些Pod副本的CPUUtilizationPercentage的值超过90%时自动触发动态扩容行为,在扩容或者缩容时必须满足的一个约束条件是 Pod 的副本数量为1~10个。除了可以通知直接定义yaml文件并且调用kubectl create 命令来创建一个HPA资源对象的方式,还可以直接通过下面命令来创建等价的HPA对象:kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=101.4.8 StatefulSet 在k8s中,Pod的管理对象RC,Deployment,DaemonSet和Job都是面向无状态的服务的,但其实有很多是有状态的。特别是一些复杂的中间件集群,例如mysql集群,mongodb集群,zookeeper集群,这些集群有4个共同点:1.每个节点都有固定的ID,通过这个ID,集群中的成员可以互相发现并通信;2.集群的规模是比较固定的,集群规模不能随意变动;3.集群中的每个节点都是有状态的,通常会持久化到永久存储中;4.如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。如果通过RC或者Deployment控制pod副本数量来实现上述有状态的集群,就会发现第1点是无法满足的,因为Pod的名称是随意产生的,pod的ip地址也是在运行期间才确定的且有可能变动,我们事先无法为每个Pod都确定唯一不变的ID。另外,为了能够在其他节点上恢复某个失败的节点,这种集群中的pod需要挂接某种共享存储,为了解决这个问题,k8s从1.4开始引入了 PetSet 这个新的 资源对象,并且在1.5版本时更名为 StatefulSet,StatefulSet从本质上来说,可以看做是 Deployment/RC 的一个特殊变体,它有如下特性:1.StatefulSet里的每个pod都很稳定,唯一的网络标识,可以用来发现集群内的其他成员。假设 StatefulSet 的名称为kafka,那么第1个pod叫kafka-0,第2个叫kafka-1 ...2.StatefulSet 控制的pod副本启动顺序是受控的,操作第n个pod时,前n-1个pod已经是运行且准备好的状态3.StatefulSet里的pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据安全)StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service 配合使用,即在每个StatefulSet定义中都要声明它属于哪个 HeadlessService。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析 Headless Service的dns域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例都创建一个dns域名,这个域名的格式为:$(podname).$(headless service name)比如一个3节点的kafka的StatefulSet 集群对应的 Headless Service的名称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS名称分别为 kafka-0.kafka,kafka-1.kafka,kafka-3.kafka,这些dns名称可以直接在集群的配置文件中固定下来。1.4.9 Service Service 服务也是k8s离的核心资源对象之一,k8s里的每个Service 其实就是微服务架构中的一个服务。k8s的Service 定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问背后的一组由Pod副本组成的集群实例,Service 与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service 的服务能力与服务质量始终服务预期标准。既然每个pod都会被分配一个单独的ip地址,而且每个pod都提供了一个独立的Endpoint(Pod IP + ContainerPort)来被客户端访问,现在多个pod副本组成了一个集群来提供服务,那么客户端如何来访问它们的呢?一般的做法是部署一个负载均衡器,为这组pod开启一个对外的服务端口如8080,并且将这些pod的Endpoint列表加入8080端口的转发列表,客户端就可以通过负载均衡器的对外ip地址+服务端口来访问此服务。客户端的请求最后被转发到哪个pod,由负载均衡器的算法决定。k8s也遵循上述的做法,运行在每个pod上的 kube-proxy 进程其实就是一个智能的负载均衡器,负载把对Service的请求转发到后端的某个pod实例上,并在内部实现服务的负载均衡与会话保持机制。但k8s发明了一种很巧妙又影响深远的设计:Service没有共用一个负载均衡器的ip地址,每个Service都被分配了一个全局唯一的虚拟ip地址,这个虚拟ip地址被称为Cluster IP。这样一来,每个服务就变成了具备唯一的ip地址的通信节点,服务调用就成了最基础的tcp网络通信问题。我们知道pod的Endpoint地址会随着pod的销毁和重新创建而发生改变,因为新Pod的ip地址与之前旧的pod不同。而Service一旦被创建,K8s就会自动为它分配一个可用的Cluster IP,而在Service的整个生命周期内,它的Cluster IP不会发生变化。于是,服务发现这个棘手的问题在k8s的架构里也可以轻松的解决:只要Service的Name与Service的Cluster IP地址做一个dns域名映射即可完美的解决问题。如下:
apiVersion:v1
kind:Service
metadata:name:tomcat-service
spec:ports:- port:8080selector:tier:frontend上述内容定义了一个名为 tomcat-service 的Service,它的服务端口为8080,拥有"tier=frontend"这个Label的所有Pod都属于它,运行下面的命令创建:kubectl create -f tomcat-server.yaml运行下面命令查看tomcat-service 的 Endpoint列表:kubectl get endpoints运行下面命令查看tomcat-service被分配的Cluster IP以及更多的信息:kubectl get src tomcat-service -o yaml在 spec.ports 的定义中,targetPort属性用来确定提供该服务的容易所暴露的(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供tcp/ip接入,port属性则定义了Service的虚端口。前面定义的tomcat服务时没有定义targetPort,则默认与port相同。接下来看Service的多端口问题。很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat,Codis等常见中间件。k8s Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint都定义一个名称来区分。如下:
apiVersion:v1
kind:Service
metadata:name:tomcat-service
spec:ports:- port:8080name:service-port- port:8005name:shutdown-port....多端口为什么要给每个端口都命名呢?这就涉及到了k8s的服务发现机制了。2.k8s的服务发现机制任何分布式系统都会涉及服务发现这个基础问题,大部分分布式系统都通过提供特定的api接口来实现服务发现的功能,但这样会导致平台的入侵性比较强,也增加了测试、开发的难度。首先,k8s中的每个Service都有唯一的Cluster IP以及唯一的名称,而名称是由开发者自己定义的,部署时也没有必要改变,所以完全可以被固定在配置中。接下来的问题是如何通过Service名称去找到对应的Cluster IP。最早采用了Linux的环境变量来解决这个问题,即每个Service都生成一些对应的Linux环境变量(ENV),并在每个Pod的容器启动时自动注入这些环境变量。考虑到通过环境变量获取Service地址的方式仍然不太方便,后来k8s通过Add-On增值包引入了dns系统,把服务名作为dns域名,这样程序就可以直接通过服务名来建立通信了。3.外部系统访问Service的问题k8s里的3种IP,这3种IP分别如下:a) Node IP:Node的IP地址b) Pod IP:Pod的IP地址c) Cluster IP: Service 的 Ip地址首先,Node IP是k8s集群中每个节点的物理网卡的ip地址,是一个真实存在的物理网络,所有属于这个网络的服务器都能通过这网络直接通信,不管其中是否有部分节点不属于这个k8s集群。这也表明k8s集群之外的节点访问k8s集群之内的某个节点或者tcp/ip服务时,都必须通过Node IP通信。其次,Pod IP是每个Pod的ip地址,它是docker Engine根据 docker0 网桥的ip地址段进行分配的,通常是一个虚拟的二层网络,k8s要求位于不同Node上的Pod都能够彼此直接通信,所以k8s里一个pod里的容器访问另外一个pod里的容器时,就是通过Pod IP所在的虚拟二层网络进行通信的,而真实的tcp/ip流量是通过Node IP所在的物理网卡流出的。最后,Service 的 Cluseter IP,是一种虚拟ip,更像是伪造的ip,原因有如下:a) Cluster IP仅仅作用于k8s Service这个对象,并由k8s管理和分配ip地址(来源于Cluster IP地址池)b) Cluster IP无法被ping,因为没有一个"实体网络对象"来响应c) Cluster IP只能结合 Service Port组成一个具体的通信端口,单独的Cluster IP不具备tcp/ip通信的基础,并且它们属于k8s集群这样一个封闭的空间,集群外的节点如果要访问这个通信端口,则需要做一些额外的工作d) 在k8s集群内,Node IP 网,Pod IP网与 Cluster IP网之间的通信,采用的是k8s自己设计的一种编程方式的特殊路由规则,与我们熟知的路由规则有很大不同。根据上面总结,Service的Cluseter IP属于k8s集群的内部地址,无法在集群外部使用这个地址。那么矛盾来了:实际上在我们开发业务系统中肯定多少有一部分服务要提供给k8s集群外部的应用或者用来来使用的,用户怎么用?采用 NodePort 是解决上述问题的最直接、有效的方法。以tomcat-service为例,在Service的定义里做如下扩展即可:
apiVersion:v1
kind:Service
metadata:name:tomcat-service
spec:type:NodePortports:- port:8080nodeport:31002selectors:tier:frontend其中,nodePort:31002这个属性表明手动指定 tomcat-service 的 NodePort为31002,否则k8s会自动分配一个可用的端口。在浏览器输入,http://<nodePortIP>31002 即可访问。NodePort的实现方式是在k8s集群中的每个Node上都为需要外部访问的Service开启一个对应的tcp监听端口,系统外部只要用任意一个Node的Ip地址+具体的端口号即可访问此服务,在任意Node上运行 netstat -tlp | grep 31002但NodePort还没有完全解决外部访问 Service的所有问题,比如负载均衡问题。对于每个Service,我们通常需要配置一个对应的lb实例来转发流量到后端Node上,这样的确增加了工作量以及出错的概率。于是k8s提供了自动化的解决方案,如果我们的集群位于谷歌的公有云GCE上,那么只要把Service的 type=NodePort改成 type=LoadBalancer,k8s就会自动创建一个对应的 Load balancer并返回它的ip地址提供外部客户端使用。其他公有云提供商只要实现了支持此特性的驱动,则也可以达到上述目的。1.4.10 Job 从1.2版本开始,k8s支持批处理类型的应用,我们可以通过k8s Job 这种新的资源对象定义启动一个批处理任务。Job也控制一组容器,从这个角度说,Job也是一种特殊的Pod副本控制器,同时Job控制Pod副本与RC等控制器的工作机制有以下区别:  1.Job所控制的Pod副本是短暂运行的,可以将其视为一组docker容器,其中的每个docker容器仅仅运行一次。当Job控制的所有Pod副本都运行结束时,对应的Job也就结束了。Job在实现方式上与RC等副本控制器不同,job生成的pod副本是不能自动重启的,对应的pod副本的RestartPoliy都被设置为Never。因此,当对应的pod副本都执行完成时,对应的job也就完成了控制使命,即job生成的pod在k8s中是短暂存在的。在k8s 1.5版本之后又提供了类似的crontab的定时任务---CronJob,解决了某些批处理需要定时反复执行的问题。2.job所控制的pod副本的工作模式能够多实例并行计算。1.4.11 Volume Volume(存储卷)是pod中能够被多个容器访问的共享目录。k8s的Volume概念、用途和目的与docker的Volume比较类似,但不能等价。首先,k8s中的Volume被定义在pod上,然后被一个pod里的多个容器挂载到具体的文件目录下;其次,k8s中的Volume与pod的生命周期相同,但与容器的生命周期不相关,当容器停止或者重启时,Volume中的数据也不会丢失。最后,k8s支持多种类型的Volume,例如GlusterFS,Ceph等先进的分布式文件系统。Volume的使用也比较简单,在大多数情况下,我们先在pod上声明一个Volume,然后在容器里引用该Volume并挂在(Mount)到容器的某个目录上。举例来说,我们要给之前的tomcat pod增加一个名为 datavol 的Volume,并且挂载到容器的 /mydata-data 的目录上,则只需要对pod的定义文件做如下修正即可:
template:metadata:lables:app:app-demotier:frontendspec:volumes:- name:datavolemptyDir:{}containers:- name:tomcat-demoimage:tomcatvolumeMounts:- mountPath:/mydata-dataname:datavolimagePullPolicy:IfNotPresent除了可以让一个pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写到网络存储中,k8s的Volume还扩展出一种非常有使用价值的功能,即容器配置管理集中化定义与管理,这是通过ConfigMap来实现的。k8s提供了非常丰富的Volume类型,如下:1.emptyDir一个emptyDir Volume是在pod分配到Node时创建的,从它的名字就可以看出,它的初始化内容为空,并且无需指定宿主机上对应的目录文件,这是因为k8s自动分配了一个目录,当pod从Node上移除时,emptyDir中的数据也会被永久删除。emptyDir的一些用途如下:  a) 临时空间,例如用于某些应用程序运行时所需的临时目录,且无需永久保留 b) 长时间任务的中间过程 CheckPoint 的临时保存目录c) 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)目前,用户无法控制emptyDir使用的介质种类。如果kubectl的配置使用的是磁盘,那么所有emptyDir都将被创建在磁盘上。pod在将来可以设置emptyDir是位于磁盘,固态硬盘还是基于内存的tmpfs上。2.hostPathhostPath为在pod上挂在宿主机上的文件或目录,它作用:1.容器应用程序生成的日志文件需要永久保留时,可以使用宿主机的高速文件系统进行存储。2.需要访问宿主机上docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机 /var/lib/docker 目录,使容器内部应用可以直接访问docker文件系统。在使用这种类型的Volume时,需要注意:1.在不同的node上具有相同配置的pod,可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致;2.如果使用了资源配额管理,则k8s无法将hostPath在宿主机上使用的资源纳入管理。下面使用了宿主机的/data目录定义了一个hostPath的类型的Volume:
volumes:- name:"persistent-storage"hostPath:path:"/data"3.gcePersistentDisk使用这种类型的Volume表示使用谷歌云提供的永久磁盘(Persistent Disk,PD)存放Volume的数据,它与emptyDir不同,PD上的内容会被永久保存,当pod被删除时,PD只是被卸载(Unmount),但不会被删除。需要注意的是,你需要先创建一个PD,才能使用 gcePersistentDisk。使用gcePersistentDisk 有以下限制条件:1.Node(运行kubectl的节点)需要是GCD虚拟机;2.这些虚拟机需要与PD存在于相同的GCE项目和Zone中。通过gcloud命令即可创建一个PD:gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk定义gcePersistentDisk类型的Volume定义如下:
volumes:- name:test-volumegcePersistentDisk:pdName:my-data-diskfsType:ext44.awwElasticBlockStore该类型的Volume使用亚马逊公有云提供的 EBS Volume 存储数据,需要先创建一个 EBS Volume 才能使用 awsElasticBloxkStore。限制如下:1.Node(运行kubectl节点)需要是aws ec2实例;2.这些aws ec2实例需要与 EBS Volume 存在于相同的region和availability-zone中。3.ebs只支持单个ec2实例挂载一个Volume通过aws ec2 create-volume 创建一个 EBS Volume:awc ec2 create-volume --availability-zone eu-west-la --size 10 --volume-type gp2定义 awsElasticBlockStore 类型的Volume定义如下:
volumes:- name:test-volumeawsElasticBlockStore:volumeID:aws://<availability-zone>/<volume-id>fsType:ext45.NFS使用NFS网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个NFS Server。定义NFS类型的Volume定义如下:
volumes:- name:nfsnfs:#改为你的nfs服务器地址server:nfs-server.localhostpath:"/"6.其他类型的Volume1.iscsi使用iSCSI存储设备上的目录挂载到pod上。2.flocker使用Flocker管理存储卷。3.glusterfs使用开源GlusterFS网络文件系统的目录挂载到Pod中。4.rbd使用Ceph块设备共享存储(Rados Block Device)挂载到pod中。5.gitRepo通过挂载一个空目录,并从git库clone一个git repository以供Pod使用。6.secret一个Secret Volume用于为Pod提供加密的信息,你可以将定义在k8s中的 Secret直接挂载为文件让pod访问。Secret Volume 是通过TMFS(内存文件系统)实现的,这种类型的Volume总是不会被持久化的。1.4.12 Persistent Volume 之前提到的Volume是被定义在pod之上的,属于计算资源的一种,而实际上,网络存储是相对独立于计算资源而存在的一种实体资源。比如在使用虚拟机的情况下,我们通常会先定义一个网络存储,然后从中划出一个"网盘"并挂接到虚拟机上。Persistent Volume(PV)和与之相关联的Persistent Volume Claim(PVC)也起到了类型作用。PV可以被理解为k8s集群中的某个网络存储对应的一块存储,它与Volume类似,但有以下区别:1.PV只能是网络存储,不属于任何Node,但可以在每个Node上访问;2.PV并不是被定义在pod上的,而是独立于pod之外定义的3.PV目前支持的类似包括:...下面给出了NFS类型的PV的一个yaml文件定义,声明了需要5Gi的存储空间:
apiVersion:v1
kind:PersistentVolume
metadata:name:pv003
spec:capacity:storage:5GiaccessModes:- ReadWriteOncenfs:path:/somepathserver:172.17.0.2比较重要的是 PV的 accessModes属性,目前有以下类型:1.ReadWriteOnce:读写权限,并且只能被单个node挂载2.ReadOnlyMany:只读权限,允许被多个node挂载3.ReadWritemany:读写权限,允许被多个node挂载如果某个pod想申请某种类型的PV,则首先需要定义一个PersistentVolumeClaim对象:
kind:PersistentVolumeClaim
apiVersion:v1
metadata:name:myclaim
spec:accessModes:- ReadWriteOnceresources:requests:storage:8Gi然后,在pod的Volume定义中引用上述PVC即可:
volumes:- name:mypdpersistentVolumeClaim:claimName:myclaimPV的状态,PV是有状态的对象,有以下几种:1.Available:空闲状态2.Bound:已经绑定到某个PVC上3.Released:对应的PVC已经被删除,但资源还没被集群回收4.Failed:PV自动回收失败1.4.13 Namespace Namespace(命名空间) 在很多情况下用于实现多租户的资源隔离,Namespace通过将集群内部的资源对象"分配"到不同的Namespace中,形成逻辑上分组的不同项目、小组或者用户组,便于不同的分布在共享整个集群的资源的同时还能被分别管理。k8s集群在启动后会创建一个名为default的Namespace,通过kubectl查看:kubectl get namespaces接下来,如果不特别指明Namespace,则用户创建的Pod,RC,Service 都将被系统创建到这个默认的default下面.Namespace的定义:
apiVersion:v1
kind:Namespace
metadata:   name:development一旦创建了Namespace,我们在创建资源的时候就可以指定这个资源对象属于哪个Namespace。如下,
apiVersion:v1
kind:Pod
metadata:name:busyboxnamespace:development...使用 kubectl get 查看,将无法显示:kubectl get pods这是因为不加参数的话,则kubectl get命令仅显示default命名空间的:kubectl get pods --namespace=development当给每个租户创建一个Namespace来实现多租户的资源隔离时,还能结合k8s的资源配额管理,限定不同的租户能占用的资源。1.4.14 Annotation Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的k8s对象的元数据(Metadata),并且用于Label Selector。Annotation 则是用户任意定义的附加信息,以便于外部工具查找。很多时候,k8s的模块自身会通过Annotation标记资源对象的一些特殊信息。通常,用Annotation来记录的信息如下:1.build信息,release信息,docker镜像信息等,例如时间戳,release id号,PR号,镜像Hash值,docker Registry地址等;2.日志库,监控库,分析库等资源库的地址信息3.程序调试工具信息,如工具名称,版本号等4.团队的联系信息1.4.15 ConfigMap 为了能准确和深刻的理解k8s ConfigMap 的功能和价值,我们从docker说起。我们知道,docker通过将程序,依赖库,数据及配置文件"打包固化"到一个不变的镜像文件中的做法,解决了应用部署的难题,但同时带来了棘手的问题,即 配置文件中的参数在运行期如何修改的问题。我们不可能在启动docker容器后再修改容器里的配置文件,然后用新的配置文件重启容器的用户主进程。为了解决这个问题,docker提供了2种办法:1.在运行时通过容器的环境变量来传递参数2.通过Docker Volume 将容器外的配置文件映射到容器内这2种方式都有优缺点,在大多数情况下,后一种方式更适合我们的系统,因为大多数应用通常从一个或者多个配置文件中读取参数。但这种方式也有明显的缺陷:我们必须在目标主机上先创建好对应的配置文件,然后才能映射到容器里。上述缺陷在分布式情况下边的更为严重,因为无论采用哪种方式,写入多台服务器上的某个指定文件,并确保这些文件保持一致,都是一个很难完成的任务。此外,在大多数情况下,我们都希望能集中管理系统的配置参数,而不是管理一堆配置文件。针对上述问题,k8s给了一个很巧妙的实现:a) 首先,把所有的配置项都当做 key-value 字符串,当然value 可以来自某个文本文件,比如配置项 password=123456。这些配置项可以作为 Map表的一个项,整个Map的数据可以被持久化存储在k8s的etcd中,然后提供api以方便k8s相关组件或者客户端 curd 操作这些数据,上述专门用来保存配置参数的Map就是k8s ConfigMap资源对象。b) 接下来,k8s提供了一种内建机制,将存储在etcd中的ConfigMap 通过Volume映射的方式变成目标Pod内的配置文件,不管目标pod被调度到哪一台服务器上,都会完成自动映射。进一步的,如果ConfigMap中的key-value数据被修改,则映射到pod中的"配置文件"也会随着更新。于是,k8s ConfigMap 就变成了分布式系统中最简单且对应用无入侵的配置中心。

1.Kubernetes权威指南 --- Kubernetes入门相关推荐

  1. Kubernetes权威指南第2版 和 Docker技术入门与实战第2版 两本容器的书下载地址

    两本书的下载链接 下载链接 链接:https://pan.baidu.com/s/13gv0ZQRiHfvLwgwjsvUiEA 密码:hi8o Kubernetes权威指南第2版 目录: 第1章 K ...

  2. Kubernetes单机创建MySQL+Tomcat演示程序:《Kubernetes权威指南》第一章demo报错踩坑

    欢迎访问我的个人博客:https://midoq.github.io/ 引言 最近做边缘计算项目,因为没有基础,所以首先学习Kubernetes.感觉系统的中文入门资料比较少,只找到<Kuber ...

  3. Kubernetes权威指南

    网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击"普通下载" ...

  4. 赠书福利丨Kubernetes权威指南第4版

    将本篇内容转发至朋友圈,并截图于"京东云开发者社区"公众号后台进行回复.\color{blue}{将本篇内容转发至朋友圈,并截图于"京东云开发者社区"公众号后台 ...

  5. Kubernetes权威指南精彩语录

    2019独角兽企业重金招聘Python工程师标准>>> 上述代码的风格和逻辑再也熟悉不过了:创建一个SchedulerServer对象,将命令行参数传入,并且进入SchedulerS ...

  6. Kubernetes权威指南精彩段落

    2019独角兽企业重金招聘Python工程师标准>>> 看到上述两段代码,你可能会有一种"开门复动竹, 疑是故人来"的感觉.的确,这段代码经笔者反复考证后认定:1 ...

  7. kubernetes权威指南_如何快速上手成为大厂标配的kubernetes?

    程序员书库(ID:CodingBook) 猿妹 综合整理 过去几年,以docker.kubernetes为代表的容器技术已发展为一项通用技术,BAT.滴滴.京东.头条等大厂,都争相把容器和k8s项目作 ...

  8. Kubernetes权威指南(下)

    Phineas # Kubernetes权威指南<Kubernetes权威指南>第5版勘误:https://github.com/kubeguide/K8sDefinitiveGuide- ...

  9. 《Kubernetes权威指南》一起来免费下载学习

    ​人人都想拥有的K8s重磅级案头手册 本书从架构师.开发人员和运维人员的角度,阐述了Kubernetes 的基本概念.实践指南.核心原理.开发指导.运维指南.新特性演进等内容,图文并茂.内容丰富.由浅 ...

  10. 《Kubernetes权威指南:从Docker到Kubernetes实践全接触》读书笔记

    写在前面 之前简单的了解过,但是机器的原因,只有单机,因为安装Docker的原因,本机VM上的红帽节点起不来了.懂得不多,视频上都是多节点的,所以教学视屏上的所以Demo没法搞. 前些时间公司的一个用 ...

最新文章

  1. 老子今天不加班,程序员也需要自由
  2. 解决CUDA driver version is insufficient for CUDA runtime version
  3. oracle 调用存储过程返回集合插入临时表_技术分享:集合
  4. cPanel虚拟主机上运行Python的方法
  5. 来一发,网页下拉刷新
  6. ffmpeg命令下载m3u8原画质视频
  7. 信庭嵌入式工作室-嵌入式Linux漫谈之Linux系统
  8. ZooKeeper作为注册中心
  9. MYSQL 命令行大全 (简洁、明了、全面)
  10. 洪灾面前,能抗衡的很少,但能做的很少
  11. 名帖194 米芾 行书《珊瑚帖》
  12. 如何在mac版chrome安装第三方插件
  13. 关于Android 抓包 与 反抓包
  14. 详解200行Python代码实现控制台版2048【总有一款坑适合你】【超详细】
  15. REST API规范
  16. 产业安全专家谈|金融行业如何践行《反电信网络诈骗法》?
  17. iOS 有效编写高质量Objective-C方法(一)
  18. python:实现SlopeOne算法(附完整源码)
  19. 听音扒谱app_广播/会的人都说听音写谱没有那么难!听歌扒谱子技巧关键是方法正确...
  20. Gallery2修改记录

热门文章

  1. DAY 34 进程通信、消费者模型和线程
  2. html php插入百度地图定位
  3. Python2安装说明
  4. 郁闷的C小加(一)(后缀表达式)
  5. UIScrollView---iOS-Apple苹果官方文档翻译
  6. [转]制作适合手机的网页遇到的问题
  7. AiLearning:一个 GitHub万星的中文机器学习资源
  8. 时间序列深度学习:状态 LSTM 模型预测太阳黑子(中)
  9. 再学点分治——动态点分治
  10. P1330 阳光封锁大学