joeの小窝

Loading...

k8s和docker面试题

k8s面试题:主要就是面试会问的一下

前言:本篇模拟面试官提问的各种docker,k8s问题,意在提高面试的通过率

docker工作原理是什么,讲一下

docker是一个cs结构的系统,客户端和服务端,docker守护进程运行在宿主机上,手机进程从客户端接收命令并管理运行在主机上的容器,容器是一个运行时环境,相当于是一个进程在里面跑,这个就是我们说的集装箱

docker组成包含哪些部分?

一个完整的docker包含一下几个部分组成

  1. docker client,客户端,为用户提供一系可执行命令,用户用这些命令实现跟docker daemon交互

  2. docker daemon 守护进程,一般在主机后台运行,等待接收客户端的请求消息

  3. docker image 镜像,镜像run之后就会生成docker容器

  4. docker container 容器,一个系统级别的镜像,精简版的虚拟机,通常只有几百MB,甚至更少,拥有自己的ip和系统目录结构,运行容器需要本地存在对应的镜像,如果没有的话,就会从网上的镜像仓库进行拉取镜像

docker使用客户端-服务器(c/s)架构模式,使用远程api来管理和创建docker容器,docker容器通过镜像来创建,容器和镜像的关系类似面向对象中类和对象,镜像是一个模板,然后通过这个模板生成一个容器,可以在这个容器上做一些操作

docker与传统虚拟机的区别是什么呢?

  1. 传统虚拟机是需要安装整个操作系统的,然后再在上面安装业务应用,启动应用,通常需要几分钟启动应用,而docker是直接使用镜像来运行业务容器,容器启动属于秒级别的

  2. docker需要的资源更少,docker在操作系统级别进行虚拟化,docker容器共享的宿主机的内核,几乎没有什么性能损耗,而虚拟机运行整个操作系统,占用物理机的资源比较多

  3. docker更加的轻量,docker的架构可以共用一个内核,所占内存极少,同样的硬件环境,docker运行的容器数量远多于虚拟机数量,对系统的利用率非常的高

  4. 与虚拟机相比,docker的隔离性比较弱,docker属于进程之间的隔离,虚拟机可以实现系统级别的隔离

  5. 虚拟化创建是分钟级别的,docker容器化创建是秒级别的,docker的快速迭代性,决定了无论是开发,测试,部署都可以节省大量时间

  6. 虚拟机可以通过镜像实现环境交付的一致性,docker在dockerfile中记录容器构建古城,可以在集群中实现快速分发和快速部署

  7. 移植性好,不管到哪一个平台上面,都能够保持这个环境的一致性

docker技术的三大核心概念是什么?

镜像:是一个轻量级,可执行的独立软件包,包含运行某个软件所有需要的所有内容,把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码,运行时需要库,环境变量,配置文件等),这个打包好的运行环境就是image镜像文件,可以通过docker history查看构建过程

容器:容器是基于镜像创建的,是镜像运行起来之后的一个实例,容器才是真正的运行业务程序的地方,如果把镜像比做程序里面的类,那么容器就是对象,根据类创建出来的,容器里面跑就是一个进程

镜像仓库:存放镜像的地方,研发工程师打包好镜像之后需要把镜像上传到镜像仓库中去,其他人员可以从这个镜像仓库里面拉取镜像下来,运行容器

centos镜像几个G,但是docker centos镜像才几百兆 为什么呢?

一个完整的操作系统是包含linux内核和rootfs根文件系统,就是我们熟悉的/dev/,/proc,/bin目录,我们平时运行的centos,还会安装很多的软件,服务,图形化界面等,所以几个G也是很常见的

而对于容器镜像而言的话,所有容器都是共享宿主机的linux内核的,docker镜像只需要提供一个很小的rootfs即可,只需要包含最基本的命令,工具,程序库即可,只需要包含运行环境的包即可,无关的包都不会安装的,所以centos镜像才那么小

讲一下镜像的分层结构以及为什么要使用镜像的分层结构

一个新的镜像其实是从base镜像一层一层叠加上去的,就是有一个基础镜像,再在上面做一些定制化的操作,每安装一个软件,dockerfile中使用run指令,就会在现有的镜像的基础上叠加一层,这样一层一层的叠加最后构建整个镜像,所有我们docker pull拉取镜像的时候才会看到docker是一层一层的拉取的

