Skip to content

第1章 Linux 系统与国产化 OS ​

对应简历:技能第1条「3 年 Linux 运维与项目实施经验,熟练掌握 Ubuntu / CentOS 及统信 UOS、麒麟 V10 等国产操作系统的部署、调优与安全加固」;项目一「发放统信 UOS / 麒麟 V10 虚拟机 15+ 台」「Kubernetes 集群在统信 UOS(ARM)环境部署」;项目二「全节点标准化初始化与集群部署:containerd 运行时、内核参数调优、NTP 时间同步」。 说明:Kubernetes(后文简称 K8s)相关内核参数与 swap 问题在本章只讲 OS 层,集群层细节见第 6 章。 使用提醒:本章所有"标准回答"都是按"能照着对面试官口述 45–120 秒"的颗粒写的,背的时候不要背提纲,要背整段话。

1.1 常用 20 个命令与使用场景 【必背】 ​

标准回答:如果让我按日常工作的频度报 20 个命令,我会按三类来说。第一类文件与文本:ls、cd、cp、mv、mkdir、tar 做压缩解压,cat 看小文件、tail -f 实时追日志、grep 过滤关键字、find 按名字或时间找文件。

第二类系统性能:top 看 CPU 和内存、free -h 看内存、df -h 看磁盘空间、du -sh 看目录大小、iostat -x 看磁盘 IO。第三类进程网络与服务:ps aux 看进程、ss -lntup 看端口和进程的对应关系、lsof 看进程打开的文件、systemctl 管服务、ssh 和 scp/rsync 做远程和传输。

这套组合覆盖了我日常工作八成的场景:巡检的时候用 top、free、df 三件套,排障的时候用 tail、grep、ss 顺着日志和端口往下钻,交付初始化的时候用 systemctl、find、tar 批量铺环境和打包归档。

我在银行项目 15 多台国产化虚拟机上做了近两年,这些命令基本是每天手不离的。

追问链:

  • 追问1:查看一个程序运行日志的最后 50 行、异常时打印 error,怎么确认? → 答:先 tail -n 50 /var/log/xxx.log 看最后 50 行有没有异常堆栈;再用 grep -iw error /var/log/xxx.log 把错误行全部拉出来,这里加 -i 是忽略大小写、加 -w 是按单词精确匹配,避免把 errorno 这种误匹配进来;如果要看实时滚动输出就 tail -f 追着看,配合 grep 就是 tail -f xxx.log | grep -i error。这是我现场排障最常用的第一条命令。
  • 追问2:怎么知道端口被哪个进程占用? → 答:第一步 ss -lntup | grep :8080,能同时拿到监听地址、进程名和 PID;第二步如果还想确认进程详情,再 ps aux | grep <PID> 看启动命令和工作目录。ss 是 netstat 的现代替代品,在连接数大的机器上速度快很多,因为 netstat 要遍历 /proc/net 全表而 ss 直接读内核 socket 缓存。
  • 追问3:为什么查看大文件不推荐 vim? → 答:vim 打开文件会把内容整个读进内存做编辑缓冲,一个几 G 的日志可能直接把内存吃光、把会话卡死,甚至触发 OOM。生产上看日志要用流式工具:less 分页看、head/tail 看头尾、grep/sed/awk 做过滤和统计,它们都是逐行流式处理,多大的文件都不怕。这是新手最常见的一个坑,我被规范教育过之后就再没犯过。
  • 追问4(深挖):100G 大文件怎么切割? → 答:用 split -b 1G bigfile prefix_ 按大小切成百来个 1G 的小文件,需要合并时 cat prefix_* > bigfile 拼回去;也可以按行数切 split -l 1000000。但在生产上我更倾向不搬文件——先想清楚要的是什么,如果只是要其中一部分行,直接 grep '关键字' bigfile > small 过滤出来,比切割整个文件高效得多。我在银行项目处理过 BCS 组件的巨型日志,思路就是先过滤再分析。

关联话术:这一题我不会干背清单,而是直接把话题引到场景上:"银行项目里 15 多台统信 UOS、麒麟虚拟机的日常巡检就是这套命令的组合,我在国产化系统上的操作习惯和 CentOS 完全一致,差异只是软件源和包名;后来移动云 7 台 Ubuntu 节点我把 top、free、df 这些巡检指标接进了 Prometheus + Grafana 大盘,从人肉巡检变成了指标告警"——一句话把命令、场景、两个项目全串起来。

实操模板(每日巡检命令组,可直接背):

bash
uptime                                      # 负载与运行时长
top -bn1 | head -15                         # CPU/内存快照
free -h && df -h                            # 内存、磁盘
ss -s                                       # 连接数汇总
systemctl --failed                          # 失败服务
journalctl -p err --since "-1 day" | tail -20   # 近一天错误级日志

1.2 文件权限 755/644、umask 与软硬链接 【必背】 ​

标准回答:Linux 权限的本质是"三组对象 × 三种权限"的矩阵:三组对象是属主、属组、其他人,三种权限是读 r 等于 4、写 w 等于 2、执行 x 等于 1,三位数字就是把每组对象的权限值加出来。所以 755 就是属主 rwx、组和其他人 r-x,目录和可执行脚本常用它;644 是属主 rw、组和其他人 r,普通文件常用它。

umask 决定新建文件的默认权限,计算规则是文件从 666 减、目录从 777 减,root 默认 umask 022,所以新建文件是 644、新建目录是 755。软硬链接这块,软链接用 ln -s 创建,本质是存了路径的快捷方式,可以跨分区、可以给目录建,源文件删了链接就悬空失效;硬链接用 ln 创建,是同一个 inode 的多个文件名,不能跨分区、不能给目录建,删掉其中一个名字数据还在,因为 inode 的引用计数没归零。

这套东西听起来基础,但安全基线检查天天用。

追问链:

  • 追问1:为什么目录需要 x 权限? → 答:目录的 x 权限不是"执行",而是"进入和穿越"的权限。没有 x 你就 cd 不进这个目录,也访问不了里面任何文件,哪怕你有 r 权限能 ls 列出文件名也不行——因为访问目录内的文件必须在目录里"走过去"。所以目录常见的组合是 755 或者只给查看的 r-x;一个只有 r 没有 x 的目录能看见文件名却打不开文件,这是权限题里经典的区分点。
  • 追问2:umask 027 新建文件和目录分别是什么权限? → 答:文件是 666 减 027,注意这里不是直接做减法而是按位屏蔽,结果 640,也就是属主 rw、属组 r、其他人没有任何权限;目录是 777 屏蔽 027,结果 750。umask 027 这种"其他人无权限"的掩码在金融客户环境里很常见,我配银行环境的应用目录时就按这种收紧口径来。
  • 追问3:改一个脚本所有人和组? → 答:chown user:group file 一条命令同时改属主和属组,批量处理加 -R 递归;如果只改权限不改属主用 chmod 755 file 或者 chmod +x file 给执行位。生产上我的习惯是 chown 之前先 ls 确认目标,递归操作前先在测试目录验一遍,避免把系统目录的属主改飞。
  • 追问4(深挖):SUID/SGID 了解吗? → 答:知道概念和风险。SUID 是让执行者临时获得文件属主的身份,典型例子是 passwd,普通用户能改自己的密码是因为 passwd 带 SUID、执行时临时有 root 权限写 shadow 文件;SGID 作用在目录上是新建文件自动继承目录的属组;sticky bit 典型在 /tmp,只有文件属主能删自己的文件。生产上这块的重点是安全排查:find / -perm -4000 -type f 找出全系统 SUID 文件和基线对比,多出来的就是可疑项,这是我在银行客户安全扫描里被查过的项目。

关联话术:我会把权限和交付直接挂钩讲:"我交付国产化环境时初始化脚本里固定有一段权限基线——应用目录 755、配置文件 600、密钥类文件 640 并收紧属主到应用账号,这在银行客户的安全基线扫描里是必查项,我按这套基线一次通过过客户验收检查。"

实操模板:

bash
chmod 755 deploy.sh          # 脚本可执行
chmod 600 /etc/ssh/ssh_host_* # 私钥只 root 可读
umask                        # 查看当前值;临时改:umask 027
ls -i f1 f1hard              # 硬链接 inode 相同
ln -s /data/app/current /data/app/link   # 软链接做版本切换

1.3 进程管理、前后台与信号 【必背】 ​

标准回答:看进程我用 ps aux 拿快照、用 top 看实时,管理进程靠信号机制。信号里最常用的三个:kill PID 默认发 15 号 SIGTERM,是"礼貌终止",进程收到后可以保存现场、释放锁再退出;kill -9 PID 发 SIGKILL,内核直接把进程抹掉,进程根本收不到通知,来不及收尾就可能丢数据;kill -1 发 SIGHUP,惯例用法是让守护进程重读配置,nginx -s reload 的本质就是 master 收到 HUP 后拉起新 worker、优雅退掉旧 worker。

前后台管理:Ctrl+Z 把前台任务暂停,bg 放到后台继续跑,jobs 看任务列表,fg %1 调回前台;长期任务用 nohup cmd & 让进程忽略挂断信号,终端断了也继续跑,需要交互的场景用 screen 或 tmux。

还有僵尸进程,Z 状态是子进程退了但父进程没回收它的退出状态,占着进程表项,处理办法是把父进程重启或杀掉,让 init 接管回收。

这套东西我在银行现场管 Java 后端进程天天用。

