K8s基础问题

Kubernetes为什么又叫K8s?

因为中间省略了8个字母,这是硅谷那些程序员的一个惯例。

busybox是什么?

BusyBox是一个遵循GPL协议、以自由软件形式发行的应用程序。Busybox在单一的可执行文件中提供了精简的Unix工具集,可运行于多款POSIX环境的操作系统。在Kubernetes的使用中,BusyBox常作为Pod的init container执行一些预配置工作。

什么是容器,容器和虚拟机有什么区别?

容器是运行在Linux上的一个隔离进行运行环境,使用Linux的namespace机制做隔离,cgroups做资源限制,rootfs做文件系统。运行在容器里的进程貌似自己单独运行在一个操作系统中。
容器和虚拟机的区别是,虚拟机真的模拟出了一个操作系统,而容器仅仅是环境隔离,运行在同一个宿主机上的容器是共享宿主机内核的。

什么是容器镜像?

容器镜像的概念来自于Docker。Docker是一个打包,分发和运行应用程序的平台。它将应用程序以及其依赖的环境,包括操作系统打包到一起,叫做镜像。通过镜像分发的应用具有完全相同的环境,分发以后,Docker还可以以容器的方式运行镜像。镜像也叫Docker Image,分发Docker Image的平台叫做镜像仓库,运行Docker Image的容器就叫做Docker Container。

什么是Kubernetes?

Kubernetes是一个部署和管理容器化软件的平台。它将底层的基础设施都抽象化,让开发者部署一个集群就像部署一个节点那样简单。
Kubernetes由两部分组成,控制面板和工作节点。控制面板运行在主节点上,容器运行在工作节点上。控制面板组件提供了

  • Kubernetes API服务,用于和Kubernetes通信
  • Scheduler,调度器,负责分配容器运行的节点。(实际上分配的单位是Pod)
  • Controller Manager,集群级别的控制,比如跟踪工作节点,处理节点失败等
  • etcd,一个可靠的分布式数据存储,持久化存储集群配置

工作节点上的组件有:

  • Docker(rtk)或者其他容器运行时
  • Kubelet,与Kubernetes API通信,管理所在节点的容器。
  • Kubernetes Service Proxy,负责组件之间的负载均衡网络流量

什么是Pod?

Pod是一组并置的容器,代表了Kubernetes中基本的调度单元。一个Pod可以运行多个容器,然而大部分时间只运行一个。在同一个Pod中的容器,总会被调度到同一个节点上。所以Pod管理的是容器组。

  • 同一个Pod中的容器具有相同的UTS和network命名空间,所以它们有相同的主机名和网络接口,这意味着同一个Pod中的进程可以发生端口冲突(不同Pod中的进程永远不会端口冲突),同时一个Pod中的容器也具有相同的loopback网络接口,所以不同的容器的localhost是相同的。
  • Pod之间的网络是平坦的,不管实际节点之间的网络拓扑结构如何,这些Pod内的容器都可以像在局域网中一样通信。
  • 同一个Pod中的容器共享IPC命名空间,因此可以进行IPC进程通信。
  • 同一个Pod中的容器不共享文件系统,需要使用叫做Volume的Kubernetes资源来共享目录。

有了Pod标签,为什么还需要命名空间?

命名空间提供了另外一个维度的隔离,标签组织的Pod(或者其他资源)好比在数据库中的一张表上,可以通过不同的字段来选出不同Pod组,但是主键(Pod的名字)不能重复。而命名空间相当于另外一张表,Pod名字不仅可以重复,在一个命名空间中的kubectl命令操作也不会影响另外一个命名空间的资源。命名空间可以用来组织Kubernetes中的不同项目,或者不同开发,测试,线上的环境。

什么是存活探针,存活探针有哪几种?