分层最大的好处就是,共享资源,比如有很多镜像都是从相同的base镜像构建的,那么本地只需要保存一份base镜像即可,这样就可以为所有容器服务了,而且镜像的每一层都可以被共享的

讲一下容器的copy-on-write的特性,修改容器里面的内容会修改镜像吗?

我们都知道,镜像是分层的,镜像的每一层都可以被共享,同时,镜像是只读的,当一个容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称为容器层,就是可写的,容器层下面称为镜像层(只读的)

所有对容器的改动,添加,删除,文件等操作都会发生在容器层里面,因为只有容器层是可写的,镜像层是只读的,镜像的层数量可能会很多,所有镜像层会联合在一起组成一个大的文件系统,如果不同层中有一个相同的路径,那么上的路径会覆盖掉下层的路径,也就是说用户只能访问到上层的路径,在容器层中,用户看到的是一个叠加后的文件系统

简单描述一下dockerfile整个的构建流程

  1. 首先,创建一个目录用于存放应用程序以及构建过程中使用到的各个文件等

  2. 然后,在这个目录下创建一个dockerfile文件,文件名就是Dockerifle,不然的话,build的话,需要加上-f参数,指定文件名

  3. 编写dockerfile文件,编写指令,如,FROM指令指定基础镜像,COPY指令复制文件,RUN指令指定要运行的命令,ENV设置环境变量,EXPOSE指定容器暴露的端口,WORKDIR指令设置当前的工作目录,CMD容器启动运行的命令,等等来构建镜像

  4. dockerfile编写完后,就可以构建镜像了,build -t 镜像名:tag . 最后一个. 表示在当前目录,如果不使用默认的文件名的话,就需要-f指定dockerfile文件,-f /docker/123file

  5. 使用docker build 命令构建后,docker会将当前目录下的所有文件发送给docker daemon,顺序指定dockerfile文件里的指令,在这个过程中会生成一个临时容器,在临时容器里面执行RUN命令,安装成功后,docker底层会将这个容器保存为一个镜像,然后删除临时容器,依次类推,一层一层的构建这个镜像,直到最后镜像构建成功

dockerfile基本指令有哪些呢?

FROM:指定基础镜像,必须为第一个指令,使用哪一个基础镜像来构建镜像
COPY 复制文件到镜像
ADD 复制文件到镜像,ADD会自动解压,tar,zip,tgz,xz等文件
ENV 设置环境变量
EXPOSE 暴露容器端口,仅仅是提示,还需要-P才能暴露
RUN 在容器里面运行的命令
CMD 指定容器启动时默认的运行的命令,多个CMD的话,最后一个生效,另外docker run 之后的参数会替代CMD
ENTRYPOINT 指定容器启动时运行哪些命令,如果有多个的话,最后一个生效,另外如果dockerfile中同时存在CMD和ENTRYPOINT,那么CMD会当成参数传递给ENTRYPOINT

如何进入容器呢?

docker attach ,docker exec 2个都可以进入容器

attach 是进入容器启动命令的终端,exec 是在容器里面启动一个tty终端

attach 进入后 crtl+c 退出后,会直接关闭容器终端,这样的话,容器没有进程,就会死掉
ctrl+pq 退出,不会关闭容器终端,仅退出

exec进入后,开启一个新的bash终端,退出容器的话,只是退出我们自己的bash窗口,容器内部没有影响

exec是启动一个新的进程,attac会直接进入到容器启动命令时的终端

什么是k8s,说出你的理解

k8s是kubernetes的简称,本质就是一个容器编排系统,主要用于管理容器化应用,其目标是让部署容器化的应用简单并且高效,k8s提供了应用部署,规划,更新,维护的一种机制

说简单点: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是k8s最重要的核心组件之一,它是k8s集群管理的统一访问入口,提供了restful api接口,实现了认证,授权,准入控制等安全功能,api-server还是其他组件之间的数据交互和通信枢纽,其他组件彼此之间并不会直接通信,其他组件对资源对象的增删改查,都是交由api-server处理后,api-serevr在交给etcd数据库做持久化存储,只有api-server才能直接操作etcd数据库,其他组件都不能直接操作etcd数据库,其他组件都是通过api-server间接读取,写入数据到etcd

