Skip to content

什么是k8s?说出你的理解

K8s是kubernetes的简称,其本质是一个开源的容器编排系统,主要用于管理容器化的应用,其目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。 说简单点:k8s就是一个编排容器的系统,一个可以管理容器应用全生命周期的工具,从创建应用,应用的部署,应用提供服务,扩容缩容应用,应用更新,都非常的方便,而且还可以做到故障自愈,所以,k8s是一个非常强大的容器编排系统。

k8s的组件有哪些,作用分别是什么?

k8s主要由master节点和node节点构成。master节点负责管理集群,node节点是容器应用真正运行的地方。 master节点包含的组件有:kube-api-server、kube-controller-manager、kube-scheduler、etcd。 node节点包含的组件有:kubelet、kube-proxy、container-runtime

  • kube-api-server:以下简称api-server,api-server是k8s最重要的核心组件之一,它是k8s集群管理的统一访问入口,提供了RESTful API接口, 实现了认证、授权和准入控制等安全功能;api-server还是其他组件之间的数据交互和通信的枢纽,其他组件彼此之间并不会直接通信,其他组件对资源对象的增、删、改、查和监听操作都是交由api-server处理后,api-server再提交给etcd数据库做持久化存储,只有api-server才能直接操作etcd数据库,其他组件都不能直接操作etcd数据库,其他组件都是通过api-server间接的读取,写入数据到etcd。

  • kube-controller-manager:以下简称controller-manager,controller-manager是k8s中各种控制器的的管理者,是k8s集群内部的管理控制中心,也是k8s自动化功能的核心;controller-manager内部包含deployment控制器、replicaSet控制器、statefulset控制器、daemonset控制器、job控制器、cronjob控制器、node控制器、endpoint控制器等等各种资源对象的控制器,每种控制器都负责一种特定资源的控制流程,而controller-manager正是这些controller的核心管理者。

  • kube-scheduler:以下简称scheduler,scheduler负责集群资源调度,其作用是将待调度的pod通过一系列复杂的调度算法计算出最合适的node节点,然后将pod绑定到目标节点上。shceduler会根据pod的信息,全部节点信息列表,过滤掉不符合要求的节点,过滤出一批候选节点,然后给候选节点打分,选分最高的就是最优节点,scheduler就会把目标pod安置到该节点。

  • etcd:etcd是一个分布式的键值对存储数据库,主要是用于保存k8s集群状态数据,比如,pod,service等资源对象的信息;etcd可以是单个也可以有多个,多个就是etcd数据库集群,etcd通常部署奇数个实例,在大规模集群中,etcd有5个或7个节点就足够了;另外说明一点,etcd本质上可以不与master节点部署在一起,只要master节点能通过网络连接etcd数据库即可。

  • kubelet:每个node节点上都有一个kubelet服务进程,kubelet作为连接master和各node之间的桥梁,负责维护pod和容器的生命周期,当监听到master下发到本节点的任务时,比如创建、更新、终止pod等任务,kubelet 即通过控制docker来创建、更新、销毁容器;kubelet还会定时执行pod中容器定义的探针,然后根据容器重启策略执行对应的操作。每个kubelet进程都会在api-server上注册本节点自身的信息,用于定期向master汇报本节点资源的使用情况。

  • kube-proxy:kube-proxy运行在node节点上,在Node节点上实现pod网络代理,维护网络规则和四层负载均衡工作,kube-proxy会监听api-server中从而获取service和endpoint的变化情况,创建并维护路由规则以提供服务ip和负载均衡功能。简单理解此进程是Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个pod实例上。

kubelet的功能、作用是什么?