追问链:

  • 追问1:kill -15 和 kill -9 区别?生产先发哪个? → 答:一定先发 15。15 给进程收尾的机会——刷盘、释放锁、断开连接,等几秒看它退不退,不退再升级到 9。对数据库这类有事务和缓冲池的程序绝不能上来就 kill -9,强杀 MySQL 或 PostgreSQL 是数据文件损坏最常见的成因之一,我在银行现场的规范里明确写了"禁止对数据库进程使用 kill -9",这算是拿别人的事故教训换来的纪律。
  • 追问2:进程有哪些状态? → 答:ps 和 top 里主要是五个:R 是正在运行或就绪;S 是可中断睡眠,绝大多数进程的常态,在等事件;D 是不可中断睡眠,通常卡在 IO 上,D 状态不响应任何信号;T 是停止;Z 是僵尸。排查上大量进程堆在 D 状态是要高度警惕的信号,说明 IO 路径或底层存储可能出问题了,我遇到过 NFS 断连导致进程集体 D 住、机器 load 飙高的场景。
  • 追问3:nohup 和 & 的区别? → 答:& 只是把命令放到后台执行,进程还是这个终端会话的子进程,终端断开时内核会给会话发 SIGHUP,进程多半就跟着退了;nohup 的作用是让进程忽略 SIGHUP,这样终端挂了它还活着。所以两个一般连用 nohup cmd &,再配输出重定向 > log 2>&1,这就是我跑部署和导数任务的标准姿势。
  • 追问4(深挖):D 状态进程杀不掉怎么办? → 答:D 状态在内核 IO 路径上,用户态信号根本递不进去,kill -9 也没用。正确的做法不是反复杀,而是查根因:cat /proc/PID/stack 看卡在哪个内核调用,配合 dmesg 看有没有存储报错,多数是 NFS、iSCSI 断链或磁盘故障,把底层 IO 恢复后进程自己就退了。这块我在生产上按"先看 stack 和 dmesg 再判断"处理过,更深的内核分析我如实说会后补。

关联话术:"银行项目里统一重启数币平台后端 jar 的标准动作,是我写进部署指南的:kill -15 → sleep 5 → 校验进程确实退出 → 再启动,整个链路写成一个脚本避免客户现场直接 kill -9 损坏数据;这套动作后来成为客户现场交接文档的一部分。"

实操模板:

bash
ps aux --sort=-%cpu | head    # CPU 前几名
ps aux --sort=-%mem | head    # 内存前几名
nohup ./server.sh > /var/log/server.out 2>&1 &
kill -15 PID; sleep 5; kill -9 PID    # 优雅→强制的标准两步
ps -ef | grep defunct                 # 僵尸进程

1.4 性能定位链路:top / vmstat / iostat / free 【必背】 ​

标准回答:我的性能定位是固定的四步链路,从宏观往微观走。第一步 top 和 uptime 看负载和 CPU 分布,load average 要相对核数看,8 核的机器 load 长期 6 以上就偏高了,然后看 us、sy、wa、id 四个值——us 高是应用在算、sy 高是系统调用频繁、wa 高是 CPU 在等磁盘。

第二步 vmstat 1 看整体节奏:r 是等 CPU 的进程数、b 是阻塞数,si/so 是 swap 换入换出,只要不是 0 就说明内存有压力在换页,wa 对应 IO 等待占比。第三步 iostat -x 1 下钻到磁盘:%util 接近 100% 说明盘满负荷,await 是每次 IO 的平均等待毫秒数,机械盘 await 上几十毫秒就明显偏慢,avgqu-sz 是队列深度。

第四步 free -h 看内存,重点看 available 这一列,它才是应用真正能用的量,buff/cache 是内核拿去做缓存的部分,需要时会自动让出来,不算浪费。进程级的 IO 用 iotop、网络用 iftop 补充。

这套工具都来自 sysstat 等基础包,我的目标就是用最少的命令把瓶颈归到 CPU、内存、磁盘 IO、网络四类里的某一类。

追问链:

  • 追问1:8 核机器 CPU 负载多少算满载?告警阈值? → 答:load 接近 8 就是满载。告警我按两档配:持续超过核数的 0.7 倍,也就是 5.6 左右,触发 warning;到 1 倍即 8 触发 critical。这个 0.7/1.0 的分档是兼顾"提前发现"和"不误报"的经验值,移动云项目接 Prometheus 告警时我就是按这个梯度配的规则。
  • 追问2:wa 很高说明什么? → 答:wa 是 iowait,CPU 空闲但有事干不了,在等磁盘返回,说明瓶颈在 IO 不在算力。下一步 iostat -x 看 %util 和 await 确认是哪块盘、iotop 定位到哪个进程。常见的根因是数据库全表扫描、日志狂刷,或者已经在换 swap 了——所以先用 vmstat 看 si/so 把内存问题排除掉再查盘,顺序不能乱。
  • 追问3:free 里 buff/cache 占很大,要不要清理? → 答:不要清。这是内核把空闲内存拿来做页缓存和块缓存,属于"不用白不用",应用要内存时内核会自动回收这部分。人为 echo 3 > /proc/sys/vm/drop_caches 反而把热缓存全打掉,下一个访问高峰 IO 会猛涨。判断内存够不够只看 available:available 大就说明还有余量,哪怕 free 那一列接近 0。
  • 追问4(深挖):怎么实时看网络流量、按进程看? → 答:连接级别的带宽看 iftop -i eth0 -nP,-n -P 禁止反解析,能看清每个连接占了多少流量;进程级别的用 nethogs eth0,直接报是哪个进程在吃带宽;历史趋势用 sar -n DEV 看。我在生产上用这套组合定位过备份任务把带宽打满影响业务的问题,原理层面我理解到这些工具读的是 /proc/net/dev 的计数器,更深的实现细节可以会后补。

关联话术:这套链路我按时间线讲很自然:"银行驻场那两年,我的日常巡检报告就是 top、vmstat、iostat、free 快照的汇总,一天两次发群里;到移动云项目,我把这套指标全部接进了 Prometheus + Grafana 大盘并配了告警规则,从'人肉定时看'进化到'指标越线自动找人'——这是我两个项目在性能监控上的演进,也是我理解的运维价值提升。"

实操模板(性能排查命令组,背熟):

bash
uptime                          # 1.负载
top                             # 2.CPU分布:us/sy/wa/id,P、M排序
vmstat 1 5                      # 3.r/b 队列、si/so 换页、wa
iostat -x 1 5                   # 4.%util、await、avgqu-sz
free -h                         # 5.available
iotop -o                        # 6.进程级IO(-o 只看有IO的)
pidstat -u -r -p PID 1 5        # 7.单进程CPU/内存趋势
sar -n DEV 1 5                  # 8.网络(sar -d 看磁盘历史)

1.5 CPU 负载高定位:top → top -Hp 线程级排查 【必背】 ​

标准回答:CPU 高的定位我有一套四步标准链路,能一直走到"哪一行代码"。第一步 top 按 P 键排序找到吃 CPU 最高的进程,记下 PID;第二步先分型——看是 us 高还是 wa 高还是 sy 高,us 高是应用真在计算,wa 高是磁盘慢拖累的,方向完全不同;第三步 top -Hp PID 进入进程内部,看是哪个线程 TID 在吃 CPU;第四步拿到 TID 之后分语言处理:Java 程序用 printf '%x\n' TID 把线程号转成十六进制,再 jstack PID | grep -A 20 nid=0x十六进制 直接定位到代码行甚至方法名;非 Java 的用 strace -p TID 看它在反复做什么系统调用,或者 perf top -p PID 看热点函数。

拿到结论之后的处理原则也要说:先判断是不是异常进程——挖矿木马、失控循环,是就直接处理进程;如果是业务瓶颈,就限流、扩容,或者把定位结果交给开发去优化代码。运维的价值是把问题精确递到"哪个线程哪一行"这一步。

追问链:

  • 追问1:CPU 400% 怎么回事? → 答:这不是报错,是正常现象。top 默认按单核 100% 计量,一个进程显示 400% 说明它的多个线程合计占了约 4 个核,多线程进程在 8 核机器上最高可以到 800%。看到 400% 不要慌,接着 top -Hp 看线程分布:如果几个线程均分说明是正常并发,如果单个线程吃满一核以上就是循环或者锁自旋,处理方向完全不同。
  • 追问2:怎么区分计算密集和 IO 等待导致的 CPU 高? → 答:看 top 的 us 和 wa 的比例。us 高是应用真在执行计算,优化方向是看代码逻辑和算法;wa 高是 CPU 闲着等磁盘,罪魁在 IO,方向是加索引、上缓存、换 SSD。这两者弄反了就是白忙——给 IO 瓶颈的机器加 CPU 是最常见的无效扩容,我给别人讲这个问题时就强调"先看 us/wa 再决定加什么资源"。
  • 追问3:load 很高但 CPU idle 也高,什么问题? → 答:这是个经典陷阱。load 统计的是"可运行 + 不可中断睡眠"的进程数,大量进程卡在 D 状态等 IO 时,runnable 加 D 队列一起把 load 推高,但 CPU 其实没在算所以 idle 很高。这时候要看 iostat 的 %util 和 await、看存储链路是不是故障、NFS 是不是断了。这个组合现象一出现,方向就要从 CPU 转向磁盘和网络存储。
  • 追问4(深挖):限制一个进程只用 50% CPU? → 答:工具上有 cpulimit 这种按百分比节流的,也有正统的 cgroup 方案——设置 cpu.cfs_quota_us 为周期的一半就等于限半核,K8s 里 limits 的 500m 本质就是这套 cgroup 配额。生产上我更常用 K8s 的 resources.limits 来做容器级限制,cgroup 的手工配额换算我理解到这层,更细的调度组可以会后补。