简单而言:集群的唯一访问入口,能直接操作etcd数据库,写入数据,其他组件通过api-server间接通信,所有的操作都提交给api-server,处理后,交给etcd做数据持久化存储

kube-controller-manage:是k8s各种控制器的管理者,是k8s集群内部的管理控制中心,也是k8s自动功能的核心,包含了deploy,replicaset,daemonset,job,cronjob,node控制器,statefulset,endpoint等等各种资源对象的控制器,每个控制器都负责一种特定资源的控制流程,而controller-manager证是这些controller的核心管理者

简单而言:就是管理所有控制器的,一些资源对象的最高管理者

kube-scheduler:负责集群资源的调度,其作用是将待调度的pod通过一系列的调度算法计算出最合适node的节点,然后将这个pod调度在这个node节点上,scheduler会根据pod的信息,全部节点信息列表,过滤掉不符合要求的节点,过滤出后,对候选节点进行打分,选出最高分的节点,scheduler就会把目标pod安置在这个节点上

etcd:etcd是一个分布式键值对存储数据库,主要就是保存k8s集群状态数据,比如pod,service等资源的信息,etcd可以是单个,也可以是多个,另外说明一下,etcd本质上不可以与master节点部署在一起,只要master节点能通过网络连接etcd数据库即可

kubelet:每个node节点上都会有一个kubelet服务进程,是master管控所有node的节点代理(master不会直接远程操作node主机,所有指令都是走的kubelet中转),负责维护pod和容器的生命周期,当监听到master下发到本节点的任务时,比如创建,更新,终止pod等任务,kubelet立刻通过docker/containerd来创建,更新,销毁容器,kubelet还会定时执行pod中容器定义的指针,然后根据容器重启策略执行对应的操作,每个kubelet进程都会在api-server上注册本节点的自身信息,用于定期选向master汇报本节点资源的使用情况

简单而言;kubelet就是使用容器运行时,来管理容器的,接收来自master下发的指令

kube-proxy:kube-proxy运行在node节点上,在node节点上实现pod网络代理,维护网络规划和四层负载均衡工作,kube-proxy会监听api-server中从而获取service和endpoint的变化情况,创建并维护路由规则以提供服务ip和负载均衡功能

简单而言:是service的透明代理兼负载均衡器,其核心功能是将到某个service的访问请求转发到后端的多个pod实例上

container-runtime:容器运行时环境,就是运行容器所需要的系列的程序,目前k8s支持的容器运行时有很多种,docker,containerd或其他,但是新版本的k8s已经弃用docker,使用containerd

kubelet功能,作用是什么?

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

  1. 节点管理,kubelet启动时会向api-server进行注册,然后定时向api-server汇报本节点信息状态,资源使用状态,这样master就知道了node节点资源剩余,直到了整个集群所有节点的资源情况,这对于pod的调度和正常运行非常的重要

  2. pod管理:kubelet负责维护node节点上pod的生命周期管,当kubelet监听到master下发到自己节点任务时,比如要创建,更新,删除一个pod,kuebelt就会通过容器运行时来创建,更新容器

  3. 容器健康检查,pod中定义启动探针,存活探针,就绪探针,kubelet会定期调用容器中探针来检测容器是否存活,是否就绪

  4. Metrics Server资源监控,在node节点上部署metrics server 用于监控node节点,cpu,mem,文件系统,网络使用情况,kubelet则通过metrics server获取所有节点和容器数据