答:kubelet部署在每个node节点上的,它主要有4个功能:

  1. 节点管理。kubelet启动时会向api-server进行注册,然后会定时的向api-server汇报本节点信息状态,资源使用状态等,这样master就能够知道node节点的资源剩余,节点是否失联等等相关的信息了。master知道了整个集群所有节点的资源情况,这对于 pod 的调度和正常运行至关重要。
  2. pod管理。kubelet负责维护node节点上pod的生命周期,当kubelet监听到master的下发到自己节点的任务时,比如要创建、更新、删除一个pod,kubelet 就会通过CRI(容器运行时接口)插件来调用不同的容器运行时来创建、更新、删除容器;常见的容器运行时有docker、containerd、rkt等等这些容器运行时,我们最熟悉的就是docker了,但在新版本的k8s已经弃用docker了,k8s1.24版本中已经使用containerd作为容器运行时了。
  3. 容器健康检查。pod中可以定义启动探针、存活探针、就绪探针等3种,我们最常用的就是存活探针、就绪探针,kubelet 会定期调用容器中的探针来检测容器是否存活,是否就绪,如果是存活探针,则会根据探测结果对检查失败的容器进行相应的重启策略;
  4. Metrics Server资源监控。在node节点上部署Metrics Server用于监控node节点、pod的CPU、内存、文件系统、网络使用等资源使用情况,而kubelet则通过Metrics Server获取所在节点及容器的上的数据。

kube-api-server的端口是多少?各个pod是如何访问kube-api-server的?

kube-api-server的端口是8080和6443,前者是http的端口,后者是https的端口,(注意:有些8080是k8s低版本的才有的端口,高版本中不开放此端口了)以我本机使用kubeadm安装的k8s为例: 在命名空间的kube-system命名空间里,有一个名称为kube-api-master的pod,这个pod就是运行着kube-api-server进程,它绑定了master主机的ip地址和6443端口,但是在default命名空间下,存在一个叫kubernetes的服务,该服务对外暴露端口为443,目标端口6443,这个服务的ip地址是ClusterIP地址池里面的第一个地址,同时这个服务的yaml定义里面并没有指定标签选择器,也就是说这个kubernetes服务所对应的endpoint是手动创建的,该endpoint也是名称叫做kubernetes,该endpoint的yaml定义里面代理到master节点的6443端口,也就是kube-api-server的ip和端口。这样一来,其他pod访问kube-api-server的整个流程就是:pod创建后嵌入了环境变量,pod获取到了kubernetes这个服务的ip和443端口,请求到kubernetes这个服务其实就是转发到了master节点上的6443端口的kube-api-server这个pod里面。

k8s中命名空间的作用是什么?

namespace是kubernetes系统中的一种非常重要的资源,namespace的主要作用是用来实现多套环境的资源隔离,或者说是多租户的资源隔离。 k8s通过将集群内部的资源分配到不同的namespace中,可以形成逻辑上的隔离,以方便不同的资源进行隔离使用和管理。不同的命名空间可以存在同名的资源,命名空间为资源提供了一个作用域。 可以通过k8s的授权机制,将不同的namespace交给不同的租户进行管理,这样就实现了多租户的资源隔离,还可以结合k8s的资源配额机制,限定不同的租户能占用的资源,例如CPU使用量、内存使用量等等来实现租户可用资源的管理。

k8s提供了大量的REST接口,其中有一个是Kubernetes Proxy API接口,简述一下这个Proxy接口的作用,已经怎么使用。

好的。kubernetes proxy api接口,从名称中可以得知,proxy是代理的意思,其作用就是代理rest请求;Kubernets API server 将接收到的rest请求转发到某个node上的kubelet守护进程的rest接口,由该kubelet进程负责响应。我们可以使用这种Proxy接口来直接访问某个pod,这对于逐一排查pod异常问题很有帮助。 下面是一些简单的例子:

pod是什么?

在kubernetes的世界中,k8s并不直接处理容器,而是使用多个容器共存的理念,这组容器就叫做pod。pod是k8s中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最小资源对象模型,其他的资源对象都是用来支撑pod对象功能的,比如,pod控制器就是用来管理pod对象的,service或者imgress资源对象是用来暴露pod引用对象的,persistentvolume资源是用来为pod提供存储等等,简而言之,k8s不会直接处理容器,而是pod,pod才是k8s中可以创建和管理的最小单元,也是基本单元。

pod的原理是什么?

在微服务的概念里,一般的,一个容器会被设计为运行一个进程,除非进程本身产生子进程,这样,由于不能将多个进程聚集在同一个单独的容器中,所以需要一种更高级的结构将容器绑定在一起,并将它们作为一个单元进行管理,这就是k8s中pod的背后原理。

pod有什么特点?