存活探针检查容器是否在正常运行。有时候容器并没有崩溃,但是已经不能正常工作了,比如进入死循环,比如死锁(最近发现一个例子是数据库连接池资源耗尽,程序缓慢的等待一个连接超时才能进行一个查询),这时候Kubelet并不能发现容易运行异常,需要使用外部手段探测,存活探针就是干这个的。
存活探针有3种:

  • HTTP GET 探针,发送HTTP GET请求到容器,如果返回的状态码不是2xx或者3xx,就被认为探测失败,容器将会被重启。
  • TCP套接字探针,尝试与容器指定端口进行TCP连接,连接成功则认为探测成功。
  • Exce探针,在容器内执行一个命令,如果命令的退出码是0,则探测成功,否则重启容器。

为什么需要ReplicationController或ReplicationSet?二者有什么区别?

如果一个容器崩溃了,节点上的kubelet会重启这个Pod,但是如果这个节点完蛋了,就没有人负责这个Pod了。ReplicationController和ReplicationSet是Kubernetes集群级别的资源,由它们可以通过标签选择负责一组Pod,在节点Fail的时候,会选择其他节点运行Pod,保证Pod的数目符合预期。使用ReplicationController或者ReplicationSet创建的Pod也叫托管Pod。托管Pod的扩容,缩容只需要简单指定期望个数即可,ReplicationController或者ReplicationSet会自动增加或者删除Pod。

ReplicationSet的行为和ReplicationController完全相同,但是Pod选择器的表达能力更强。

ReplicationController只允许包含某个标签的匹配,ReplicationSet还允许缺失某个标签的匹配,标签多个值的匹配等等。应该使用ReplicationSet完全替代ReplicationController。

什么是DaemonSet,什么时候使用DaemonSet?

DaemonSet在每一个节点上运行一个Pod,一般都是和系统相关的,比如Kubernetes的kube-proxy进程。DaemonSet也可以通过标签选择节点,比如,把所有的SSD节点打上标签,然后将一个ssd-monitor的进程托管给DaemonSet,选择有SSD标签的节点。

什么是Job?

Job用于运行一次的任务(Pod)。因为一般的Pod如果退出,会被ReplicationSet重启,Job正常退出后不会被重启,但是如果是异常退出(崩溃或者返回错误的退出码),或者节点失败,仍会被调度到其他节点重新运行。

还有一种定时运行的Job叫做CronJob,可以在将来某一个时刻,或者周期性的时刻运行一个Pod。

Job和ReplicationSet, DaemoSet是相同级别的概念,都是运行Pod的一种方式,在YAML定义中都是先定义自己,然后定义Pod,所以有两个spec

为什么需要服务?什么是headless服务?

因为Pod是不稳定的,可能由于扩容,缩容, 节点失败等原因被调度到其他地方,并且Pod的IP是调度的时候才赋予的,因此,通过静态的配置文件是无法获得稳定的Pod IP地址列表。服务相当于一个集群内的反向代理,使得提供相同服务的Pod可以通过一个单一稳定的ClusterIP来访问提供服务的Pod。

通过集群IP,客户端的请求每次只能转发到一个Pod上,有时候客户端需要连接所有Pod或者自己选择连接哪个Pod,这时候可以使用headless服务。将服务定义中的clusterIP设置为None,就会创建一个headless服务。这时候用过域名(FQDN)访问Kubernetes的DNS会返回所有Pod的IP地址。

通过nslookup查看服务的DNS记录,正常的服务返回的是服务的IP地址,headless服务返回的是Pods的IP列表。

Kubernetes是如何做服务发现的?

分为三种类型,集群内服务发现,集群内发现集群外服务,集群外发现集群内服务。

集群内服务发现

  • 通过环境变量发现。在一个Pod开始运行的时候,Kubernetes会将已有的服务写入到Pod的环境变量中。Pod上的进程可以根据环境变量获取服务的IP和端口号。但是在Pod创建之后创建的服务无法被这个Pod发现。

  • 通过DNS发现服务。Kubernetes有一个Pod叫做kube-dns,是集群内部的DNS服务器,如果Pod使用这个内部的DNS服务(通过dnsPolicy属性指定),就可以通过服务的名字找到服务的IP。这种方式只能获得服务的IP,端口号如果是标准端口号(比如80,3306等)也没有问题。否则就需要程序预先配置或者从环境变量读取。