k8s创建pod的工作流程是什么呢?

  1. 用户提交创建请求,发送到api-server

  2. api-server 校验后,写入到etcd,

  • 鉴权,权限校验,准入控制,判断用户有没有权限

  • 校验通过后,将pod信息写入到etcd

  • 返回创建成功给客户端,但是此时pod只是一条数据,还没有真正运行

  1. 调度器感知新pod
  • kube-scheduler持续监听api-server,发现etcd新增未绑定节点的pod
  1. 调度器筛选node
  • 预选:过滤掉不符合条件的(资源不足,有污点,标签不匹配)

  • 优选:在待选的节点上依次进行打分,选出最优node

  • 给pod打上nodename,再次提交给api-server

  1. 绑定关系存入etcd
  • api-server 接收调度结果,更新etcd,pod与目标node完成了绑定
  1. 目标节点kubelet感知变更
  • 每个node上的kubelet实时监听api-server,发现有pod被调度到本机
  1. kubelet下发指令给容器运行时
  • kubelet解析pod配置,调用本机容器运行时

  • 检查本地镜像,不存在则拉取镜像

  • 创建pod沙箱(pauser容器,负责网络/进程共享)

  • 依次创建,启动业务容器

  1. CNI配置网络

pause容器启动后,调用cni插件

  • 给pod分配ip

  • 配置网卡,路由,网桥,打通集群网络

  1. 容器探针,状态检测

kubelet会持续执行,存活探针,就绪探针,启动探针,监控容器状态

  1. 状态写会etcd中

kubelet把pod/容器当前运行状态,上报给api-server,最终更新到etcd

集群所有组件看到,pod状态为Running,流程结束

补充点:

如果是deploy创建pod,比纯pod多一层控制器,前置流程

  1. 用户创建deploy-->api-server---->存入到etcd

  2. deploy控制器监听api-server变化,根据副本数,自动创建对应数量的pod对象

  3. 后面流程就一样了(调度--->kubelet--->启动)

真正干活的是kubelet+容器运行时

全程组件不通信,全部通过api-server中转

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

api-server的端口是8080和6443,前者是http的端口,后者是https端口

pod不会直连master节点的6443,而是通过集群内部的service+固定的dns+自动挂载令牌来访问

  1. 访问入口 kubernetes这个service
  • default下面的kuberenetes服务

  • dns kuberenetes.default.svc.cluster.local

  1. pod自动拿到地址和端口,每个pod启动时,容器里会自动注入环境变量
KUBERNETES_SERVICE_HOST=10.96.0.1  # 就是 kubernetes Service 的 ClusterIP
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
  1. 认证,自动挂载service account令牌

默认每个pod都会挂default服务账号

/var/run/secrets/kubernetes.io/serviceaccount/
├── token       # JWT 令牌(用来认证)
├── ca.crt      # 根证书(验证 TLS)
└── namespace

访问api时带上这个token,api-server就认你是谁

k8s名称空间有什么作用?

ns是一个非常重要的资源,主要就是实现多套环境的资源隔离

将就能内部的资源分配到不同ns中,可以形成逻辑上的隔离,以方便不同的资源进行隔离使用和管理,不同命名空间可以存在同名的资源

可以通过k8s授权机制,将不同的ns交给不同的租户进行管理,这样就是实现多租户的资源隔离

pod是什么呢?

在k8s的世界中,k8s并不直接处理容器,而是使用多个容器共存的理念,这组容器就叫做pod,pod是k8s中可以创建和管理的最小单元,是资源对象模型中用户创建或部署的最小资源对象,其他资源对象都是用来支撑pod对象功能,

比如,pod控制器就是用来管理pod,svc或者ingress资源对象就是用来暴露pod,引用对象的,pv资源就是为pod提供存储的等等

k8s不会直接处理容器,而是pod,pod才是k8s中可以创建和管理的最小基本单元

pod里面有多个容器,管理pod就相当于是管理了一组容器

pod原理是什么呢?

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

pod里面可以跑多个容器,多个进程

pod有什么特点?

  1. 每一个pod就像一个独立的精简版的虚拟机,k8s会为每个pod分配一个集群内部唯一的ip地址,所以每个pod都拥有自己的ip地址,主机名,进程等

  2. 一个pod可以包含1个或者多个容器,一个容器被设计为只运行一个进程,一个pod只能运行在单个节点上,pod不能跨节点运行

  3. 每个pod都一个根容器,pause容器,属于k8s平台的一部分,这个就是pod和k8s进行交互用的

  4. 一个pod里的多个容器共享pod ip,这就意味着pod里面的多个容器进程占用的端口不能相同,否则这个pod里面的端口就会产生冲突,既然每个pod都有自己的ip和端口空间,那么对不同的2个pod来说,就不会存在端口冲突

  5. pod是k8s中扩容,缩容的基本单位,也就是说k8s中扩容和缩容是针对pod而言,而非容器