Pod 作为 Kubernetes 的最小调度单元,其生命周期管理是集群运维的核心知识:

  1. 生命周期短暂:Pod 是临时的,可以被创建、销毁和重新调度
  2. 原子性调度:Pod 中的所有容器总是被调度到同一个节点上
  3. 共享网络:Pod 内的容器通过 localhost 互相通信
  4. 共享存储:Pod 可以定义一组共享的存储卷

pause容器作用是什么?

每个pod里运行着一个特殊的被称之为pause的容器,也称根容器,而其他容器则称为业务容器;

  1. 创建pause容器主要是为了为业务容器提供 Linux命名空间,共享基础:包括 pid、icp、net 等,这些业务容器共享pause容器的网络命名空间和volume挂载卷,当pod被创建时,pod首先会创建pause容器,从而把其他业务容器加入pause容器,从而让所有业务容器都在同一个网络命名空间中,这样就可以实现网络共享。这种网络命名空间共享设计确保了同一个pod内的容器可以直接通过localhost地址互相通信,从而实现高效的内部通信。
  2. pod还可以共享存储,在pod级别引入数据卷volume,业务容器都可以挂载这个数据卷从而实现持久化存储。
  3. pause容器还负责处理僵尸进程。在传统的Unix系统中,当一个子进程结束而其父进程尚未读取其退出状态时,子进程会成为僵尸进程,占用系统资源。pause容器通过持续监听并清理这些僵尸进程,优化了系统的资源管理
  4. 由于pause容器始终保持运行状态,它还承担了维护pod ip地址的角色。pod的ip地址通常是动态分配的,但只要pause容器在运行,就可以维持这个ip地址不变,即便pod内的其他容器重新启动也不会影响ip地址。

pod的重启策略有哪些?

pod的重启策略(RestartPolicy)决定了当容器异常退出或健康检查失败时,kubelet将如何响应。(注意是kubelet重启容器,因为是kubelet负责容器的健康检测) 需要注意的是,虽然名为pod的重启策略(更规范的说法应该是pod中容器重启策略),但实际上是作用于pod内的所有容器。所有容器都将遵守这个策略,而不是单独的某个容器。 可以通过pod.spec.restartPolicy字段配置重启容器的策略,重启策略如下3种配置:

  • Always:当容器终止退出后,总是重启容器,默认策略
  • OnFailure:当容器异常退出(退出状态码非0)时,才重启容器
  • Never:当容器终止退出,从不重启容器

pod的镜像拉取策略有哪几种?

pod镜像拉取策略可以通过imagePullPolicy字段配置镜像拉取策略,主要有3种镜像拉取策略,如下:

  • Always(始终拉取):每次创建或重启 Pod 时,强制从镜像仓库拉取最新镜像,即使本地节点已存在同名镜像
  • IfNotPresent(本地不存在拉取)优先使用本地节点已缓存的镜像;仅当本地不存在时,才从仓库拉取
  • Never(永不拉取)仅使用本地节点已存在的镜像,绝不尝试从仓库拉取;若本地缺失,则 Pod 启动失败

pod的存活探针有哪几种?

kubernetes可以通过存活探针检查容器是否还在运行,可以为pod中的每个容器单独定义存活探针,kubelet将定期执行探针,如果探测失败,将杀死容器,并根据restartPolicy策略来决定是否重启容器,kubernetes提供了3种探测容器的存活探针,如下:

  • 启动探针 Startup Probe :用于判断容器内应用程序是否启动,如果配置了startupProbe,就会先禁止其他的探针,直到它成功为止,成功后将不再进行探测。(1.16版本后加入,针对容器内应用服务是否已经启动监控)
  • 就绪探针 Readiness Probe :判断容器是否已经就绪,若未就绪,容器将会处于未就绪,未就绪的容器,不会进行流量的调度。 Kubernetes 会把 Pod 从 service endpoints 中剔除。
  • 存活探针 Liveness Probe :判断容器内的应用程序是否正常,若不正常,根据 Pod 的restartPolicy 重启策略操作,如果没有配置该探针,默认就是success

存活探针的属性参数有哪几个?

