Base
谈谈你对 SRE 的理解
SRE 是 Google 提出来的概念,它的全称是站点可靠性工程(Site Reliability Engineering),其核心目标是 通过工程化手段,提升系统的稳定性、可靠性,当落实到实际应用场景时:
- 可以通过监控、告警、日志采集等手段提高系统整体的可观测性,并在出现故障时可及时发现;
- 通过一些辅助手段,例如状态检测脚本,定期检测后端服务可用性,当出现宕机时将流量转移并尝试对其重启恢复。还可以通过 keepalived + VIP 的方式为服务提供统一的访问入口,防止单点失败的情况发生。
谈谈你对 Devops 的理解
- xxx
实践场景
一个项目运维交给你后,你会如何梳理,以实现快速上手
- 信息收集:根据现有的材料,了解:
- 当前网络拓扑
- 服务部署方式(容器/service,以及部署数量)
- 并根据服务配置文件,判断有哪些依赖组件(数据库、外部接口、以及像kafka等其他中间件)
- 是否有监控、告警、日志采集等运维系统
- CICD流程等
- 信息梳理:将收集的信息,通过表格、拓扑图等方式进行整理归纳;
- 运维阶段:分析系统薄弱环节,并进行优化,例如:是否有单点失败问题?重要数据是否有备份?监控是否全面?告警策略是否合理?
项目要进行重保,前期、期间、后期如何处理?
“项目重保(重点保障)”通常是指某个重要时期的系统稳定性保障,比如:618、双11、促销活动、系统迁移、重大发布、节假日流量高峰等。
- 重保前期:
- 系统巡检,确认关键服务、依赖组件、数据库、缓存等状态正常;
- 准备应急预案,例如线上服务出现问题时的紧急回滚策略等;
- 权限冻结,只允许紧急变更;
- 安全漏扫
- 重保期间:
- 24h实时监控,关注核心指标(QPS、RT、错误率、CPU、延迟等);
- 灰度/限流策略:根据流量情况动态限流或启用熔断;
- 扩缩容策略:自动/手动扩容,保证服务稳定;
- xxx
- 重保后期:
- 复盘,总结经验
遇到过最复杂的问题与解决方案?
磁盘空间使用率超过百分之80%
- 排查思路:
- 通过
du -sh /*从根开始逐级排查大文件,发现应用日志占用了大约 100G 的空间,但将日志删除后,磁盘空间并未释放- 使用
lsof | grep 文件名后发现,该文件仍被进程所占用,进而导致文件描述符未被释放- 解决方案:
- 重启
lsof中显示的进程,磁盘空间释放,问题解决- 以后再删除大文件,找到了一个更好的方案,就是使用
> 文件,通过标准输出重定向的方式将文件置为空,这样可以使磁盘空间立刻得到释放安装完 docker 并启动容器后,发现宿主机无法访问其他网段的IP
- 排查思路:
- 使用
traceroute 其他网段的IP跟踪路由后,发现数据包到达的网关不对,而且没有下一跳了- 通过
ip a查看 IP 、ip r查看路由表后,发现新创建的容器网段与目标网段冲突了- 解决方案:
- 删除容器,通过
docker network create新建一个没有冲突的其他网段,启动容器时指定该自定义网段
toC 运维场景是怎样的?
目前工作属于 toB,但上家属于 toC(五金电商平台),从客户端角度出发:
- 当客户端访问时,静态资源(图片、视频)由CDN直接响应,
- xxx
- xxx
计算机基础
*进程、线程、协程的区别?
定义
- 进程:程序运行的基本单位,每个进程都有自己的独立内存空间。
- 线程:线程从属于进程,是程序的实际执行者。一个进程可以有多个线程,最少有一个线程。
- 协程:协程从属于线程,调度完全由用户控制,主要的作用是在单线程的条件下实现并发的效果,但实际上还是串行的。
区别
对比项 进程 (Process) 线程 (Thread) 协程 (Coroutine) 内存空间 拥有独立的内存空间 共享所属进程的内存空间 共享所属线程(进而所属进程)的内存空间 通信方式 需要 IPC(如管道、消息队列、共享内存) 共享内存、锁、信号量 直接函数调用或消息队列(无需锁) 调度者 操作系统内核(内核态调度) 操作系统内核(内核态调度) 用户程序(用户态调度) 切换开销 最大(上下文 + 地址空间切换) 中等(线程上下文切换) 最小(用户态上下文切换) 并行能力 ✅ 可多核并行 ✅ 可多核并行 ❌ 通常单线程并发(除非结合多线程) 异常影响 崩溃不会影响其他进程 崩溃可能导致整个进程异常 视语言实现而定(如 Python 仅影响该协程,C/C++ 可能导致进程崩溃) 适用场景 高安全隔离、独立服务 CPU 密集型并行计算 高并发 I/O、任务切换频繁
*多进程和多线程又有啥区别,什么场景下会使用多进程,什么场景下又用多线程。?
多进程和多线程区别:
- 多进程时,进程与进程间完全独立,某个进程出现问题时不会影响其他进程,所以安全性更高。
- 多线程时,安全性不及多进程,但因为线程共享进程的内存空间,所以线程间通信效率要比进程间通信高。 应用场景:
- 多进程适合 CPU 密集型任务,例如视频编码/解码、数据加密/解密、AI 模型训练。
- 多线程适合 I/O 密集型任务,即网络IO、磁盘IO相关的,(多线程 I/O 等待时切换效率更高,所以更适合)。
- PS:在现代编程中,协程(Coroutines) 进一步优化了切换效率,因为协程的切换(也叫“任务切换”)完全发生在用户态,避免了内核介入,开销比线程切换还要小得多,是处理 I/O 密集型任务的最理想模型。
*一个进程在操作系统中,它有哪几种状态表现?
- R(Running)运行态:表示进程正在CPU上执行。
- S(Sleeping)可中断睡眠态:表示空闲进程正在等待一个事件,可以被信号中断。
- D(Disk Sleep)不可中断睡眠态:进程正在等待I/O完成,这个状态不能被信号中断。
- Z(Zombie)僵尸态:进程已经终止,但父进程并未将其回收(调用 wait() 或 waitpid() 系统调用来回收其资源和退出状态信息)。
- T(Stopped)停止态:进程被信号(如 SIGSTOP 或 SIGTSTP)暂停执行。它可以通过 SIGCONT 信号恢复执行。
IPC 方式有哪些?
- 管道
- 消息队列
- 共享内存
- 信号
- 信号量
- Socket(套接字)
用户态和内核态的区别,本质上是啥?在什么情况下会发生内核态和用户态的切换?
- 本质区别:内核态可以直接操作硬件资源,而用户态需要通过系统调用间接操纵。
- 常见发生场景:执行系统调用时、程序发生异常时、硬件中断时。
零拷贝是什么?
- 零拷贝简单来说就是文件内容直接从内核缓冲区传输到 socket 缓冲区,无需经过用户空间,从而提高性能。
- 底层基于 Linux
sendfile()系统调用
堆和栈的区别?
- 存放内容:栈存放局部变量等信息,而堆存放对象实例等信息;
- 大小:栈较小,且访问速度快,而堆较大,访问速度较慢;
- 生命周期:栈随函数调用结束自动释放,堆需要垃圾回收。
网络
*访问一个网站背后的过程?
- 首先通过DNS解析,将域名解析为对应的IP地址,解析顺序依次为 “浏览器缓存” → “本地 hosts 文件” → “递归查询(本地 DNS)” → “迭代查询(根→顶级→权威)”;
- 获取到IP地址后,进行TCP三次握手建立连接;
- 如果请求的域名是HTTPS的,客户端和服务器还会通过 TLS 握手,服务端会将证书发送给客户端(其中包含公钥),客户端会校验证书的合法性(是否由权威CA签发、是否过期等,有问题的话会在浏览器弹出告警对话框),然后客户端会生成一个对称密钥,用服务端的公钥把对称密钥进行非对称加密,服务端收到后会用私钥解密,后续数据传输都使用这个对称密钥进行;
- 之后向服务端发送HTTP请求报文,请求报文中包含方法(GET/POST/PUT/DELETE等)、路径、HTTP版本号、首部字段(一组键值对,如 Host、User-Agent、Accept 等 )、请求体(可选,例如在 POST 请求中携带 JSON 数据);
- 服务端收到后,返回HTTP响应报文,其中包含版本号、状态码、短语、首部字段(Content-Type、Server等)、空行、数据实体;
*GET 和 POST 的区别?
- 用途:GET 表示向服务器获取数据,POST 用于向服务器发送数据。
- 数据存放位置:GET 数据存储在 URL 中,POST 数据放在请求体(Request Body)中。
- 幂等性:GET 幂等(多次请求结果相同),POST 非幂等(多次请求结果不同)。
- 能否被缓存:GET 可被浏览器缓存,POST 不会被浏览器缓存。
*TCP三次握手的过程?
- 第一次握手(SYN):客户端向服务器发送一个 SYN。此时,客户端进入 SYN_SENT 状态;
- 第二次握手(SYN-ACK):服务器收到客户端的 SYN 报文后,会发送一个 SYN-ACK(同步确认)报文段作为响应。此时,服务器进入 SYN_RCVD 状态;
- 第三次握手(ACK):客户端收到服务器的 SYN-ACK 报文后,发送一个 ACK(确认)报文段作为最终确认。此时,客户端进入 ESTABLISHED 状态。服务器收到客户端的 ACK 后也进入 ESTABLISHED 状态,连接正式建立。
*TCP和UDP的区别?
- 连接与可靠性:TCP是面向连接的,会通过三次握手、流量和拥塞控制、重传等机制使传输更可靠 ;而UDP仅提供最基本的数据传输能力,只管发送,不管对方是否收到。
- 传输速度:UDP要高于TCP。
- 通信方式:TCP 仅支持一对一通信(四元组唯一标识连接);UDP支持一对一、一对多的方式通信。
- 应用场景:TCP 适合下载文件等对数据传输可靠性要求高的场景;而 UDP 更多的用于在线视频、语音等对数据实时传输效率要求高的场景,像 DNS、DHCP 协议是基于 UDP 的。
*OSI七层模型指的是哪几层?分别有什么协议?
- 物理层,负责传输比特流(0和1);
- 数据链路层,负责在同一局域网内通过 MAC 地址传输数据帧,相关协议有:IEEE 802.1Q(以太网 VLAN 标签协议)、STP/RSTP/MSTP(生成树协议)、PPP(点对点链路协议)等;
- 网络层,通过 IP 和路由实现跨网络的数据传输。相关协议有:IP、ICMP、ARP(IP 地址解析为 MAC 地址)、OSPF/RIP/BGP(动态路由选择协议)等;
- 传输层,定义数据的传输方式,主要标识为端口号,相关协议有 TCP(可靠、面向连接)、UDP(不可靠、无连接)等;
- 会话层,建立、管理和终止会话;
- 表示层,数据表现形式(格式、加密、压缩等);
- 应用层,具体的应用服务,相关协议有 HTTP、HTTPS、FTP、SMTP、POP3、IMAP、DNS、Telnet、SNMP 等。
*三次握手四次挥手有几个状态?
连接建立阶段(三次握手):
- LISTEN 服务器监听客户端请求。
- SYN-SENT 客户端发送 SYN,等待服务器回应。
- SYN-RECEIVED 服务器收到 SYN 并回应 SYN+ACK,等待客户端 ACK。
- ESTABLISHED 三次握手完成,连接建立成功。
连接释放阶段(四次挥手):
- FIN-WAIT-1 主动关闭端发送 FIN。
- FIN-WAIT-2 对方确认 FIN,等待对方发送 FIN。
- CLOSE-WAIT 被动关闭端收到 FIN,等待应用层关闭连接。
- CLOSING 双方几乎同时发送 FIN。
- LAST-ACK 被动关闭端发送 FIN,等待 ACK。
- TIME-WAIT 主动关闭端等待 2MSL 后真正关闭。
*TIME-WAIT 会出现在连接的哪一方?
- 客户端和服务端都有可能出现,但一般是在客户端。
*静态路由与动态路由的区别?
对比维度 静态路由 动态路由 配置方式手动配置 自动学习 可扩展性差,仅适用于小型网络 好,适合大型复杂网络 CPU/带宽开销极低 较高(需周期性交换路由信息) 安全性高(不易被外部影响) 相对较低(可能受攻击伪造LSA) 依赖协议无 OSPF、RIP、BGP等
DNS 解析流程?
将域名解析为对应的IP地址,解析顺序依次为 “浏览器缓存” → “本地 hosts 文件” → “递归查询(本地 DNS)” → “迭代查询(根→顶级域→权威)
- 递归查询:假设宿主机的 DNS 服务器指向了 223.5.5.5(阿里云DNS),本机向这个 DNS 服务器发起的查询就是递归查询。
- 迭代查询:这个DNS服务器(223.5.5.5),如果没有命中缓存,它会向根服务器(返回 .com 的 DNS 地址)、顶级域服务器(返回 baidu.com 的 DNS 地址)、权威服务器(返回 www.baidu.com 的 IP)发起的查询,就叫迭代查询。
DNS相关的测试工具有 dig、nslookup
以太网帧结构?
帧就是给网络层传来的 IP 数据包(相当于快递里的物品),加上 “头部” 和 “尾部” 包装,形成的一个完整传输单元。头部里装着接收方的 MAC 地址(相当于收件人地址)、发送方的 MAC 地址(相当于寄件人地址),尾部则负责校验数据是否损坏。
字段名称 作用(类比快递) 长度(参考) 前导码(Preamble) 告诉接收设备 “准备接收数据” 7 字节 帧起始定界符(SFD) 标记帧的正式开始 1 字节 目的 MAC 地址 接收设备的 “硬件地址” 6 字节 源 MAC 地址 发送设备的 “硬件地址” 6 字节 类型 / 长度字段 说明帧内包裹的 “数据类型”(如 IP 数据) 2 字节 数据字段 真正要传的内容(即 IP 数据包) 46-1500 字节 帧校验序列(FCS) 检查数据在传输中是否损坏 4 字节
常见服务的端口号
- 未特别说明的,均为TCP,但有些服务,既支持UDP,又支持TCP
Port Service 20ftp-data 21ftp 22ssh 23telnet 25smtp 53/udpdns 67/udpdhcp 68/udpdhcp-client 69/udptftp 80http 110pop3 119nntp 123/udpntp 443https 465smtps 514/udpsyslog 873rsync 995pop3s 1521oracle 2049nfs 3306mysql 3389rdp 5432postgresql 5672RabbitMQ 6379redis 8080tomcat 9092Kafka 9200elasticsearch 11211memcached
实践场景
*网络丢包如何排查?
从不同的客户端 ping 服务端,如果所有客户端都丢包,排查服务端的 CPU、内存使用量、负载,如果没有异常:
- 客户端与服务端位于统一网段(排查二层网络),排查内容包括:
- 分别在客户端和服务端执行
watch 'ip n | grep 172.16.30.65'排查 arp 表中的 mac 地址是否和目标相符、arp 缓存是否频繁失效(频繁变为 STALE 或 FAILED)- 分别在客户端和服务端执行
tcpdump -i ens160 -nn 'host 172.16.30.65 and (arp or icmp)'查看 arp 和 icmp 报文的请求和响应是否正常- 客户端与服务端跨网段,在排查二层网络的基础上,排查三层网络:
- 最简单的方式,可以使用
mtr 172.16.30.65查看具体是到哪一跳丢包了- 在客户端和服务端执行
ethtool -S <网卡接口名> | grep -E 'drop|error'查看是否有大量的 rx_dropped (接收丢弃), tx_dropped (发送丢弃) 或 CRC 校验错误。- 使用
iptables -vnL查看是否有规则匹配了流量并执行了 DROP 或 REJECT,以及对应的匹配计数。
*网络中服务慢怎么排查,具体到链路之间呢?
- 从不同客户端访问服务,先判断是客户端慢还是服务端慢,如果是服务端慢,可以从应用层到网络层依次排查;
- 应用层排查内容:服务运行运行节点的负载、CPU、内存使用率、应用日志、数据库慢查询日志等,如果都没问题,则从网络层排查;
- 网络层排查内容:看具体的拓扑,比如从 防火墙 -> 负载均衡 -> 应用 -> 数据库 等依次排查,再配合一些工具,比如:
- ping 查看延迟、是否丢包
- traceroute 查看路由延迟
- tcpdump 抓包看有没有异常报文
- ss -s 查看 socket 信息,例如 TIME_WAIT 是否过多
- 结合监控大盘,或云厂商的VPC流日志排查
*主机网络不通,如何排查?(可以继续补充)
- 首先可以查看IP、子网掩码、网关、路由配置是否正确,相关的排查命令有ip、ping、traceroute;
- 然后还可以看防火墙策略是否对其限制,并结合tcpdump抓包测试;
- 最后可以看网卡、网线、路由器、交换机等硬件设备是否工作正常。
*程序上线后,服务器出现了很多 time_wait 的连接状态,原因是什么?如何解决?
- 程序在高并发场景下,TCP 短连接过多,导致四次挥手时出现了大量 TIME_WAIT。
- 解决方案:
- 应用层:nginx 开启 keepalive 长连接
- 系统层:缩短TIME_WAIT存活时间(net.ipv4.tcp_fin_timeout = 30 (默认60秒))、TIME_WAIT socket 快速复用(net.ipv4.tcp_tw_reuse = 1)
*DNS解析缓慢如何排查?
先从简单着手,可以先使用top命令,查看客户端的 CPU/内存使用率、负载,如果没有异常,可以使用 dig 命令,分别使用默认DNS(
dig baidu.com)、 其他DNS(dig @223.5.5.5 baidu.com)解析测试,将两者的解析耗时进行对比(观察 Query time 字段)
- 如果仅默认 DNS 解析慢,说明本机指向的 DNS 服务器有问题,可以尝试换 DNS,如果是自己搭建的内网 DNS 服务器,可以登录查看其CPU/内存使用率、负载、日志有无报错;
- 如果更换 DNS IP 还是慢,说明是网络有问题:
- 可以
ping DNSIP查看有无丢包,有丢包的话,使用mtr -n DNSIP分析具体是哪一跳产生的丢包- 没有丢包的话,可能是 udp/53 被防火墙限流了?
172.16.0.120/18 和 172.16.30.65/18 受否在同一网段?
- 确定子网掩码:它们的子网掩码都是 /18,这表示子网掩码中有 18 个连续的 1,即子网掩码为 255.255.192.0
- 补充:子网掩码中,连续的 1 是网络位,末尾连续的 0 是主机位
- 计算网络地址:将 IP 地址和子网掩码进行位逻辑与运算(都是 1 时,结果位才是 1。只要有一个是 0,结果就是 0。)。
# 172.16.0.120/18 # IP 地址 (二进制) 10101100.00010000.00000000.01111000 # 掩码 (二进制) 11111111.11111111.11000000.00000000 # 逻辑与结果 (网络地址) 10101100.00010000.00000000.00000000 # 网络地址为 172.16.0.0 # 172.16.30.65/18 # IP 地址 (二进制) 10101100.00010000.00011110.01000001 # 掩码 (二进制) 11111111.11111111.11000000.00000000 # 逻辑与结果 (网络地址) 10101100.00010000.00000000.00000000 # 网络地址为 172.16.0.0
- 因为它们的网络地址相同,所以 172.16.0.120/18 和 172.16.30.65/18 在同一个网段内。
Linux
*ext4 和 xfs 文件系统的区别?
- ext4 稳定性、兼容性比较好
- xfs 性能比较好,擅长处理大文件和高并发场景
- PS:
- CentOS 6 使用 ext4,CentOS 7、8 使用 XFS;
- Ubuntu 从 16.04 到 24.04 默认使用的都是 ext4
*du -sh /data 和 df -h /data 看到的数据大小一样吗?
不一样,du -sh /data看到的是目录或文件大小,而df -h /data看到的是分区大小。
*free -h 会输出哪些内容? buff/cache 的区别?
- total:内存总量
- used:内存使用量
- free:空闲内存
- shared:共享内存量
- buff/cache:
- buff 负责写缓冲,提高写入效率。
- 它主要作为数据的暂存区,将零散的写入请求集中起来,然后一次性、批量地写入磁盘。这种批量操作减少了对磁盘I/O的次数,从而提高了系统的写入效率。
- cache 负责读缓存,提高读取速度。
- 它缓存了从磁盘读取过的数据。当程序需要再次读取相同数据时,可以直接从速度快得多的内存中获取(称为缓存命中),而无需再次访问慢速的磁盘,从而显著提高了读取速度。
- available:实际可用内存,available ≈ free + 可回收的 buff/cache(因为 buff/cache 所占用的空间可被释放,用于其他程序运行)
- Swap:将磁盘空间临时充当内存来使用,性能很差,通常建议关闭。
Linux 系统启动流程是怎样的?
- BIOS/UEFI 加电自检,检查 CPU、内存、硬盘、网卡等硬件是否正常;
- 加载 Bootloader(GRUB),包含内核镜像(vmlinuz)和 initramfs(临时根文件系统);
- 内核初始化,内核解压缩并加载到内存、初始化硬件驱动(存储、网络、文件系统等)、启动第一个用户空间进程(systemd);
- 根据 /etc/fstab 挂载磁盘分区;
- 启动系统服务(syslog、network、sshd)和用户空间服务(nginx、docker…);
- 最后进入 ssh 登录界面。
如何排查一台 Linux 服务器的负载过高问题?
- 首先可以通过监控面板,或登录宿主机使用 w、uptime 等命令查看 1 分钟、5 分钟、15 分钟的平均负载(load average),如果 1 分钟的平均负载偏高,有可能是瞬时的,可以继续观察,如果如果 15 分钟的平均负载也高,则需要继续排查;
- 平均负载 > CPU核心数 = 高(CPU 和 IO 的排队时间)
- 使用 top、ps、iotop、nethogs 等命令获取占用 CPU 或 IO 高的进程;
- 最后通过查看服务日志、系统日志、内核日志等方式排查是否有报错。
- 系统日志:
/var/log/syslog(Ubuntu)、/var/log/messages(CentOS)。- 内核日志可以使用
dmesg查看(重点关注 I/O error、OOM killer)。
请解释 top 命令输出中 load average 的含义。
- top 的 load average 表示在过去 1、5、15 分钟内,系统中处于运行态和不可中断睡眠态的进程的平均数量;
- 运行态 (R) → 正在使用 CPU 或等待 CPU 的进程。
- 不可中断睡眠态 (D) → 一般是等待 I/O(如磁盘或网络)的进程的平均数量。
- 是否算高要结合 CPU 核心数来看,正常应该 load < CPU 核数,例如1核CPU的负载应小于1.0、8核CPU的负载应小于8.0。
如何查看和分析内存使用情况?
- free -h 查看内存和SWAP的总体使用情况,还可以使用 top 命令 + M 按内存使用率排序。
硬盘空间满了你会如何排查?
- 先使用
df -h查看是哪块磁盘满了,以及对应的挂载点;- 然后使用
du -sh /挂载点/*,逐级定位是哪个目录或者文件占用空间大,也可以使用find / -xdev -type f -size +1G搜索大于指定大小的文件;- 解决方案:如果是日志等无用的文件,可以先使用
>文件名立即释放磁盘空间,持久解决方案可以选择使用逻辑卷扩展。其他的优化手段,比如减少程序的日志写入量等;- 还可使用
lsof | grep 文件名来定位文件对应的进程。
*磁盘空间还有,但数据无法写入是什么原因?
- inode 用尽,可以使用 df -i 检查;
- 文件系统只读,可以使用 mount | grep 挂载点 检查,ro 表示只读,rw 读写;
- 用户没有写权限。
删除文件后,磁盘空间并未释放,为什么?
- 是因为这个文件的 inode 仍然被其他进程持有,可以使用
lsof | grep 文件名定位到进程后重启进程。
你如何查看某个进程打开了哪些文件?
- 可以使用
lsof -p <PID>查看进程打开的所有文件。
软链接和硬链接的区别?
- 软连接相当于Windows下的快捷键方式,而硬链接本质上是同一个文件;
- 软连接与源文件的 inode 不同,而硬链接与源文件的 inode 是相同的;
- 软链接可跨分区,硬链接不可以;
- 源文件删除,软链接不可访问,硬链接可以;
- 软链接可以对文件、目录创建,而硬链接只能针对文件。
你如何限制某个用户的资源使用(CPU、内存等)?
- 可以用 ulimit 限制用户的 CPU 时间、内存、进程数和打开文件数;更精细的可以用 cgroups 限制 CPU、内存、IO;如果是 systemd 管理的服务,也可以在 service 文件里配置 CPUQuota 和 MemoryMax。
什么是文件描述符?如何查看和调整目前文件描述符的限制量?
- 文件描述符是一个非负整数,用于标识进程打开的文件或 I/O 资源(包括普通文件、目录、设备、管道、socket 等)。
- 查看进程打开的文件描述符,有两种方案,一种是
ls -l /proc/<pid>/fd,另一种是lsof -p <pid>。在较新的系统中,比如 Ubuntu 2404,还可以使用lsfd命令查看。- 如果程序打开文件过多导致 “too many open files” 错误,可以通过 ulimit 临时调整上限,或编辑 /etc/security/limits.conf 永久修改。
- 临时查看进程能打开的文件描述符上限:
ulimit -n/etc/security/limits.conf中的软/硬限制含义:硬限制(hard)表示最高能开多高,软限制(soft)当前用户可调整的数量,但不能超过硬限制。
Linux 系统级优化你做过哪些?
内核参数级优化(通过 sysctl 或 /etc/sysctl.conf 配置,然后通过 sysctl -p 生效。):
net.ipv4.tcp_max_tw_buckets = 65536 # 减少TIME_WAIT连接 net.ipv4.tcp_tw_reuse = 1 # 复用TIME_WAIT连接 net.ipv4.tcp_fin_timeout = 30 # 缩短FIN等待时间 net.core.somaxconn = 65535 # 监听队列最大值其他系统资源/环境级优化(配置文件、资源限制、包管理等):
- apt/yum 源指向内部仓库或国内镜像站。
- 修改打开文件描述符的最大数量(通常软硬限制都设置为 65535)。
- 时间同步,ntp源指向内部或国内NTP服务器。
- 服务器多 CPU 场景下,调整 NUMA node ,让 CPU 优先访问本地内存,以避免跨 NUMA 节点的远程内存访问带来的延迟损耗。
- 一般通过 BIOS/UEFI 配置调整。
- 假设服务器 2 个 CPU,每个 8 核,512 GB 内存。CPU 0-7 属于 node 0,CPU 8-15 属于 node 1,给每个 NUMA 节点 256 GB 内存。
- 可以用
numactl -H、numastat -m、lscpu查看目前 NUMA 分配情况。- 关闭 Swap、SELinux
实战场景
如果一个磁盘目前使用率是99%,该怎么去止损和解决问题?
- 首先,磁盘使用率达到99%说明有可能马上或已经影响服务的正常运行,当务之急应该先找到无用的大文件进行删除,让磁盘空间腾出地方,保证服务不受影响;
- 无用文件包括镜像构建时的缓存、旧的日志等。像文件类型的文件,可以优先使用大于号>,即标准输入重定向将其置为空,以防文件被其他进程占用,而导致磁盘空间无法立刻得到释放;
- 后续排查阶段,可以使用
du -sh /挂载点/*,逐级定位是哪个目录或者文件占用空间大,也可以使用find / -xdev -type f -size +1G搜索大于指定大小的文件,排查磁盘空间满的原因;
- 是程序日志打印多了?还是磁盘空间应该扩容了?前者减少日志打印,例如关闭debug、后者可以使用LVM扩容。
如果发现某个进程占用的内存很高,该如何排查来解决这个问题?
free -h查看整体内存使用top + 大写M找出占用内存最多的进程- 通过查看日志、并结合一些工具进行排查Java(jmap、jstack)、Python(objgraph)。常见原因:
- 内存泄漏,程序不断申请内存但不释放。
- 缓存/数据积累,程序大量缓存数据或加载大文件。
- 外部依赖问题,数据库连接、线程池、队列(消息积压)等未正确释放。
- 如果内存剩余空间非常少,已经快要影响程序运行,可以先将程序重启,但不是根本解决方案
如何查看服务器防火墙状态?
不同系统所使用的防火墙工具不同,如 CentOS 系使用 firewalld,Ubuntu 系使用 UFW,但本质上都是基于 iptables
- firewall-cmd –list-all
- ufw status
- iptables -vnL
SQL
SQL语句执行过程?
- 客户端(如 Navicat 或 JDBC)发送 SQL 请求时,服务端进行TCP连接、校验用户名密码;
- 查询缓存(MySQL 8.0 之后 已移除查询缓存,因为维护缓存反而降低性能。);
- 解析器检查 SQL 语法;
- 预处理器检查表是否存在、字段是否存在等;
- 执行器发起查询,查找索引(如果存在),以及对应值;
- 如果是 MySQL,B+tree索引,还会根据其左前缀索引特性,从根节点、枝节点依次发起查询,最终从叶子节点获取数据。
- 最后,数据库把结果数据发送给客户端。
笔试
SELECT 列名或表达式 -- 选择要查询的列或计算结果
FROM 表名 -- 指定要查询的数据表
WHERE 条件 -- (可选)过滤行:先筛选出满足条件的记录
GROUP BY 分组列 -- (可选)按某些列对结果进行分组
HAVING 分组条件 -- (可选)过滤分组:筛选满足条件的分组(作用于聚合结果)
ORDER BY 列名 [ASC|DESC] -- (可选)对结果排序:ASC升序(默认),DESC降序
LIMIT 数量 [OFFSET 起始位置] -- (可选)限制返回的结果行数(分页或取前N条)
练习表
- 在数据库中执行该sql语句,创建练习表
-- 创建测试数据库 CREATE DATABASE IF NOT EXISTS test; -- 使用该数据库 USE test; -- 创建部门表 CREATE TABLE departments ( department_id INT PRIMARY KEY, department_name VARCHAR(100) NOT NULL ); -- 插入部门数据 INSERT INTO departments VALUES (1, 'Engineering'), (2, 'Human Resources'), (3, 'Marketing'), (4, 'Finance'); -- 创建员工表 CREATE TABLE employees ( employee_id INT PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50), gender CHAR(1), hire_date DATE, department_id INT, FOREIGN KEY (department_id) REFERENCES departments(department_id) ); -- 插入员工数据 INSERT INTO employees VALUES (101, 'Alice', 'Smith', 'F', '2015-03-01', 1), (102, 'Bob', 'Johnson', 'M', '2017-07-15', 1), (103, 'Carol', 'Williams', 'F', '2019-01-10', 2), (104, 'David', 'Brown', 'M', '2018-11-23', 3), (105, 'Eve', 'Davis', 'F', '2020-06-17', 4); -- 创建薪资表 CREATE TABLE salaries ( salary_id INT PRIMARY KEY, employee_id INT, salary DECIMAL(10,2), from_date DATE, to_date DATE, FOREIGN KEY (employee_id) REFERENCES employees(employee_id) ); -- 插入薪资数据 INSERT INTO salaries VALUES (1, 101, 80000, '2020-01-01', '2021-12-31'), (2, 101, 85000, '2022-01-01', '9999-12-31'), (3, 102, 75000, '2019-01-01', '9999-12-31'), (4, 103, 60000, '2019-01-10', '9999-12-31'), (5, 104, 72000, '2018-11-23', '9999-12-31'), (6, 105, 68000, '2020-06-17', '9999-12-31'); -- 创建项目表 CREATE TABLE projects ( project_id INT PRIMARY KEY, project_name VARCHAR(100), start_date DATE, end_date DATE ); -- 插入项目数据 INSERT INTO projects VALUES (1, 'Project Apollo', '2021-01-01', '2021-12-31'), (2, 'Project Zephyr', '2022-03-01', NULL), (3, 'Project Orion', '2020-06-01', '2021-06-30'); -- 创建员工-项目关系表(多对多) CREATE TABLE employee_projects ( employee_id INT, project_id INT, role VARCHAR(50), PRIMARY KEY (employee_id, project_id), FOREIGN KEY (employee_id) REFERENCES employees(employee_id), FOREIGN KEY (project_id) REFERENCES projects(project_id) ); -- 插入员工项目关系数据 INSERT INTO employee_projects VALUES (101, 1, 'Developer'), (101, 2, 'Lead Developer'), (102, 1, 'Tester'), (103, 2, 'HR Manager'), (104, 3, 'Marketing Lead'), (105, 2, 'Accountant');
练习题 1:查询所有员工的姓名及其所在部门名称
- 查询每位员工的 first_name、last_name 和其对应的 department_name。
- 提示:内连接(INNER JOIN)
SELECT e.first_name, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id;
SELECT e.first_name, e.last_name, d.department_name指定要查询的列,来自员工表 e 的 first_name、last_name,以及来自部门表 d 的 department_name。FROM employees e指定主表是 employees,并给它起一个别名 e(简化书写)。JOIN departments d使用 内连接(INNER JOIN,INNER 可省略) 把员工表和部门表连接起来。departments 起别名为 d。ON e.department_id = d.department_id指定连接条件,员工表中的 department_id 必须和部门表中的 department_id 相匹配。PS: 如果有员工暂时没有部门(department_id 为 NULL 或未匹配),则不会出现在结果中。如果希望“即使没部门也显示员工”,可以用 LEFT JOIN(左外连接),它会保留左表(FROM 后面的表)中的所有记录,LEFT JOIN 后面表匹配不到则显示 NULL:
SELECT e.first_name, e.last_name, d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id;
练习题 2:查询当前薪资最高的员工信息
- 找出当前薪资(to_date = ‘9999-12-31’)最高的员工的姓名和薪资。
SELECT e.first_name, e.last_name, s.salary FROM salaries s JOIN employees e ON s.employee_id = e.employee_id WHERE s.to_date = '9999-12-31' ORDER BY s.salary DESC LIMIT 1;
练习题 3:统计每个部门员工的平均薪资
SELECT d.department_name, AVG(s.salary) AS average_salary FROM employees e JOIN departments d ON e.department_id = d.department_id JOIN salaries s ON e.employee_id = s.employee_id WHERE s.to_date = '9999-12-31' GROUP BY d.department_name;
- GROUP BY 是用来聚合数据的。没有 GROUP BY,SQL 只能对整张表做汇总;有了 GROUP BY,SQL 可以对“每个部门”“每个性别”“每年”等进行单独统计。
- 只要你在 SELECT 中用了 聚合函数(aggregate functions),就可能需要 GROUP BY。
练习题 4:列出参与了“Project Zephyr”的员工及其角色
SELECT e.first_name, e.last_name, ep.role FROM employee_projects ep JOIN employees e ON ep.employee_id = e.employee_id JOIN projects p ON ep.project_id = p.project_id WHERE p.project_name = 'Project Zephyr';
练习题 5:查询至少参与了两个项目的员工
- 列出参与两个或以上项目的员工的姓名和项目数量。
SELECT e.first_name, e.last_name, COUNT(ep.project_id) AS project_count FROM employee_projects ep JOIN employees e ON ep.employee_id = e.employee_id GROUP BY e.employee_id HAVING COUNT(ep.project_id) >= 2;
练习题 6:查询“Engineering”部门的所有员工当前薪资
- 显示 Engineering 部门所有员工的姓名及其当前薪资。
SELECT e.first_name, e.last_name, s.salary FROM employees e JOIN departments d ON e.department_id = d.department_id JOIN salaries s ON e.employee_id = s.employee_id WHERE d.department_name = 'Engineering' AND s.to_date = '9999-12-31';
练习题 7:查询所有项目的负责人(Lead Developer 或 Manager)
- 列出所有项目的名称、员工姓名及他们担任的领导角色(如包含“Lead”或“Manager”的角色)。
SELECT p.project_name, e.first_name, e.last_name, ep.role FROM employee_projects ep JOIN employees e ON ep.employee_id = e.employee_id JOIN projects p ON ep.project_id = p.project_id WHERE ep.role LIKE '%Lead%' OR ep.role LIKE '%Manager%';
练习题 8:查询未参与任何项目的员工
- 列出没有参与任何项目的员工。
SELECT e.first_name, e.last_name FROM employees e LEFT JOIN employee_projects ep ON e.employee_id = ep.employee_id WHERE ep.project_id IS NULL;
实践场景
如何修改表中某行某字段值?
- 最安全的方式其实是使用事务,
BEGIN;开启事务,然后用 SELECT 查看要修改的范围是否符合预期SELECT * from employees WHERE employee_id='101';- 使用 UPDATE 更新内容
UPDATE employees SET first_name='Azheng' WHERE employee_id='101';
- 注意:更新表时一定要加 WHERE,否则可能会导致全表修改。
- 发现修改错误后使用
ROLLBACK;撤销,无误后使用COMMIT;提交。
内连接和外连接是什么?
- 内连接(INNER JOIN)相当于取两张或多张表的交集。
- 外连接分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)、完全外连接(FULL JOIN)。
- 左外连接保留左表全部内容,右表未匹配的显示为NULL
- 右外连接保留右表全部内容,左表未匹配的显示为NULL
- 完全外连接会将左右不匹配的行全显示出来,并置为NULL(MySQL不支持)
- 还有一种是自连接,相当于把一张表想象成两张表进行查询。
监控与告警
*Prometheus 服务发现原理?
取决于服务发现类型,常见的有基于 K8s 的、基于文件、基于 consul 的等:
- kubernetes_sd:通过监听 Kubernetes API Server,获取 node、service、pod、endpoints、endpointslice、ingress 的信息。
- file_sd:
- consul_sd:
*Prometheus Metric 类型有哪些?
- Counter 计数器:记录单调递增(只增不减,例如年龄)的数据,例如 HTTP GET 数量 ,通常与 rate()、irate() 等函数配合使用。
- Gauge 仪表盘:记录可增可减的数据,例如 CPU、内存 的使用率。
- Histogram 直方图:它会在一段时间范围内对数据进行采样,是一组统计数据,需要在服务端 (Prometheus) 使用 histogram_quantile() 函数计算分位数。
- Summary 摘要:与 Histogram 类似,都是在一段时间内对数据进行采样,但它直接在客户端(被监控的应用端)计算分位数。
PS:分位数是把数据平均分成多份,分别统计
*Prometheus有哪些查询函数?
- xxx
*Prometheus 查询函数中 rate 和 irate 的区别?
- rate 计算的是单位时间内的平均增长速率,适合像 CPU 使用率、磁盘 IO 这种场景,线条比较平滑。
- irate 是高灵敏函数,计算的是单位时间内最后两个数据点的瞬时速率,适合像 HTTP 请求速率(QPS)、突发性网络流量这种场景,线条比较陡峭。
- 简单来讲,irare 更加敏感,它能看到一些瞬时、突发的情况。而rate 更平滑,
Prometheus 主要由哪些组件组成?
- Prometheus Server:负责数据采集、存储和查询。
- Exporter:指标暴露器,负责把应用/系统的监控数据转成 Prometheus 能识别的格式。
- Node Exporter:采集主机 CPU、内存、磁盘、网络等系统指标
- cAdvisor:采集容器运行时的资源指标
- MySQL/Redis/Kafka Exporter:采集中间件运行指标
- Pushgateway:用于 短生命周期任务 或 无法被主动拉取的任务。用于 短生命周期任务 或 无法被主动拉取的任务。
- 场景:一次性批处理任务(job 运行几秒就结束,Prometheus 来不及拉取)。Job 可以把指标主动 推送(push) 给 Pushgateway,Prometheus 再去拉取 Pushgateway。
- Alertmanager:处理告警通知。
- 去重(避免重复通知)
- 分组(相似告警合并)
- 抑制(例如主机宕机时抑制其子服务告警)
- 静默(临时屏蔽某些告警的通知)
- 路由(邮件、Slack、钉钉、企业微信等)
- Grafana(可选,用于可视化)
Prometheus 的数据模型是什么?
Prometheus 使用多维数据模型,每个时间序列由以下部分组成:
- 指标名称(Metric Name):说明“监控的是什么”。(如 http_requests_total)。
- 标签(Labels):区分相同指标的不同维度。(如 method=“GET”、status=“200”)。
- 时间戳(Timestamp):表示采样的时间点。
- 值(Value):表示该时间点的数值。
如何监控 K8s 集群?
- 通常的方案是 Prometheus + Grafana + Alertmanager,Prometheus作为时序数据库采集指标数据,Grafana 基于 Prometheus 的指标进行图形展示,Alertmanager 实现告警;
- 还可以通过 KubeSphere、Rancher 等 K8s 管理平台实现监控,这些平台都集成了监控告警功能;
- 常见的监控指标:
- Node:CPU、内存、磁盘使用率、磁盘IO
- Pod/容器:CPU、内存使用情况
- Pod 状态:Running、Pending、CrashLoopBackOff 等
- API Server 的每秒请求数、请求延迟
- 网络流量和带宽使用情况
- 存储类的使用情况
如何监控应用程序?
- Prometheus + 第三方或程序内部集成的 /metrics 端点。
如何设计一个有效的监控系统?
- 全面覆盖:监控系统应覆盖所有关键组件,包括服务器、网络设备、数据库、应用层等。
- 多维度监控:不仅要监控硬件资源(如CPU、内存、磁盘),还要监控应用性能(如请求延迟、错误率)和服务健康状态(如服务可用性)。
- 实时性:监控数据应尽可能实时更新,以便及时发现问题。
- 告警机制:设置合理的告警阈值和通知渠道(如邮件、短信、Slack等),确保问题能够被及时发现和处理。
- 可视化:通过仪表盘展示关键指标,便于快速了解系统状态。
- 历史数据分析:保留历史数据,便于分析趋势和排查问题。
如何设置合理的告警阈值?
- 基于历史数据:分析历史数据,了解系统的正常运行范围,确定合适的阈值。
- 区分紧急和非紧急告警:对于影响用户体验的关键指标(如请求失败率),设置较为严格的阈值;对于次要指标(如磁盘使用率),可以适当放宽。
- 动态调整:随着系统负载的变化,定期调整告警阈值,确保其始终处于合理范围内。
- 避免误报:设置合理的触发条件,避免频繁的误报。例如,可以通过多次采样确认异常后再触发告警。
如何减少告警风暴?
- 分级:例如将告警分为P0-P3,每个级别通知的人群不同,像P0级别就全员发送,快速处理。像磁盘空间大于80%这种告警仅发送给相关负责人;
- 聚合:例如节点故障,输出“5 分钟内有 12 个节点异常”,而不是 12 条独立告警;
- 抑制:例如 Redis 宕机,就不发送上游的 API 超时告警了。
实践场景
宿主机(物理机/虚拟机)的核心监控指标有哪些?
- CPU使用率、内存使用率、系统1分钟、5分钟、15分钟的平均负载、系统运行时间;
- 磁盘:磁盘分区使用率、磁盘IO、Inode使用率;
- 网络:内网和公网的流入流出带宽、TCP 连接数,以及各种状态的数量(ESTABLISHED、TIME-WAIT等);
- 进程:进程数量、进程打开的文件描述符数量等。
JVM 的核心监控指标有哪些?
- 堆内存(Heap)使用率;
- 垃圾回收(GC)次数、耗时、频率;
- 当前活跃线程总数、运行以来的最大线程数;
- 类加载(Class Loading):已加载类总数、累计加载类数量、卸载类数量;
- 运行时(Runtime):JVM 启动时间 / 运行时长、Java 版本。
- 宿主机
Elasticsearch 的核心监控指标有哪些?
- 写入和查询的 QPS
- 集群健康状态(green、yellow、red)
- 索引(index)与分片(shard)状态
- 宿主机、JVM
MySQL 的核心监控指标有哪些?
- SQL 的 QPS
- 当前连接数、最大连接数
- 主从复制延迟、I/O 线程与 SQL 线程状态
- 宿主机
Kafka 的核心监控指标有哪些?
- 生产者(Producer)和消费者(Consumer)的 QPS、延迟
- Topic 与 Partition 的状态
- 宿主机、JVM
K8s 的核心监控指标有哪些?
- Pod 状态,比如:Running、Pending、CrashLoopBackOff 等
- API Server状态,比如:每秒请求数、请求延迟
- xxx 状态,比如:
- 宿主机
CI/CD
如何配置CICD流水线?
Jenkins :
- 配置 Gitlab 登录凭据,定义 pipeline 或 Jenkinsfile ,添加构建流程、构建分支等。
Gitlab :
- 配置 webhook,添加Jenkins 项目的 URL。
配置完成后的数据流:
# pipeline GitLab → Webhook → Jenkins Job → 执行 pipeline(拉代码 + 构建 + 部署) # Jenkinsfile GitLab → Webhook → Jenkins Job → Git clone → 找到 Jenkinsfile → 执行 pipeline
如何用 git 创建和切换一个分支?
- git checkout -b branch_name
容器
*容器与虚拟机的区别?
- 容器使用宿主机的内核,而每个虚拟机都有自己独立的内核。
- 容器更加轻量,迁移和部署都比虚拟机更加方便,并且启动速度也要比虚拟机快。
- 容器的隔离性不如虚拟机,因为容器是公用宿主机的内核 + 通过内核的 namespace 实现隔离。
Dockerfile 的指令有哪些?
- FROM 指定基础镜像。
- LABEL 给镜像打标签。
- COPY 和 ADD,都可以将宿主机的文件拷贝至容器,但 ADD 还可以将压缩包解压缩拷贝,以及从 URL 下载文件拷贝至镜像。
- RUN 指定镜像构建时执行的shell命令,每个 RUN 指令都会在镜像中新增一层。
- ARG 和 ENV,ARG 是 “构建时变量”,ENV 是 “运行时变量”。
- USER 指定容器运行时的用户。
- WORKDIR 为后续的RUN、COPY、ADD、CMD、ENTRYPOINT等指令设置工作目录”(构建和运行阶段均生效)
- EXPOSE 声明容器运行时 “监听的端口”(仅为元数据说明,不实际映射端口)。
- VOLUME 定义容器中的 “匿名卷”(持久化数据的目录),避免容器内的数据随容器删除而丢失。
- ONBUILD 定义 “触发器”,当当前镜像被用作其他镜像的基础镜像时,自动执行ONBUILD后的指令。
- STOPSIGNAL 指定容器停止时发送的系统信号(默认是SIGTERM),用于优雅关闭应用。
- CMD 和 ENTRYPOINT,都是指定容器启动时的命令,但 ENTRYPOINT 不可变。CMD 通常作为容器启动时的参数,可以被覆盖。
Dockerfile 中的 COPY 和 ADD 有什么区别?
- COPY 和 ADD,都可以将宿主机的文件拷贝至容器,但 ADD 还可以将压缩包解压缩拷贝,以及从 URL 下载文件拷贝至镜像。
Dockerfile 中的 CMD 和 ENTRYPOINT 有什么区别?
- CMD 和 ENTRYPOINT,都是指定容器启动时的命令,但 ENTRYPOINT 不可变。CMD 通常作为容器启动时的参数,可以被覆盖。
namespace 有哪些?
- Network namespace:隔离网络资源(如网卡、IP 地址、端口等)
- PID namespace:隔离进程 ID(每个 namespace 中的进程看到的 PID 是独立的)
- IPC namespace:隔离进程间通信(如信号量、消息队列等)
- User namespace:隔离用户和组 ID
- UTS namespace:隔离主机名和域名
- Mount namespace:隔离文件系统挂载点
Overlay 与 Underlay 的区别?
Overlay 与 Underlay 的本质上都是为了解决容器跨宿主机通信的问题
- Overlay 是在底层网络之上构建一层虚拟网络(典型代表有 Calico 的 IPIP/VxLAN 模式),来实现容器跨宿主机通信。而 Underlay 是直接使用底层网络,来实现容器跨宿主机通信( K8s 的 hostNetwork 模式)。
- IP 方面,Overlay 网络下,容器 IP 有独立的地址段,而 Underlay 网络下,容器需要和宿主机处于同一网段。
- 性能开销方面,Overlay 封装 / 解封装会带来少量 CPU 开销,Underlay 无封装 / 解封装过程,转发效率接近物理网络。
Docker 的网络模式有哪些?containerd呢?
- bridge(默认),容器连接到一个虚拟网桥(docker0),容器间可互通,可通过端口映射与宿主机交互。
- host,容器与宿主机共用网络命名空间,没有隔离,性能好但安全性低。
- none,容器没有网络,只有 loopback。
- container,多个容器共享同一个容器的网络命名空间。
- overlay,用于多主机网络,常结合 Swarm 或 Kubernetes。 PS:containerd 本身不提供复杂网络模式,只负责容器运行。但它对外提供了 CNI 接口,可对接不同的网络插件。
Docker 的 bridge 网络模式工作原理?
Docker 在 bridge 网络模式下,默认会创建一个 docker0 的网桥,它相当于虚拟的二层交换机,同时它还会被分配一个IP,作为容器跨网段通信的网关; 当创建容器时,会生成一个 Veth Pair(一对虚拟网卡),它就像一个虚拟网线,一端在容器(通常命名为 eth0),另一端在主机(通常命名为 vethxxxxxx),主机这端的 vethxxxxxx 还会连接 docker0 网桥。
容器间通信,需要容器都归属同一个网桥,因为二层可达,所以可直接通信:
[容器A eth0] ⇆ [vethA] ⇆ [docker0 bridge] ⇆ [vethB] ⇆ [容器B eth0]容器与外部通信:
[容器 eth0] ⇆ [vethX] ⇆ [docker0 bridge] ⇆ [NAT 到宿主机网卡] ⇆ [Internet]
实践场景
如何定位容器的异常网络流量?如何对其进行封禁?
- 首先通过看日志等方式,定位到对方的源IP地址;
- 进行抓包,但抓包方式取决于容器的运行方式;
- host 网络模式:可以直接在宿主机抓。
- bridge 网络模式:两种方案:
docker inspect -f '{{.State.Pid}}' ced6cf42f7d3定位到容器的 PID,然后使用nsenter --target 3981895 --net进入到该容器的 netns 使用tcpdump进行抓包;- 进入容器,使用
ip a命令找到宿主机对应的网卡(vethxxxxx)对,宿主机对应的网卡对上抓。- 使用 iptables 或 WAF 上进行封禁,封禁时要注意规则下发的细粒度,需精确到IP端口,以免影响正常客户端访问。
iptables -I INPUT -s <malicious-ip> -j DROP
使用 Dockerfile 制作镜像时,如何减少镜像的大小?
- 使用轻量级镜像,如 alpine。
- 清理无用的缓存文件。
- 尽量减少 RUN 指令的使用,以减少镜像分层。
- 使用多阶段构建,比如第一阶段使用nodejs镜像构建前端代码,第二阶段仅把前端构建结果拷贝至nginx镜像。
K8s
*Kubernetes 的核心组件有哪些?
Master 节点
- API Server:提供 Kubernetes API,是集群的前端接口。
- Controller Manager:管理各种控制器(如 Deployment、StatefulSet、DaemonSet等),确保实际状态与期望状态一致。
- 比如:Deployment Controller 会监控实际状态,发现副本数不匹配后,会自动创建一个新的 Pod,确保实际状态与期望状态一致。
- Scheduler:负责将 Pod 调度到合适的节点上。
- etcd:分布式键值存储,保存集群的所有配置数据。
Worker 节点
- Kubelet:管理节点上的 Pod,并定期向 API Server 报告 Pod 状态。
- Kube Proxy:负责 Service 的负载均衡和流量转发,确保客户端请求的流量转发到对应的 Pod 上,有 iptables 和 IPVS 两种模式。
- Container Runtime:运行容器的引擎(如 Docker、containerd)。
其他:
- CoreDNS:集群内部 DNS 服务。
*kubernetes 中的 Pod 是什么?
- 在 Kubernetes 中,Pod 是最小的调度和运行单位,Pod 中可以包含一个或多个容器,这些容器共享网络和存储资源。
- 共享网络是指 Pod 内所有容器共享同一个网络命名空间,容器之间可以通过 localhost 直接通信。
- 共享存储是指 可以挂载同一个 Volume,多个容器可同时读写,常用于日志共享、配置共享等。
*Pod 状态有哪些?
- Pending:Pod 已创建,但还没有被调度到节点上。
- Running:Pod 中的所有容器都已启动并运行。
- Succeeded:在一次性任务中出现,表示任务已成功执行完毕。
- Terminating:Pod 已经收到了删除请求,但还没有完全删除,正在进行资源清理和容器终止的过程。
- CrashLoopBackOff(容器状态):表示容器启动失败并多次重启失败。
- Failed:表示 Pod 中的容器以非零状态码退出。
- Unknown:Kubelet 无法获取 Pod 的状态。
*Deployment 如何实现滚动更新?
- 通过 Deployment 的 strategy 字段定义滚动更新策略,并通过 maxUnavailable、maxSurge 控制速率。
- maxUnavailable 表示升级时最多有几个 Pod 不可用。
- maxSurge 表示升级时最多比期望副本数多出的 Pod 数量。
- 最常见配置(高可用):maxUnavailable = 0,maxSurge = 1,先多启动一个新 Pod,确认可用后再删旧的,不会出现服务空档。
*Pod 创建过程?
- 首先 kubectl 或其他客户端会向 kube-apiserver 发送 PodTemplate;
- API Server 对请求进行 认证、鉴权、准入控制;
- 认证:你是谁?(比如通过证书验证身份)
- 鉴权:你有没有权限做这个操作?(比如基于角色的访问控制中,Alice 是否有 “create pods” 这个权限。)
- 准入控制:这个操作是否合法、合规?(namespace的Pod 数量、CPU、内存使用总量是否超限?PodSecurity、ResourceQuota、LimitRanger 这三种都属于准入控制器)
- 验证通过后,Pod 对象被转换成 json 格式存入 etcd;
- Controller Manager 监控到 etcd 中有新的 Pod 资源对象后,会将 Pod 状态置为 Pending;
- Scheduler 从 etcd 拿到 Pending 状态的 Pod 后,根据调度策略(资源量、亲和性/反亲和、污点/容忍、优先级等)选择合适的节点,把调度结果(Pod.Spec.NodeName) 通过 API Server 写回 etcd;
- kubelet 监听到本节点被分配了新的 Pod 后,根据 Pod 定义(PodSpec),就会开始调用 CNI 插件配置网络,CSI 插件挂载持久化存储,CRI拉镜像、启动 pause 容器和业务容器,以及启动、就绪、存活探针开始探测;
- 最后 kubelet 会定期向 API Server 上报 Pod 状态(Pending、Running、Succeeded、Failed…)。
*Pod 是如何调度的?
- xxx
pause 容器是干嘛的?
- 每个 Pod 启动时,都会启动一个 pause 容器,这个容器用于维护 Pod 的 namespaces,在一个 Pod 中,多个业务容器共享以下命名空间:
- Network Namespace(网络)
- IPC Namespace(进程间通信)
- UTS Namespace(主机名等)
Pod 运行模式?
- 单容器应用:一个 Pod 就运行一个业务容器(最常见)。
- Sidecar 模式:在主应用容器旁边放一个辅助容器(如日志收集、监控代理)。
- Adapter 模式:主应用容器不变,旁边加一个数据转换容器。
- Ambassador 模式:通过代理容器来与外部服务通信。
Pod 控制器有哪些?
- 在 K8s 中,常见的 Pod 控制器有 Deployment、StatefulSet、DaemonSet、Job、CronJob;
- Deployment 是 ReplicaSet 增强版,主要用于运行无状态服务。
- StatefulSet 主要用于运行有状态服务。
- DaemonSet 确保在集群的每个 Worker 节点上都运行一个 Pod,通常用于集群级别的服务,如日志收集器、监控代理等。
- Job 用于执行一次性任务,任务完成后 Pod 会被终止。
- CronJob 是周期性任务,任务会定时执行。
Deployment 和 StatefulSet 有什么区别?
- Deployment 通常用于运行无状态服务,而 StatefulSet 通常用于运行有状态服务;
- Pod 名称方面,Deployment 是随机分配的,而 StatefulSet 有固定的名称后缀,比如-0、-1、-2;
- 启动和删除顺序方面,Deployment 没有要求,而 StatefulSet 启动和删除都有严格的先后顺序要求;
ReplicaSet 和 Deployment 有什么区别?
- Deployment 是 ReplicaSet 增强版,增加了滚动更新和回滚功能。
Pod 删除过程?
- 首先 kubectl 或其他客户端向 kube-apiserver 发送 Pod 删除请求;
- API Server 将 Pod 标记为 Terminating 状态,并设置一个宽限期(默认30秒)。在宽限期内,kubelet 会尝试优雅地停止 Pod 中的容器;
- 如果定义了停止前钩子(preStop Hook),会在此期间执行。
- 如果宽限期内容器仍未停止,kubelet 会向容器发送 9(SIGKILL) 信号,强制终止进程;
- 最后 kubelet 会向 API Server 报告 Pod 已终止,API Server 收到后,最终会把 Pod 对象从 etcd 中移除。
Pod 的探针有哪些?
- Startup Probe(启动探针):检查容器是否成功启动。
- Readiness Probe(就绪探针):检查容器是否已准备好接收流量,探测成功才会通过 Service 将流量路由到该容器。
- Liveness Probe(存活探针):周期性检查容器是否健康。
- PS:
- 探测方式有三种,分别是基于命令、基于 HTTP 状态码、基于 TCP 连接。
- 基于命令是根据命令执行的退出状态码判断是否执行成功,0表示成功,非0表示失败;
- 基于 HTTP 状态码,2xx、3xx表示成功,否则表示失败;
- 基于 TCP 连接,是根据能否建立 TCP 连接来判断(类似 telnet)
- 执行顺序上,启动探针是首先执行,启动探针探测成功后,就绪和存活探针并行执行。
- 启动探针成功后不再持续探测,存活和就绪探针会在容器运行期间进行周期性探测。
kubernetes 中的 Service 是什么?
- kubernetes 中的 Service 是 Pod 的代理,可以为 Pod 提供一个稳定的访问入口,并且 Pod 之间还可以通过 Service 的域名进行通信;
- Service 的代理是基于 iptables 或 ipvs 的。
Service 的类型有哪些?
- ClusterIP(默认):提供一个仅在集群内部可访问的虚拟 IP。
- NodePort:在每个节点上打开一个端口,通过 NodeIP:NodePort 的方式访问服务。
- 在生成 NodePort 的同时,也会生成一个 ClusterIP。可以通过 NodeIP:NodePort 从外部访问服务;但在集群内部,依然可以通过 ClusterIP 来访问。
- LoadBalancer:在公有云(如 AWS、GCP、Azure)中使用时,会自动创建一个云负载均衡器,将外部流量转发到 Service。常用于生产环境对外暴露服务。
- 会生成一个 ClusterIP + NodePort,然后再由云提供商创建一个外部的负载均衡器,转发流量到 NodePort。
- 请求流程:外部 → LoadBalancer 公网 IP → NodePort → ClusterIP → Pod。
- ExternalName:把集群外部服务引入到集群内。
什么是 Headless Service?它与普通 Service 有什么区别?
- Headless Service 是一种特殊的 Service 类型,它与普通的 Service 主要区别在于不分配 Cluster IP。
- Headless Service 的核心作用在于它改变了 DNS 解析的行为。
- 对于普通 Service:当你查询其 DNS 名称时,DNS 服务器会返回 Service 的 Cluster IP。
- 对于 Headless Service:当你查询其 DNS 名称时,DNS 服务器会返回与该 Service 相关联的所有 Pod 的 IP 地址。
- 说白了就是可以实现 Pod 间直接进行通信,而不使用 service 进行负载均衡,通常用于有状态服务需要通过 Pod IP 直接通信的场景。
- 定义方式是在 Service 的 clusterIP 处定义为 None。
kubernetes 中的 Ingress 是什么?
- “Kubernetes 中的 Ingress 是一种 API 对象,主要作用是管理集群外部到内部服务的 HTTP/HTTPS 流量路由。简单说,它就像集群的‘入口网关’,能根据域名、路径等规则,把外部请求转发到对应的内部 Service;
- Ingress 本身只是规则的定义,必须配合 Ingress 控制器(比如 Nginx Ingress Controller)才能生效,常见的控制器有 Nginx、Traefik、Istio。
Service 和 Ingress 的区别?
- 在 K8s 中,Service 和 Ingress 都是对外暴露流量的方式;
- Service 是给一个或多个 Pod 提供稳定访问入口,负责四层(TCP/UDP)负载均衡,有 ClusterIP、NodePort 和 LoadBalancer 类型,适合内部访问或者简单对外暴露;
- Ingress 是给一个或多个 Service 提供稳定访问入口,负责七层(HTTP/HTTPS)路由规则,需要依赖 Ingress Controller,比如 Nginx、Traefik、Istio,支持基于域名和路径的流量分发,还可以配置 TLS;
- 简单来讲,Ingress 通过七层代理转发到 Service,Service 通过四层代理转发到Pod。
外部请求 → Ingress Controller → Service → Pod
Service 怎么让集群外部访问?
- NodePort:定义 NodePort 类型的 Service,客户端访问 任意节点IP(Master 或 Worker)+ NodePort,NodePort 默认端口范围在 30000–32767 之间。
- Ingress:将 Service 通过 Ingress 映射,客户端访问 IngressControllerIP + IngressControllerPort。
- LoadBalancer:定义 LoadBalancer 类型的 Service,客户端访问 外部负载均衡器IP + ServicePort。
kubernetes 中的 Namespace 是什么?
- Namespace 实现了 K8s 中各种资源的逻辑隔离;
- 默认的 Namespace 有 default、kube-system、kube-public、kube-node-lease。
- kube-system 下默认运行了coredns、etcd、apiserver、controller-manager、kube-proxy、scheduler。
kubernetes 中的 Configmap 和 Secret 是什么?
- Configmap:用于存储非敏感的配置数据(如环境变量、配置文件)。
- Secret:用于存储敏感数据(如密码、密钥),内容经过 Base64 编码。
k8s 中 StorageClass、PV、PVC的关系是?
- 这三个是 K8s 中最常用的持久化存储解决方案,可以实现静态或动态供应持久化存储资源;
- 静态供应(Static Provisioning)
- 管理员手动创建好 PV;
- 用户创建 PVC;
- 系统自动将 PVC 和符合条件的 PV 绑定;
- StorageClass 可选。
- 动态供应(Dynamic Provisioning)
- 用户创建 PVC,并指定 StorageClass;
- StorageClass 的 Provisioner 插件会自动创建一个 PV,并且 PVC 会自动绑定这个 PV;
- 这是现代集群更常用的方式。
kubernetes 中的 Volume 有哪些?
- 临时卷:
- emptyDir:在 Pod 被分配到节点时创建,生命周期与 Pod 一致。当 Pod 从节点移除时,emptyDir 中的数据会被永久删除。常用于 Pod 内多个容器之间共享数据。
- 持久卷:
- PersistentVolume(PV):由管理员配置的集群级存储资源,独立于 Pod 生命周期。
- PersistentVolumeClaim(PVC):由用户申请的存储资源,类似 “存储需求订单”,Kubernetes 会自动匹配满足条件的 PV。
- 本地存储卷
- hostPath:宿主机的文件系统路径直接挂载到 Pod 中。
- local:与 hostPath 类似,但更强调稳定性,需要显式指定节点亲和性,适用于需要高性能本地存储的场景(如数据库)。
- 配置类卷:
- Configmap:用于存储非敏感的配置数据(如环境变量、配置文件)。
- Secret:用于存储敏感数据(如密码、密钥),内容经过 Base64 编码。
- downwardAPI:将 Pod 或容器的元数据(如 Pod 名称、IP、标签等)以文件形式暴露给容器。
- 网络存储卷
- nfs:挂载 NFS(网络文件系统)共享目录,适用于需要多节点共享数据的场景。
K8s 如何实现蓝绿部署?
- 蓝绿部署是指存在A、B两套环境,平常A对外提供服务,当B升级测试完成后,把流量迁移到B环境中;
- K8s中要实现蓝绿部署,可以基于 service 的标签选择器,切换到不同的 Pod 控制器来实现。
K8s 如何实现金丝雀发布?
- 金丝雀发布又称灰度部署,指的是先进行小范围的升级,当运行一段时间没问题后,再进行增量或全量替换。
- K8s中要实现灰度部署,可以通过 service 的标签选择器控制转发的后端 Pod。也可以使用服务网格(如 Istio),通过基于权重的方式实现流量控制。
K8s 如何实现金丝雀发布?
- 金丝雀发布又称灰度部署,指的是先进行小范围的升级,当运行一段时间没问题后,再进行增量或全量替换。
- K8s中要实现灰度部署,可以通过 service 的标签选择器控制转发的后端 Pod。也可以使用服务网格(如 Istio),通过基于权重的方式实现流量控制。
K8s 中的 CRD 和 Operator 是什么?
- CRD 是自定义资源类型,Operator 是一个自定义的控制器,两者结合通常用于管理有状态应用。
什么是 Helm?
- Helm 是 Kubernetes 的包管理工具,类似于 Linux 的 apt 或 yum,也像 Node.js 的 npm。
- 它把一组 Kubernetes 资源(Deployment、Service、ConfigMap、Ingress 等)打包成一个 Chart,可以安装、升级、回滚和删除。
Helm 相关组件有哪些?
- Chart:类似于 yum/apt 里的软件包;
- Release:Chart 的一次部署实例,支持版本回滚;
- Repo:Chart 的仓库;
- Values:Chart 的配置文件;
- Templates:通过 Go 模板机制渲染生成最终的 K8s YAML。
什么是 Helm?
- Helm 是 Kubernetes 的包管理工具,类似于 Linux 的 apt 或 yum,也像 Node.js 的 npm。
- 它把一组 Kubernetes 资源(Deployment、Service、ConfigMap、Ingress 等)打包成一个 Chart,可以安装、升级、回滚和删除。
nodeSelector 和 nodeAffinity 有什么区别?
- nodeSelector 仅支持简单的节点 K/V 标签来选择 Pod 调度的节点。
- nodeAffinity 功能更加强大,支持权重、软亲和、硬亲和、表达式等功能。
- 硬亲和:必须完全匹配,否则 Pod 将处于 pending 状态。
- 软亲和:尽量匹配,没有匹配的就运行到其他节点。
实践场景
K8S 备份恢复如何实现?
- xxx
关系型数据库
*MySQL 如何实现主从复制?
- 主库上创建创建复制账号;
- 主库(Master) 定义 server-id、开启 binlog(二进制日志);
- 从库(Slave)定义 server-id、开启 relay-log(中继日志)、开启只读,执行 CHANGE MASTER TO 输出主库相关信息,START SLAVE 启动复制,SHOW SLAVE STATUS 验证 IO 和 SQL 线程的状态;
- 主库创建测试库或表,从库验证复制状态。
*MySQL 主从复制的原理?复制模式有哪些?
- 主库(Master) 开启 binlog(二进制日志),记录所有更改数据的 SQL 操作。
- 从库(Slave) 通过 I/O 线程 连接主库,请求 binlog。
- 主库将 binlog 内容发送给从库,从库保存到自己的 relay log(中继日志)。
- 从库的 SQL 线程 读取 relay log,执行其中的 SQL,从而使数据与主库保持一致。
客户端写主库 ↓ 主库执行并写入 binlog ↓ 从库 I/O 线程读取 binlog → 写入 relay log ↓ 从库 SQL 线程重放 relay log → 数据同步完成全量复制:主库首次向从库复制时为全量复制,后续通过 binlog 增量复制。
异步复制(默认):主库事务提交成功后,不等待从库确认。性能最好,但可能丢失数据
半同步复制:主库事务提交成功后,至少有一个从库 relay log 写入成功后,主库才认为事务提交成功。性能略差,但数据安全性高。
全同步复制(Group Replication):需要所有节点数据同步完成,性能最差,但数据安全性最高。
*MySQL 如何做备份和恢复?
备份(看数据量):
- 较小:每日全量备份(每日定时任务,K8s 或系统 crontab) + 开启二进制日志(binlog),工具可以使用 mysqldump。
- 较大:每周或每月全量备份 + 每日增量备份 + 开启二进制日志(binlog),工具可以使用 xtrabackup(xtrabackup 既支持全量备份,也支持增量备份,但不支持差异备份)。
- PS:结合 rsync、scp 等工具同步到其他主机或云储存做冗余,再配合 shell 脚本(用 find -mtime +N 查找文件修改时间超过 N 天的备份)或工具定期清理历史备份以防磁盘空间满。
恢复(看使用的工具和备份时所采用的方式):
- mysqldump:mysql -u root -p < full_backup.sql(标准输入重定向)。
- 增量备份:仅备份自上次备份以来变化的数据,恢复时必须按顺序应用:全量 → 第1次增量 → 第2次增量 → …
- binlog 恢复到某个时间点(例如误操作前):mysqlbinlog –start-datetime=“2025-10-14 10:00:00” –stop-datetime=“2025-10-14 10:30:00” /var/lib/mysql/binlog.000123 | mysql -u root -p
*MySQL 慢查询如何排查?
- 开启慢查询日志,阈值看场景,一般不超过3秒(一秒两秒较常见);
- 结合 mysqldumpslow、pt-query-digest(更推荐)、EXPLAIN 等工具分析慢查询日志,重点关注:
- 执行时间(Query_time):这是判断一个查询是否慢的直接指标。
- 锁的等待时间(Lock_time):高锁等待时间表明查询被其他事务阻塞,可能存在事务设计或并发问题。
- 扫描的行数(Rows_examined)、返回的行数(Rows_sent):这是最重要的优化指标之一。扫描行数远大于返回行数(Rows_sent)时,说明查询效率低下,通常是索引缺失或使用不当。
- 是否使用索引:通过 EXPLAIN 查看 key 字段,确保使用了合适的索引。
*如何避免慢查询?
- 对合适的字段定义索引:
- 查询时基于 B+ 树的左前缀特性,充分利用索引来查询;
- 在虚拟机或物理机部署 MySQL,且磁盘采用 SSD,以提升 MySQL 性能。
事务特性有哪些?
- 原子性(Atomicity):整个事务中的所有操作要么全部成功执行,要么全部失败后回滚。
- 一致性(Consistency):事务做完后,数据得保持前后一致(比如转账时,A少了100块,B就必须多100块)。
- 隔离性(Isolation):事务在未提交前不可见,但有多种隔离级别:读未提交、读已提交、可重复读、串行化。
- MySQL 的 InnoDB 存储引擎默认是可重复读。
- 持久性(Durability):事务一旦提交,其所做的修改会永久保存于数据库中。 PS:事务还依赖于事务日志,其中包含 redo log 和 undo log,redo log保障事务的持久性,undo log保障事务的原子性。
MySQL 的锁有哪几种?
- 按粒度划分:全局锁、表锁、行锁;
- 按类型划分:共享锁(读锁)和排他锁(写锁)。
MySQL最常用的高可用方案?
- 8.0 以上推荐用 MGR (MySQL Group Replication)
如果数据库 CPU 飙高怎么排查?
- 首先使用 top 等命令排查是否是 MySQL 本身导致 CPU 飙高;
- SHOW PROCESSLIST 或 SHOW FULL PROCESSLIST(Info 列显示完整的 SQL 语句),检查慢查询、是否有全表扫描、索引是否失效;
- Redis 缓存失效,导致查询全到 MySQL,也会导致 MySQL CPU 飙高。
什么是 B+ 树,有什么特点?
B+ 是一种多路平衡查找树,使用左前缀作为索引条件
- 它的根节点和枝节点不存储数据,而是存储键和指向子节点的指针,叶子节点存储数据。
- MySQL 的 InnoDB 存储引擎使用 B+ 树作为索引类型
MYSQL数据库有哪些常见索引?
按数据结构分类:主要是 B+Tree 索引(此外还有 Hash 索引、全文索引); 按字段特性分类:主键索引、唯一索引、普通索引、前缀索引; 按字段数量分类:单列索引、联合索引。
实践场景
假如说是银行转账这种对于一致性要求极高的场景,在主从复制场景下,你会怎么保证写mysql主库后,用户读从库数据是一致的?
- 可以开启半同步复制或全同步复制
- 半同步复制下,至少有一个从节点事务日志写入成功(relay log),才会给客户端返回成功的结果
- 全同步复制下,所有的从节点事务日志写入成功,才会给客户端返回成功的结果
- PS:但半同步和全同步复制都会影响性能
- 代码层面:像银行转账成功后的余额查询,直接读主库,保证读写一致。其他非关键查询走从库,兼顾性能。
创建索引可以提高查询效率,那么在创建索引时需要注意什么?
- 选择经常出现在 WHERE、JOIN、ORDER BY、GROUP BY 的列,避免对布尔类型字段建立索引(例如性别字段);
- 避免创建过多的索引,因为会影响写性能;
- 定期使用 EXPLAIN(MySQL)或 EXPLAIN ANALYZE(PostgreSQL)分析查询计划,确认索引是否被实际使用。
非关系型数据库
*redis 持久化方式有哪些?
- RDB:基于内存的快照,有自动触发和手动触发两种方式:
- 自动触发:定义配置文件(save 多少秒内 有多少key被修改)就触发。
- 手动触发:SAVE、BGSAVE,SAVE 阻塞 Redis,立即生成 RDB,BGSAVE 后台异步生成 RDB,不阻塞客户端。
- AOF:每次写命令(修改数据的命令),追加到 .aof 文件,可定义写入策略(appendfsync 参数):
- always:每次写命令都同步,数据安全性最高,但性能也是最差;
- everysec:每秒同步一次,默认,性能与安全平衡;
- no:由操作系统决定何时写入(通常是当缓冲区满了之后),最快,数据安全性最低。
- 混合模式:RDB + AOF,生产环境推荐,兼顾“性能 + 数据安全”,AOF 文件的开头部分存储 RDB 快照(快速加载),后续是增量的 AOF 命令(高可靠性)。
*为什么同时需要aof和rdb,他们分别有什么局限性?
RDB:
- 优点:恢复速度快(二进制快照文件)、文件体积小(经过压缩)、对性能影响小(bgsave 子进程完成,主线程继续处理请求)
- 缺点:数据丢失风险较高,因为快照是定时生成的,Redis 异常宕机时,会丢失最后一次快照之后的所有写操作。
AOF:
- 优点:数据更可靠(每次写都追加,或每秒追加一次)、可读性强
- 缺点:文件体积较大(但 AOF 会通过重写将日志压缩为等价的最小命令集,避免文件无限膨胀)
生产环境推荐混合模式(aof-use-rdb-preamble yes),兼顾“性能 + 数据安全”,AOF 文件的开头部分存储 RDB 快照(快速加载),后续是增量的 AOF 命令(高可靠性)。
*aof 日志能直接看到数据吗,如果不能需要经过什么处理?
- AOF 文件不是直接存储“数据”的,而是存储“写命令”,且写命令为 Redis 的特有格式。要看的话,需要启动 Redis 时指定 AOF 文件,恢复后查看。
redis 的过期 key 删除策略有哪些?
策略类型 触发时机 优点 缺点 Redis 默认启用? 定时删除 到期立即 实时清理 CPU 开销大 ❌ 惰性删除 被访问时 简单高效 可能占用内存 ✅ 定期删除 定期扫描 平衡性能与内存 非实时 ✅
实践场景
*rdb 做快照,周期一般该设置为多少?
- 需要根据数据重要性、写入频率等指标综合判断,大部分生产环境:
# Redis 会按条件触发快照(满足任意一条即可) save 900 1 # 900 秒内至少有 1 次写操作就触发快照 save 300 10 # 300 秒内至少有 10 次写操作就触发快照 save 60 10000 # 60 秒内至少有 10000 次写操作就触发快照生产环境推荐混合模式(aof-use-rdb-preamble yes),兼顾“性能 + 数据安全”,AOF 文件的开头部分存储 RDB 快照(快速加载),后续是增量的 AOF 命令(高可靠性)。
*假如说我现在想对线上的redis数据库做数据备份,你会怎么做?
- 可以使用 RDB 或 AOF 进行备份,但生产环境一半推荐使用混合模式(文件后缀为 .aof),混合模式中前部分为RDB快照,后部分为AOF追加的写命令,这样可以兼顾“性能 + 数据安全”。
- 配合定时任务,例如每天凌晨三点将备份数据拷贝至备份服务器,或云储存。还可以删除旧的备份,以防磁盘空间沾满。
ELK / EFK
消息队列
Kafka的核心组件有哪些?
- Producer(生产者):负责将数据发布(写入)到 Kafka 的 Topic 中。
- Consumer(消费者):订阅并消费 Kafka 中的消息。
- Broker(代理):Kafka 集群中的每个节点称为 Broker,负责接收、存储和传输消息。
- Topic(主题):Kafka 中消息的逻辑分类单元。
- Partition(分区):Topic 的物理分片,可以提高并行处理和扩展性。
- Zookeeper(可选):早期版本用于管理集群元数据,Kafka 2.8+ 开始,使用 KRaft(Kafka Raft Metadata mode) 模式,作为 Zookeeper 的替代方案。
高可用
*常见的负载均衡算法有哪些?
四层:
- 轮询
- 加权轮询
- 最少连接
- 加权最少连接
- 源地址哈希
七层(基于应用层特征):
- 四层负载均衡算法
- 基于请求的 uri、客户端 IP 哈希、cookie
*负载均衡中的 VIP 是什么?
- VIP(Virtual IP Address),可以为客户端提供一个统一的访问入口,防止后端服务器出现宕机等问题后,产生服务不可用的问题。
- 通常的解决方案为 keepalived + LVS、Nginx等负载均衡服务搭配使用,keepalived 可以在检测节点或服务的状态,在出现问题时,将 VIP 漂移到健康的节点,其本质上是通过 VRRP (虚拟路由冗余协议)实现的。
实践场景
*说说你对高可用的理解
- xxx
*gitlab 高可用如何实现?
- xxx
*如何保证负载均衡器不会把流量负载到宕机/不健康的节点?
- 配置健康检查策略,自动下线不健康的节点;
- 健康检查策略分为很多种,例如:基于TCP的三次握手是否成功、请求某个URI,且状态码返回200等;
- 还有一些其他配置项,比如:检测间隔、重试次数、失败阈值(几次探测失败即认为失败)等
Web 服务
*nginx location 的匹配规则是怎样的?
取决于匹配表达式的符号,优先级从高到低依次为:
=精确匹配^~前缀匹配~区分大小写的正则匹配~*不区分大小写的正则匹配- 没有符号优先级最低
最后会根据请求的 uri,location 会从上到下依次匹配,找到匹配结果将直接返回。
常见 http 状态码以及含义?
2XX(成功):
200OK 成功3XX(重定向):
4XX:
5XX:
配置 Nginx 作为 Web Server 时,你常用的优化配置有哪些?
- 性能优化:
- 开启和 CPU 核心数相同的 worker 进程(worker_processes auto;)
- worker 进程与 CPU 亲缘性绑定(worker_cpu_affinity 0001 0010 0100 1000; # 假设4核CPU,绑定每个进程到特定核心)
- 增大 worker 进程的最大连接数(worker_connections 10240;)
- 使用 epoll 事件通知模型
- 开启 gzip 压缩
- 客户端频繁请求的场景下,开启 keepalive,并设置合理的 timeout
- 安全优化:
- 普通用户运行 worker 进程(user www-data; # master 进程还是以 root 身份运行,因为只有 root 身份才能绑定 <1024 的特权端口)
- 隐藏 Nginx 版本号(server_tokens off;)
- 防盗链(仅允许指定域名访问图片等静态资源)
- 反向代理:
- 开启缓存
- 开启客户端 IP 透传(proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;)
- 其他:
- 日志输出改为 json 格式(便于 ELK 分析)
Shell
实践场景
*写过的 Shell 脚本有哪些?
- 一键部署服务
- 自动构建镜像,并推送到 harbor
- 批量测试域名解析结果
- 批量处理文本,统计境内外 IP 数量
- 通过 es 接口查询 index 内容
Python
Python 的数据类型有哪些?
- 基本类型:int(整型), float(浮点型), complex(复数), bool(布尔型), 字符串;
- 容器类型:list(列表), tuple(元组), set(集合), dict(字典)。
列表和元组的区别?
- 列表可变,元组不可变;
- 定义方式上,列表使用方括号[],元组使用圆括号()或直接用逗号分隔;
Python 中 *args 和 **kwargs 的作用?
- *args:接收任意数量的位置参数,返回元组
- **kwargs:接收任意数量的关键字参数,返回字典
Python 中 is 与 == 的区别?
is比较对象的id,即内存地址(是否是同一个对象),==比较对象的值a = [1, 2] b = [1, 2] print(a == b) # True print(a is b) # False
Python的反射机制是什么?
是指 程序在运行时动态地获取对象的信息(如类型、属性、方法等),并能对其进行操作 的能力。
反射常见应用场景:
- 根据配置文件动态加载类或模块
- 框架内部自动注册与加载类
反射常见内建函数:
- getattr(obj, name) 获取对象的属性或方法
- hasattr(obj, name) 判断对象是否有某属性
- setattr(obj, name, value) 设置属性
- delattr(obj, name) 删除属性
Python的装饰器有哪些类型?
- 大体分为函数装饰器、类装饰器,还可以细分为有参装饰器和无参装饰器;
- 装饰器主要用于在不修改原函数或类定义的前提下,动态地添加功能。
自动化运维
实践场景
*ansible在一百台机器上创建一个目录怎么做?
ansible all -i hosts.ini -b -m file -a "path=/opt/mydir state=directory mode=0755"
- 用 file 模块而不是 shell,因为 file 模块是幂等的,多次执行不会重复创建或报错。
公有云
*VPC 是做什么的?
- 专有网络 VPC(Virtual Private Cloud)提供云上安全隔离的虚拟网络环境。
- 每个 VPC 至少由三部分组成:私网网段、交换机和路由表。
*AWS你用过哪些组件?
- xxx
算法
*写个简单排序算法(冒泡、快排)
xxx