连接集群外部服务
先了解下什么是EndPoint?EndPoint也是Kubernetes的资源,是一组IP和端口的抽象。服务通过Pod的选择器得到一组EndPoint。当客户端连接到服务时,服务代理选择一组EndPoint,将连接重定向过去。

  • 创建一个不包含Pod选择器的服务,这样这个服务就没有EndPoint,然后,手动创建EndPoint,将外部服务的IP和Port赋值给EndPoint,并制定这个EndPoint属于之前那个服务。这样内部到服务的连接就会被重定向到外部服务。

  • 为外部服务创建别名。创建一个新服务,type属性为ExternalName,然后执行externalName属性为外部域名。然后内部Pod就可以使用内部服务域名来访问外部服务了。ExternalName服务仅在DNS级别实施,为服务创建了简单的CNAME DNS记录,连接到服务的客户端将直接连接到外部服务,完全绕过代理。

这个功能的用途是,可以让只少数集群节点具有外网访问功能,创建External服务的时候指定节点选择器,放在有外网服务的节点上,其他Pod都没有外网访问权限。

将服务暴露给外部

  • 使用NodePort。NodePort是服务的type属性,NodePort服务在每一个节点上打开一个端口(不管这个节点是否运行着服务的Pod),发给任何一个节点指定端口的连接,会被重定向到服务Pod中。这种方法太霸道,没啥必要的理由不要用。
  • 使用LoadBalance服务。同样将服务的type指定为LoadBalance,将创建一个LoadBalance类型的服务,这种类型的服务通过集群提供的负载均衡器来转发连接,但是并不是所有集群都提供负载均衡器的。
  • 通过Ingress暴露服务。Ingress是一种资源而不是一种服务类型。Ingress是工作在HTTP层的,因此可以解析HTTP协议,根据不同的主机和路径名路由到不同的服务。Ingress只需要一个公网IP就能为许多服务提供访问。

使用Ingress必须确保集群的Ingress控制器已经启用,可以通过addons查看。(基本上都会启用)

什么是就绪探针,就绪探针和存活探针的区别?

Pod启动后,会立即加入服务,接收到请求,这时候如果Pod中的服务进程还没有初始化完毕,这些请求就会失败,就绪探针就是解决这个问题的。就绪探针周期性检查容器,如果检查失败,则将Pod从服务中移除,然后继续检查,如果随后容器就绪,再重新加回服务。
就绪探针和存活探针一样,有三种类型,Exec,HTTP GET和TCP Socket。但是与存活探针不同的是,就绪探针检查失败,并不重启容器,而是将Pod移出服务。就绪探针确保服务中的Pod都是可用的。

Kubernetes是如何使用存储空间的?

因为每个容器都有自己独立的文件系统,默认是无法共享的。Kubernetes使用存储卷来管理文件系统。卷不是Kubernetes的顶级资源,而是Pod的一部分。Pod中的容器通过卷来共享数据。