存活探针的附加属性参数有以下几个:

  • initialDelaySeconds:表示在容器启动后延时多久秒才开始探测;
  • periodSeconds:表示执行探测的频率,即间隔多少秒探测一次,默认间隔周期是10秒,最小1秒;
  • timeoutSeconds:表示探测超时时间,默认1秒,最小1秒,表示容器必须在超时时间范围内做出响应,否则视为本次探测失败;
  • successThreshold:表示最少连续探测成功多少次才被认定为成功,默认是1,对于liveness必须是1,最小值是1;
  • failureThreshold:表示连续探测失败多少次才被认定为失败,默认是3,连续3次失败,k8s 将根据pod重启策略对容器做出决定;

注意:定义存活探针时,一定要设置initialDelaySeconds属性,该属性为初始延时,如果不设置,默认容器启动时探针就开始探测了,这样可能会存在 应用程序还未启动就绪,就会导致探针检测失败,k8s就会根据pod重启策略杀掉容器然后再重新创建容器的莫名其妙的问题。 在生产环境中,一定要定义一个存活探针。

pod的就绪探针有哪几种?

我们知道,当一个pod启动后,就会立即加入service的endpoint ip列表中,并开始接收到客户端的链接请求,假若此时pod中的容器的业务进程还没有初始化完毕,那么这些客户端链接请求就会失败,为了解决这个问题,kubernetes提供了就绪探针来解决这个问题的。 在pod中的容器定义一个就绪探针,就绪探针周期性检查容器,如果就绪探针检查失败了,说明该pod还未准备就绪,不能接受客户端链接,则该pod将从endpoint列表中移除,被剔除了service就不会把请求分发给该pod,然后就绪探针继续检查,如果随后容器就绪,则再重新把pod加回endpoint列表。k8s提供了3种就绪探针,如下:

  • exec:通过在容器内执行指定命令,来判断命令退出时返回的状态码,返回状态码是0表示正常。
  • httpGet:通过对容器的 IP 地址、端口和 URL 路径来发送 GET 请求;如果响应的状态码在 200 ~399 间,表示正常。
  • tcpSocket:通过对容器的 IP 地址和指定端口,进行 TCP 检查,如果端口打开,发起 TCPSocket 建立成功,表示正常。

就绪探针的属性参数有哪些

就绪探针的附加属性参数有以下几个:

  • initialDelaySeconds:延时秒数,即容器启动多少秒后才开始探测,不写默认容器启动就探测;
  • periodSeconds :执行探测的频率(秒),默认为10秒,最低值为1;
  • timeoutSeconds :超时时间,表示探测时在超时时间内必须得到响应,负责视为本次探测失败,默认为1秒,最小值为1;
  • failureThreshold :连续探测失败的次数,视为本次探测失败,默认为3次,最小值为1次;
  • successThreshold :连续探测成功的次数,视为本次探测成功,默认为1次,最小值为1次;

就绪探针与存活探针区别是什么?

两者作用不一样。 存活探针,是检测容器是否存活,如果检测失败,kubelet将调用容器运行时(如docker)将检查失败的容器杀死,创建新的启动容器来保持pod正常工作; 就绪探针,是检测容器是否可以正常接收流量,当就绪探针检查失败,并不重启容器,而是将pod移出endpoint列表,就绪探针确保了service中的pod都是可用的,确保客户端只与正常的pod交互并且客户端永远不会知道系统存在问题。

pod的生命周期有哪几种?

pod生命周期有的5种状态(也称5种相位),如下:

  • Pending(挂起):API server已经创建pod,但是该pod还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程;
  • Running(运行中):pod内所有的容器已经创建,且至少有一个容器处于运行状态、正在启动括正在重启状态;
  • Succeed(成功):pod内所有容器均已退出,且不会再重启;
  • Failed(失败):pod内所有容器均已退出,且至少有一个容器为退出失败状态
  • Unknown(未知):某于某种原因api-server无法获取该pod的状态,可能由于网络通行问题导致;

pod状态一般有哪些?

