k8s学习笔记-23-资源指标和集群监控

[TOC] 1、资源指标和资源监控 一个集群系统管理离不开监控,同样的Kubernetes也需要根据数据指标来采集相关数据,从而完成对集群系统的监控状况进行监测。这些指标总体上分为两个组成:监控集群本身和监控Pod对象,通常一个集群的衡量性指标包括以下几个部分: 节点资源状态:主要包括网络带宽、磁盘空间、CPU和内存使用率 节点的数量:即时性了解集群的可用节点数量可以为用户计算服务器使用的费用支出提供参考。 运行的Pod对象:正在运行的Pod对象数量可以评估可用节点数量是否足够,以及节点故障时是否能平衡负载。 另一个方面,对Pod资源对象的监控需求大概有以下三类: Kubernetes指标:监测特定应用程序相关的Pod对象的部署过程、副本数量、状态信息、健康状态、网络等等。 容器指标:容器的资源需求、资源限制、CPU、内存、磁盘空间、网络带宽的实际占用情况。 应用程序指标:应用程序自身的内建指标,和业务规则相关 2、Weave Scope监控集群 Weave Scope 是 Docker 和 Kubernetes 可视化监控工具。Scope 提供了至上而下的集群基础设施和应用的完整视图,用户可以轻松对分布式的容器化应用进行实时监控和问题诊断。 对于复杂的应用编排和依赖关系,scope可以使用清晰的图标一览应用状态和拓扑关系。 (1)Weave Scope部署 [root@k8s-master mainfests]# kubectl apply -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')" namespace/weave created #创建名称空间weave,也可以在创建时指定名称空间 serviceaccount/weave-scope created #创建serviceaccount clusterrole.rbac.authorization.k8s.io/weave-scope created clusterrolebinding.rbac.authorization.k8s.io/weave-scope created deployment.apps/weave-scope-app created #创建deployment service/weave-scope-app created #创建service daemonset.extensions/weave-scope-agent created #创建deamonset [root@k8s-master mainfests]# kubectl get ns NAME STATUS AGE default Active 68d ingress-nginx Active 28d kube-public Active 68d kube-system Active 68d weave Active 1m [root@k8s-master mainfests]# kubectl get deployment -n weave NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE weave-scope-app 1 1 1 1 1m [root@k8s-master mainfests]# kubectl get svc -n weave NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE weave-scope-app ClusterIP 10.97.229.215 <none> 80/TCP 33s [root@k8s-master mainfests]# kubectl get pod -n weave NAME READY STATUS RESTARTS AGE weave-scope-agent-5876w 1/1 Running 0 1m weave-scope-agent-d6jgt 1/1 Running 0 1m weave-scope-agent-t9p5g 1/1 Running 0 1m weave-scope-app-578556559-nfxrf 1/1 Running 0 1m DaemonSet weave-scope-agent,集群每个节点上都会运行的 scope agent 程序,负责收集数据。 Deployment weave-scope-app,scope 应用,从 agent 获取数据,通过 Web UI 展示并与用户交互。 Service weave-scope-app,默认是 ClusterIP 类型,为了方便已通过 kubectl edit 修改为 NodePort。 [root@k8s-master mainfests]# kubectl edit svc/weave-scope-app -n weave 将service的type改为NodePort service/weave-scope-app edited [root@k8s-master mainfests]# kubectl get svc -n weave NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE weave-scope-app NodePort 10.97.229.215 <none> 80:32313/TCP 11m (2)使用 Scope 浏览器访问 http://192.168.56.11:32313/,Scope 默认显示当前所有的 Controller(Deployment、DaemonSet 等)。 ...

September 18, 2018

k8s学习笔记-20-K8S组件运行原理详解总结