pause容器作用是什么呢?

每个pod里面都会运行一个pause容器,根容器,其他容器称为业务容器

  1. pause容器主要就是为业务容器提供linux命名空间,共享基础,pid,net,icp等,这些业务容器共享pause容器的网络命名空间和volume挂载卷的,当pod被创建时,pod会先创建pause人工气,从而把其他业务容器加入到pause容器,从而让所有业务容器都在同一个网络命名空间中,这样就实现了网络共享,这个设计确保了同一个pod内容器可以直接通过localhost地址互相通信,从而实现了高效的内部通信

  2. pod还可以共享存储,在pod级别引入数据卷volume,业务容器可以挂载这个数据卷从而实现了持久化存储

  3. 由于pause容器始终保持着运行状态,他还承担着维护pod ip地址角色,pod的ip地址通常是动态分配的,只要这个pause容器在运行,就可以维持这个ip地址不变,即使pod内的其他容器重新启动也不会影响这个ip地址,但是这个pause容器重启后,这个ip地址会变化的

pod重启策略有哪些呢?

pod重启策略,决定了当容器异常退出或健康检查失败时,kubelet将如何响应,注意是kubelet重启容器,因为是kubelet负责容器的健康检测

pod中的容器遵守这个规矩

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

pod镜像拉取策略有哪些呢?

imagePullpolicy配置拉取策略

Always:默认值,总是重新拉取,每个创建pod都会从镜像仓库重新拉取镜像

IfNotPresent:镜像在node节点宿主机不用拉取,不在的话,就需要拉取

Never:永远不会主动拉取镜像,仅使用本地镜像,需要你手动拉取镜像到node节点,如果本地不存在的话,则pod启动失败

pod探针有哪些(3种探测方式)

启动探针,存活探针,就绪探针

启动探针优先级最高,执行时,其他探针不能执行

专门针对启动很慢的应用的,java,mysql这些

主要作用就是:给足启动时间,避免还没启动成功被存活探针误杀重启,启动探针失败次数超限,认为启动失败,重启pod

  • mysql启动要60s,不加启动探针,存活探针10秒检测,10秒还没启动,直接重启pod,无限死循环

  • 有了启动探针后,给它60s到120s启动时间,期间不判活,不重启,起来后在开启存活探针检测

存活探针:判断容器进程是否卡死,判断容器存活的,判断失败,重启

就绪探针:检测容器是否能够正常对外提供服务的,检测失败的话,svc删除对应的pod的endpoint,请求就不会转发到这个pod上,不重启,只是没有流量进来

# httpGet: 通过容器的ip,端口,路径,发送http请求,返回200-400范围内的状态码表示成功

# exec:在容器内执行shell命令,根据命令退出码是否为0进行判断,0表示健康,非0表示不健康

# TcpSocket:与容器的ip,端口建立TCP Socket连接,能建立则说明探测成功,不能建立表示失败

其他参数:

initialDelaySeconds:容器启动后等待多长时间开始检测

periodSeconds:表示执行探测的频率,间隔多久探测一次

timeoutSeconds:表示探测超时时间,必须在超时时间内做出响应,否则本次探测失败,默认1s,1s内必须给答复,不然探测失败

successThreshold:表示最少探测多少次才被认定为成功,默认是1

failureThreshold:表示连续探测多少次才被认定为失败,默认是3,连续3次失败,认定为失败

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

就绪探针和存活探针的区别?

2者的作用不同

存活探针检测容器是否存活,如果检测失败的话,kubelet会调用容器运行时,重启容器

就绪探针检测容器服务是否就绪,失败的话,不允许pod接收流量,确保了svc中的pod都是可用的