pod的状态一般会有以下这些:

  • ContainerCreating(容器正在创建):容器正在创建中
  • Pending(挂起):API server已经创建pod,但是该pod还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程;
  • Running(运行中):pod内所有的容器已经创建,且至少有一个容器处于运行状态、正在启动括正在重启状态;
  • MatchNodeSelector (匹配节点选择器):pod正在等待被调度到匹配其nodeSelector的节点上,当一个pod定义有节点选择器但没有任何节点存在指定的标签时,pod将处于"MatchNodeSelector"状态。
  • ErrImagePull(镜像拉取异常): 这个错误表示Kubernetes无法从指定的镜像仓库拉取镜像。可能的原因有很多,比如网络问题、镜像名称或标签错误、或者没有权限访问这个镜像仓库等。
  • ImagePullBackOff(镜像拉取异常): 这个错误表示Kubernetes尝试拉取镜像,但是失败了,然后它回滚了之前的操作。这通常是因为镜像仓库的问题,比如网络问题、镜像不存在、或者没有权限访问这个镜像仓库等。
  • Error(pod异常):可能是容器运行时异常
  • CrashLoopBackOff(崩溃重启) :pod正在经历一个无限循环的崩溃和重启过程。
  • Succeed(成功):pod内所有容器均已退出,且不会再重启;
  • Failed(失败):pod内所有容器均已退出,且至少有一个容器为退出失败状态
  • Unknown(未知):某于某种原因api-server无法获取该pod的状态,可能由于网络通行问题导致;

pod一直处于pending状态一般有哪些情况,怎么排查?

(这个问题被问到的概率非常大) 答:一个pod一开始创建的时候,它本身就是会处于pending状态,这时可能是正在拉取镜像,正在创建容器的过程。 如果等了一会发现pod还一直处于pending状态,那么我们可以使用kubectl describe命令查看一下pod的Events详细信息。一般可能会有这么几种情况导致pod一直处于pending状态:

  1. 调度器调度失败。Scheduer调度器无法为pod分配一个合适的node节点。而这又会有很多种情况,比如,node节点处在cpu、内存压力,导致无节点可调度;pod定义了资源请求,没有node节点满足资源请求;node节点上有污点而pod没有定义容忍;pod中定义了亲和性或反亲和性而没有节点满足这些亲和性或反亲和性;以上是调度器调度失败的几种情况。
  2. pvc、pv无法动态创建。如果因为pvc或pv无法动态创建,那么pod也会一直处于pending状态,比如要使用StatefulSet创建redis集群,因为粗心大意,定义的storageClassName名称写错了,那么会造成无法创建pvc,这种情况pod也会一直处于pending状态,或者,即使pvc是正常创建了,但是由于某些异常原因导致动态供应存储无法正常创建pv,那么这种情况pod也会一直处于pending状态。

集群内 Pod A 访问 Pod B 失败,如何排查?

  1. 确认 Pod IP 和端口
    • 获取 Pod B IP:kubectl get pod <B> -o wide
    • 进入 Pod A:kubectl exec -it <A> -- sh
    • Ping 测试:ping <PodB-IP>(注意跨节点通常 ICMP 可能被 CNI 限制,只作为参考)
  2. 四层连通性(TCP/UDP)
    • telnet <PodB-IP> <port>nc -zv <PodB-IP> <port>
  3. DNS 解析
    • Service 名称解析:nslookup <service-name>.<namespace>.svc.cluster.local
    • 若 CoreDNS 返回错误,检查 CoreDNS Pod 状态、/etc/resolv.conf 配置。
  4. Service 与 Endpoint
    • kubectl get svc <service> 查看 ClusterIP 和端口映射。
    • kubectl get endpoints <service> 确认后端 Pod IP 列表是否正确。
  5. 网络策略(NetworkPolicy)
    • 检查是否有 NetworkPolicy 限制了进出流量。
    • kubectl get networkpolicies -n <namespace>
  6. CNI 插件与节点网络
    • 查看 CNI 插件日志,如 Calico 的 calico-node Pod。
    • 检查节点间路由是否正常,节点防火墙规则是否放行 Pod 网段

pod的钩子函数有哪几种,作用是什么?

