在Kubernetes中,几乎所有的概念,包括Master、Node、Pod、Label、Namespace、Volume等都可以看作是一种“资源对象”。
从这个角度上来说,Kubernetes是一个高度自动化的资源控制系统,它通过对比etcd中保存的“资源期望状态”和当前环境的“资源实际状态”,
以此来实现自动控制和自动纠错的功能。
1.Master
Master是Kubernetes集群的控制节点,每个kubernetes集群至少有一个Master节点,
它负责整个集群的控制和管理,几乎所有的kubectl的命令都是同时Master节点来执行的。
Master节点也可以参与实际任务的执行,但是并不建议这样做,因为master节点必须保证高可用,
一旦master节点宕机,那么整个集群都会处于停滞状态。生产环境建议使用3台独立的服务器作为Master节点。
Master节点的主要组件包括:APIServer、Controller-Manager、Scheduler,还有kubelet、kubectl、etcd等组件。
这些组件会以进程的形式展开。
APIServer:整个集群的唯一入口,也是连接etcd的唯一入口,所有对资源对象进行的操作都必须通过这个组件来展开。
所有组件的操作也必须通过APIServer这个组件来实现。
Controller-Manager:所有资源对象的自动化控制中心,比如自动启动和删除容器来维护容器的数量。
Scheduler:负责资源调度,也就是将Pod分配到具体的Node上。
2.Node
在Kubernetes集群中,除了master节点之外就是Node节点,当然Etcd集群除外。
Node节点是Kubernetes的工作负载节点,每个Node节点都会被Master分配一些工作任务,
当Node节点宕机的时候其上的工作任务会被转移到其它节点上去。
Node节点可以在运行期间动态的加入到Kubernetes集群中。
在默认情况下,Node节点上的kubelet组件会自动注册到master节点,
一旦被纳入到集群的范围,Kubelet就会定期向master节点汇报自身的情况。比如CPU内存的使用情况。
如果master在一定时间之内没有收到Node节点的报备信息,Node节点就会被标记为不可用,
随后master节点就会进行“工作负载转移”的自动化流程。
root@VM-16-6-ubuntu:~/test# kubectl get nodes NAME STATUS ROLES AGE VERSION vm-0-3-ubuntu Ready <none> 2d v1.10.2 vm-16-6-ubuntu Ready master 2d v1.10.2 vm-16-8-ubuntu Ready <none> 2d v1.10.2
查看某一个Node的详细信息
root@VM-16-6-ubuntu:~# kubectl describe node/vm-0-3-ubuntu Name: vm-0-3-ubuntu #主机名称 Roles: <none> Labels: beta.kubernetes.io/arch=amd64 #标签 beta.kubernetes.io/os=linux kubernetes.io/hostname=vm-0-3-ubuntu Annotations: node.alpha.kubernetes.io/ttl=0 #注解 volumes.kubernetes.io/controller-managed-attach-detach=true CreationTimestamp: Wed, 19 Jun 2019 11:59:12 +0800 #创建时间 Taints: <none> Unschedulable: false Conditions: Type Status LastHeartbeatTime LastTransitionTime Reason Message ---- ------ ----------------- ------------------ ------ ------- NetworkUnavailable False Wed, 19 Jun 2019 11:59:38 +0800 Wed, 19 Jun 2019 11:59:38 +0800 WeaveIsUp Weave pod has set this OutOfDisk False Sun, 23 Jun 2019 19:24:09 +0800 Sat, 22 Jun 2019 04:16:58 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available MemoryPressure False Sun, 23 Jun 2019 19:24:09 +0800 Sat, 22 Jun 2019 04:16:58 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available DiskPressure False Sun, 23 Jun 2019 19:24:09 +0800 Sat, 22 Jun 2019 04:16:58 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure PIDPressure False Sun, 23 Jun 2019 19:24:09 +0800 Wed, 19 Jun 2019 11:59:12 +0800 KubeletHasSufficientPID kubelet has sufficient PID available Ready True Sun, 23 Jun 2019 19:24:09 +0800 Sat, 22 Jun 2019 04:16:58 +0800 KubeletReady kubelet is posting ready status. AppArmor enabled Addresses: InternalIP: 172.27.0.3 #主机地址与主机名 Hostname: vm-0-3-ubuntu Capacity(容量): #描述Node可用的系统资源 cpu: 1 #可用的CPU核数 ephemeral-storage: 51474044Ki hugepages-2Mi: 0 memory: 884964Ki #可用内存数量 pods: 110 #最大可调度的Pod数量 Allocatable(可分配的容量): #系统剩余可分配的资源 cpu: 1 ephemeral-storage: 47438478872 hugepages-2Mi: 0 memory: 782564Ki pods: 110 System Info: #系统信息 Machine ID: c5f311a0edd169fa9fb1933c58105309 System UUID: 5F71CCD1-781D-4FC3-BC67-630A2127EC53 #系统UUID Boot ID: 2e3bcb7a-95dd-47c5-81ac-1ffad0bd9ba6 Kernel Version: 4.4.0-130-generic #Linux内核版本 OS Image: Ubuntu 16.04.1 LTS #系统版本 Operating System: linux Architecture: amd64 Container Runtime Version: docker://17.3.2 Kubelet Version: v1.10.2 #kubelet核kube-proxy信息 Kube-Proxy Version: v1.10.2 ExternalID: vm-0-3-ubuntu #正在运行的Pod列表概要信息 Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits --------- ---- ------------ ---------- --------------- ------------- default deployment-example-9956dd665-hzcbf 0 (0%) 0 (0%) 0 (0%) 0 (0%) kube-system kube-proxy-hc9bc 0 (0%) 0 (0%) 0 (0%) 0 (0%) kube-system weave-net-cshgn 20m (2%) 0 (0%) 0 (0%) 0 (0%) Allocated resources: #已分配的资源使用概要信息 (Total limits may be over 100 percent, i.e., overcommitted.) CPU Requests CPU Limits Memory Requests Memory Limits ------------ ---------- --------------- ------------- 20m (2%) 0 (0%) 0 (0%) 0 (0%) Events: <none> #Node相关的Event信息。
3.Replication Controller
RC是kubernetes的核心概念之一,它定义了一个期望的场景。
即声明某种pod的数量在任意时刻都符合某个预期值。RC的定义至少包括以下几个部分:
Pod的数量,即replicas
用于删选pod的Label Selector,即selector
用于生成pod的模板,即template
下面是一个定义RC的简单示例:
apiVersion: v1 kind: ReplicationController #资源对象的类型 metadata: #rc的元数据 name: mysql spec: #rc的相关属性定义 replicas: 1 #pod数量 selector: app: mysql #给pod定义标签 template: #根据此模板创建pod metadata: labels: app: mysql #pod的标签,对应于selector的标签 spec: #pod的相关属性 containers: #容器的相关属性定义,属性值是一个数组 - name: mysql #容器名 image: mysql #使用的镜像名称 ports: - containerPort: 3306 #端口号,可能会有多个端口,使用数组作为值 env: - name: MYSQL_ROOT_PASSWORD value: "123456"
当我们定义了一个RC并提交到系统之后,Master节点的Controller-Manager就会得到通知,并定期巡检目标Pod的数量,
如果目标数量不等于期望值,那么就会执行相应的增加或删除操作,这就实现了应用集群的高可用性。
通过kubectl scale和kubectl apply可以修改pod的副本数量。
当我们需要更新一个服务的时候,可以使用滚动升级,就是当前容器逐个停止,新的服务容器逐个启动,不改变pod的副本数量。
在1.2版本之后,Replica Set和Deployment这个两个资源对象逐渐取代了RC。
Replica Set是RC的升级版,基本上可以替代RC,只是功能更加强大,
比如支持基于集合的Label Selector,RC只支持等式的Selector。
Replica Set主要被Deployment这个更高层次的对象所使用,从而形成一整套Pod创建、删除、更新的编排机制。
当我们使用deployment的时候,无需关心和维护Replica Set。
Replica Set和deployment实现了自动扩容和伸缩的功能。
4.Deployment
Deployment是kubernetes1.2版本中为了更好的解决Pod的编排问题而引入的概念。
Deployment内部是使用Replica Set来实现的,之前说过Replica Set是对RC的一次升级,唯一的区别是支持基于集合的Select Label。
Deployment与RC的相似度非常高,最大的升级是随时知道当前Pod部署的进度。
一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一过程需要一定的时间。
Deployment和Replica Set以及RC的定义非常类似。下面是一个简单的示例:
apiVersion: extensions/v1beta1 kind: Deployment #资源对象的类型 metadata: name: frontend #这个对象的名称,通过kubectl get deployment可以获取 spec: #deployment与RC一样,至少由replicas、selector、template组成 replicas: 1 selector: #deployment使用Replica Set,因此支持基于集合的Selector Label matchLabels: tier: frontend matchExpressions: - {key: tier, operator: In, values: [frontend]} template: metadata: labels: app: app-demo tier: frontend #这个需要与Selector对应 spec: containers: #容器的一些属性定义 - name: tomcat-demo image: tomcat imagePullPolicy: IfNotPresent ports: - containerPort: 8080
创建deployment:
[root@VM_16_6_centos chapter01]# kubectl create -f tomcat-deployment.yaml deployment "frontend" created
查看deployment:
[root@VM_16_6_centos chapter01]# kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 1 1 1 1 5s
关于名词解释:
DESIRED:Pod的期望数量
CURRENT:Pod的当前数量,Pod的数量会逐步等于DESIRED的值。
UP-TO-DATE:最新版本的Pod数量,在版本升级中,已经进行升级了的Pod数量
AVAILABLE:当前集群中可用的Pod的数量。
如果你感觉Deployment与RC并没有什么区别,那么可以查看以下RC。
[root@VM_16_6_centos chapter01]# kubectl get rc NAME DESIRED CURRENT READY AGE mysql 3 3 3 9h
虽然告诉你了期望值、当前值和已经可用的值以及运行时间,但是并没有解决服务在滚动升级过程的推进过程。
这就是deployment与RC最大的不同之处。
deployment内部是调用Replica Set来实现的,因此也会自动创建:
[root@VM_16_6_centos chapter01]# kubectl get rs NAME DESIRED CURRENT READY AGE frontend-141477217
查看已经创建的Pod:
[root@VM_16_6_centos chapter01]# kubectl get pods|grep frontend frontend-141477217-94ltm 1/1 Running 0 6m
创建了一个Pod。
查看容器:
[root@VM_16_6_centos chapter01]# docker ps|grep front 0f2f83d4b8d9 tomcat "catalina.sh run" 9 minutes ago Up 9 minutes k8s_tomcat-demo.b9a602ba_frontend-141477217-94ltm_default_2d3f9e82-9cc0-11e9-a763-52540095a842_eb119b9f 160304a783ba registry.access.redhat.com/rhel7/pod-infrastructure:latest "/pod" 9 minutes ago Up 9 minutes k8s_POD.24f70ba9_frontend-141477217-94ltm_default_2d3f9e82-9cc0-11e9-a763-52540095a842_506a66db
每个pod都会创建一个Pause容器用于共享网络和存储卷。
5.pod
Pod是Kubernetes的核心概念之一,Pod是kubernetes的最小管理单元。
每个Pod在创建的时候默认都会创建一个pause容器,这个容器用来共享网络和文件。
除了pause容器之外,每个Pod还包含一个或多个紧密相关的业务容器。
需要注意的是Pod存储的并不是一个或多个相同的容器,而是业务紧密相关的一组容器。
这样Pause容器就非常好的解决了一个pod内多个容器之间的通信和文件共享的问题。
如果是Pod存储的是相同类型的容器,这些容器之间是不需要进行通信,更多的是和外部的通信。
Kubernetes会为每一个Pod分配一个IP。Pod中所有的容器共享这一个IP。
Kubernetes在设计之初,就要求不同Node上的容器能够直接进行通信,kubernetes的网络方案有非常多种,之后会有说明。
Kubernetes中Pod一般分为普通Pod和静态Pod(static pod).
静态Pod使用来为了维护集群而创建,比如你修改了集群的配置文件,静态pod会进行定期检查,这样就会自动更新配置。
静态Pod并不会存储在etcd中,而是存放在某个Node的一个文件中,并且只在此Node上启动运行。
普通Node一旦创建之后就会被存储到etcd中,然后被scheduler调度到某个具体的Node上进行绑定,
然后该Node上的kubelet组件就会进行容器的创建工作。
简单示例:
apiVersion: v1 kind: Pod metadata: name: myweb labels: name: myweb spec: containers: - name: myweb1 image: kubeguide/tomcat-app:v1 ports: - containerPort: 8010 env: - name: MYSQL_SERVICE_HOST value: 'mysql' - name: MYSQL_SERVICE_PORT value: '3307'
创建pod:
[root@VM_16_6_centos chapter01]# kubectl create -f myweb-pod.yaml pod "myweb" created
查看Pod:
[root@VM_16_6_centos chapter01]# kubectl get pods|grep myweb myweb 1/1 Running 0 4m
通过查看Pod的详细信息,可以了解该Pod的所有Event,包括其整个生命周期中所进行的操作。
从下面的Event中我们可以看出,经历了pull、create和start这个三个过程。
当然还可以查看到一些容器的详细信息。
[root@VM_16_6_centos chapter01]# kubectl describe pods/myweb Name: myweb Namespace: default Node: 127.0.0.1/127.0.0.1 Start Time: Tue, 02 Jul 2019 20:43:33 +0800 Labels: name=myweb Status: Running IP: 172.17.0.6 Controllers: <none> Containers: myweb1: Container ID: docker://a482b4d1c1a9fd7a5cd643fc5ce3f20c5797966ff58a5e7b136d8b599cc75c55 Image: kubeguide/tomcat-app:v1 Image ID: docker-pullable://docker.io/kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc526b96ac69ac4 Port: 8010/TCP State: Running Started: Tue, 02 Jul 2019 20:43:33 +0800 Ready: True Restart Count: 0 Volume Mounts: <none> Environment Variables: MYSQL_SERVICE_HOST: mysql MYSQL_SERVICE_PORT: 3307 Conditions: Type Status Initialized True Ready True PodScheduled True No volumes. QoS Class: BestEffort Tolerations: <none> Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 48s 48s 1 {default-scheduler } Normal Scheduled Successfully assigned myweb to 127.0.0.1 48s 48s 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy. 48s 48s 1 {kubelet 127.0.0.1} spec.containers{myweb1} Normal Pulled Container image "kubeguide/tomcat-app:v1" already present on machine 48s 48s 1 {kubelet 127.0.0.1} spec.containers{myweb1} Normal Created Created container with docker id a482b4d1c1a9; Security:[seccomp=unconfined] 48s 48s 1 {kubelet 127.0.0.1} spec.containers{myweb1} Normal Started Started container with docker id a482b4d1c1a9
Kubernetes中的所有资源对象都可以采用yaml或者JSON格式的文件来进行定义和描述。
当然在Pod里面还可以进行资源配额的限制。
6.Label
Label同样是Kubernetes中非常核心的一个概念。Label是以键值对的形式进行定义的。
Label可以附加到各种资源对象上,比如Node、Service、Pod、RC等。
一个资源对象可以绑定任意多个Label,一个Label也可以绑定到任意个资源对象上去。
Label通常在资源对象定义时确定,也可以在资源对象创建之后动态添加或者删除。
我们可以给指定的资源对象绑定一个或者多个不同的Label来实现多维度的资源分组。
Label对象之所非常重要,这是因为Label是不同资源对象之间进行关联、查询和筛选的方式。
之前说过RC只支持基于等式的筛选,而RS支持基于集合形式的筛选。
RC的定义方式:
replicas: 1 selector: app: mysql template: metadata: labels: app: mysql
RS的定义方式:
selector: mathLabels: app: myweb matchExpressions: - {key: tier, operator: In, values: [frontend]} - {key: environment, operator: NotIn, Value: [dev]}
Label Selector在Kubernetes中重要使用场景有以下几处:
kube-controller进程通过资源对象RC上定义的Selector Label来查找pod,然后与replicas的数量对比,以此维护期望值。
kube-proxy通过Service的Selector Label来选择对应的Pod,自动建立起每个Service到对应Pod的请求转发路由表,实现自动负载均衡的功能。
7.Horizontal Pod Autoscaler
通过kubectl scale或者kubectl apply或者kubectl edit就可以实现来对pod扩容和缩容。
但是这种手动操作的方式显然并不是很多人所期待的。
更多人希望系统能够根据当前负载的变化情况自动触发水平扩展或缩容的行为,希望这个过程完全是自动化、智能化的。
Kubernetes在1.1版本中首次发布了Horizontal Pod Autoscaling(Pod横向自动扩容,简称HPA)。
在1.2版本中HPA被升级为稳定版本(apiVersion:autoscaling/v1),但是依然保留了旧版本(apiVersion:extensions/v1betal)。
从1.6版本开始,对根据应用自定义指标进行自动扩容和缩容的功能进行增强,API版本为autoscaling/v2alphal。
每一个yaml或者json文件都会指定apiVersion,其实这是指定调用那个APIService的接口来处理这个任务。
因为可能对于同一种该资源类型可能会有不同的处理方式。
HPA与之前的RC、Deployment一样也属于一种kubernetes资源对象。
通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性的调整目标pod的副本数,这是HPA的实现的原理。
HPA可以有以下两种方式来衡量Pod负载的度量指标:
CPUUtilizationPercentage
应用程序自定义的度量指标,比如服务在每秒内的相关请求数(TPS或QPS)。
CPUUtilizationPercentage是一个算术平均值,即目标Pod所有副本自身的CPU利用率的平均值。
一个Pod自身的CPU利用率是该Pod当前CPU的使用量除以它的Pod Request的值。
比如某个时刻CPUUtilizationPercentage的值超过了预设值,则意味着当前的Pod的数量不足以支撑接下来的请求,此时则需要进行动态扩容。
当请求高峰时段过去后,Pod的CPU利用率又会降下来,此时对应的Pod副本数又会自动降低到一个合理的水平。
CPUUtilizationPercentage计算过程中使用到的Pod的CPU使用通常是1min内的平均值。目前通过查询Heapster扩展组件来得到这个值。
如果你没有定义Pod的Request的值,则无法使用CPUUtilizationPercentage来实现横向自动扩容的能力。
下面是一个HPA的简单示例:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: php-apache namespace: default spec: maxReplicas: 10 #扩容或缩容Pod数量的约束 minReplicas: 1 scaleTargetRef: kind: Deployment name: php-apache targetCPUUtilizationPercentage: 90 #当CPU的占用率超过90%的时候自动触发扩容行为
8.StatefulSet
在kubernetes中,Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务,
因为Pod是非持久性的,随时可能被删除,是无法长期存储有状态服务的数据的。
在现实业务中,最常用到的有状态的服务是数据库类的服务,比如MySQL集群、MongoDB集群。
这些应用集群有一些共同点:
每个节点都有固定的ID,通过这个ID,集群中的成员可以相互发现并通信。
集群的规模比较固定,集群的规模不能随意变动。
集群的每个节点都有状态,通常会将持久化的数据永久存储。
如果某个节点无法正常访问,那么集群的功能将会受到一定的影响。
如果你使用RC/Deployment来控制Pod,那么Pod的名字是随机产生的,IP也不是固定的,
这样你根本无法给每一个Pod分配一个固定的IP。
Kubernetes为了兼顾这种有状态的服务,在1.4版本中引入了PetSet这个新的资源对象。
在1.5版本中将其变更为StatefulSet,StatefulSet从本质上来说是RC/Deployment的一个变种,只是改变了一些特性:
StatefulSet控制的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其它成员。
StatefulSet控制的Pod的启动顺序是受控制的。
StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC来实现,删除pod时默认不会删除与StatefulSet相关的存储卷(保证数据安全)
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,
即在每个StatefulSet的定义中要声明它属于哪一个Headless Service。
Headless Service与普通Service的区别在于它没有Cluster IP,
如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。
StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例创建了一个DNS域名。
9.Service
(1)概论
Service也是Kubernetes里面最核心的概念之一,Kubernetes里的每个Service其实就是微服务架构中的一个微服务。
之前我们所说的RC、Deployment、Pod其实都是为Service来服务的。
下面是对Pod、RC和Service的关系说明图:
Service定义了一个服务的访问入口地址,前端Pod通过这个入口访问Service下面的一组Pod集群实例,
Service与Pod集群之间通过Label Selector来实现关联。
RC用来保证Service的服务能力和服务质量永远保持在预期的标准。
下面是Kubernetes所提供的微服务网格架构:
通过分析、识别并建模系统中的所有服务为微服务,最终我们的系统由多个提供不同业务能力而又彼此独立的微服务单元所组成。
服务之间通过TCP/IP进行通信,从而形成了我们强大而又灵活的服务网格,拥有了强大的分布式能力、弹性扩展能力、容错能力。
每个Pod都会被分配一个单独的IP,与container的Port来组成Endpoint来被客户端访问。
多个Pod组成一个集群,那么客户端是如何来进行访问的了?一般是通过负载均衡来进行转发。
在Kubernetes中,每个Node上都有一个kube-proxy组件来实现自动的负载均衡,
kube-proxy负责将对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡和会话保持机制。
在Kubernetes中,Service不是共用一个负载均衡器的IP地址,而是每个Service分配一个全局唯一的虚拟IP地址。
这个IP被称为Cluster IP,这样每个服务就变成了具备唯一IP地址的“通信节点”,服务的调用就变成了最基础的TCP网络通信问题。
Pod的EndPoint会随着Pod的销毁和重建而发生改变,因为Pod的IP地址会在这个过程中发生改变。
而Service一旦被创建之后,就会被分配一个唯一的Cluster IP,在整个Service的生命周期内,Cluster IP都不会发生改变。
许多时候,我们可以直接将Service暴漏出去,这样访问Service的名称就可以访问到对应的服务。
下面是一个Service创建的简单实例:
apiVersion: v1 kind: Service #定义资源对象的类型 metadata: name: tomcat-service #该service的名称 spec: ports: - port: 8091 #对外的服务端口号 selector: tier: frontend #通过这个Lable筛选Pod
创建Service:
[root@VM_16_6_centos chapter01]# kubectl create -f tomcat-service.yaml service "tomcat-service" created
端口已经被暴漏出来了,而且还分配了一个ClusterIP:
[root@VM_16_6_centos chapter01]# kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes 10.254.0.1 <none> 443/TCP 6d mysql 10.254.40.50 <none> 3306/TCP 5d myweb 10.254.167.209 <nodes> 8080:30001/TCP 5d tomcat-service 10.254.165.154 <none> 8091/TCP 12s
查看该Service的详细信息:
[root@VM_16_6_centos chapter01]# kubectl get svc tomcat-service -o yaml apiVersion: v1 kind: Service metadata: creationTimestamp: 2019-07-03T11:41:44Z name: tomcat-service namespace: default resourceVersion: "633043" selfLink: /api/v1/namespaces/default/services/tomcat-service uid: 887daf77-9d87-11e9-895d-52540095a842 spec: clusterIP: 10.254.165.154 ports: - port: 8091 protocol: TCP targetPort: 8091 selector: tier: frontend sessionAffinity: None type: ClusterIP status: loadBalancer: {}
targetport属性用来确定提供该服务的容器所暴露的端口号,而port则定义了Service的虚拟端口号,
如果没有定义targetPort,则默认targetPort与Port相同。
(2)Kuberbetes的服务发现机制
在Kubernetes中,每个Service都有一个唯一的Cluster IP和唯一的名字,名字是用户自己定义的。
在实际中,我们不可能通过IP来定位Service对象,更多的是通过name,那么Service 的Cluster IP与name是如何关联和定义的了?
早期的时候,kubernetes通在每个Pod启动的时候,自动注入环境变量,通过访问变量就可以找到对应的Cluster IP。
后来Kubernetes通过Add-On增值包的方式引入DNS系统,将服务名作为DNS的域名,这样就可以通过服务名来建立通信了。
(3)外部系统访问Service的问题
在Kubernetes中,最常见到的IP有三种:
Node IP:Node节点的IP,这是一个真实存在的物理IP,通过这个IP就可以直接访问到Node这个服务器。
Pod IP:Pod的IP地址,每个Pod的IP其实是Pause容器的IP,这个一个容器IP,
这是容器在创建的过程中,docker0虚拟网桥随机分配的一个IP。这个IP是可以被访问的,可以ping通,可以看作是一个虚拟IP。
Cluster IP:Service的IP,更像一个“虚假”的IP,虚拟IP至少还能够使用,而Cluster IP不能直接使用。
Cluster IP仅仅作用于Service这个对象,由Kubernetes管理和分配。
Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备通信的能力,
并且Kubernetes属于集群内的封闭空间,集群之外的节点如果要访问这个通信端口,则需要进行额外的操作。
Cluster IP属于集群内部的地址,无法在集群外部直接使用这个地址。
在微服务中,外部或内部应用会直接访问Service的服务,那么客户该如何来进行访问了?
采用NodePort是解决上述问题的最直接、最有效、最常用的做法。
NodePort的实现方式是在Kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口。
我们可以通过一个完整的例子来展示:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: frontend spec: replicas: 1 selector: matchLabels: tier: frontend matchExpressions: - {key: tier, operator: In, values: [frontend]} template: metadata: labels: app: app-demo tier: frontend spec: containers: - name: tomcat-demo image: tomcat imagePullPolicy: IfNotPresent ports: - containerPort: 8080
我们创建了一个叫做frontend的deployment:
[root@VM_16_6_centos chapter01]# kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 1 1 1 1 18m
创建了一个pod,这里是随机命名:
[root@VM_16_6_centos chapter01]# kubectl get pod NAME READY STATUS RESTARTS AGE frontend-141477217-pm1m9 1/1 Running 0 19m
然后启动了一个叫做tomcat-demo的容器:
[root@VM_16_6_centos chapter01]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bbce24a9ea39 tomcat "catalina.sh run" 19 minutes ago Up 19 minutes k8s_tomcat-demo.b9a602ba_frontend-141477217-pm1m9_default_1308260d-9d91-11e9-895d-52540095a842_b0445ecc e2b768eea56c registry.access.redhat.com/rhel7/pod-infrastructure:latest "/pod" 19 minutes ago Up 19 minutes k8s_POD.24f70ba9_frontend-141477217-pm1m9_default_1308260d-9d91-11e9-895d-52540095a842_8687e42c
frontend这个deployment负责保证pod保持在期望的状态。
apiVersion: v1 kind: Service metadata: name: tomcat-service spec: type: NodePort ports: - port: 8080 nodePort: 31002 selector: tier: frontend
现在我们又创建了一个service对象:
[root@VM_16_6_centos chapter01]# kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes 10.254.0.1 <none> 443/TCP 6d tomcat-service 10.254.38.147 <nodes> 8080:31002/TCP 20m
Nodeport为tomcat-service启动了31002这个外部的监听端口,自然而然的将其访问引入到了容器内部。
之后我们就可以访问这个Tomcat服务了。
10.Volume
Kubernetes中的Volume与容器中的Volume在概念和用途上面比较类似,但是还是有差异之处。
Kubernetes中的Volume被定义在Pod上,然后被pod中的一个或者多个容器挂载到相应的目录下面。
Volume的生命周期与Pod的生命周期相同,与容器的生命周期无关。
下面是volume的简单实例:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: deployment-example spec: volumes: #在pod中定义了一个volumes - name: detavol #name和emptyDir都是定义的该volume的属性 emptyDir: {} replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.10 volumeMounts: #将volumes挂载到容器中 - mountPath: /mydata-data #挂载到那个目录 name: detavol #挂载哪一个volume
Volume的使用比较简单,只需要在Pod中定义,然后在容器中进行引用就可以了。
Pod中的多个容器通过共享一个volume,就是实现容器间文件共享的问题,
这样就可以将容器中数据保存到一个目录,最后实现永久性存储。
不仅如此,通过Volume还可以实现容器配置文件集中化配置和管理,这个功能通过ConfigMap来进行实现。
Volume常见类型:
(1)emptyDir
emptyDir类型的volume是Pod分配到Node时创建的。
它的初始内容空,并且无须指定宿主机上对应的文件,当Pod从Node上移除时,emptyDir中的数据会永久被删除。
(2)hostPath
hostPath为在Pod上挂载宿主机上的文件文件或者目录,常用于日志文件存储或者直接访问docker的文件系统
(3)gcePersistentDisk
使用谷歌公有云提供的永久磁盘(PD)存放Volume数据,PD上的内容会被永久保存,当Pod被删除时,PD只是被卸载,但不会被删除。
(4)awsElasticBlockStore
与.gcePersistentDisk类似使用亚马逊提供的永久存储保存数据
volumes: - name: test-volume awsElasticBlockStore: volumeID: aws://<availability-zone>/<volume-id> fsType: ext4
(5)NFS
使用NFS网络文件系统提供的共享目录存储数据,需要在系统中部署一个NFS Server。
volumes: - name: nfs nfs: #改为你的NFS服务器地址,该volume绑定在某一个Pod上面,作用对象是该Pod上的一个或多个容器 server: nfs-server.localhost path: “/”
(6)其它类型的Volume
还有iscsi、flocker、glusterfs、rbd、gitRepo、secret
11.Persistent Volume
Volume是定义在Pod上的,属于“计算资源”的一部分。
实际上,“网络存储”是相对于“计算资源”而存在的一种实体资源。
PV可以理解成Kubernetes集群中的某个网络存储中对应的一块存储,它与Volume类似,但又有所区别:
PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
PV并不是定义在Pod上,而是独立于Pod之外定义
PV的作用对象是Node,当然就可以作用Pod。
下面是一个NFS类型PV的一个yaml定义文件,声明了需要5Gi的存储空间:
apiVersion: v1 kind: PersistentVolume metadata: name: pv0003 #PV的名称属性 spec: capacity: #PV的需求 storage: 5Gi accessModes: #模式 - ReadWriteOnce nfs: #nfs的路径和地址 path: /somepath server: 172.17.0.2
PV常见的accessModes类型:
ReadWriteOnce:读写权限,只能被单个Node挂载
ReadOnlyMany:只读权限,允许被多个Node挂载
ReadWriteMany:读写权限,允许被多个Node挂载
如果某个Pod想申请某种类型的PV,则首先需要定义一个PersistentVolumeClaim(PVC)对象:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi
然后在Pod的volumes中引用上述PVC即可:
volumes: - name: mypd persistenVolumeClaim: claimName: myclaim
PVC是针对于Pod来准备的,因为PV能够被很多的对象使用,只有pod在使用的时候才需要先定义PVC。
volume是在Pod内应用,而PV是外部的网络存储资源。
PV有以下几种状态:
Available:空闲状态
Bound:已经绑定到某个PVC上
Released:对应的PVC已经删除,但资源还没有被集群回收
File:PV自动回收失败
12.Namespace
Namespace是Kubernetes系统中非常重要的核心概念,
Namespace在很多情况下用于实现多用户的资源隔离。
Namespace通过将集群内部的资源对象“分配”到不同namespace中,形成逻辑上分组的不同项目、小组或用户组,
便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
[root@VM_16_6_centos ~]# kubectl get namespaces
NAME STATUS AGE
default Active 7d
kube-system Active 7d
集群启动之后,会创建一个名为“default”的Namespace,如果没有指定Namespace,那么创建的资源自将自动归属到default下面。
13.Annotation
Annotation与Label类似,也使用key/value键值对的形式进行定义。
不同的是Label具有严格的命名规范,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。
而Annotation则是用户任意定义的“附加”信息,以便于外部工具进行查找,
很多时候,Kubernetes的模块自身会通过Annotation的方式标记资源对象的一些特殊信息。