关联话术:"这条链路我在移动云集群上完整跑通过一次:Grafana 上一个节点 CPU 告警,我 top 定位到某个业务 Pod 里的 Java 进程 us 90% 多,top -Hp 找到具体线程,jstack 一看是一段死循环,把线程栈丢给开发十分钟就改完了。运维没有写那行代码,但问题是我精确递过去的——这就是我理解的运维深度。"

实操模板:

bash
top                    # P排序 → 记 PID
top -Hp <PID>          # 记 TID
printf '%x\n' <TID>    # 转16进制
jstack <PID> | grep -A 20 nid=0x<hex>   # Java定位代码行
# 非Java:strace -p <TID> / perf top -p <PID>

1.6 内存与 swap:为什么 Kubernetes 要禁用 【必背】 ​

标准回答:先说 Linux 的内存观:内核的原则是"空闲内存不浪费,拿去做页缓存",所以 free -h 里 free 很小是正常的,判断内存是否紧张要看 available 这一列。物理内存吃紧时,内核会把不活跃的内存页换出到磁盘上的 swap 分区,腾出内存但代价极大——磁盘比内存慢几个数量级,一旦开始频繁换页,整个系统性能会断崖式抖动。

K8s 节点必须禁用 swap 有三个原因:第一,kubelet 的调度和驱逐模型建立在"容器内存 limit 是确定值"的假设上,swap 让实际可用内存变得不确定,驱逐阈值会误判,OOM 行为不可预期;第二,容器内存靠 cgroup 隔离,swap 命中带来的不可控 IO 等误会拖慢同一节点上所有 Pod;第三,kubelet 在 1.28 及之前的版本默认检测到 swap 就拒绝启动。

所以部署集群的标准动作是三连:swapoff -a 立即关、注释 /etc/fstab 里的 swap 行防止重启复活、再加内核参数 vm.swappiness=0 双保险。

追问链:

  • 追问1:swappiness 的取值含义? → 答:取值 0 到 100,表示内核换出内存页的积极程度:0 是尽量不换,只有极度紧张才动 swap;60 是很多发行版的默认值;100 是激进换出。K8s 节点我设 0 并配合 swapoff 物理关闭;传统数据库服务器也一般调到 10 以下,减少换页抖动。这个参数管的是倾向不是禁止,所以才有"参数调 0 + 物理关 swap"的双保险组合。
  • 追问2:swap 到底该不该留? → 答:分场景。K8s 节点和性能敏感的数据库生产机,坚决关掉,避免不可控抖动;而内存余量足、允许"极端情况下慢一点但不死"的通用服务器,可以留一块小 swap 作为 OOM 前的缓冲垫——被换页拖慢总比被 OOM killer 直接杀掉好。我的回答框架是"先问这个机器上跑的东西能不能容忍延迟抖动",能容忍就留兜底,不能容忍就关。
  • 追问3:内存不足怎么应急? → 答:第一步 ps aux --sort=-%mem | head 找出内存大户,区分是页缓存还是进程 RSS 真涨了;第二步如果是进程内存泄漏,先重启止血,重启前把 jmap dump 或者 /proc 信息留一份现场给开发;第三步 dmesg | grep -i kill 查有没有 OOM killer 记录,有的话把被杀进程和时间点对上。应急的顺序是先保业务再查根因,重启前一定记得留现场。
  • 追问4(深挖):buffer 和 cache 区别? → 答:buffer 偏向写方向,是块设备的缓冲;cache 偏向读方向,是文件内容的页缓存。现代内核里两者已经统一在 page cache 体系里,free 输出也合并成 buff/cache 一列。回收机制我理解到 LRU 链表分级(活跃/不活跃链表)这一层,内核按冷热程度决定先回收谁——更深的实现细节我如实说属于会后补的范围。

关联话术:"移动云项目 7 台节点的标准化初始化脚本里,'关 swap + swappiness=0'是固定的第一条,配套写进了我的 9 项避坑清单和初始化基线文档;后来公司新项目加节点,直接跑脚本十分钟完成全套基线,这条经验已经被复制到后续项目了。"

实操模板:

bash
free -h && swapon -s            # 确认swap状态
swapoff -a                      # 立即关闭
sed -ri 's/^(.*\sswap\s.*)/#\1/' /etc/fstab   # 注释开机挂载
echo 'vm.swappiness=0' >> /etc/sysctl.conf && sysctl -p
dmesg | grep -i "out of memory" # 查OOM记录

1.7 systemd 服务管理与服务故障排查 【必背】 ​

标准回答:现代 Linux——CentOS 7 以上、Ubuntu 16.04 以上、统信和麒麟——统一用 systemd 管服务。日常命令一组:systemctl start/stop/restart/reload/status/is-active,其中 enable --now 是"设开机自启并立刻启动"一步到位,我最常用;改了 unit 文件之后必须 systemctl daemon-reload 让 systemd 重新加载,这是新手最常漏的一步。

服务起不来我有一套固定排查链:第一步 systemctl status xxx 看状态摘要和最近几行日志;第二步 journalctl -u xxx -e --no-pager -n 50 看详细报错,-e 直接跳到尾部;第三步 ss -lntup | grep 端口 检查端口是不是被别人占了;第四步把 unit 文件里 ExecStart 那行命令拿出来手动执行一遍,看真实报错是什么——这一步往往一锤定音;第五步再查用户权限、目录权限、配置语法。

自己写服务就是 /etc/systemd/system/ 下一个 xxx.service,[Unit]、[Service]、[Install] 三段式,我能直接默写。

追问链:

  • 追问1:reload 和 restart 区别? → 答:restart 是把主进程整个杀掉重新拉起,服务有中断窗口;reload 是给主进程发信号让它重读配置文件,进程不换、连接不断,nginx reload、sshd reload 都是典型。生产原则是"能 reload 的优先 reload",只有配置真的没法热加载或者行为异常时才 restart。滚动窗口、变更评审里我都会标注这次变更是 reload 级还是 restart 级,中断风险完全不同。
  • 追问2:服务文件怎么写一个开机自启的脚本? → 答:三段式我能默写:[Unit] 段写 Description 和 After=network.target,保证网络起来再起我;[Service] 段写 Type=simple、ExecStart 用绝对路径、Restart=always 让它挂了自动拉起、User 指定运行账号;[Install] 段写 WantedBy=multi-user.target。写完依次 systemctl daemon-reload、systemctl enable --now xxx,然后 journalctl -u xxx -f 盯日志确认。完整模板在本节实操里,这是我给客户环境做组件托管的标准动作。
  • 追问3:journalctl 常用姿势? → 答:按服务跟踪用 journalctl -u 服务 -f;看本次开机以来用 -b,上一次开机用 -b -1,查"重启前发生了什么"特别有用;按时间窗 --since "09:00" --until "10:00";按级别 -p err 只看错误级。日志实体在 /var/log/journal,要配 SystemMaxUse 限额或定期 vacuum,不然小磁盘会被日志吃满——这个我已经形成肌肉记忆写进初始化基线。
  • 追问4(深挖):unit 里 Type 有哪些? → 答:常用的四种:simple 是默认,ExecStart 起的进程就是主进程;forking 是进程会 fork 到后台,需要配 PIDFile 让 systemd 追踪主进程;oneshot 跑一次就退出,适合初始化任务;notify 是服务自己通过 sd_notify 通知 systemd"我就绪了",K8s 的组件不少用这种。我写运维脚本基本 simple + Restart=always 就覆盖了,知道边界在哪。

关联话术:"银行项目里 Vastbase 的高可用组件 HAS 和 DCS 就是 systemd 托管的,部署完必须 systemctl enable has dcs;Node Exporter、我写的自研启停脚本也都统一做成 service 单元,客户现场的人只需要会 systemctl 和 journalctl 两个命令就能完成日常管理——交付门槛低是我做实施时特别在意的点。"

实操模板(标准服务单元,可默写):

ini
# /etc/systemd/system/appsvc.service
[Unit]
Description=Company App Service
After=network.target

[Service]
Type=simple
User=app
ExecStart=/opt/app/bin/start.sh
Restart=always
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
bash
systemctl daemon-reload && systemctl enable --now appsvc
journalctl -u appsvc -f          # 排障入口

1.8 系统安全加固基线(SSH / 密码策略 / 防火墙 / SELinux)【必背】 ​

标准回答:安全加固我按四块来讲,这也是我做基线的检查单。第一块 SSH:PermitRootLogin no 禁 root 直连、PasswordAuthentication no 改密钥登录、MaxAuthTries 4 限制试错次数、AllowUsers 白名单限定可登录账号,改完 restart sshd 前先留一个会话防锁死。第二块密码策略:/etc/login.defs 里 PASS_MAX_DAYS 90 天过期、PASS_MIN_LEN 8 位起步,复杂度用 pam_pwquality 强制大小写加数字加符号,再用 chage 管存量账号。

第三块防火墙:CentOS 和麒麟用 firewalld,firewall-cmd --permanent --add-port=6443/tcp 之后必须 --reload 才落盘;Ubuntu 和统信用 ufw,ufw allow 22 之后 ufw enable,原则都是默认拒绝、按服务精确放行。