pod的钩子函数有PostStart和PreStop两种,它们在容器的生命周期中特定时刻被调用并执行指定的操作。

  1. postStart构子,作用是在容器创建后立即执行,但并不能保证该钩子会在容器的ENTRYPOINT之前运行。它主要用于资源部署、环境准备等。如果该钩子执行失败或花费太长时间,容器将无法达到"Running"状态。一个典型的PostStart应用是在容器启动时执行一些配置或准备工作,比如修改配置文件、更新本地资源等。例如,可以在此钩子中修改Nginx的默认首页。
  2. preStop构子,作用是在容器终止之前立即被调用,是阻塞的,即同步的。它主要用于优雅关闭应用程序、通知其他系统以及完成清理工作。如果钩子执行 期间挂起,pod的状态将停留在"Running"状态并且不会达到"Failed"状态。在其完成之前会阻塞删除容器的操作。 应用场景:一个常见的PreStop应用是在容器关闭前优雅地停止一个服务,如Nginx或MySQL服务。

注意: postStart构子是异步的,并不能保证该钩子会在容器的ENTRYPOINT之前运行; preStop构子是同步的,它会阻塞当前容器的结束流程,直到Hook定义操作完成之后才允许容器被结束。 不管是postStart构子还是preStop构子,都可以使用 exec、httpGet、tcpSocket等3种方法来定义。

pod的初始化容器是干什么的?

init container,初始化容器用于在启动应用容器之前完成应用容器所需要的前置条件,初始化容器本质上和应用容器是一样的,但是初始化容器是仅运行一次就结束的任务,初始化容器具有两大特征:

  1. 初始化容器必须运行完成直至成功结束,若某初始化容器运行失败,那么kubernetes需要重启它直到成功完成;

  2. 初始化容器必须按照定义的顺序执行,当且仅当前一个初始化容器成功之后,后面的一个初始化容器才能运行;

举个例子,我们最常见的es容器里面就有一个初始化容器,这个初始化容器的执行命令就是配置内核参数,因为es对某些内核参数要求设置比较大,所以直接通过初始化容器修改了内核参数。(容器与宿主机共享内核,所以修改的就是宿主内核)

pod的资源请求、限制如何定义?

pod的资源请求、资源限制可以直接在pod中定义,主要包括两块内容,limits,限制pod中容器能使用的最大cpu和内存,requests,pod中容器启动时申请的cpu和内存。

yaml
resources: #资源配额
  limits: #限制最大资源,上限
    cpu: 2 #CPU限制,单位是code数
    memory: 2G #内存最大限制
  requests: #请求资源(最小,下限)
    cpu: 1 #CPU请求,单位是code数
    memory: 500G #内存最小请求

pod的定义中有个command和args参数,这两个参数不会和docker镜像的entrypointc冲突吗?

不会。 在pod中定义的command参数用于指定容器的启动命令列表,如果不指定,则默认使用Dockerfile打包时的启动命令,args参数用于容器的启动命令需要的参数列表; 特别说明: kubernetes中的command、args其实是实现覆盖dockerfile中的ENTRYPOINT的功能的。当:

  1. 如果command和args均没有写,那么使用Dockerfile的配置;
  2. 如果command写了但args没写,那么Dockerfile默认的配置会被忽略,执行pod容器指定的command;
  3. 如果command没写但args写了,那么Dockerfile中的ENTRYPOINT的会被执行,使用当前args的参数;
  4. 如果command和args都写了,那么Dockerfile会被忽略,执行pod当前定义的command和args。

标签及标签选择器是什么,如何使用?

标签是键值对类型,标签可以附加到任何资源对象上,主要用于管理对象,查询和筛选。标签常被用于标签选择器的匹配度检查,从而完成资源筛选;一个资源可以定义一个或多个标签在其上面。

标签选择器,标签要与标签选择器结合在一起,标签选择器允许我们选择标记有特定标签的资源对象子集,如pod,并对这些特定标签的pod进行查询,删除等操作。 标签和标签选择器最重要的使用之一在于,在deployment中,在pod模板中定义pod的标签,然后在deployment定义标签选择器,这样就通过标签选择器来选择哪些pod是受其控制的,service也是通过标签选择器来关联哪些pod最后其服务后端pod。

service是如何与pod关联的?

答案:通过标签选择器。每一个由deployment创建的pod都带有标签,这样,service就可以定义标签选择器来关联哪些pod是作为其后端了,就是这样,service就与pod关联在一起了。

service的域名解析格式、pod的域名解析格式