[TOC] 一、看图说K8S 先从一张大图来观看一下K8S是如何运作的,再具体去细化K8S的概念、组件以及网络模型。 从上图,我们可以看到K8S组件和逻辑及其复杂,但是这并不可怕,我们从宏观上先了解K8S是怎么用的,再进行庖丁解牛。从上图我们可以看出: Kubernetes集群主要由Master和Node两类节点组成 Master的组件包括:apiserver、controller-manager、scheduler和etcd等几个组件,其中apiserver是整个集群的网关。 Node主要由kubelet、kube-proxy、docker引擎等组件组成。kubelet是K8S集群的工作与节点上的代理组件。 一个完整的K8S集群,还包括CoreDNS、Prometheus(或HeapSter)、Dashboard、Ingress Controller等几个附加组件。其中cAdivsor组件作用于各个节点(master和node节点)之上,用于收集及收集容器及节点的CPU、内存以及磁盘资源的利用率指标数据,这些统计数据由Heapster聚合后,可以通过apiserver访问。 要了解K8S的所有组件,没去走一遍,永远不知道它是怎么跑起来的,那么下面就带着几个新手疑问来看K8S 1、K8S是如何对容器编排? 在K8S集群中,容器并非最小的单位,K8S集群中最小的调度单位是Pod,容器则被封装在Pod之中。由此可知,一个容器或多个容器可以同属于在一个Pod之中。 2、Pod是怎么创建出来的? Pod并不是无缘无故跑出来的,它是一个抽象的l逻辑概念,那么Pod是如何创建的呢?Pod是由Pod控制器进行管理控制,其代表性的Pod控制器有Deployment、StatefulSet等。这里我们先有这样的一个概念,后面再详细解刨。 3、Pod资源组成的应用如何提供外部访问的? Pod组成的应用是通过Service这类抽象资源提供内部和外部访问的,但是service的外部访问需要端口的映射,带来的是端口映射的麻烦和操作的繁琐。为此还有一种提供外部访问的资源叫做Ingress。 4、Service又是怎么关联到Pod呢? 在上面说的Pod是由Pod控制器进行管理控制,对Pod资源对象的期望状态进行自动管理。而在Pod控制器是通过一个YAML的文件进行定义Pod资源对象的。在该文件中,还会对Pod资源对象进行打标签,用于Pod的辨识,而Servcie就是通过标签选择器,关联至同一标签类型的Pod资源对象。这样就实现了从service–>pod–>container的一个过程。 5、Pod的怎么创建逻辑流程是怎样的? (1)客户端提交创建请求,可以通过API Server的Restful API,也可以使用kubectl命令行工具。支持的数据类型包括JSON和YAML。 (2)API Server处理用户请求,存储Pod数据到etcd。 (3)调度器通过API Server查看未绑定的Pod。尝试为Pod分配主机。 (4)过滤主机 (调度预选):调度器用一组规则过滤掉不符合要求的主机。比如Pod指定了所需要的资源量,那么可用资源比Pod需要的资源量少的主机会被过滤掉。 (5)主机打分(调度优选):对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把容一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等。 (6)选择主机:选择打分最高的主机,进行binding操作,结果存储到etcd中。 (7)kubelet根据调度结果执行Pod创建操作: 绑定成功后,scheduler会调用APIServer的API在etcd中创建一个boundpod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步boundpod信息,一旦发现应该在该工作节点上运行的boundpod对象没有更新,则调用Docker API创建并启动pod内的容器。 从上面的几个疑问,大致了解了K8S怎么工作的,那么现在再从三个面去了解Kubernetes,分别是Kubernetes概念和术语、集群组件、网络模型。 二、K8S的概念和术语 Kubernetes是利用共享网络将多个物理机或者虚拟机组成一个集群,在各个服务器之间进行通信,该集群是配置Kubernetes的所有租金啊啊、功能和负载的物理平台。 一个Kubernetes集群由master和node组成。如下图: Master:是集群的网关和中枢枢纽,主要作用:暴露API接口,跟踪其他服务器的健康状态、以最优方式调度负载,以及编排其他组件之间的通信。单个的Master节点可以完成所有的功能,但是考虑单点故障的痛点,生产环境中通常要部署多个Master节点,组成Cluster。 Node:是Kubernetes的工作节点,负责接收来自Master的工作指令,并根据指令相应地创建和销毁Pod对象,以及调整网络规则进行合理路由和流量转发。生产环境中,Node节点可以有N个。 Kubernetes从宏观上看分为2个角色:Master和Node,但是在Master节点和Node节点上都存在着多个组件来支持内部的业务逻辑,其包括:运行应用、应用编排、服务暴露、应用恢复等等,在Kubernetes中这些概念被抽象为Pod、Service、Controller等资源类型。先来了解一下这些常用概念和术语: (1)Pod 从上图,我们可以看到K8S并不直接地运行容器,而是被一个抽象的资源对象–Pod所封装,它是K8S最小的调度单位。这里要注意的是,Pod可以封装一个活多个容器!同一个Pod中共享网络名称空间和存储资源,而容器之间可以通过本地回环接口:lo 直接通信,但是彼此之间又在Mount、User和Pid等名称空间上保持了隔离。 (2)资源标签(Label) 标签(Label)是将资源进行分类的标识符,就好像超市的商品分类一般。资源标签具体化的就是一个键值型(key/values)数据,相信了解redis的友友应该知道什么是键值数据。使用标签是为了对指定对象进行辨识,比如Pod对象。标签可以在对象创建时进行附加,也可以创建后进行添加或修改。要知道的是一个对象可以有多个标签,一个标签页可以附加到多个对象。如图: (3)标签选择器(Selector) 有标签,当然就有标签选择器,它是根据Label进行过滤符合条件的资源对象的一种 机制。比如将含有标签role: backend的所有Pod对象挑选出来归并为一组。通常在使用过程中,会通过标签对资源对象进行分类,然后再通过标签选择器进行筛选,最常见的应用就是讲一组这样的Pod资源对象创建为某个Service的端点。如图: (4)Pod控制器(Controller) 虽然Pod是K8S的最小调度单位,但是K8S并不会直接地部署和管理Pod对象,而是要借助于另外一个抽象资源–Controller进行管理。其实一种管理Pod生命周期的资源抽象,并且它是一类对象,并非单个的资源对象,其中包括:ReplicationController、ReplicaSet、Deployment、StatefulSet、Job等。 以Deployment为例,它负责确保定义的Pod对象的副本数量符合预期的设置,这样用户只需要声明应用的期望状态,控制器就会自动地对其进行管理。如图: (5)服务资源(Service) Service是建立在一组Pod对象之上的资源对象,在前面提过,它是通过标签选择器选择一组Pod对象,并为这组Pod对象定义一个统一的固定访问入口(通常是一个IP地址),如果K8S存在DNS附件(如coredns)它就会在Service创建时为它自动配置一个DNS名称,用于客户端进行服务发现。 通常我们直接请求Service IP,该请求就会被负载均衡到后端的端点,即各个Pod对象,从这点上,是不是有点像负载均衡器呢,因此Service本质上是一个4层的代理服务,另外Service还可以将集群外部流量引入至集群,这就需要节点对Service的端口进行映射了。 (6)存储卷(Volume) 在使用容器时,我们知道,当数据存放于容器之中,容器销毁后,数据也会随之丢失。这就是需要一个外部存储,以保证数据的持久化存储。而存储卷就是这样的一个东西。 存储卷(Volume)是独立于容器文件系统之外的存储空间,常用于扩展容器的存储空间并为其提供持久存储能力。存储卷在K8S中的分类为:临时卷、本地卷和网络卷。临时卷和本地卷都位于Node本地,一旦Pod被调度至其他Node节点,此类型的存储卷将无法被访问,因为临时卷和本地卷通常用于数据缓存,持久化的数据通常放置于持久卷(persistent volume)之中。 (7)Name和Namespace 名称(Name)是K8S集群中资源对象的标识符,通常作用于名称空间(Namespace),因此名称空间是名称的额外的限定机制。在同一个名称空间中,同一类型资源对象的名称必须具有唯一性。 ...