卷有很多种,下边介绍几个简单的:

  • emptyDir,适用用Pod中多个容器共享数据,生命周期和Pod相同,用于临时存储。
  • gitRepo,通过检出Git仓库的内容来初始化的卷。本质上还是一个emptyDir
  • hostPath,将节点上的目录挂在到Pod中,这个是持久性的,但是只能节点上访问,因此一般使用hostPath卷的Pod会使用节点选择器选择固定的节点运行。
  • 使用网络存储,一些集群的基础设施会提供网络化的存储,比如集群运行在GCE上,可以使用GCE持久磁盘;如果运行在AWS EC2上,可以使用awsElasticBlockStorage卷;如果运行在Azure上可以使用azureDisk卷。这些都是持久化存储。
  • 上述方法都需要Pod的制作者理解存储的实现,Kubernetes提供了一种将将存储和Pod解耦的技术,这就是PV和PVC。运维人员和底层存储打交道,预先创建一些持久卷(PV);存储的使用者无需知道存储的底层细节,只需要描述所需存的存储的大小和权限等要求,也就是创建一个持久卷声明(PVC),Kubernetes会自动找到适当的持久卷并将其绑定到声明。因为一个PV只能绑定一个PVC,因此很容易造成浪费。Kubernetes又引入了一个StorageClass的资源,用来动态的创建持久卷,能更充分利用存储空间。运维人员先创建多个StorageClass,存储使用者在PVC声明中指定StorageClass类型,以及存储大小,StorageClass根据PVC动态的创建PV。

给容器传递参数有几种方式?
  • 将配置文件打入docker镜像,这相当与将配置硬编码,不是一个好主意。
  • 使用gitRepo卷,通过git保存配置文件,Pod使用gitRepo卷获得配置文件。
  • 在Pod spec container中覆盖命令和参数。设置 container中的command和args字段。
  • 通过环境变量传递参数,在container中设置evn字段。
  • 使用ConfigMap。通过命令行参数或者环境变量的方式,需要为不同环境下的Pod编写不同的部署文件。使用ConfigMap可以将环境的配置与Pod的定义解耦。被打入ConfigMap中的参数,可以以环境变量的方式,以命令行参数的方式,以文件的方式被容器使用。使用ConfigMap还有一个好处,就是热更新配置。将ConfigMap挂载到容器中,容器应用需要观察文件变化,支持热更新配置。这是环境变量和命令行参数做不到的。

如果应用不支持动态更新配置,最好不要使用热更新配置的特性,因为已经存在的Pod不会更新配置,新创建的Pod使用新配置,造成容器的不一致性。除非这种不一致是预期的。


ConfigMap和Secret的区别?

ConfigMap用于传递非敏感配置,数据明文存放的,Secret用于传递敏感配置,数据是base64编码的,并且只在内存中加载,不会写入磁盘

通过环境变量暴露的Secret可能在程序崩溃的时候被dump出来,另外子进程自动继承父进程的环境变量,这都是可能泄露的原因。推荐使用卷的范式传递Secret


在Pod中获取Kubernetes集群信息的方式有哪些?

Downward API 和 访问Kubernetes API Server

Downward API
在yaml文件中将Pod自身的元数据写入环境变量或者映射到Downward API卷中。这种方式只能获取自身的metadata,并且信息有限。

Kubernetes API Server
通过Kubernetes API Server的RESTFul API获取集群中的信息,这种方式可以获取集群中更多的资源信息。不仅限于Pod自身的运行环境。

Downward API 中为什么Pod的Label和Annotation不能使用环境变量的方式获取?

因为Pod的Label和Annotation是可以动态改变的(不用重启Pod ),而环境变量一旦被加载,就不能改变了。

Pod如何与Kubernetes API Server 通信?
要解决两个问题,一个是Pod如何找到API Server的地址,第二个是Pod和API Server如何互相信任。

Pod如何找到API Server的地址?
Kubernetes API Server也是一个服务,它的地址和端口在Pod启动的时候就已经作为环境变量写入到每一个容器中了。

root@curl:/# env | grep KUBERNETES_SERVICE 
KUBERNETES_SERVICE_PORT=443 
KUBERNETES_SERVICE_HOST=10.0.0.1 
KUBERNETES_SERVICE_PORT_HTTPS=443