service的DNS域名表示格式为<servicename>.<namespace>.svc.<clusterdomain>,servicename是service的名称,namespace是service所处的命名空间,clusterdomain是k8s集群设置的域名后缀,一般默认为 cluster.local,一般的,我们不会去改k8s集群设置的域名后缀,同时,当pod要链接的svc处于pod当前命名空间时,可以省略<namespace>以及后面的.svc不写,这样,就可以有下面三种方式来表示svc的域名:

bash
#查看k8s集群设置的域名后缀
grep -i clusterDomain /opt/kubernetes/config/kubelet-config.yml	#二进制安装的k8s集群,可以这样查看
grep -i clusterDomain /etc/kubernetes/kubelet.conf 	#kubeadm安装的k8s集群,各个节点的kubelet.conf文件中的字段clusterDomain
grep -i clusterDomain /var/lib/kubelet/config.yaml	#kubeadm安装的k8s集群,各个节点的config.yaml文件中的字段clusterDomain
kubectl  -n kube-system get cm coredns -oyaml		#coredns cm里面也可以看到
kubectl  exec -it busybox --  cat /etc/resolv.conf	#直接看pod里面的resolv.conf文件亦可

svc-nginx.default.svc.cluster.local	#完整的写法
svc-nginx.default					#带命名空间写法,省略了后面的.svc.<clusterdomain>
svc-nginx							#如果pod与svc在同一个命名空间,可以将命名空间省略不写,换句话说链接的是当前命名空间的svc

#于是,svc域名+svc的端口,我们就可以在pod里面访问svc对应的应用了,如下
wget http://svc-deployment-nginx.default.svc.cluster.local:80		#完整的写法
wget http://svc-deployment-nginx.default:80							#带命名空间写法
wget http://svc-deployment-nginx:80									#如果pod与svc在同一个命名空间,可以将命名空间省略不写

pod的DNS域名格式为:<pod-ip>.<namespace>.pod.<clusterdomain> ,其中,pod-ip需要使用-将ip之间的点替换掉,namespace为pod所在的命名空间,clusterdomain是k8s集群设置的域名后缀,一般默认为 cluster.local ,如果没有改变k8s集群默认的域名后缀,则可以省略该后缀不写。除此之外,其他的均不可省略,这一点与svc域名有所不同。 演示如下:

对于deployment、daemonsets等创建的无状态的pod,还还可以通过<pod-ip>.<deployment-name>.<namespace>.svc.<clusterdomain> 这样的域名访问。(这点存疑,一直测试失败,不指定是书中写错了还是什么)

对于StatefulSet创建的pod,statefulset.spec.serviceName字段解释如下:

也就是说StatefulSet创建的pod,其pod的域名为:pod-specific-string.serviceName.default.svc.cluster.local,而pod-specific-string就是pod的名称。 例如:redis-sts-0.redis-svc.default.svc.cluster.local:6379,redis-sts-1.redis-svc.default.svc.cluster.local:6379,redis-sts-2.redis-svc.default.svc.cluster.local:6379,redis-sts-3.redis-svc.default.svc.cluster.local:6379,redis-sts-4.redis-svc.default.svc.cluster.local:6379,redis-sts-5.redis-svc.default.svc.cluster.local:6379,pod里面的后端应用程序就可以拿这串字符串去连接Redis集群了。

service的类型有哪几种

service的类型一般有4种,分别是:

  • ClusterIP:表示service仅供集群内部使用,默认值就是ClusterIP类型
  • NodePort:表示service可以对外访问应用,会在每个节点上暴露一个端口,这样外部浏览器访问地址为:任意节点的ip:NodePort就能连上service了
  • LoadBalancer:表示service对外访问应用,这种类型的service是公有云环境下的service,此模式需要外部云厂商的支持,需要有一个公网ip地址
  • ExternalName:这种类型的service会把集群外部的服务引入集群内部,这样集群内直接访问service就可以间接的使用集群外部服务了

一般情况下,service都是ClusterIP类型的,通过ingress接入的外部流量。

一个应用pod是如何发现service的,或者说,pod里面的容器用于是如何连接service的?