September 15, 2018

k8s学习笔记-22-Pod资源调度

[TOC] API Server在接受客户端提交Pod对象创建请求后,然后是通过调度器(kube-schedule)从集群中选择一个可用的最佳节点来创建并运行Pod。而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点。如图: 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择 当我们有需求要将某些Pod资源运行在特定的节点上时,我们可以通过组合节点标签,以及Pod标签或标签选择器来匹配特定的预选策略并完成调度,如MatchInterPodAfinity、MatchNodeSelector、PodToleratesNodeTaints等预选策略,这些策略常用于为用户提供自定义Pod亲和性或反亲和性、节点亲和性以及基于污点及容忍度的调度机制。 1、常用的预选策略 预选策略实际上就是节点过滤器,例如节点标签必须能够匹配到Pod资源的标签选择器(MatchNodeSelector实现的规则),以及Pod容器的资源请求量不能大于节点上剩余的可分配资源(PodFitsResource规则)等等。执行预选操作,调度器会逐一根据规则进行筛选,如果预选没能选定一个合适的节点,此时Pod会一直处于Pending状态,直到有一个可用节点完成调度。其常用的预选策略如下: CheckNodeCondition:检查是否可以在节点报告磁盘、网络不可用或未准备好的情况下将Pod对象调度其上。 HostName:如果Pod对象拥有spec.hostname属性,则检查节点名称字符串是否和该属性值匹配。 PodFitsHostPorts:如果Pod对象定义了ports.hostPort属性,则检查Pod指定的端口是否已经被节点上的其他容器或服务占用。 MatchNodeSelector:如果Pod对象定义了spec.nodeSelector属性,则检查节点标签是否和该属性匹配。 NoDiskConflict:检查Pod对象请求的存储卷在该节点上可用。 PodFitsResources:检查节点上的资源(CPU、内存)可用性是否满足Pod对象的运行需求。 PodToleratesNodeTaints:如果Pod对象中定义了spec.tolerations属性,则需要检查该属性值是否可以接纳节点定义的污点(taints)。 PodToleratesNodeNoExecuteTaints:如果Pod对象定义了spec.tolerations属性,检查该属性是否接纳节点的NoExecute类型的污点。 CheckNodeLabelPresence:仅检查节点上指定的所有标签的存在性,要检查的标签以及其可否存在取决于用户的定义。 CheckServiceAffinity:根据当前Pod对象所属的Service已有其他Pod对象所运行的节点调度,目前是将相同的Service的Pod对象放在同一个或同一类节点上。 MaxEBSVolumeCount:检查节点上是否已挂载EBS存储卷数量是否超过了设置的最大值,默认值:39 MaxGCEPDVolumeCount:检查节点上已挂载的GCE PD存储卷是否超过了设置的最大值,默认值:16 MaxAzureDiskVolumeCount:检查节点上已挂载的Azure Disk存储卷数量是否超过了设置的最大值,默认值:16 CheckVolumeBinding:检查节点上已绑定和未绑定的PVC是否满足Pod对象的存储卷需求。 NoVolumeZoneConflct:在给定了区域限制的前提下,检查在该节点上部署Pod对象是否存在存储卷冲突。 CheckNodeMemoryPressure:在给定了节点已经上报了存在内存资源压力过大的状态,则需要检查该Pod是否可以调度到该节点上。 CheckNodePIDPressure:如果给定的节点已经报告了存在PID资源压力过大的状态,则需要检查该Pod是否可以调度到该节点上。 CheckNodeDiskPressure:如果给定的节点存在磁盘资源压力过大,则检查该Pod对象是否可以调度到该节点上。 MatchInterPodAffinity:检查给定的节点能否可以满足Pod对象的亲和性和反亲和性条件,用来实现Pod亲和性调度或反亲和性调度。 在上面的这些预选策略里面,CheckNodeLabelPressure和CheckServiceAffinity可以在预选过程中结合用户自定义调度逻辑,这些策略叫做可配置策略。其他不接受参数进行自定义配置的称为静态策略。 2、优选函数 预选策略筛选出一个节点列表就会进入优选阶段,在这个过程调度器会向每个通过预选的节点传递一系列的优选函数来计算其优先级分值,优先级分值介于0-10之间,其中0表示不适用,10表示最适合托管该Pod对象。 另外,调度器还支持给每个优选函数指定一个简单的值,表示权重,进行节点优先级分值计算时,它首先将每个优选函数的计算得分乘以权重,然后再将所有优选函数的得分相加,从而得出节点的最终优先级分值。权重可以让管理员定义优选函数倾向性的能力,其计算优先级的得分公式如下: finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + ...... 下图是关于优选函数的列表图: 3、节点亲和调度 节点亲和性是用来确定Pod对象调度到哪一个节点的规则,这些规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。 定义节点亲和性规则有2种:硬亲和性(require)和软亲和性(preferred) 硬亲和性:实现的是强制性规则,是Pod调度时必须满足的规则,否则Pod对象的状态会一直是Pending 软亲和性:实现的是一种柔性调度限制,在Pod调度时可以尽量满足其规则,在无法满足规则时,可以调度到一个不匹配规则的节点之上。 定义节点亲和规则的两个要点:一是节点配置是否合乎需求的标签,而是Pod对象定义合理的标签选择器,这样才能够基于标签选择出期望的目标节点。 需要注意的是preferredDuringSchedulingIgnoredDuringExecution和requiredDuringSchedulingIgnoredDuringExecution名字中后半段字符串IgnoredDuringExecution表示的是,在Pod资源基于节点亲和性规则调度到某个节点之后,如果节点的标签发生了改变,调度器不会讲Pod对象从该节点上移除,因为该规则仅对新建的Pod对象有效。 3.1、节点硬亲和性 下面的配置清单中定义的Pod对象,使用节点硬亲和性和规则定义将当前Pod调度到标签为zone=foo的节点上: apiVersion: v1 kind: Pod metadata: name: with-require-nodeaffinity spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - {key: zone,operator: In,values: ["foo"]} containers: - name: myapp image: ikubernetes/myapp:v1 #创建Pod对象 [root@k8s-master ~]# kubectl apply -f require-nodeAffinity-pod.yaml pod/with-require-nodeaffinity created #由于集群中并没有节点含有节点标签为zone=foo,所以创建的Pod一直处于Pending状态 [root@k8s-master ~]# kubectl get pods with-require-nodeaffinity NAME READY STATUS RESTARTS AGE with-require-nodeaffinity 0/1 Pending 0 35s #查看Pending具体的原因 [root@k8s-master ~]# kubectl describe pods with-require-nodeaffinity ...... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 3s (x21 over 1m) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector. #给node01节点打上zone=foo的标签,可以看到成功调度到node01节点上 [root@k8s-master ~]# kubectl label node k8s-node01 zone=foo node/k8s-node01 labeled [root@k8s-master ~]# kubectl describe pods with-require-nodeaffinity Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 58s (x25 over 2m) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector. Normal Pulled 4s kubelet, k8s-node01 Container image "ikubernetes/myapp:v1" already present on machine Normal Created 4s kubelet, k8s-node01 Created container Normal Started 4s kubelet, k8s-node01 Started container [root@k8s-master ~]# kubectl get pods with-require-nodeaffinity -o wide NAME READY STATUS RESTARTS AGE IP NODE with-require-nodeaffinity 1/1 Running 0 6m 10.244.1.12 k8s-node01 在定义节点亲和性时,requiredDuringSchedulingIgnoredDuringExecution字段的值是一个对象列表,用于定义节点硬亲和性,它可以由一个或多个nodeSelectorTerms定义的对象组成,此时值需要满足其中一个nodeSelectorTerms即可。 ...