简单描述一下pod的终止过程?

  1. 用户向api-server发送删除pod对象命令

  2. api-server中的pod对象信息会随着时间的推动而更新,在宽限期内(默认30s),pod被视为dead

  3. 将pod标记为terminating状态

  4. kubectl通过watch机制监听api-server,监控到pod对象为terminating状态了就会启动pod关闭过程

  5. kube-proxy 更新这个转发规则,endpoint控制器监控到pod对象关闭行为,移除endpoint对应的pod

  6. 如果当前pod定义了preStop钩子处理器,则在被标记为terminating后,会以同步的方式启动执行

  7. pod对象中的容器接收到停止信息,宽限期结束后,pod中还存在运行的进程,pod对象会收到立刻终止的信息

  8. kubelet请求api-server将此pod资源的宽限期设置为0从而完成删除操作,此时pod对用户已不可见

pod生命状态有哪几种?

Pending(挂起):api-server已经创建pod,但是pod还有一个或者多个容器的镜像没有创建,包括正在下载镜像的过程

Running:pod内所有容器已经创建,且至少有一个容器处于运行状态

Succeed(成功):pod内所有容器均已退出,且不会再重启

Failed(失败):pod内所有容器均已退出,且至少有一个容器为退出失败状态

Unknown(未知):某种原因api-server无法获取该Pod的状态,可能由于网络通信问题导致

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

  1. 一个pod在开始创建的时候,本身处于pending状态,这时可能是正在拉取镜像,正在创建容器的过程

  2. 如果等了一会还处于pending状态,我们可以使用kubectl describe 查看pod详细信息,一般会有一下情况导致处于pending状态

  3. 调度器调度失败,scheduler调度器无法为这个pod分配一个合适的node节点,node节点的cpu,内存压力,导致没有节点可以调度,或者node节点有污点,而pod没有容忍污点,亲和性调度失败等等

  4. pv,pvc无法动态创建,这些没有创建,pod也会处于pending状态

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

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

  1. 初始化容器必须运行完成至成功结束,如果失败的话,那么k8s需要重启直到成功完成

  2. 初始化容器必须按照顺序执行,当前一个容器执行成功后,后面的容器才能执行,这个叫做阻塞

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

直接在pod中定义,limits限制pod容器能使用最大的cpu和内存,request就是pod中容器启动时申请的cpu和内存,最小的,至少需要这个才能启动

pod中定义command和args参数,不会和docker镜像的entrypoint冲突吗?

在pod中定义command参数用于指定容器的启动命令列表,如果不指定,则默认使用dockerfile打包时的启动命令,args参数用于容器的启动命令需要的参数列表

kubernetes中的command、args其实是实现覆盖dockerfile中的ENTRYPOINT的功能的

  1. 如果command和args均没有写,则使用dockerfile的配置

  2. 如果command写了但是args没有写,则dockerifle默认的配置被忽略掉,执行pod容器指定的command

  3. 如果command没写但是args写了,则dockerfile中的entrypoint会被执行,使用当前args的参数

  4. 如果command和args都写了,那么dockerfile会被忽略掉,执行pod当前定义的command和args

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

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

最重要的就是,deploy中定义了pod的标签,然后定义了标签选择器,这样就能通过标签选择器来选择哪些pod是被deploy控制的,service也是通过标签选择器来关联哪些pod

service的域名解析格式,pod的域名解析格式?

service的dns域名表示格式为:servicename.namespace.svc.cluster.local

pod的dns域名格式为:pod-ip.namespace.pod.cluster.local

statefuleset创建的pod,需要无头服务,域名格式就是每个pod都有一个单独的域名,这样的话,数据库就能单独连接那一个了

service类型有几种?

ClusterIP:集群内部使用,集群之间的通信

NodePort:service对外访问应用,会在每个节点上暴露一个端口,这样外部浏览器访问地址为,任意节点ip:NodePort 就能连接上service

LoadBalancer:表示service对外访问应用,这种类型的service是公有云环境下的service,需要外部云厂商的支持,需要一个公网ip地址

ExternalName:这种类型的service会把集群外部的服务引入集群内部,这样集群内直接访问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、endpoint、kube-proxy三种的关系是什么?

  1. service:在kubernetes中,service是一种为一组功能相同的pod提供单一不变的接入点的资源。当service被建立时,service的ip和端口不会改变,这样外部的客户端(也可以是集群内部的客户端)通过service的ip和端口来建立链接,这些链接会被路由到提供该服务的任意一个pod上。通过这样的方式,客户端不需要知道每个单独提供服务的pod地址,这样pod就可以在集群中随时被创建或销毁。

  2. 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对象。

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