答:有两种方式,一种是通过环境变量,另一种是通过service的dns域名方式。 1、环境变量:当pod被创建之后,k8s系统会自动为容器注入集群内有效的service名称和端口号等信息为环境变量的形式,这样容器应用直接通过取环境变量值就能访问service了,如,每个pod都会自动注入了api-server的svc:curl http://${KUBERNETES_SERVICE_HOST}:{KUBERNETES_SERVICE_PORT} 2、DNS方式:使用dns域名解析的前提是k8s集群内有DNS域名解析服务器,默认k8s中会有一个CoreDNS作为k8s集群的默认DNS服务器提供域名解析服务器;service的DNS域名表示格式为<servicename>.<namespace>.svc.<clusterdomain>,servicename是service的名称,namespace是service所处的命名空间,clusterdomain是k8s集群设置的域名后缀,一般默认为 cluster.local ,这样容器应用直接通过service域名就能访问service了,如wget http://nginx-svc.default.svc.cluster.local:80,另外,service的port端口如果定义了名称,那么port也可以通过DNS进行解析,格式为:_<portname>._<protocol>.<servicename>.<namespace>.svc.<clusterdomain>

如何创建一个service代理外部的服务,或者换句话来说,在k8s集群内的应用如何访问外部的服务,如数据库服务,缓存服务等?

答:可以通过创建一个没有标签选择器的service来代理集群外部的服务。 1、创建service时不指定selector标签选择器,但需要指定service的port端口、端口的name、端口协议等,这样创建出来的service因为没有指定标签选择器就不会自动创建endpoint; 2、手动创建一个与service同名的endpoint,endpoint中定义外部服务的ip和端口,endpoint的名称一定要与service的名称一样,端口协议也要一样,端口的name也要与service的端口的name一样,不然endpoint不能与service进行关联。 完成以上两步,k8s会自动将service和同名的endpoint进行关联,这样,k8s集群内的应用服务直接访问这个service就可以相当于访问外部的服务了。

service、endpoint、kube-proxy三种的关系是什么?

service:在kubernetes中,service是一种为一组功能相同的pod提供单一不变的接入点的资源。当service被建立时,service的ip和端口不会改变,这样外部的客户端(也可以是集群内部的客户端)通过service的ip和端口来建立链接,这些链接会被路由到提供该服务的任意一个pod上。通过这样的方式,客户端不需要知道每个单独提供服务的pod地址,这样pod就可以在集群中随时被创建或销毁。 endpoint:service维护一个叫endpoint的资源列表,endpoint资源对象保存着service关联的pod的ip和端口。从表面上看,当pod消失,service会在endpoint列表中剔除pod,当有新的pod加入,service就会将pod ip加入endpoint列表;但是正在底层的逻辑是,endpoint的这种自动剔除、添加、更新pod的地址其实底层是由endpoint controller控制的,endpoint controller负责监听service和对应的pod副本的变化,如果监听到service被删除,则删除和该service同名的endpoint对象,如果监听到新的service被创建或者修改,则根据该service信息获取得相关pod列表,然后创建或更新service对应的endpoint对象,如果监听到pod事件,则更新它所对应的service的endpoint对象。 kube-proxy:kube-proxy运行在node节点上,在Node节点上实现pod网络代理,维护网络规则和四层负载均衡工作,kube-proxy会监听api-server中从而获取service和endpoint的变化情况,创建并维护路由规则以提供服务ip和负载均衡功能。简单理解此进程是Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个pod实例上。

无头service和普通的service有什么区别,无头service使用场景是什么?

答:无头service没有cluster ip,在定义service时将 service.spec.ClusterIP:None,就表示创建的是无头service。 普通的service是用于为一组后端pod提供请求连接的负载均衡,让客户端能通过固定的service ip地址来访问pod,这类的pod是没有状态的,同时service还具有负载均衡和服务发现的功能。普通service跟我们平时使用的nginx反向代理很相识。 但是,试想这样一种情况,有6个redis pod ,它们相互之间要通信并要组成一个redis集群,不在需要所谓的service负载均衡,这时无头service就是派上用场了,无头service由于没有cluster ip,kube-proxy就不会处理它也就不会对它生成规则负载均衡,无头service直接绑定的是pod 的ip。无头service仍会有标签选择器,有标签选择器就会有endpoint资源。 使用场景:无头service一般用于有状态的应用场景,如Kaka集群、Redis集群等,这类pod之间需要相互通信相互组成集群,不在需要所谓的service负载均衡。