September 15, 2018

k8s学习笔记-21-k8s的网络模型和网络策略

[TOC] 1、Kubernetes网络模型和CNI插件 在Kubernetes中设计了一种网络模型,要求无论容器运行在集群中的哪个节点,所有容器都能通过一个扁平的网络平面进行通信,即在同一IP网络中。需要注意的是:在K8S集群中,IP地址分配是以Pod对象为单位,而非容器,同一Pod内的所有容器共享同一网络名称空间。 1.1、Docker网络模型 了解Docker的友友们都应该清楚,Docker容器的原生网络模型主要有3种:Bridge(桥接)、Host(主机)、none。 Bridge:借助虚拟网桥设备为容器建立网络连接。 Host:设置容器直接共享当前节点主机的网络名称空间。 none:多个容器共享同一个网络名称空间。 #使用以下命令查看docker原生的三种网络 [root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 0efec019c899 bridge bridge local 40add8bb5f07 host host local ad94f0b1cca6 none null local #none网络,在该网络下的容器仅有lo网卡,属于封闭式网络,通常用于对安全性要求较高并且不需要联网的应用 [root@localhost ~]# docker run -it --network=none busybox / # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) #host网络,共享宿主机的网络名称空间,容器网络配置和host一致,但是存在端口冲突的问题 [root@localhost ~]# docker run -it --network=host busybox / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000 link/ether 00:0c:29:69:a7:23 brd ff:ff:ff:ff:ff:ff inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic eth0 valid_lft 84129sec preferred_lft 84129sec inet6 fe80::20c:29ff:fe69:a723/64 scope link valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue link/ether 02:42:29:09:8f:dd brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:29ff:fe09:8fdd/64 scope link valid_lft forever preferred_lft forever / # hostname localhost #bridge网络,Docker安装完成时会创建一个名为docker0的linux bridge,不指定网络时,创建的网络默认为桥接网络,都会桥接到docker0上。 [root@localhost ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024229098fdd no [root@localhost ~]# docker run -d nginx #运行一个nginx容器 c760a1b6c9891c02c992972d10a99639d4816c4160d633f1c5076292855bbf2b [root@localhost ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024229098fdd no veth3f1b114 一个新的网络接口veth3f1b114桥接到了docker0上,veth3f1b114就是新创建的容器的虚拟网卡。进入容器查看其网络配置: [root@localhost ~]# docker exec -it c760a1b6c98 bash root@c760a1b6c989:/# apt-get update root@c760a1b6c989:/# apt-get iproute root@c760a1b6c989:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever 从上可以看到容器内有一个网卡eth0@if39,实际上eth0@if39和veth3f1b114是一对veth pair。veth pair是一种成对出现的特殊网络设备,可以想象它们由一根虚拟的网线进行连接的一对网卡,eth0@if39在容器中,veth3f1b114挂在网桥docker0上,最终的效果就是eth0@if39也挂在了docker0上。 ...