kube-proxy有哪几种模式呢?

kube-proxy有3种模式,分别是userspace(用户空间)、iptables、ipvs,其中user namespace已经被废弃了这种就不在讲解了。

生产环境中使用ipvs。因为ipvs性能比iptables高。

iptables本质上是Linux的一个高效的防火墙,提供数据包处理和过滤方面的能力,kube-proxy使用iptables这种模式的时候,其连接处理算法复杂度是O(n),其中的n随集群规模同步增长,换句话说,当k8s集群中的service数量很多,比如集群有1000个service,每个service后端又有多个Pod副本,那每个节点上的iptable规则将非常多,kube-proxy需要动态维护这些庞大的规则将带来严重的节点性能问题。

而ipvs是Linux内核功能中专门用于处理高性能负载均衡的功能,它使用更高效的数据结构,如hash表并支持索引,IPVS有一套优化过的 API,使用优化的查找算法,而不是简单的从列表中查找规则,ipvs还支持多种调度算法,如rr、wrr、lc、wlc,而iptables就只有一种随机平等的选择算法。其次,kube-proxy在IPVS模式下,连接处理算法复杂度是O(1)而不是O(n)。换句话说,多数情况下,连接处理效率是和集群规模大小无关。

无头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负载均衡。

deployment怎么扩容或缩容?

直接修改pod副本数即可,可以通过下面的方式来修改pod副本数:
1、直接修改yaml文件的replicas字段数值,然后kubectl apply -f xxx.yaml来实现更新;
2、使用kubectl edit deployment xxx 修改replicas来实现在线更新;
3、使用kubectl scale --replicas=5 deployment/deployment-nginx命令来扩缩容

deployment的更新升级策略有哪些?

deployment的升级策略主要有两种。
1、Recreate 重建更新:这种更新策略会杀掉所有正在运行的pod,然后再重新创建的pod;
2、rollingUpdate 滚动更新:这种更新策略,deployment会以滚动更新的方式来逐个更新pod,同时通过设置滚动更新的两个参数maxUnavailable、maxSurge来控制更新的过程。

deployment的滚动更新策略有两个特别主要的参数,解释一下它们是什么意思?
答:deployment的滚动更新策略,rollingUpdate 策略,主要有两个参数,maxUnavailable、maxSurge。

maxUnavailable:最大不可用数,maxUnavailable用于指定deployment在更新的过程中不可用状态的pod的最大数量,maxUnavailable的值可以是
一个整数值,也可以是pod期望副本的百分比,如25%,计算时向下取整。
maxSurge:最大激增数,maxSurge指定deployment在更新的过程中pod的总数量最大能超过pod副本数多少个,maxUnavailable的值可以是一个整数
值,也可以是pod期望副本的百分比,如25%

简述一下deployment的更新过程 (经常问)

deploy通过控制rs来实现的,rs创建真正的pod副本,每更新一次,deploy都会创建一个新的rs

deployment的回滚使用什么命令

在升级deployment时kubectl set image 命令加上 --record 参数可以记录具体的升级历史信息,使用kubectl rollout history deployment/deployment-nginx 命令来查看指定的deployment升级历史记录,如果需要回滚到某个指定的版本,可以使用kubectl rollout undo deployment/deployment-nginx --to-revision=2 命令来实现。

讲一下都有哪些存储卷,作用分别是什么?

emptyDir 用于存储临时数据的简单空目录 一个pod中的多个容器需要共享彼此的数据 ,emptyDir的数据随着容器的消亡也会销毁

hostPath 用于将目录从工作节点的文件系统挂载到pod中 不常用,缺点是,pod的调度不是固定的,也就是当pod消失后deployment重新创建一个pod,而这pod如果不是被调度到之前pod的节点,那么该pod就不能访问之前的数据

configMap 用于将非敏感的数据保存到键值对中,使用时可以使用作为环境变量、命令行参数arg,存储卷被pods挂载使用 将应用程序的不敏感配置文件创建为configmap卷,在pod中挂载configmap卷,可是实现热更新