如果API Server使用的是通用端口(443),即使不通过环境变量,也可以通过DNS直接访问API Server服务(https://kubernetes.)

Pod和API Server如何互相信任?
Kubernetes 会创建一个叫做default-token-xyz的Secret,这个Secret会被自动mount到每一个容器中。

root@curl:/# ls /var/run/secrets/kubernetes.io/serviceaccount/ 
ca.crt namespace token

Pod使用ca.crt文件来验证API Server的真实性。Pod通过传递token来证明自己的合法性。

root@curl:/# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

由于Pod与API Server 的互相验证过程复杂且程序化,因此可以使用一个代理容易来做这件事,将验证过程抽出来镜像化,之后就可以复用。这种模式叫做ambassador模式。
另外也可以使用Kubernetes API客户端来做这件事


Deployment是如何做滚动升级的?

Deployment会创建另外一个ReplicaSet,启动新版本的应用Pod,待新版本的Pod加入服务并且Ready之后,原来的ReplicaSet会删除一个旧Pod,实现滚动更新,一次更新几个Pod是可以通过maxSurge和maxUnavailable配置的,默认是总Pod数的25%。还可以使用minReadySeconds减慢滚动更新速度,来确保滚动升级的安全。

滚动升级过程中,有了就绪探针,为什么还要用minReadySeconds?

因为就绪探针成功一次就会被认为就绪了,然后就加入到服务,之后会删除旧版本的Pod然后再启动一个新版本的Pod,造成更新不可控。使用minReadySecond可以多观察一会服务,稳定了再进行下一步。最好minReadySecond中要有几次就绪探测。

Deployment,ReplicaSet,Service,Ingress之间的关系?
  • ReplicaSet是Pod的控制器,用于保证Pod的数量和预期一致。
  • Service可以看做是一组Pod的负载均衡,为这组Pod提供一个稳定统一的访问接口(Pod是动态的,IP不稳定),创建一个Service会在集群内部创建一个虚拟IP(headerless服务不会创建虚拟IP),集群内部使用FQDN可以解析出服务的虚拟IP或者Pod的IP(headless)。Service和ReplicaSet并无直接联系。
  • Deployment是负责管理服务升级的,它不直接控制Pod,而是通过ReplicaSet控制Pod,和Service没有啥关系。
  • Ingress是将一个或者多个Service暴露出来的入口,是一个7层的反向代理,可以通过http的path路由到不同的Service。Ingress只和Service发生联系。

StatefulSet和ReplicaSet有什么区别?

对于无状态的服务,应该使用RelicaSet,RelicaSet管理的Pod之间提供的服务没有实质的差别。对于有状态的服务,应该使用StatefulSet,StatefulSet管理的Pod都是独一无二的,如果一个Pod死掉了,StatefulSet保证新启动的Pod具有一样的名称,网络地址,存储。一些例子:显然,对于提供持久存储服务的Pod,应该使用StatefulSet管理,这样才能不丢失数据。另外对于一些分布式应用,不同的Pod有不同的角色,应用需要通过稳定的网络地址访问服务,即使这个角色的Pod挂掉了,新启动的Pod需要恢复这个角色的状态。

StatefulSet为什么需要headless服务?

如果不使用headless服务,服务的DNS解析返回的是服务的虚拟IP地址,无法访问指定的Pod。headless服务解析的时候,会返回所有Pod的IP,StatefulSet会给这些IP配置一条DNS域名,类似a-0.foo.default.svc.cluster.local 。因为StatefulSet Pod名字是稳定不变的,因此就可以通过每一个Pod的域名来访问Pod,这在不同的Pod有不同的司职的情况下能提供方便。

否则需要将不同司职的Pod创建不同的服务

同时每个Pod有了域名之后,StatefulSet还会为headless服务创建一条SRV记录。

SRV记录是DNS服务器的数据库中支持的一种资源记录的类型,它记录了哪台计算机提供了哪个服务这么一个简单的信息。与常见的A记录、cname不同的是,SRV中除了记录服务器的地址,还记录了服务的端口,并且可以设置每个服务地址的优先级和权重。
SRV的记录格式为:_Service._Proto.Name TTL Class SRV Priority Weight Port Target


你可能感兴趣的:(后端)