September 12, 2018

k8s学习笔记-19-Kubernetes dashboard认证访问

[TOC] Dashboard:https://github.com/kubernetes/dashboard 一、Dashboard部署 由于需要用到k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0,这里有2种方式进行pull 镜像。docker search该镜像名称,直接pull,再重新进行tag;另外一种方式是通过谷歌容器镜像拉取。 [root@k8s-node01 ~]# docker pull siriuszg/kubernetes-dashboard-amd64 [root@k8s-node01 ~]# docker tag siriuszg/kubernetes-dashboard-amd64:latest k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0 或者是 [root@k8s-node01 ~]# docker pull mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.10.0 再看其部署的过程: [root@k8s-master ~]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml secret/kubernetes-dashboard-certs created serviceaccount/kubernetes-dashboard created role.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created deployment.apps/kubernetes-dashboard created service/kubernetes-dashboard created [root@k8s-master ~]# kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-78fcdf6894-nmcmz 1/1 Running 1 54d coredns-78fcdf6894-p5pfm 1/1 Running 1 54d etcd-k8s-master 1/1 Running 2 54d kube-apiserver-k8s-master 1/1 Running 9 54d kube-controller-manager-k8s-master 1/1 Running 5 54d kube-flannel-ds-n5c86 1/1 Running 1 54d kube-flannel-ds-nrcw2 1/1 Running 1 52d kube-flannel-ds-pgpr7 1/1 Running 5 54d kube-proxy-glzth 1/1 Running 1 52d kube-proxy-rxlt7 1/1 Running 2 54d kube-proxy-vxckf 1/1 Running 4 54d kube-scheduler-k8s-master 1/1 Running 3 54d kubernetes-dashboard-767dc7d4d-n4clq 1/1 Running 0 3s [root@k8s-master ~]# kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 54d kubernetes-dashboard ClusterIP 10.105.204.4 <none> 443/TCP 30m [root@k8s-master ~]# kubectl patch svc kubernetes-dashboard -p '{"spec":{"type":"NodePort"}}' -n kube-system #以打补丁方式修改dasboard的访问方式 service/kubernetes-dashboard patched [root@k8s-master ~]# kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 54d kubernetes-dashboard NodePort 10.105.204.4 <none> 443:32645/TCP 31m 浏览器访问:https://192.168.56.12:32645,如图:这里需要注意的是谷歌浏览器会禁止不安全证书访问,建议使用火狐浏览器,并且需要在高级选项中添加信任 ...

