k8s生产中遇到什么特别影响深刻的问题吗,问题排查解决思路是怎么样的?
(此问题被问到的概率高达90%,所以可以自己准备几个自己在生产环境中遇到的问题进行讲解)
前端的lb负载均衡服务器上的keepalived出现过脑裂现象。
- 当时问题现象是这样的,vip同时出现在主服务器和备服务器上,但业务上又没受到影响;
- 这时首先去查看备服务器上的keepalived日志,发现有日志信息显示凌晨的时候备服务器出现了vrrp协议超时,所以才导致了备服务器接管了vip;查看主服务器上的keepalived日志,没有发现明显的报错信息,继续查看主服务器和备服务器上的keepalived进程状态,都是running状态的;查看主服务器上检测脚本所检测的进程,其进程也是正常的,也就是说主服务器根本没有成功执行检测脚本(成功执行检查脚本是会kill掉keepalived进程,脚本里面其实就是配置了检查nginx进程是否存活,如果检查到nginx不存活则kill掉keepalived,这样来实现备服务器接管vip);
- 排查服务器上的防火墙、selinux,防火墙状态和selinux状态都是关闭着的;
- 使用tcpdump工具在备服务器上进行抓取数据包分析,分析发现,现在确实是备接管的vip,也确实是备服务器也在对外发送vrrp心跳包,所以现在外部流量应该都是流入备服务器上的vip;
- 怀疑:主服务器上设置的vrrp心跳包时间间隔太长,以及检测脚本设置的检测时间设置不合理导致该问题;
- 修改vrrp协议的心跳包时间间隔,由原来的2秒改成1秒就发送一次心跳包;检测脚本的检测时间也修改短一点,同时还修改检测脚本的检测失败的次数,比如连续检测2次失败才认定为检测失败;
- 重启主备上的keepalived,现在keepalived是正常的,主服务器上有vip,备服务器上没有vip;
- 持续观察:第二天又发现keepalived出现过脑裂现象,vip又同时出现在主服务器和备服务器上,又是凌晨的时候备服务器显示vrrp心跳包超时,所以才导致备服务器接管了vip;
- 同样的时间,都是凌晨,vrrp协议超时;很奇怪,很有理由怀疑是网络问题,询问第三方厂家上层路由器是否禁止了vrrp协议,第三方厂家回复,没有禁止vrrp协议;
- 百度、看官方文档求解;
- 百度、看官网文档得知,keepalived有2种传播模式,一种是组播模式,一种是单播模式,keepalived默认在组播模式下工作,主服务器会往主播地址224.0.0.18发送心跳包,当局域网内有多个keepalived实例的时候,如果都用主播模式,会存在冲突干扰的情况,所以官方建议使用单播模式通信,单播模式就是点对点通行,即主向备服务器一对一的发送心跳包;
- 将keepalived模式改为单播模式,继续观察,无再发生脑裂现象。问题得以解决。
测试环境二进制搭建etcd集群,etcd集群出现2个leader的现象。
- 问题现象就是:刚搭建的k8s集群,是测试环境的,搭建完成之后发现,使用kubectl get nodes 显示没有资源,kubectl get namespace 一会能正常显示全部的命名空间,一会又显示不了命名空间,这种奇怪情况。
- 当时经验不是很足,第一点想到的是不是因为网络插件calico没装导致的,但是想想,即使没有安装网络插件,最多是node节点状态是notready,也不可能是没有资源发现呀;
- 然后想到etcd数据库,k8s的资源都是存储在etcd数据库中的;
- 查看etcd进程服务的启动状态,发现etcd服务状态是处于running状态,但是日志有大量的报错信息,日志大概报错信息就是集群节点的id不匹配,存在冲突等等报错信息;
- 使用etcdctl命令查看etcd集群的健康状态,发现集群是health状态,但是居然显示有2个leader,这很奇怪(当初安装etcd的时候其实也只是简单看到了集群是健康状态,然后没注意到有2个leader,也没太关注etcd服务进程的日志报错信息,以为etcd集群状态是health状态就可以了)
- 现在etcd出现了2个leader,肯定是存在问题的;
- 全部检测一遍etcd的各个节点的配置文件,确认配置文件里面各个参数配置都没有问题,重启etcd集群,报错信息仍未解决,仍然存在2个leader;
- 尝试把其中一个leader节点踢出集群,然后再重新添加它进入集群,仍然是报错,仍然显示有2个leader;
- 尝试重新生成etcd的证书,重新颁发etcd的证书,问题仍然存在,仍然显示有2个leader;日志仍是报错集群节点的id不匹配,存在冲突;
- 计算etcd命令的MD5值,确保各个节点的etcd命令是相同的,确保在scp传输的时候没有损耗等等,问题仍未解决;
- 无解,请求同事,架构师介入帮忙排查问题,仍未解决;
- 删除全部etcd相关的文件,重新部署etcd集群,etcd集群正常了,现在只有一个leader,使用命令kubectl get nodes 查看节点,也能正常显示了;
- 最终问题的原因也没有定位出来,只能怀疑是环境问题了,由于是刚部署的k8s测试环境,etcd里面没有数据,所以可以删除重新创建etcd集群,如果是线上环境的etcd集群出现这种问题,就不能随便删除etcd集群了,必须要先进行数据备份才能进行其他方法的处理。
etcd集群节点可以设置为偶数个吗,为什么要设置为奇数个呢?
不能,也不建议这么设置。 etcd采用了Raft一致性算法来确保数据的一致性和高可用性。根据Raft算法的要求,为了确保算法的正确性和容错性,集群一般包含2n+1个节点,所以进行Leader选举和数据复制时,节点数必须是奇数个。 奇数个节点与配对的偶数个节点(如3个节点和4个节点)相比,容错能力相同,但可以少一个节点;其次,偶数个节点的集群在选举过程中由于等额选票的存在,有较大概率触发下一轮选举,从而增加了不可用的风险。因此,综合考虑性能和容错能力,etcd官方文档推荐的etcd集群大小是3, 5, 7。同时需要注意的是,虽然增加节点可以提高读的吞吐和提高集群的可用性,但节点数越多可能会导致写操作的吞吐降低。
etcd官方推荐3、5、7个节点,虽然raft算法也是半数以上投票才能有 leader,但奇数只是推荐,其实偶数也是可以的。如 2、4、8个节点。下面分情况说明:
- 1 个节点:就是单实例,没有集群概念,不做讨论
- 2 个节点:是集群,但没人会这么配,这里说点废话:双节点的etcd能启动,启动时也能有主,可以正常提供服务,但是一台挂掉之后,就选不出主了,因为他只能拿到1票,剩下的那台也无法提供服务,也就是双节点无容错能力,不要使用。
- 3 节点:标准的3 节点etcd 集群只能容忍1台机器宕机,挂掉 1 台此时等于2个节点的情况,如果再挂 1 台,就和 2节点的情形一致了,一直选,一直增加任期,但就是选不出来,服务也就不可用了
- 4 节点:最大容忍1台服务器宕机
- 5 节点:最大容忍2台服务器宕机
- 6 节点:最大容忍2台服务器宕机
- 7和8个节点,最大容忍3台服务器宕机
- 以此类推,9和10个节点,最大容忍4台服务器宕机
总结以上可以得出结论:偶数节点虽然多了一台机器,但是容错能力是一样的,也就是说,虽然可以设置偶数节点,但没增加什么容错能力,还浪费了一台机器。同时etcd 是通过复制数据给所有节点来达到一致性,因此偶数集群多出一台机器既增加不了性能,反而还会拉低写入速度。
你们生产环境etcd节点一般是几个节点?
我们使用的3节点的etcd集群,3节点etcd集群允许存在1台机器宕机,如果此时两台etcd节点宕机,那此时剩余的1台节点由于无法进行选举,所以整个etcd集群服务就不可用了,同理5个节点则可以最大容忍2个节点不可用,7节点可以容忍3个节点不可用。 目前etcd官方推荐etcd集群节点为3节点、5节点、7个节点,3节点可以支撑小规模的k8s集群,5到7节点可以支持中大型规模的k8s集群。
etcd节点是越多越好吗?
不是,etcd 集群是一个 Raft Group,没有 shared。所以它的极限有两部分,一是单机的容量限制,内存和磁盘;二是网络开销,每次 Raft 操作需要所有节点参与,每一次写操作需要集群中大多数节点将日志落盘成功后,Leader 节点才能修改内部状态机,并将结果返回给客户端。因此节点越多性能越低,并且出错的概率会直线上升,并且是呈现线性的性能下降,所以扩展很多 etcd 节点是没有意义的,其次,如果etcd集群超过7个达到十几个几十个,那么,对运维来说也是一个不小的压力了,并且集群的配置什么的也会更加的复杂,而不是简单易用了。因此,etcd集群的数量一般是 3、5、7, 3 个是最低标准,7个已经是最高了。
etcd集群节点之间是怎么同步数据的?
Raft 算法数据同步机制
- 领导者选举(Leader Election)
- 集群启动时,节点随机超时后发起投票竞选 Leader
- 获得半数以上节点投票的节点成为 Leader(如 3 节点集群需 2 票)
- Leader 独占写入权限,负责同步数据到其他节点
- 日志复制(Log Replication)
- 步骤 1:客户端向 Leader 写入数据(如
key=foo, value=bar) - 步骤 2:Leader 将操作包装为 Log Entry,持久化到本地日志
- 步骤 3:Leader 发送
AppendEntries RPC请求给所有 Follower - 步骤 4:Follower 校验一致性后,持久化日志并返回成功
- 步骤 5:Leader 收到半数以上节点成功响应后提交日志
- 步骤 6:Leader 更新
CommitIndex,通知 Follower 提交日志
- 步骤 1:客户端向 Leader 写入数据(如
- 状态机应用(State Machine Apply)
- 所有节点按日志顺序将已提交的 Log Entry 应用到状态机(即 etcd 的键值存储)
- 保障强一致性:所有节点最终应用相同日志序列 → 数据完全一致
k8s中etcd挂了,已经运行的pod服务是否受影响呢?
这是一个很经典的面试题,考察对 etcd 在 k8s 中作用的理解深度。回答需要分场景讨论,不能简单回答"受影响"或"不受影响"。
核心结论
已经正常运行(Running)的 Pod 服务在短期内不会受到影响,但集群的管理功能会完全失效。具体影响取决于 etcd 挂掉的方式和时间长短。
为什么运行中的 Pod 不受影响?
K8s 组件的工作原理决定了这一点:
- kubelet 独立运行:每个 Node 上的 kubelet 是独立运行的进程,它会根据本地缓存的 Pod 清单维护已有 Pod 的生命周期,包括健康检查(存活探针、就绪探针)和容器重启。kubelet 不依赖 etcd 来完成这些工作。
- 容器运行时不受影响:kubelet 通过 CRI 调用容器运行时(containerd/docker)来管理容器,容器的运行状态由容器运行时维护,不依赖 etcd。
- 已有网络通信正常:CNI 插件已在 Pod 创建时完成了网络配置,已有的 Pod IP、网络规则不依赖 etcd 的实时查询。Pod 之间的通信(同节点和跨节点)基于已配置的路由和 iptables/ipvs 规则正常进行。
- Pod 数据不变更:已运行的 Pod 其 spec 不会发生改变,kubelet 只需按本地缓存的 spec 维持 Pod 状态即可。
etcd 挂了具体会有什么影响?
| 影响范围 | 具体表现 | 原因 |
|---|---|---|
| kubectl 命令 | kubectl get pods、kubectl describe 等读取操作失败或超时 | api-server 无法从 etcd 读取数据 |
| Pod 创建/删除 | 无法创建新 Pod,无法删除已有 Pod | api-server 无法将变更持久化到 etcd |
| Pod 调度 | Scheduler 无法正常工作,新 Pod 一直 Pending | Scheduler 需要通过 api-server 查询节点信息,而 api-server 读不到 etcd |
| Deployment 扩缩容 | 无法扩容或缩容 | controller-manager 无法读写 etcd |
| Service 更新 | 无法创建、更新 Service 和 Endpoint | api-server 无法写入 etcd |
| 节点注册/心跳 | 新节点无法加入集群,已有节点的心跳上报失败 | kubelet 的心跳更新需要通过 api-server 写入 etcd |
长时间 etcd 不可用的风险
如果 etcd 长时间不可用,会逐渐出现更严重的问题:
- Pod 健康检查失败后无法重建:如果已有 Pod 中的容器异常退出,kubelet 虽然可以重启容器,但如果 Pod 整体被删除或节点宕机,新的 Pod 无法被调度创建。
- 节点宕机后 Pod 无法漂移:如果某个 Node 宕机,该节点上的 Pod 不会被重新调度到其他节点,因为 controller-manager 无法更新状态。
- kubelet 心跳超时导致 Node 状态异常:kubelet 无法向 api-server 上报心跳,一段时间后 Node 状态会变成 NotReady,集群会误判节点失联。
- ConfigMap/Secret 更新无法同步:如果 Pod 依赖通过 Volume 挂载的 ConfigMap 或 Secret 热更新,这些更新将无法同步到 Pod。
etcd 挂掉后集群的极限状态
etcd 完全不可用 →
├── 已有 Running Pod:继续运行,健康检查正常,容器正常
├── 已有网络通信:正常
├── 已有 Service:ClusterIP 和 iptables/ipvs 规则不变,访问正常
│
├── kubectl 命令:全部不可用(读写都失败)
├── 新 Pod 创建:完全不可用
├── Pod 删除:完全不可用
├── 扩缩容:完全不可用
├── 滚动更新:完全不可用
│
└── 长时间不可用后 →
├── 节点宕机 → Pod 无法漂移 → 服务降级
├── Pod 异常退出 → 无法重建 → 服务降级
└── 最终服务逐步不可用生产环境建议
- etcd 高可用部署:至少 3 节点,最好 5 节点,分布在不同的物理服务器上。
- 定期备份 etcd 数据:使用 etcd 快照功能定期备份,确保灾难发生时能快速恢复。
- 监控告警:对 etcd 集群进行全方位的监控(集群健康状态、Leader 变化、磁盘 IO、DB 大小等)。
- 资源隔离:etcd 集群独立部署或使用专用节点,避免与其他应用争抢资源。
- 限速保护:配置 etcd 的请求限速(
--quota-backend-bytes等参数),防止突发流量打垮 etcd。
总结
一句话回答:etcd 挂了不影响已运行 Pod 的短期运行,但集群变成"只读不可写"状态,无法做任何变更操作。长时间不可用会导致服务逐步降级。
k8s后端存储使用的是什么?
后端存储方面,我们不同的项目使用的方案不一样,这可能是早期系统架构决定的,现在也是一直沿用,主要就是包含3种存储方案:
- 第一种存储方案是,hostPath和local-path-provisioner,先说hostPath:pod中直接使用hostPath卷来挂载数据,然后pod中要节点选择器固定调度到指定的节点,我们一般采用这种方式来实现pod直接挂载服务器数据或者pod日志落盘到服务器磁盘;但是hostPath卷属于静态卷,所以我们还使用了local-path-provisioner来动态供给localPath,local-path-provisioner能够让Kubernetes的本地存储支持动态pv,当使用local-path-provisioner的pod被调度时,scheduler调度器和pv控制器会同时进行控制,然后在pod所在节点上创建对应的本地存储目录,当pod被重新调度后,因为pod所对应的pv存在节点选择器,所以pod仍然能够调度到之前的节点上,从而继续使用或读取之前的数据。
- 第二中存储方案是glusterfs:我们使用glusterfs作为后端存储,先在服务器上大磁盘单独划分数据分区,创建目录,然后创建一个glusterfs文件系统(glusterfs没有选举一说,可以任意数量扩展),然后在glusterfs文件系统上创建卷,然后在k8s创建存储类,这样就实现了静态供给pv,如果需要动态供给pv,还需要使用heketi软件。有时候我们也会把glusterfs中的卷直接挂载到服务器上使用。
- 最后一种存储方案是cephfs:ceph官网上推荐在k8s集群中使用rook-ceph,我们使用的就是rook-ceph,rook-ceph我们使用的helm安装的,创建完成ceph集群之后再创建cephfs,然后创建存储类进行动态pv供给。
你们的服务发布怎么做的?或者你们的cicd流程是怎么做的?
我们服务采用cicd流程发布的,具体的说,就是只要用到两个工具: jenkins和argocd。jenkins负责实现ci,argocd负责实现cd。主要是这么几个步骤:
- 开发写好代码将代码提交到gitlab仓库;
- 然后手动在jenkins页面上点击构建任务;
- jenkins上定义了多个流水线项目,每个pipeline流水线项目都对应一个gitlab仓库的项目代码,流水线的配置都写在jenkinsfile文件里面,而jenkinsfile文件也是存放在gitlab仓库上进行托管的;
- 构建的流程只要是jenkinsfile文件定义的,jenkinsfile内容大致有这么几步:
- 第一步、先使用git命令克隆代码到工作目录并检出全部的分支然后写入到一个临时文件。
- 第二步、读取临时文件全部分支,提示用户选择要构建的分支和要发布的环境。
- 第三步、开始编译源代码,前端代码使用npm命令编译,后端代码使用maven编译。
- 第四步、代码编译完成就可以得到jar包了,这时开始构建镜像并推送镜像到harbor镜像仓库。
- 第五步、部署,这里的部署并不是真正意思上的部署,而是镜像的tag写回到gitlab仓库里去,并且使用kustomize命令修改yaml资源清单文件,这步主要是让argocd实现部署。
- 第六步、发送钉钉通知消息。 以上,第五步的更新gitlab仓库的时候,argocd会监听到gitlab仓库里面的k8s资源清单文件发生了改变,然后就会自动的应用部署,部署方式仍然是滚动更新deployment的镜像,这样就实现了自动化部署。
拿到一台新的服务器如何优化,如何进行加固安全
- 禁用SELinux
- 精简开机自启动服务
- 安装的Linux系统最小化,yum 安装软件也最小化,无用的包不安装。
- 更改ssh的默认22端口,改成其他端口,ssh禁用root远程登录,禁止空密码登录
- 配置sudoers文件,控制用户对系统命令的使用权限
- 设置linux时间同步,可以结合定时任务来同步时间服务器
- 调整系统文件描述符数量,在/etc/security/limits.conf文件里面调整
- 服务器内核参数优化
- 锁定系统关键的文件,使用chattr命令对文件锁定,锁定后所有用户都不能对文件进行修改删了,还可以将chattr命令重命名,防止被黑客识别。
- 服务器禁止被ping
- 开启防火墙
- 设置用户密码复杂度、过期策略,如最少密码长度、密码中必须包含的数字、大写字母、特殊字符等,以及密码的最大使用天数和到期警告天数。
- 修改PAM(Pluggable Authentication Modules)配置文件来设置账户锁定策略以对抗口令暴力破解
- 配置ssh登录超时策略。表示用户无操作多少秒超时自动退出 echo 'export TMOUT=300' >> /etc/profile
- 配置用户登录失败策略,在/etc/pam.d/system-auth中配置,比如密码错误锁定多少分钟
- 对多余帐户进行删除、锁定或禁止其登录,如:uucp、nuucp、lp、adm、sync、shutdown、halt、news、operator、gopher、shutdown等
- 限制保留的历史命令,HISTSIZE值,用于控制history命令保留历史记录数量;HISTFILESIZE值,控制.bash_history文件中存储历史记录数量;echo 'HISTSIZE=30' >>/etc/profile;echo 'HISTFILESIZE=30' >>/etc/profile
如何进行k8s的安全加固
API Server 加固
- 禁用匿名访问:确保
--anonymous-auth=false。 - 启用 TLS 双向认证:强制客户端证书验证,禁用 HTTP 端口(
--insecure-port=0)。 - 集成企业身份源:通过 OIDC/LDAP/AD 统一管理用户认证,避免静态 kubeconfig 分发。
- 禁用匿名访问:确保
RBAC 最小权限原则
- 限制 ClusterRoleBinding:定期审计并清理绑定了
cluster-admin的账户。 - 命名空间隔离:为不同团队创建独立命名空间,通过 RoleBinding 而非 ClusterRoleBinding 授权。
- 服务账户权限控制:
- 为每个应用创建专属 ServiceAccount,设置
automountServiceAccountToken: false避免默认挂载。 - 示例:仅允许服务账户读取 ConfigMap:yaml
apiVersion: rbac.authorization.k8s.io/v1 kind: Role rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["get"]
- 为每个应用创建专属 ServiceAccount,设置
- 限制 ClusterRoleBinding:定期审计并清理绑定了
零信任网络模型
- 默认拒绝所有流量:在每个命名空间部署
default-deny-allNetworkPolicy。 - 精细化白名单:仅开放必要通信(如前端 Pod → 数据库的 5432 端口):yaml
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy spec: podSelector: { matchLabels: { role: db } } ingress: - from: - podSelector: { matchLabels: { app: web } } ports: [{ protocol: TCP, port: 5432 }] - 依赖 CNI 插件:确保集群使用 Calico/Cilium 等支持 NetworkPolicy 的 CNI。
- 默认拒绝所有流量:在每个命名空间部署
节点级防火墙
- 控制平面仅开放 6443(API Server)、2379-2380(etcd)端口。
- Worker 节点限制 kubelet API(10250/TCP)仅内网访问。
Pod 安全策略
- 启用 Pod Security Admission (PSA):替代已废弃的 PSP,通过命名空间标签强制执行策略:bash
kubectl label ns prod pod-security.kubernetes.io/enforce=restricted - 安全上下文配置:yaml
securityContext: runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: ["ALL"]
- 启用 Pod Security Admission (PSA):替代已废弃的 PSP,通过命名空间标签强制执行策略:
内核级防护
- 启用 AppArmor/seccomp 限制容器系统调用。
- 使用 gVisor/Kata 等沙箱容器运行时隔离高危工作负载。
镜像漏洞扫描
- CI/CD 集成 Trivy/Clair,阻断含高危 CVE 的镜像入集群。
镜像签名验证
- 使用 cosign 签名镜像,部署时校验完整性:bash
cosign verify --key public-key.pem your-registry/image:tag
- 使用 cosign 签名镜像,部署时校验完整性:
私有仓库+白名单:限制仅从受信仓库拉取镜像。
etcd 静态加密
- 启用 AES-CBC 加密 Secrets:yaml
apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: ["secrets"] providers: [{ aescbc: { keys: [{ name: key1, secret: <BASE64_KEY> }] } }]
- 动态密钥管理:
- 使用 Vault+CSI 驱动注入敏感数据,避免硬编码。
- 审计日志全覆盖
- 记录所有 API 请求,关联用户身份和操作时间:yaml
apiVersion: audit.k8s.io/v1 kind: Policy rules: [{ level: Metadata, verbs: ["*"] }] - 日志集成 SIEM 系统(如 ELK/Splunk)实时告警。
- 运行时入侵检测
- 部署 Falco 监控异常行为(如宿主机文件访问或特权提升)。