第四块 SELinux:严格环境 enforcing,排障期临时 setenforce 0,永久改 /etc/selinux/config,Ubuntu 对应的是 AppArmor。再叠加审计项:盯 /var/log/secure 的失败登录、last 和 lastb 看登录记录、关键日志外送集中。

这套基线我在银行客户环境是逐条被扫描验证过的。

追问链:

  • 追问1:firewalld 的 --permanent 忘了 --reload 会怎样? → 答:规则只写进了内存运行态没落盘,看起来生效了但机器一重启就丢。正确顺序是 firewall-cmd --permanent --add-port=X/tcp 先写持久配置,再 firewall-cmd --reload 把持久配置加载进运行态。反过来只加运行态也一样丢。这是防火墙变更里最高频的坑,我给客户开端口后有"过五分钟 firewall-cmd --list-ports 复核"的习惯。
  • 追问2:SELinux 报权限拒绝怎么快速确认? → 答:先 getenforce 确认当前模式,再 ls -Z 文件 看安全标签对不对,然后查 /var/log/audit/audit.log 或者 ausearch -m avc 找 denied 记录,日志里会写清楚哪个进程、访问哪个文件、被什么规则拒绝。应急时 setenforce 0 切 permissive 先恢复业务;根治要用 restorecon 修复标签,或者用 audit2allow 生成定制策略——修复标签能解决的优先修标签。
  • 追问3:生产 SELinux 你们是开还是关? → 答:分环境回答。银行客户环境按客户安全基线执行,客户要求开就 enforcing,遇到应用不兼容走变更流程申请切 permissive 并记录;互联网和内部环境,我在部署期关闭它,因为 K8s 组件、容器挂载在 SELinux 下配置复杂度高,上线后靠防火墙最小化放行、密钥登录、最小化安装来兜底。两种策略我都实际执行过,能讲清各自的取舍,不会一刀切说"必须开"或"必须关"。
  • 追问4(深挖):除了这些还有什么加固? → 答:还有一批内核和系统层面的:sysctl 里开 rp_filter 反欺骗、关不必要的服务(蓝牙、打印)、最小化安装、fail2ban 按 SSH 失败次数自动封 IP、定期 find / -perm -4000 比对 SUID 文件发现可疑提权点、yum/apt 只装安全更新。fail2ban 我了解原理是 tail 认证日志按失败计数写防火墙规则,生产上按客户要求配过基础规则;内核级更深的加固模块我理解到知道有哪些的层面。

关联话术:"银行项目的安全基线是金融行业最严的一档:密钥登录、端口白名单、密码 90 天强制过期、操作留痕,都是客户方的检查项,我是被这套体系'训练'出来的;到移动云项目我把'防火墙和安全组只放行 K8s 必需端口、分层防御'写进了建设方案的安全设计章节——从执行别人基线到输出自己的安全设计,这是我三年的成长轨迹。"

实操模板(加固基线速查):

bash
# SSH
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
# 密码策略 /etc/login.defs: PASS_MAX_DAYS 90
# firewalld
firewall-cmd --permanent --add-port=6443/tcp && firewall-cmd --reload
# ufw
ufw allow 22/tcp && ufw enable
# SELinux
getenforce; setenforce 0   # 临时;永久改 /etc/selinux/config
# 查异常SUID
find / -perm -4000 -type f 2>/dev/null

1.9 统信 UOS / 麒麟 V10 与 ARM 架构实践 【必背】 ​

标准回答:先说国产化 OS 的背景一句话:信创要求金融、政企从芯片到软件全栈国产替代,所以我在银行项目里整套环境都是国产的。主流两家的血缘要分清:麒麟 V10 服务器版基于 openEuler/CentOS 系,操作习惯和 yum 生态接近 CentOS;统信 UOS Server 20 基于 Debian 系,包管理是 apt。对我运维来说,两家系统 95% 的操作和主流发行版一致,差异集中在软件源要用厂商的内网源或镜像站、很多商业软件要拿厂商适配包,包名经常带 kylin、aarch64、kunpeng 这类标识。

ARM 架构我在银行项目整栈用了近两年,注意四点:第一,uname -m 确认是 aarch64,软件源必须选 aarch64 专用源;第二,Docker 当时没有仓库版,用的是 aarch64 静态二进制包 docker-18.09.8.tgz 解压安装;第三,所有容器镜像必须 arm64 版本,内网拉不了海外镜像就 docker load 离线导入,我为此写过批量加载脚本;第四,源码编译 Redis、Nginx 这类软件要确认 ARM 依赖库齐全。内核参数、防火墙、systemd 这些与 x86 完全一致,心智负担不大。

追问链:

  • 追问1:UOS 和麒麟怎么选型? → 答:选型是客户信创目录和华为配套体系决定的,我们公司是华为渠道商,我负责按配套表落地。银行项目实际是两家混用:业务虚机跑统信 UOS Server 20,数据库节点跑麒麟 V10 SP1——因为 Vastbase 的安装包就是 kylin_v10sp1-aarch64_kunpeng 专用包。所以我的经验是"同一套平台两家混用很常见,关键是把版本配套表对齐",这句在面试里说出来比站队任何一家都专业。
  • 追问2:ARM 上踩过最深的坑? → 答:镜像架构不匹配。在 aarch64 节点上 load 了 x86 的镜像,容器一起就报 exec format error,因为指令集根本不一样。那次之后我把"镜像架构清单提前验证"固化成流程:交付前用 docker manifest inspect 或 docker inspect --format '{{.Architecture}}' 把每个镜像过一遍,统一离线导入,后面再没出过这类问题。踩坑变流程,是我做实施最大的习惯。
  • 追问3:信创环境装软件的通用思路? → 答:按优先级三步走:第一优先厂商适配包,rpm 或 deb,从内网源拿,兼容性最好;第二选同架构的静态二进制包,解压即用,Docker 就是这么装的;最后才是源码编译,编译前要确认 ARM 的依赖库齐不齐。最忌讳的是把 x86 的包直接拷过来用——报错还查半天。这套优先级我在银行内网环境验证过两年。
  • 追问4(深挖):ARM 和 x86 的本质区别? → 答:本质是指令集架构不同:ARM 是 RISC 精简指令集,x86 是 CISC 复杂指令集,鲲鹏属于 ARMv8 的 aarch64。对运维的实际影响就一句话——"一切二进制和镜像都要同架构",应用层的 jar、Python 脚本这类解释型或字节码的可以跨架构,编译型的二进制和容器镜像必须匹配。微架构级别的流水线差异我理解到这层就到边界了,更深可以会后补。

关联话术:"这是我简历上最实的差异化:4 台 TaiShan 鲲鹏服务器加 15 台 UOS/麒麟虚拟机,上面还跑着 ARM 版 K8s 集群,从 iBMC 装机、RAID 规划到应用上线全链路是我做的。面试我会说:'国产化环境一次装通、装稳、装到能交付'这件事我真做过近两年,这在国内三年经验的运维里不算多见。"

实操模板:

bash
cat /etc/os-release && uname -m          # 确认发行版与架构
# ARM 批量离线导入镜像(项目一实际脚本核心)
for f in /opt/images/*.tar.gz; do
  case "$f" in *edge-manager*) continue;; esac   # 排除管理面镜像
  cat "$f" | docker load
done
docker inspect <镜像> --format '{{.Architecture}}'  # 验证 arm64

1.10 内核参数:ip_forward、bridge-nf-call-iptables、swappiness 【理解】 ​

标准回答:K8s 节点必调的三个内核参数,每个都有明确的原因。第一 net.ipv4.ip_forward=1 开启 IP 转发:节点上的容器流量要跨节点、跨网段,内核默认不替别人转发包,不开这个参数跨节点 Pod 就不通。第二 net.bridge.bridge-nf-call-iptables=1:让走网桥的包也经过 iptables 处理。

Flannel、Calico 的 Service 转发规则都写在 iptables 里,这个参数不开,Service 的 ClusterIP 就是摆设;它依赖 br_netfilter 内核模块,要先 modprobe 加载并写进 /etc/modules-load.d 保证重启生效。

第三 vm.swappiness=0 尽量不用 swap,原因见 1.6。统一配置在 /etc/sysctl.d/k8s.conf,然后 sysctl --system 生效,sysctl 参数名 或读 /proc/sys 下对应文件验证当前值。

这套参数在移动云 7 台节点的初始化脚本里是固定段落。

追问链:

  • 追问1:除了这三个还调过什么? → 答:还有一批常用项:fs.file-max 系统级总句柄数;net.core.somaxconn 监听队列长度,低版本内核默认 128,高并发应用要调大否则 accept 不过来;net.ipv4.ip_nonlocal_bind=1 允许绑定本机不存在的 IP,Keepalived 的 VIP 场景必需;数据库机器还会关透明大页 THP,避免内存分配抖动影响延迟。我按组件需求分模块写进不同的 sysctl.d 文件,避免一锅炖。
  • 追问2:改了 sysctl.conf 不生效怎么查? → 答:第一用 sysctl --system 看完整加载过程,它会按目录和文件名顺序依次加载,后加载的会覆盖前面的,很可能我的配置被优先级更高的文件盖掉了;第二 bridge 相关参数要先确认 br_netfilter 模块已加载且写进了 modules-load.d,模块没起来参数根本不存在;第三用 sysctl -a | grep 参数名 看运行时的真实值,和 /proc/sys 下文件对照确认。
  • 追问3:怎么验证参数当前值? → 答:最直接 sysctl net.ipv4.ip_forward 单查一个;或者 cat /proc/sys/net/ipv4/ip_forward——/proc/sys 就是内核参数的文件视图,读它就是读内核当前值,写它就是临时改参数。我做初始化脚本验收时就跑一组 sysctl 查询逐条核对并截图留档。

关联话术:"移动云项目 7 台节点的初始化脚本里,sysctl.d/k8s.conf 这一段(模板在 1.20)是'全节点标准化初始化'的组成部分——新节点十分钟跑完整套基线,参数不一致导致的'同一种报错在不同节点表现不同'这类问题从此绝迹,这是我做标准化最大的收益感受。"

实操模板:

bash
# /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
vm.swappiness = 0
bash
modprobe br_netfilter && echo br_netfilter > /etc/modules-load.d/br.conf
sysctl --system && sysctl net.ipv4.ip_forward

1.11 磁盘分区与挂载(fdisk / parted / fstab)【理解】 ​

标准回答:分区有两套工具,按容量分:小于 2TB 用 fdisk,走 MBR 分区表,最多 4 个主分区,交互式操作"n 新建 → p 主分区 → 大小 → w 写盘";大于 2TB 必须用 parted 走 GPT,支持 128 个分区。标准流程四步:lsblk 或 fdisk -l 认盘 → 分区 → mkfs.ext4 或 mkfs.xfs 格式化 → mount 临时挂载,确认没问题再写 /etc/fstab 开机自动挂载。

fstab 一行六列:设备、挂载点、文件系统类型、挂载选项 defaults、dump 备份标志 0、fsck 检查顺序 0。设备列建议写 UUID 而不是 sdb1 这种名字,因为设备名在插拔盘、重启之后可能漂移,UUID 是格式化时生成的全球唯一标识,稳定。

fstab 写错的经典事故是重启直接进紧急模式,所以我的铁律是写完 fstab 必须 mount -a 验证语法——能过 mount -a 重启才敢保平安。

追问链:

  • 追问1:为什么 fstab 建议 UUID? → 答:因为内核枚举设备的顺序不保证稳定,sda 和 sdb 可能在重启或插拔后互换,fstab 里写设备名就可能把数据盘挂错位置,轻则挂载失败进紧急模式,重则数据写错盘。UUID 是 mkfs 时生成的文件系统唯一标识,用 blkid 就能查到,跟设备物理顺序解绑。这是磁盘变更里成本最低、收益最明确的一条规范。
  • 追问2:怎么查磁盘类型和分区表? → 答:一套组合拳:lsblk -f 看块设备和文件系统;blkid 拿 UUID 和类型;fdisk -l 看分区表是 DOS 还是 GPT;df -T 看已挂载文件系统的实际类型。四个命令各管一段信息,我盘点一台新机器的存储就是按这个顺序过一遍。
  • 追问3:mount -a 的作用? → 答:按 fstab 的内容把当前还没挂载的条目全部挂一遍。它有两个价值:一是补挂载,改完 fstab 不用重启就生效;二是语法验证——fstab 有错 mount -a 会当场报错,而不是等到重启时进紧急模式才发现。所以"写 fstab 后必跑 mount -a"是我给自己定的硬规则。

关联话术:"银行项目 TaiShan 服务器上架时,我做的就是'系统盘 RAID1 + 数据盘 RAID5'的分区规划:数据库节点单独划 SSD 数据盘、日志盘和数据盘分开,这个规划直接影响后面 Vastbase 的 IO 表现。磁盘规划是实施交付的第一步,我在《智能合约引擎安装部署指南》里把分区表都固化成了模板。"

1.12 LVM 与 RAID 【理解】 ​

标准回答:LVM 是在物理磁盘之上加三层抽象的动态空间管理:物理卷 PV 对应实际分区,卷组 VG 把多个 PV 池化成一个大空间,逻辑卷 LV 再从 VG 里切出来当"虚拟分区"用。命令链:pvcreate /dev/sdb1 → vgcreate vg_data /dev/sdb1 → lvcreate -L 100G -n lv_data vg_data,格式化挂载后就能在线扩容:lvextend -L +10G /dev/vg_data/lv_data 再 resize2fs(ext4)或 xfs_growfs 挂载点(xfs),全程不停机。

它的价值是空间弹性调度和快照备份。RAID 是多块盘组合冗余:RAID0 条带化纯提速无冗余;RAID1 镜像浪费一半容量但读性能好;RAID5 至少 3 盘带分布式校验、可坏 1 盘;RAID10 先镜像后条带、至少 4 盘,性能和冗余兼顾——数据库推荐 RAID10,预算紧用 RAID5。硬 RAID 由阵列卡做、对 OS 透明性能好;软 RAID 用 mdadm、灵活但吃 CPU。

追问链:

  • 追问1:你们生产用的 RAID 几? → 答:银行项目的 TaiShan 2280 不带 RAID 卡,我们按华为配套实施文档做的软 RAID:两块系统盘做 RAID1 保证系统盘冗余,数据盘做 RAID5 平衡容量和可靠性。这是真实执行过的方案,mdadm 的创建过程和安装向导里勾选两种路径我都走过。生产上"数据库用 RAID10"是理想答案,但实际项目要受硬件配置和客户预算约束,我按配套文档落地并能讲清为什么这么配。
  • 追问2:RAID5 坏一块盘怎么处理? → 答:换上同规格新盘后,阵列会自动或手工触发重建(rebuild),把校验数据反算回新盘。关键风险是重建期间:整阵列处于无冗余状态,再坏第二块盘数据就全丢,而且重建是大 IO 过程性能明显下降。所以要点是监控阵列状态(告警要能在坏盘第一时间发现)、备件快速换、重建期间避免大业务 IO——这套"重建窗口风险"的意识比命令更重要。
  • 追问3:LVM 扩容 xfs 文件系统的命令细节? → 答:两步:lvextend -L +10G /dev/vg/lv 扩逻辑卷,再 xfs_growfs /挂载点 扩文件系统。注意 xfs_growfs 的参数是挂载点不是设备名,这和 ext4 的 resize2fs 正好相反,是高频手写题的陷阱;另外 xfs 只能扩不能缩,ext4 理论可缩但生产上极少做,缩容前必须备份。这组细节我在银行扩数据盘时实际操作过。

关联话术:"iBMC 远程装机时做 RAID 规划,是我简历第 16 条技能'TaiShan 国产服务器上架、iBMC 远程管理与 RAID 规划'的实操来源:4 台物理机的盘怎么分、哪些给虚拟化底座、哪些给数据库,都是我规划并执行的——这题我能从硬件层一直讲到文件系统层。"

1.13 文件系统 ext4/xfs 与磁盘空间治理 【理解】 ​

标准回答:ext4 是通用型文件系统,稳定、兼容性好、小文件场景表现均衡;xfs 是高性能日志文件系统,擅长大文件和高并发并行 IO,CentOS 7 之后就是默认。选型口径:数据库、虚拟化存储、大日志盘用 xfs;通用场景 ext4 也完全够。注意 xfs 只能扩不能缩。

磁盘空间不足的处理我有固定流程:df -h 先定位哪个挂载点满 → du -x --max-depth=1 -h / 逐层下钻找大目录(-x 限制不跨文件系统,避免误入挂载点)→ find / -xdev -type f -size +500M 找超大文件 → 清理日志缓存类(yum clean all、journalctl --vacuum)→ 长期方案走 LVM 扩容。

这里有个经典陷阱必须会:删了大文件 df 却没变——因为文件还被进程打开着,inode 没释放,lsof | grep deleted 找到持有句柄的进程,重启它或者清空文件描述符,空间才真正回来。

追问链:

  • 追问1:df 和 du 结果差很多说明什么? → 答:两种可能,都常见。第一种是"已删除但仍被占用":文件被 rm 了但进程还握着句柄,df 统计内核层面的块占用所以还大,du 按目录遍历文件已经看不到它了,用 lsof | grep deleted 确认,重启对应进程释放。第二种是挂载点覆盖:新磁盘挂在了非空目录上,把目录原来的内容"盖住"了。遇到 df/du 对不上,先往这两个方向查,基本一查一个准。
  • 追问2:inode 用尽什么表现? → 答:非常有迷惑性——df -h 显示空间还很多,但创建新文件报 No space left on device。这时要 df -i 看 inode 使用率,如果 IUse% 到 100% 就是 inode 耗尽,根因通常是海量小文件,比如缓存目录、session 文件、邮件队列堆积。处理就是定位小文件目录批量清掉,长期方案是这类文件加自动清理任务。判断"空间问题"时 df -h 和 df -i 要一起看,这是我的习惯。
  • 追问3:日志正在被写,能直接 rm 吗? → 答:不能。rm 只是删目录项,进程持有的文件描述符还在,空间不释放、进程还继续往已删除的 inode 里写,等于白删还丢了查看入口。正确做法二选一:清空内容 > file(truncate 截断到 0,空间立刻释放且进程不受影响);或者用 logrotate 的 copytruncate 模式轮转。我给客户交付的规范里明确写了"活跃日志禁止直接 rm"。

关联话术:"这套空间治理我在两个项目都真用过:银行现场清理过 BCS 组件涨爆磁盘的日志,用的是'先 du 定位、再 lsof 查句柄、低峰期重启释放'的完整链路;移动云项目我把'日志目录统一配 logrotate + 磁盘使用率 85% 告警'写进了运维规范——磁盘满导致 Pod 驱逐是 K8s 节点最常见的自伤,提前治理比事后救火划算得多。"

1.14 日志体系:rsyslog / journalctl / logrotate 【理解】 ​

标准回答:Linux 日志体系我分三层讲,各有分工。第一层 rsyslog:内核和传统系统服务的日志按"设施.级别"规则汇总到 /var/log 下——messages 通用、secure 认证安全、cron 定时任务,配置在 /etc/rsyslog.conf,还支持远程外送做集中收集,银行客户要求日志审计时我们配过外送。

第二层 journald:systemd 托管的服务日志统一进 journal,用 journalctl -u 服务名 按 service 查、-f 跟踪、-b 本次开机、--since/--until 按时间、-p err 按级别,是我排障的第一入口。

第三层 logrotate:解决"日志无限增长吃爆磁盘",配置在 /etc/logrotate.d/ 下按服务写——daily 每天轮转、rotate 14 保留 14 份、compress 旧档压缩、copytruncate 适合不能重开句柄的程序、missingok 和 notifempty 做防御,由 crond 每天执行。三层分工一句话:rsyslog 管汇总外送、journald 管服务排障、logrotate 管不涨爆磁盘。

追问链:

  • 追问1:nginx 日志怎么切割? → 答:标准做法是 logrotate:在 /etc/logrotate.d/nginx 里配 daily 轮转,postrotate 钩子执行 nginx -s reload 让 master 重开新日志文件,配合 compress 压缩旧档、rotate 保留份数。也可以用脚本 mv 改名后 reload,本质一样。我实际用的就是 logrotate + reload 全自动方案,一个配置文件解决,不需要额外脚本。
  • 追问2:copytruncate 的作用? → 答:它的机制是先把当前日志拷贝一份做归档,然后原地清空原文件,进程的文件描述符不变、继续往原文件写。适用对象是"不支持重开日志文件"的程序——有些应用不会因为日志文件被改名就切换句柄。代价是拷贝和清空之间那个瞬间可能有少量日志丢失。所以选择逻辑是:支持 reopen 的用 postrotate + reload,不支持的用 copytruncate。
  • 追问3:journalctl 日志占满磁盘怎么办? → 答:两层处理。应急:journalctl --vacuum-size=200M 立即把日志回收压到 200M;根治:在 /etc/systemd/journald.conf 配 SystemMaxUse=500M 之类的上限,之后 journald 自动滚动清理。这个我在初始化基线里就配好,避免运行几个月后监控节点先被日志撑爆——监控节点自己挂了监控就瞎了,这是很实际的教训。

关联话术:"银行客户有日志集中审计的要求,我们做过 rsyslog 外送的配置;移动云项目每个自部署组件——Prometheus、Kuboard——都配了 logrotate,写进了运维规范文档。日志这块我的习惯是'交付任何组件,logrotate 配置同步交付',不给未来留地雷。"

实操模板(logrotate 样例,可默写):

/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    dateext
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && nginx -s reload
    endscript
}

1.15 ulimit 与文件句柄 【理解】 ​

标准回答:ulimit 是进程级的资源限制,运维最常调的是 -n 打开文件数。系统默认 1024,高并发服务——nginx、数据库、K8s 组件——轻松超过这个数,报错就是 "Too many open files"。

调整要分三个层面,这是这个知识点的核心结构:① 会话级 ulimit -n 65535 只影响当前 shell;② 用户级在 /etc/security/limits.conf 写 * soft nofile 65535 和 * hard nofile 65535,重新登录生效;③ systemd 启动的服务不读 limits.conf,必须在 unit 文件里写 LimitNOFILE=65535 再 daemon-reload——这是最高频的坑,很多人改了 limits.conf 发现服务还是报错,原因就在这。

系统级总量另有一个内核参数 fs.file-max。排查命令:ulimit -n 看当前、cat /proc/PID/limits 看具体进程的实际限制、ls /proc/PID/fd | wc -l 数句柄占用。

追问链:

  • 追问1:limits.conf 改了服务还是报 Too many open files? → 答:因为 limits.conf 是通过 PAM 在"用户登录"时生效的,而 systemd 拉起的服务走的是 unit 文件自己的资源限制体系,不经过登录流程。所以给 systemd 服务调句柄要在 unit 的 [Service] 段写 LimitNOFILE=65535,然后 daemon-reload 加 restart。我给 Node Exporter、K8s 组件写 unit 时都会预置这一行,属于"从坑里带出来的习惯"。
  • 追问2:怎么确认一个进程的句柄限制? → 答:最可靠的是 cat /proc/PID/limits,直接列出该进程的 soft 和 hard 各项限制,比猜配置文件靠谱——配置生效链路那么长,不如直接看进程的事实状态。再看占用量的趋势用 ls /proc/PID/fd | wc -l,如果这个数字持续上涨不回落,基本就是句柄泄漏,要提前处理。
  • 追问3:句柄满一般是什么泄漏? → 答:常见三类:代码没关连接或文件,异常路径漏了 close;连接池配置不当,池只借不还;TIME_WAIT 或半开连接堆积。应急是重启进程止血(重启前留个 fd 列表快照),根治要开发修资源释放逻辑,运维侧能做的是把 keepalive、超时参数配好减少无效句柄的存活时间,并给句柄数配监控告警在 80% 时预警。

关联话术:"移动云集群的初始化脚本里,limits.conf 和 fs.file-max 的调整是固定段落;我自己写的每个 systemd unit 都会确认 LimitNOFILE——这是从银行项目吃过亏之后带过来的肌肉记忆:'服务报 Too many open files 第一反应查 unit 文件'这句话我在团队内部分享过。"

1.16 Ubuntu 与 CentOS 包管理及发行版差异 【理解】 ​

标准回答:两大发行版家族的差异我按四块说。第一包管理:CentOS 和麒麟用 yum/dnf 加 rpm,Ubuntu 和统信 UOS(Debian 系)用 apt 加 dpkg,命令一一对应,yum install 对 apt install、rpm -qa 对 dpkg -l。第二服务命名:sshd 和 ssh、firewalld 和 ufw,写脚本时要做兼容判断。

第三网络配置:CentOS 在 /etc/sysconfig/network-scripts/ifcfg-xxx,Ubuntu 18.04 之后是 /etc/netplan/*.yaml 然后 netplan apply 生效。第四安全模块:SELinux 对 AppArmor。

发行版地图也顺一遍:红帽系有 RHEL、CentOS Stream、Rocky、Alma;Debian 系有 Debian、Ubuntu;国产系里麒麟是 openEuler 系、统信是 Debian 系、还有 openEuler 本身。我不是纸上谈兵——银行项目以麒麟和统信为主、移动云项目以 Ubuntu 22.04 为主,两个体系各用了一年多,踩过真实的差异坑。

追问链:

  • 追问1:apt 和 apt-get 区别? → 答:apt 是后来推出的统一入口,输出更友好、有进度条、默认行为面向交互使用者;apt-get 行为稳定、输出格式固定,适合写进脚本不被版本变化影响。我的习惯就是官方建议的用法:人机交互用 apt,脚本和 CI 里用 apt-get,这样脚本的长期可维护性更好。
  • 追问2:CentOS 8 停服后怎么办? → 答:官方路线是 CentOS Stream,但它是滚动上游版,不适合生产。企业的现实选择是迁 RHEL、Rocky Linux、AlmaLinux 这类二进制兼容替代品,或者顺势转国产系——这也是这两年信创迁移项目多的现实推手之一,我在银行项目看到的国产化节奏就与这个大背景相关。答这题的关键是体现"知道生态在往哪走"。
  • 追问3:deb 和 rpm 包怎么查内容/依赖? → 答:Debian 系 dpkg -L 包名 看这个包装了哪些文件、dpkg -l 列全部;rpm 系 rpm -ql 包名 同样用途,还有个排障利器 rpm -qf /文件路径 反查某个文件属于哪个包——比如发现一个可疑二进制,一条命令就知道它是不是系统自带的。这组"查归属"的命令我做安全排查时很常用。

关联话术:"移动云 7 台 Ubuntu 节点上我踩过很具体的坑:Grafana 的 GPG 公钥 404 装不上、promtool 在 Ubuntu 源里没有独立包——这些都是真用过 apt 生态才会知道的细节,我全记进了 9 项避坑清单。所以两个包管理体系我是'双母语',不是只会一个。"

1.17 Linux 启动流程 【了解】 ​

标准回答:以 x86 加 systemd 为例,启动是一条链:加电后 BIOS 或 UEFI 做硬件自检 → 按引导顺序找到硬盘的 MBR 或 EFI 分区,加载 GRUB 引导器 → GRUB 加载内核镜像和 initramfs → 内核解压初始化,挂载根文件系统、加载驱动 → 启动第一个用户态进程 systemd(PID 1)→ systemd 按 default.target 的依赖关系逐个拉起服务 → 最后到登录界面。

这个流程的排障价值在于"卡在哪一段就知道查什么":卡在 GRUB 之前是引导盘和 BIOS 设置问题;卡在挂载根分区,最常见就是 fstab 写错,系统会掉进紧急模式;卡在服务阶段,用 emergency 或 rescue target 进去 journalctl -xb 看是哪个服务失败。

我在现场处理过的就是 fstab 类紧急模式,进单用户把错误行注释掉再重启就恢复。

追问链:

  • 追问1:系统重启了怎么查原因? → 答:三步。第一步 last reboot 或 who -b 确定精确的重启时间点;第二步按时间点查日志——journalctl -b -1 -e 看上一次开机的尾部日志,对照 /var/log/messages 里重启前的最后记录;第三步和监控对照,看重启前有没有负载、内存的异常爬升,重点找两个关键词:OOM killer 记录(dmesg | grep -i kill)和 kernel panic。银行现场处理虚机重启,多数结论落在宿主机迁移或内存 OOM,方法都是这套。

关联话术:"银行驻场时我处理过虚机异常重启的排查工单,就是按'时间点 → 上一次启动日志 → 监控对照'三步走的模板,一般半小时内能给客户一个结论——这种小排查积累多了,面试里聊'你怎么排障'就特别有底气。"

1.18 中挖矿木马的处置 【了解】 ​

标准回答:挖矿木马的特征先记住:CPU 持续 90% 以上、有可疑进程名(xmrig、minerd 这类)、有对外矿池地址的连接。处置我按五步走:第一步断网隔离并改 root 密码,防止它继续外传和横向;第二步 top 或 ps -ef | grep -E 'xmrig|minerd' 找到进程 kill -9;第三步清持久化——这是关键,挖矿最喜欢藏 crontab,crontab -l 和 /etc/cron.d、/var/spool/cron 都要翻,只杀进程不清定时任务它几分钟就复活,还要查 systemctl 有没有恶意服务、~/.ssh/authorized_keys 有没有被人塞后门公钥;第四步删恶意文件(/tmp、/var/tmp 高发区)并用 rpm -Vf 校验系统命令有没有被替换(防止 ls、ps 本身是假的);第五步加固收口:补漏洞——入口多半是弱口令或未授权的 Redis——密钥登录、防火墙白名单。预防体系就是基线加固加 CPU 持高告警加出网流量监控。

追问链:

  • 追问1:进程 kill 掉又起来了? → 答:说明有"拉活机制"在守护它:要么 crontab 里有定时任务周期性重新下载执行,要么有一个伪装成正常名字的 systemd 服务或父进程在监控拉起。正确姿势是先停定时任务和服务,再杀进程,最后删文件,顺序反了它就边杀边起。顽固的情况直接断网操作,斩断它的下载源之后再清理——这个顺序意识比命令本身重要。

关联话术:"银行内网隔离好、暴露面小,两年里没真中过招,这点我如实说;但挖矿处置的完整链路是我巡检体系的一部分——CPU 持高告警规则、authorized_keys 定期巡检、出网连接审计——面试我按'处置流程 + 预防体系'两个维度作答,不会吹自己处理过大事故。"

1.19 tcpdump 与网络连通性排查 【了解】 ​

标准回答:网络连通性我按分层模型从下往上查,每层都有对应工具:ping 验证三层可达 → telnet IP 端口 或 nc -zv IP 端口 验证四层端口通 → traceroute 看路径在哪一跳断 → curl -v 验证七层协议和证书 → dig 或 nslookup 查 DNS 解析。

同时看本机要素:ss -lntup 确认服务在监听、ip r 看路由表、iptables -L 看防火墙。以上都定位不了就抓包:tcpdump -i eth0 -nn host 目标IP and port 端口 -w out.pcap,-nn 是禁止对地址和端口做反向解析,抓完的 pcap 用 Wireshark 分析。

这套分层法的经典应用题是"QQ 能登录但网页打不开"——QQ 走 IP 直连正常、网页依赖 DNS,答案就是 DNS 故障,分层思想一秒钟定位。

追问链:

  • 追问1:curl 能通但浏览器不行? → 答:说明服务器和网络是通的,问题在客户端这一侧。按序排查:浏览器的代理设置(很多内网环境要配代理,配置错了就全挂)、浏览器缓存和插件(无痕模式一试便知)、本机 hosts 有没有过期劫持记录。方法是"用一个已知干净的客户端对照",把变量隔离掉——这就是分层排障思想在应用层的延伸。

关联话术:"这条链路我在两个项目都是日常:银行项目排查跨节点 Pod 通信和数据库 VIP 连通性,移动云排查 CLB 健康检查失败为什么摘节点,都是这套分层方法的具体应用。tcpdump 我用得最多的是'按 host + port 过滤抓小包',避免抓全量流量把机器和 pcap 文件都搞爆。"

1.20 系统初始化基线脚本(实操模板大满贯)【理解】 ​

标准回答:这是我把两个项目的交付经验沉淀成的"新节点十分钟标准化"脚本,固定清单包括:主机名与 hosts 解析、时区与时间同步 chrony、关 swap(swapoff 加注释 fstab)、防火墙与 SELinux 按基线处理、内核参数 sysctl.d(ip_forward、bridge-nf-call-iptables、swappiness、file-max)、句柄限制 limits.conf、必要基础包安装。

脚本工程上有三个硬要求:一是幂等可重跑——跑多少遍结果一致,不会重复追加配置;二是日志留痕——exec 重定向让全部输出落文件,出问题可回溯;三是收尾自检——打印 df、free、uptime 摘要留档。这个脚本是我在移动云项目里固化出来的交付资产,公司后续外包项目的新集群初始化直接复用它。

实操模板(系统初始化基线脚本核心段,可背):

bash
#!/bin/bash
# init_node.sh —— 新节点标准化初始化(Ubuntu/CentOS 通用思路)
set -euo pipefail
LOG=/var/log/init_node.log; exec >>$LOG 2>&1

hostnamectl set-hostname "$1"                     # $1 主机名
timedatectl set-timezone Asia/Shanghai
systemctl enable --now chronyd || systemctl enable --now ntp   # 时间同步

swapoff -a && sed -ri 's/^(.*\sswap\s.*)/#\1/' /etc/fstab      # 关swap
systemctl disable --now ufw 2>/dev/null || systemctl disable --now firewalld  # 防火墙按基线
setenforce 0 2>/dev/null || true
sed -ri 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config 2>/dev/null || true

modprobe br_netfilter && echo br_netfilter >/etc/modules-load.d/br.conf
cat >/etc/sysctl.d/99-base.conf <<'EOF'
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness = 0
fs.file-max = 2097152
EOF
sysctl --system >/dev/null

cat >>/etc/security/limits.conf <<'EOF'
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
EOF

echo "==== init done: $(date) ===="
df -h | head -3 && free -h | head -2 && uptime   # 输出摘要留档

面试手写时写到 limits 段即可拿满分;脚本要点是幂等可重跑、日志留痕(exec 重定向)。

1.21 系统信息、/proc 与 dmesk 【理解】 ​

标准回答:排查系统级问题我的信息源有三个层次。第一层是标准命令:uname -a 内核版本和架构(我在 ARM 环境第一件事就是 uname -m 确认 aarch64)、cat /etc/os-release 发行版、uptime 运行时长、hostnamectl 一条看全主机名/内核/虚拟化类型。

第二层是 /proc 伪文件系统——内核状态的文件视图,我常用的几个:/proc/cpuinfo CPU 详情、/proc/meminfo 内存明细(free 的数据源就是它)、/proc/loadavg 负载、/proc/PID/ 下看具体进程的 status(内存占用)、limits(资源限制)、fd(打开的文件)、cmdline(启动命令)。

第三层是 dmesg——内核环形缓冲区,硬件报错、OOM 记录、文件系统错误都在这里,dmesg -T 带时间戳、dmesg | grep -i error 过滤。典型应用:怀疑 OOM 就 dmesg | grep -i kill 找 OOM killer 记录;怀疑磁盘就 grep I/O error。

这套"命令 → /proc → dmesg"的三层信息源是我排障的下限工具箱。

追问链:

  • 追问1:/proc/PID/status 和 ps 看到的内存有什么区别? → 答:数据同源(ps 也读 /proc),但 status 里的信息更完整:VmRSS 是实际驻留内存、VmSize 是虚拟地址空间(含映射文件和预留,远大于实际占用)、VmSwap 是被换出的部分。排查内存泄漏时我直接看 VmRSS 的趋势,比 ps 的 %MEM 更精确;分析"进程声明了几 G 但实际没用那么多"的问题就看 VmSize 和 RSS 的差值。能把这几个字段说清,内存问题的定位就细了。
  • 追问2:dmesk 和 journalctl 什么关系? → 答:dmesg 读的是内核环形缓冲区,内容是内核自己打的日志——硬件、驱动、OOM、文件系统错误,缓冲区有大小上限会滚动覆盖;journalctl 是 systemd 的统一日志,包含内核日志(也收了内核消息)加服务日志加开机过程。简单说 dmesg 专看内核层、看"机器的问题",journalctl 按服务和时间看"系统和服务的问题"。我排查硬件和 OOM 用 dmesg,排查服务用 journalctl -u,两者互补。
  • 追问3:系统变慢但 CPU 内存都正常,还看什么? → 答:按"三大隐藏瓶颈"排查:一是 IO——iostat 的 %util 和 await、iotop 找进程(见 1.4);二是网络——连接数堆积、重传、DNS 解析慢(ss -s 看连接汇总、time nslookup 测解析);三是内核层——dmesg 里有没有报错刷屏(比如磁盘重试、NIC 报错),vmstat 的 cs 列看上下文切换是否异常高。变慢不等于资源耗尽,"等待"类问题(等磁盘、等网络、等锁)才是最常见的慢源。

关联话术:"银行项目 TaiShan 服务器 iBMC 的硬件告警和 OS 层 dmesg 我做过对照排查——硬盘和内存条告警在 dmesg 里都有对应记录,OS 层证据和带外管理互相印证;/proc/PID/limits 则是我查'服务句柄限制为什么没生效'的证据来源(见 1.15),这套三层信息源是我在客户现场练出来的。"

1.22 文件打包压缩与传输(tar / gzip / zip / rsync)【了解】 ​

标准回答:打包和压缩是两件事:tar 是打包器(把目录聚合成单文件),gzip/bzip2/xz 是压缩器。常用组合:tar -czf web.tar.gz web/ 打包并 gzip 压缩(c 创建、z gzip、f 指定文件名)、tar -xzf web.tar.gz 解压、tar -tzf 不解压先看内容清单——部署前先 -t 确认包内结构再解,避免解出一堆散文件污染当前目录,这是我的固定习惯。

zip/unzip 处理 Windows 来往的压缩包(unzip -d 目标目录 file.zip)。传输三件套:scp 简单直接(scp -r 目录 目标:路径),rsync 增量同步更高效——只传差异块、支持断点续传和 --delete 保持两端一致,还支持 --exclude 排除;大文件和反复同步的场景我一定用 rsync,第一次全量之后每次都是秒级增量。传证书、同步日志、备份落异地,我用的都是这套组合。

追问链:

  • 追问1:tar 解压前为什么要先 -t? → 答:因为有些打包的人在某个目录里打包却带着相对路径,解压出来的文件结构可能散落或套着意料之外的层级;tar -tzf 先看清单就知道解开后是什么样、该在哪个目录解。我处理过客户传来的包"解开一屏散文件"的事故,之后就立了规矩:先 -t 再 -x,解压目标目录先 mkdir 好再 -C 指定进去——两条小纪律消灭一整类事故。
  • 追问2:scp 和 rsync 怎么选? → 答:一次性小文件 scp 够用;重复同步、大文件、目录树用 rsync——它的增量算法只传差异部分,第二遍起速度差一个量级,还带 --exclude、--delete(镜像同步)、-P(进度和断点)这些 scp 没有的能力。我的实践:集群配置分发、备份同步到监控节点都是 rsync;顺手补一句安全习惯——传输走内网或限速 --bwlimit 避免打满业务带宽(对应 1.4 里备份占带宽的案例)。

关联话术:"银行内网环境的镜像 tar 包分发、证书 scp 传到 master02/03、部署指南归档,全是我用这套工具做下来的日常。ARM 镜像批量加载(第 5 章)的前置动作就是 tar.gz 的整理和分发——打包传输是所有离线交付的地基动作。"

1.23 常见系统报错速查(No space / Read-only / Too many open files)【了解】 ​

标准回答:三类高频报错我按"报错含义 → 第一反应"来讲。第一类 No space left on device:第一反应不是删文件而是先 df -h 看哪个挂载点满——注意有两种"满":空间满(df -h)和 inode 满(df -i,小文件海量导致,现象是空间有余量但建文件报错),处理分别走 1.13 的空间治理流程;还有一种隐蔽情况:删除了大文件空间不释放(lsof | grep deleted),是句柄未释放。

第二类 Read-only file system:文件系统被内核挂成只读,最常见原因是磁盘 IO 错误后内核自我保护(ext4 检测到一致性风险自动 remount ro)——dmesg 里一定有对应报错;应急可以先 mount -o remount,rw /data 试恢复,根治要 fsck 检查磁盘或换盘,这类问题多数指向硬件劣化。

第三类 Too many open files:句柄超限,按 1.15 的三层结构排查(ulimit、limits.conf、systemd 的 LimitNOFILE),确认是限制太小还是泄漏。这三种报错覆盖了我现场处理的大部分"系统级救火"。

追问链:

  • 追问1:根分区满了连日志都写不了,怎么应急? → 答:先找能立刻释放的空间:journalctl --vacuum-size=100M 回收日志、删除 /tmp 下的临时文件、yum clean all 或 apt clean 清包缓存、清容器日志(docker 场景 /var/lib/docker/containers 下的 json.log 常是元凶)——这几个动作通常能立刻腾出几个 G 让系统先活过来;然后再从容 du 下钻做正式清理和扩容规划。应急的原则是"先恢复写入能力,再做根因治理",避免系统在满盘状态触发更大面积的连锁故障。

关联话术:"这三类报错我在两个项目都处理过真实案例:银行现场清过磁盘满(BCS 组件日志),移动云把'磁盘 85% 告警 + 日志轮转'做进规范就是为了把 No space 消灭在发生之前——报错速查表的价值是第一反应不慌,规范的价值是不需要用到速查表。"

1.24 本章案例 ​

  • 【真实项目】(银行·移动云)全节点标准化初始化落地:背景是移动云新集群 7 台节点必须保证环境完全一致——过往经验里"逐台手配漏一项、排查半天"的教训太多。任务是我来编制初始化方案并把"关 swap、内核参数、时间同步、句柄、hosts"这些项固化成幂等脚本加检查清单。行动分三步:先在监控节点试跑验证,再全节点统一执行,最后逐节点跑验收命令组截图留档;其中时间同步指内网 NTP 用 chrony,专门解决了避坑清单第 9 条"节点时间不同步导致 TLS 认证失败"的问题。结果:节点初始化从过去半天缩到分钟级,脚本成为公司外包项目标准底座的一部分,"时间同步检查"也作为固定项写进了运维规范——这是我从"救火队员"转向"标准化建设者"的标志性产出。
  • 【真实项目】(银行)ARM 环境镜像架构治理:背景是内网 ARM 节点加载镜像后容器报 exec format error。任务是把十几个 BCS 数据面镜像正确铺到 9 个节点。行动:先确认根因是 x86 与 arm64 指令集不匹配,然后建立"镜像架构清单"逐个用 docker inspect 验证 Architecture 字段,再用批量脚本(排除管理面专用镜像)统一离线导入。结果:数据面节点一次铺通,BCS 纳管如期完成,此后再未发生架构类故障——这个"清单先行"的方法论后来被我用到所有离线交付场景。
  • 【课程项目】CPU 飙高排查实验:实验环境模拟 Java 进程死循环把 CPU 打到 400%,按"top 按 P 排序 → top -Hp 找线程 → printf 转十六进制 → jstack 定位代码行"的完整链路走通,把 1.5 的四步法从"背过"变成"做过"。这个实验的价值在于把每个命令的输出都亲眼看过一遍,面试描述时能说出每一步屏幕上是什么,可信度完全不同。
  • 【经典故障】删了日志 df 空间不释放:清理大日志后 df -h 显示空间没变。定位:lsof | grep deleted 发现日志进程仍持有已删除文件的句柄,inode 未释放。解决:业务低峰期重启该服务释放句柄(或对 /proc/PID/fd/N 做清空)。预防:所有日志统一走 logrotate 的 copytruncate 轮转,规范里明确"活跃日志禁止直接 rm"——这是 1.13/1.14 两节的实战注脚,我在银行现场真实处理过一次,全过程约二十分钟。

章末自测清单(快问快答) ​

  1. 8 核机器 load 多少算满载? → 接近 8;告警一般 0.7 倍核数 warning、1 倍 critical。
  2. 755 和 644 分别给谁用? → 755 目录/可执行脚本(属主 rwx 其余 r-x);644 普通文件(属主 rw 其余 r)。
  3. umask 022 时新建文件/目录权限? → 文件 644、目录 755。
  4. kill -15 和 -9 的区别? → 15 礼貌终止可收尾,9 强制不可捕获可能丢数据;先 15 后 9。
  5. 硬链接和软链接最大区别? → 硬链接同 inode 不可跨分区不可给目录;软链接存路径可跨分区可给目录。
  6. CPU 高定位线程的命令? → top 找 PID → top -Hp PID 看线程 TID →(Java)jstack + nid 十六进制定位代码。
  7. 为什么 K8s 要禁 swap? → kubelet 调度/驱逐按内存确定性设计,swap 造成性能抖动与 OOM 行为不可预期,且 kubelet 检测到 swap 拒绝启动。
  8. iostat 里最关键的两个指标? → %util(磁盘利用率)和 await(IO 平均等待毫秒)。
  9. free 看哪一列判断内存够不够? → available(可用),buff/cache 是可回收缓存不算占用。
  10. bridge-nf-call-iptables 不开会怎样? → 网桥流量不过 iptables,K8s Service(iptables 模式)转发不通。
  11. firewalld 加端口两条命令? → firewall-cmd --permanent --add-port=6443/tcp && firewall-cmd --reload。
  12. systemd 服务起不来第一查什么? → systemctl status + journalctl -u 服务 -e 看报错,再看端口占用和手动执行 ExecStart。
  13. systemd 服务的句柄限制写哪里? → unit 文件 LimitNOFILE=65535(limits.conf 对 systemd 服务不生效)。
  14. 麒麟 V10 和统信 UOS 分别基于什么系? → 麒麟 openEuler/CentOS 系(yum),UOS Server Debian 系(apt)。
  15. ARM 节点装 x86 镜像什么报错? → exec format error;镜像必须 arm64,用 uname -m 确认架构。