September 10, 2018

k8s学习笔记-18-认证、授权和准入控制

[TOC] API Server作为Kubernetes网关,是访问和管理资源对象的唯一入口,其各种集群组件访问资源都需要经过网关才能进行正常访问和管理。每一次的访问请求都需要进行合法性的检验,其中包括身份验证、操作权限验证以及操作规范验证等,需要通过一系列验证通过之后才能访问或者存储数据到etcd当中。如下图: 一、ServiceAccount Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。它与User account不同 User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计; User account是跨namespace的,而service account则是仅局限它所在的namespace; 每个namespace都会自动创建一个default service account Token controller检测service account的创建,并为它们创建secret 开启ServiceAccount Admission Controller后 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout) 验证Pod引用的service account已经存在,否则拒绝创建 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/ 当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。如下: [root@k8s-master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE filebeat-ds-hxgdx 1/1 Running 1 34d filebeat-ds-s466l 1/1 Running 2 34d myapp-0 1/1 Running 0 3h myapp-1 1/1 Running 0 3h myapp-2 1/1 Running 0 4h myapp-3 1/1 Running 0 4h pod-vol-demo 2/2 Running 0 2d redis-5b5d6fbbbd-q8ppz 1/1 Running 1 2d [root@k8s-master ~]# kubectl get pods/myapp-0 -o yaml |grep "serviceAccountName" serviceAccountName: default [root@k8s-master ~]# kubectl describe pods myapp-0 Name: myapp-0 Namespace: default ...... Volumes: ...... default-token-j5pf5: Type: Secret (a volume populated by a Secret) SecretName: default-token-j5pf5 Optional: false 从上面可以看到每个Pod无论定义与否都会有个存储卷,这个存储卷为default-token-*** token令牌,这就是pod和serviceaccount认证信息。通过secret进行定义,由于认证信息属于敏感信息,所以需要保存在secret资源当中,并以存储卷的方式挂载到Pod当中。从而让Pod内运行的应用通过对应的secret中的信息来连接apiserver,并完成认证。每个 namespace 中都有一个默认的叫做 default 的 service account 资源。进行查看名称空间内的secret,也可以看到对应的default-token。让当前名称空间中所有的pod在连接apiserver时可以使用的预制认证信息,从而保证pod之间的通信。 ...

September 8, 2018

k8s学习笔记-17-statefulset控制器

[TOC] 一、statefulset简介 从前面的学习我们知道使用Deployment创建的pod是无状态的,当挂载了Volume之后,如果该pod挂了,Replication Controller会再启动一个pod来保证可用性,但是由于pod是无状态的,pod挂了就会和之前的Volume的关系断开,新创建的Pod无法找到之前的Pod。但是对于用户而言,他们对底层的Pod挂了是没有感知的,但是当Pod挂了之后就无法再使用之前挂载的存储卷。 为了解决这一问题,就引入了StatefulSet用于保留Pod的状态信息。 StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括: 1、稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现 2、稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现 3、有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现 4、有序收缩,有序删除(即从N-1到0) 5、有序的滚动更新 从上面的应用场景可以发现,StatefulSet由以下几个部分组成: Headless Service(无头服务)用于为Pod资源标识符生成可解析的DNS记录。 volumeClaimTemplates (存储卷申请模板)基于静态或动态PV供给方式为Pod资源提供专有的固定存储。 StatefulSet,用于管控Pod资源。 二、为什么要有headless?? 在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。 为了实现标识符的稳定,这时候就需要一个headless service 解析直达到pod,还需要给pod配置一个唯一的名称。 三、为什么要 有volumeClainTemplate?? 大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。Pod名称、PVC和PV关系图如下: 四、statefulSet使用演示 在创建StatefulSet之前需要准备的东西,值得注意的是创建顺序非常关键,创建顺序如下: 1、Volume 2、Persistent Volume 3、Persistent Volume Claim 4、Service 5、StatefulSet Volume可以有很多种类型,比如nfs、glusterfs等,我们这里使用的ceph RBD来创建。 (1)查看statefulset的定义 [root@k8s-master ~]# kubectl explain statefulset KIND: StatefulSet VERSION: apps/v1 DESCRIPTION: StatefulSet represents a set of pods with consistent identities. Identities are defined as: - Network: A single stable DNS and hostname. - Storage: As many VolumeClaims as requested. The StatefulSet guarantees that a given network identity will always map to the same storage identity. FIELDS: apiVersion <string> kind <string> metadata <Object> spec <Object> status <Object> [root@k8s-master ~]# kubectl explain statefulset.spec KIND: StatefulSet VERSION: apps/v1 RESOURCE: spec <Object> DESCRIPTION: Spec defines the desired identities of pods in this set. A StatefulSetSpec is the specification of a StatefulSet. FIELDS: podManagementPolicy <string> #Pod管理策略 replicas <integer> #副本数量 revisionHistoryLimit <integer> #历史版本限制 selector <Object> -required- #选择器,必选项 serviceName <string> -required- #服务名称,必选项 template <Object> -required- #模板,必选项 updateStrategy <Object> #更新策略 volumeClaimTemplates <[]Object> #存储卷申请模板,列表对象形式 (2)清单定义StatefulSet 如上所述,一个完整的StatefulSet控制器由一个Headless Service、一个StatefulSet和一个volumeClaimTemplate组成。如下资源清单中的定义: ...

September 3, 2018

k8s学习笔记-16-存储卷

[TOC] 一、存储卷的概念和类型 为了保证数据的持久性,必须保证数据在外部存储在docker容器中,为了实现数据的持久性存储,在宿主机和容器内做映射,可以保证在容器的生命周期结束,数据依旧可以实现持久性存储。但是在k8s中,由于pod分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且,在节点故障时,可能会导致数据的永久性丢失。为此,k8s就引入了外部存储卷的功能。 k8s的存储卷类型: [root@k8s-master ~]# kubectl explain pod.spec.volumes #查看k8s支持的存储类型 KIND: Pod VERSION: v1 常用分类: emptyDir(临时目录):Pod删除,数据也会被清除,这种存储成为emptyDir,用于数据的临时存储。 hostPath(宿主机目录映射): 本地的SAN(iSCSI,FC)、NAS(nfs,cifs,http)存储 分布式存储(glusterfs,rbd,cephfs) 云存储(EBS,Azure Disk) persistentVolumeClaim –>PVC(存储卷创建申请) 当你需要创建一个存储卷时,只需要进行申请对应的存储空间即可使用,这就是PVC。其关联关系如图: 上图解析:在Pod上定义一个PVC,该PVC要关联到当前名称空间的PVC资源,该PVC只是一个申请,PVC需要和PV进行关联。PV属于存储上的一部分存储空间。但是该方案存在的问题是,我们无法知道用户是什么时候去创建Pod,也不知道创建Pod时定义多大的PVC,那么如何实现按需创建呢??? 不需要PV层,把所有存储空间抽象出来,这一个抽象层称为存储类,当用户创建PVC需要用到PV时,可以向存储类申请对应的存储空间,存储类会按照需求创建对应的存储空间,这就是PV的动态供给,如图: 那么PV的动态供给,其重点是在存储类的定义,其分类大概是对存储的性能进行分类的,如图:金存储类、银存储类、铜存储类等。 总结: k8s要使用存储卷,需要2步: 1、在pod定义volume,并指明关联到哪个存储设备 2、在容器使用volume mount进行挂载 二、emptyDir存储卷演示 一个emptyDir 第一次创建是在一个pod被指定到具体node的时候,并且会一直存在在pod的生命周期当中,正如它的名字一样,它初始化是一个空的目录,pod中的容器都可以读写这个目录,这个目录可以被挂在到各个容器相同或者不相同的的路径下。当一个pod因为任何原因被移除的时候,这些数据会被永久删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod. emptyDir 磁盘的作用: (1)普通空间,基于磁盘的数据存储 (2)作为从崩溃中恢复的备份点 (3)存储那些那些需要长久保存的数据,例web服务中的数据 默认的,emptyDir 磁盘会存储在主机所使用的媒介上,可能是SSD,或者网络硬盘,这主要取决于你的环境。当然,我们也可以将emptyDir.medium的值设置为Memory来告诉Kubernetes 来挂在一个基于内存的目录tmpfs,因为 tmpfs速度会比硬盘块度了,但是,当主机重启的时候所有的数据都会丢失。 [root@k8s-master ~]# kubectl explain pods.spec.volumes.emptyDir #查看emptyDir存储定义 [root@k8s-master ~]# kubectl explain pods.spec.containers.volumeMounts #查看容器挂载方式 [root@k8s-master ~]# cd mainfests && mkdir volumes && cd volumes [root@k8s-master volumes]# cp ../pod-demo.yaml ./ [root@k8s-master volumes]# mv pod-demo.yaml pod-vol-demo.yaml [root@k8s-master volumes]# vim pod-vol-demo.yaml #创建emptyDir的清单 apiVersion: v1 kind: Pod metadata: name: pod-demo namespace: default labels: app: myapp tier: frontend annotations: magedu.com/create-by:"cluster admin" spec: containers: - name: myapp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: #在容器内定义挂载存储名称和挂载路径 - name: html mountPath: /usr/share/nginx/html/ - name: busybox image: busybox:latest imagePullPolicy: IfNotPresent volumeMounts: - name: html mountPath: /data/ #在容器内定义挂载存储名称和挂载路径 command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done'] volumes: #定义存储卷 - name: html #定义存储卷名称 emptyDir: {} #定义存储卷类型 [root@k8s-master volumes]# kubectl apply -f pod-vol-demo.yaml pod/pod-vol-demo created [root@k8s-master volumes]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-vol-demo 2/2 Running 0 27s [root@k8s-master volumes]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE ...... pod-vol-demo 2/2 Running 0 16s 10.244.2.34 k8s-node02 ...... 在上面,我们定义了2个容器,其中一个容器是输入日期到index.html中,然后验证访问nginx的html是否可以获取日期。以验证两个容器之间挂载的emptyDir实现共享。如下访问验证: [root@k8s-master volumes]# curl 10.244.2.34 #访问验证 Tue Oct 9 03:56:53 UTC 2018 Tue Oct 9 03:56:55 UTC 2018 Tue Oct 9 03:56:57 UTC 2018 Tue Oct 9 03:56:59 UTC 2018 Tue Oct 9 03:57:01 UTC 2018 Tue Oct 9 03:57:03 UTC 2018 Tue Oct 9 03:57:05 UTC 2018 Tue Oct 9 03:57:07 UTC 2018 Tue Oct 9 03:57:09 UTC 2018 Tue Oct 9 03:57:11 UTC 2018 Tue Oct 9 03:57:13 UTC 2018 Tue Oct 9 03:57:15 UTC 2018 三、hostPath存储卷演示 hostPath宿主机路径,就是把pod所在的宿主机之上的脱离pod中的容器名称空间的之外的宿主机的文件系统的某一目录和pod建立关联关系,在pod删除时,存储数据不会丢失。 ...

September 2, 2018

k8s学习笔记-15-Ingress和Ingress Controller

[TOC] 一、什么是Ingress? 从前面的学习,我们可以了解到Kubernetes暴露服务的方式目前只有三种:LoadBlancer Service、ExternalName、NodePort Service、Ingress;而我们需要将集群内服务提供外界访问就会产生以下几个问题: 1、Pod 漂移问题 Kubernetes 具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他机器启动一个新的,还可以动态扩容等,通俗地说,这个 Pod 可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;那么如何把这个动态的 Pod IP 暴露出去?这里借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外暴露只暴露 Service IP 就行了;这就是 NodePort 模式:即在每个节点上开起一个端口,然后转发到内部 Pod IP 上,如下图所示: 此时的访问方式:http://nodeip:nodeport/ 2、端口管理问题 采用 NodePort 方式暴露服务面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护;这时,我们可以能否使用一个Nginx直接对内进行转发呢?众所周知的是,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了,如下图所示: 3、域名分配及动态更新问题 从上面的方法,采用 Nginx-Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢??我们知道使用Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改nginx.conf即可实现,那在K8S中又该如何实现这种方式的调度呢??? 假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?总不能每次手动改或者Rolling Update 前端 Nginx Pod 吧!!此时 Ingress 出现了,如果不算上面的Nginx,Ingress 包含两大组件:Ingress Controller 和 Ingress。 ...

September 1, 2018

k8s学习笔记-13-Pod控制器--DaemonSet

[TOC] 一、什么是DaemonSet? DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 使用 DaemonSet 的一些典型用法: 运行集群存储 daemon,例如在每个 Node 上运行 glusterd、ceph。 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash。 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。 一个简单的用法是,在所有的 Node 上都存在一个 DaemonSet,将被作为每种类型的 daemon 使用。 一个稍微复杂的用法可能是,对单独的每种类型的 daemon 使用多个 DaemonSet,但具有不同的标志,和/或对不同硬件类型具有不同的内存、CPU要求。 二、编写DaemonSet Spec (1)必需字段 和其它所有 Kubernetes 配置一样,DaemonSet 需要 apiVersion、kind 和 metadata字段。 [root@k8s-master ~]# kubectl explain daemonset KIND: DaemonSet VERSION: extensions/v1beta1 DESCRIPTION: DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for more information. DaemonSet represents the configuration of a daemon set. FIELDS: apiVersion <string> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources kind <string> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds metadata <Object> Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata spec <Object> The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status status <Object> The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status (2)Pod模板 ...

August 29, 2018