secret 主要用于存储和管理一些敏感数据,然后通过在 pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了,pod会自动解密Secret 的信息 将应用程序的账号密码等敏感信息通过secret卷的形式挂载到pod中使用

pv的访问模式有哪几种

ReadWriteOnce,简写:RWO 表示,只仅允许单个节点以读写方式挂载;

ReadOnlyMany,简写:ROX 表示,可以被许多节点以只读方式挂载;

ReadWriteMany,简写:RWX 表示,可以被多个节点以读写方式挂载;

pv的回收策略有哪几种

主要有2中回收策略:retain 保留、delete 删除。

Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,PV被视为已释放,管理员可以手动回收卷。

Delete:删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS,GCE PD,Azure Disk,OpenStack Cinder等。

Recycle:回收,如果Volume插件支持,Recycle策略会对卷执行rm -rf清理该PV,并使其可用于下一个新的PVC,但是本策略将来会被弃用,目前只有NFS和HostPath支持该策略。(这种策略已经被废弃,不用记)

在pv的生命周期中,一般有几种状态

pv一共有4中状态,分别是:

创建pv后,pv的的状态有以下4种:Available(可用)、Bound(已绑定)、Released(已释放)、Failed(失败)

Available,表示pv已经创建正常,处于可用状态;

Bound,表示pv已经被某个pvc绑定,注意,一个pv一旦被某个pvc绑定,那么该pvc就独占该pv,其他pvc不能再与该pv绑定;

Released,表示pvc被删除了,pv状态就会变成已释放;

Failed,表示pv的自动回收失败;

存储类的资源回收策略?

主要有2种回收策略,delete 删除,默认就是delete策略、retain 保留。
Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,PV被视为已释放,管理员可以手动回收卷。

Delete:删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS,GCE PD,Azure Disk,OpenStack Cinder等。

注意:使用存储类动态创建的pv默认继承存储类的回收策略,当然当pv创建后你也可以手动修改pv的回收策略。

你们的服务发布怎么做的?或者你们的cicd流程是怎么做的?

我们服务采用cicd流程发布的,具体的说,就是只要用到两个工具: jenkins和argocd。jenkins负责实现ci,argocd负责实现cd。主要是这么几个步骤:

1、开发写好代码将代码提交到gitlab仓库;

2、然后手动在jenkins页面上点击构建任务;

3、jenkins上定义了多个流水线项目,每个pipeline流水线项目都对应一个gitlab仓库的项目代码,流水线的配置都写在jenkinsfile文件里面,而jenkinsfile文件也是存放在gitlab仓库上进行托管的;

4、构建的流程只要是jenkinsfile文件定义的,jenkinsfile内容大致有这么几步:

第一步、先使用git命令克隆代码到工作目录并检出全部的分支然后写入到一个临时文件。
第二步、读取临时文件全部分支,提示用户选择要构建的分支和要发布的环境。
第三步、开始编译源代码,前端代码使用npm命令编译,后端代码使用maven编译。
第四步、代码编译完成就可以得到jar包了,这时开始构建镜像并推送镜像到harbor镜像仓库。
第五步、部署,这里的部署并不是真正意思上的部署,而是镜像的tag写回到gitlab仓库里去,并且使用kustomize命令修改yaml资源清单文件,这步主要是让argocd实现部署。
第六步、发送钉钉通知消息。
以上,第五步的更新gitlab仓库的时候,argocd会监听到gitlab仓库里面的k8s资源清单文件发生了改变,然后就会自动的应用部署,部署方式仍然是滚动更新deployment的镜像,这样就实现了自动化部署

后面再继续补充

# 面试
https://chuna2.787528.xyz/guantou1992/p/18797484


https://www.xiaolincoding.com/interview/linux.html#ps%E5%91%BD%E4%BB%A4%E9%87%8C%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B%E9%80%89%E9%A1%B9-ps%E5%B1%95%E7%A4%BA%E5%93%AA%E4%BA%9B%E4%B8%9C%E8%A5%BF

https://chuna2.787528.xyz/crazymakercircle/p/14366893.html

posted @ 2026-05-30 14:33  乔的港口  阅读(9)  评论(0)    收藏  举报