最近看到有人在讨论哔哩哔哩2019秋招技术岗的笔试题,其中系统工程师这道题很有意思,题目大意是问候选人:如何在生产环境从零搭建一个系统并做好后续维护。乍一看这就是个开放性的聊天题,但能看出面试官想考察的点其实非常深,它既考动手能力,又考规划意识,还考你有没有真正经历过线上环境的毒打。
这篇文章我就以自己多年做系统工程师和运维的经验,把这道题完整拆开聊一遍。如果你正在准备系统工程师、运维开发、SRE这类岗位的面试,或者你刚接手一套没人维护的老系统,想从头梳理一遍搭建和维护的完整思路,这篇内容可以直接当参考手册用。我会按照从需求分析、清单梳理、实际部署到长期维护的节奏来展开,尽量把命令和参数也贴出来,方便你对着练。
1. 从笔试题看系统工程师的核心能力模型
1.1 面试官到底想从“从零搭建”里看到什么
“从零搭建一个系统”这句话,如果没上过生产环境,很容易理解成“装个操作系统,配个网络,装几个软件”。但真正在互联网公司里,系统工程师面对的不是一台机器,而是整个业务稳定性的底座。面试官问这道题,其实想看的是你能不能把一个空壳的裸机,变成一台能承载业务流量、能扛故障、能长期维护的服务器。
我当年面试别人时,最怕听到的答案就是把启动过程背一遍:u盘装系统、分区、配IP、装数据库、启动服务,然后就没有然后了。这种答案只能说明你“用过”Linux,但证明不了你“理解”生产环境。生产环境的核心就四个词:稳定、可控、可观测、可恢复。从零搭建的每一步,其实都应该围绕这四个词来做决策。
所以这道题表面上是“搭建系统”,实际上考察的是一个人在不确定性中做工程决策的能力。机器什么配置、系统选哪个版本、磁盘怎么规划、服务怎么部署、监控怎么接入、出了问题怎么回滚,这些都不是填空题,而是需要根据业务场景动态权衡的选择题。
1.2 系统工程师和运维的边界为什么越来越模糊
这个岗位在B站这种互联网公司里,其实已经不怎么区分“系统工程师”和“运维工程师”了。早期传统的运维可能只管网络、服务器、机房,系统工程师偏向OS和底层调优,但现在大家都叫SRE或者系统工程师,日常工作早就混在一起了。
原因很简单:业务迭代越来越快,如果系统工程师只管装系统,运维只管看着监控,中间就会出现巨大的真空地带。比如你装好了OS,但没配内核参数,业务一上线就出现句柄耗尽;或者你搭好了环境但不考虑日志清理,半年后磁盘被打满。这些问题都不是单一角色的锅,而是“从搭建到维护”这个链条没有打通。
我自己的体会是,这道题就是想筛选出具备“全生命周期”视角的人。你会装机器只是第一关,后面的容量规划、安全加固、自动化、监控告警、备份恢复,才是真正拉开差距的地方。所以下面我讲整个流程的时候,也会刻意把维护思维前置,而不是等系统跑起来了再亡羊补牢。
2. 系统搭建前的需求分析与方案设计
2.1 先问清楚三个问题再动手
很多人一上来就翻ISO镜像、找装机U盘,但我建议你先压住冲动,先问自己三个问题:这台机器要跑什么业务?预期流量和数据量有多大?你手里有多少资源(人力、预算、时间)?
这三个问题直接决定整个技术选型。如果是跑一个日活几万的小型Web应用,单台服务器加云数据库就够了;如果是核心支付链路,哪怕再小的系统也得双机热备+跨机房容灾;如果只是内部测试环境,那连RAID都可以省,定期快照反而更灵活。我见过太多事故,根源根本不在某个命令敲错了,而是最初设计时压根没搞清楚业务形态,把测试环境的方案直接搬到了生产。
举个例子,我曾经帮一个团队排查数据库频繁OOM的问题,排查到最后发现这台机器的swap文件压根没创建,数据库一跑起来就吃满内存。你说是运维不会配置吗?不是,是当时上机器时没有人告诉搭建的人这是个数据库节点,他就按默认模板装了个系统。所以第一件事永远是沟通需求,不要上来就动手,这个习惯能省掉后面90%的麻烦。
2.2 选择操作系统和硬件时的“保守策略”
操作系统选型是个容易被低估的决策点。很多新人喜欢追新版本,觉得内核新、功能多就爽。但生产环境恰恰相反,绝大多数公司会用当前主流稳定版本,比如CentOS 7/8(或者它的替代品Rocky Linux、AlmaLinux)、Ubuntu 18.04/20.04 LTS。为什么?因为新版本往往意味着新的坑,而生产环境最怕“不确定”。
拿我自己的例子来说,曾经在一台新内核的机器上部署一套老业务的服务,结果跑起来之后网络连接经常超时,查了半天发现是新内核默认启用了某个拥塞控制算法,和老业务的网络栈行为不匹配。最后只能换回旧内核版本的镜像重新部署。所以我的建议是:选你团队最熟悉的版本,而不是最新版本,尤其不要在生产环境里当小白鼠。
硬件选型方面,CPU要关注主频和核数,内存要先算业务需要再留20%-30%余量,磁盘则要重点考虑IOPS和容量。一个比较实用的经验法则是:如果业务是数据库或消息队列这类IO密集型,优先选SSD或NVMe盘,内存尽量配大;如果是Web服务或计算任务,CPU核数和网络带宽的重要性会更高。这些信息最好在项目启动前就确认清楚,否则后面扩容等于重搭。
2.3 单机、集群还是上云,取决于业务容忍度
搭一个系统前还得想清楚部署形态。单机部署最简单,但很多人不知道单机部署的“隐藏成本”——只要你依赖这台机器,它宕机你就得背锅。所以哪怕业务量再小,我也建议至少做到“数据可恢复”,也就是备份机制必须到位。
集群部署就复杂得多,涉及负载均衡、数据一致性、脑裂处理等一系列问题。我的建议是,在业务没发展到一定规模之前,不要盲目追求高大上的集群方案。两个节点的Keepalived+Nginx做高可用,或者直接用云平台提供的负载均衡和托管数据库,往往比你自己搭一个看起来很厉害的K8s集群要稳得多。
这里还要提一句“上云”的选择。现在很多公司已经默认上云了,但云主机同样需要系统工程师做OS层面的初始化、安全加固和监控接入,只不过不用再关心硬件和机房而已。不管在哪种形态下,系统工程师的职责都没有变——把系统的可用性和稳定性做到最好。
3. 从裸机到可用系统的完整实操流程
3.1 装机与磁盘规划的标准动作
假设现在你拿到一台裸机,第一步不是直接开始分区,而是先确认固件模式、磁盘数量和阵列卡状态。如果这台机器有多块磁盘,一般建议先做硬件RAID,比如两块盘做RAID 1用于系统盘,多块盘做RAID 5或RAID 10用于数据盘。
RAID级别选择要按业务需求来,不是越高越好。RAID 0性能好但没有冗余,一块盘挂了数据全没;RAID 1冗余好但空间利用率只有50%;RAID 5兼顾性能和空间,但写性能一般,而且大容量磁盘重建时间长;RAID 10是性能和冗余的平衡点,代价是成本高。我个人的习惯是:系统盘RAID 1,数据盘至少RAID 10,如果公司预算紧张且业务允许丢失少量数据,才考虑RAID 5。
分区方案上,我推荐把关键目录单独分区,比如/date或/data单独划分给业务数据用。这样做的好处是:日志打满数据盘时不会拖垮系统盘,备份数据也不会占用根分区空间。swap分区要不要单独分?如果内存充足且业务对性能敏感,swap可以设小一点(比如4GB),避免因为swap频繁换页导致性能抖动。但也要留一点,别彻底禁掉,有些应用的依赖库在某些异常情况会用到swap兜底。
3.2 系统初始化的关键配置项清单
装完系统后,进入系统初始化和配置阶段。初始化脚本里我一般会做这几类事情:设置主机名和DNS、配置NTP时间同步、关闭SELinux、调整文件句柄数、优化内核参数、配置yum或apt源、创建运维用户并配置sudo、加入跳板机或堡垒机的管理范围。
这里重点说几个容易被忽略的细节:
文件句柄数和进程数限制。这个不调,高并发下必然会出问题,报错通常是too many open files。调整方式是用ulimit或修改/etc/security/limits.conf,比如设置* soft nofile 655350和* hard nofile 655350,同时把fs.file-max也在/etc/sysctl.conf里调大。
内核参数里最值得关注的是网络相关的几个。例如net.core.somaxconn默认是128,并发连接稍微高一点就会触发队列溢出;net.ipv4.tcp_tw_reuse和net.ipv4.tcp_fin_timeout需要根据业务场景配置,手动打开一般能优化TIME_WAIT过多的问题,但不能无脑套用;vm.swappiness建议调整为10以下,避免系统过度使用swap导致性能下降。
SSH安全配置也要在这一步做好。建议修改默认端口、禁止root直接登录、启用密钥登录。虽然公司内网有堡垒机会统一管控,但多一层安全措施总归不是坏事。我维护的服务器,凡是没改过默认22端口又面向公网的,不出一个月肯定被扫。
3.3 常用基础组件与运行环境的部署思路
系统和网络基础配置完成后,接下来就是安装业务运行所需的基础组件。这部分的顺序和方式也很重要,我最推荐的方式是用配置管理工具(比如Ansible、SaltStack)来统一做,而不是一台一台手工敲命令。手工操作最大的问题不是慢,而是每个人装的版本、路径、配置不一致,后期维护成本巨大。
以最常见的LAMP/LEMP环境为例,安装PHP或Java运行环境时,要注意版本一致性。用包管理器装的版本通常偏旧,如果需要指定版本,推荐使用官方二进制包或容器镜像。比如Java应用,直接下载JDK 8/11/17对应版本解压后设置JAVA_HOME环境变量就行,比用系统包管理工具更可控。
数据库部署是基础组件里的硬骨头。MySQL初始化有几点必须注意:第一步要设置root密码和删除匿名用户;第二步要调整存储引擎和字符集,避免建表时默认乱七八糟的编码;第三步要配置binlog和自动清理策略;第四步要设置慢查询日志,方便后面排查SQL性能问题。如果用的是云数据库,那也要确认自动备份开关是否打开,并且建议设置至少7天的备份保留周期。
3.4 程序部署与上线发布的标准化流程
环境准备好之后,程序部署和上线就是最需要规范化的环节。我见过不少团队的程序发布方式是:开发把代码打包丢给运维,运维手动传到服务器,然后重启服务。这在只有一两台机器的时候勉强能跑,一旦机器多了,肯定出乱子。
一个标准的程序发布流程至少应该包括这几个要素:产物统一管理、版本回滚机制、灰度发布策略、发布前后的健康检查。比如用GitLab CI或Jenkins构建出产物,存到Nexus或对象存储里,部署时从统一地址拉取,而不是到处传输jar包或tar包。发布时先摘掉一台机器的流量,更新完毕后再放量,确认无异常后再继续下一批。这样即使新版本有问题,影响面也能控制在最小范围。
同时服务一定要配置成守护进程,用systemd、supervisor或者Docker的restart策略管理都可以。我不太建议直接用nohup ... &的方式启动服务,因为进程一旦意外退出没人拉起。采用systemd管理的话,还要配置日志输出路径和遇到OOM时是否自动重启的策略,这些细节决定了系统能不能“自己恢复”。
3.5 上线前的清单式自检清单
上线前一定要做一次全面自检,我每次都会把自检项列成清单,一项项核对。这个习惯帮我挡掉过很多低级事故。自检清单大致包括:
- 时间同步是否正常:执行
chronyc sources -v或ntpq -p,确认服务器时间偏差在100ms以内 - 磁盘空间是否足够:
df -h查看所有分区,特别关注根分区和数据分区剩余容量 - 关键端口是否监听:
ss -lntp确认服务端口和应用预期一致 - 防火墙是否放行:检查firewalld或iptables规则,避免服务通了但端口被拦
- 权限和属主是否正确:确认运行服务的是专用账号,不是root
- 日志目录是否存在且有写权限:这个不起眼,但没注意就会导致服务启动失败或静默丢失日志
- 监控是否已接入:至少确认CPU、内存、磁盘、网络这四项基础指标能采集到
- 备份任务是否已创建:确认crontab中备份任务已配置并手动执行过一次
这个过程看起来繁琐,但真的能逼着你把很多“我觉得没问题”的地方查清楚。上线不是终点,恰恰是维护阶段的起点。
4. 生产环境日常维护与故障排查的关键经验
4.1 配置变更必须走“先备份、后变更、可回滚”
系统上线之后,日常维护的挑战才真正开始。一个最常见的导致故障的原因不是初始搭建做得不好,而是后续变更操作失误。比如说,某天你为了优化Nginx性能,改了nginx.conf里某个参数,结果忘记测试就直接reload,全站直接502。
所以我对变更管理有个铁律:任何配置变更之前,先备份原文件;变更过程中尽量用“增加”而不是“替换”的方式;变更后要立刻验证服务状态。出了问题,第一步不是调参数,而是恢复原状。这个“先回滚再排查”的顺序非常重要,因为如果你在错误配置的基础上继续调参,只会越调越乱。
另外,强烈建议把配置管理和自动化工具用起来。Ansible的playbook天然具备幂等性,同一个任务执行两遍结果是一致的,这比手工改文件安全得多。用自动化工具管理配置,还顺带解决了“某台机器配置漂移”的问题。我在团队里推行过一个简单规矩:谁改配置谁负责把改动纳入版本库,没纳入版本库的修改一律视为不允许,执行后一旦与版本库内容不一致,以版本库为准自动回滚。
4.2 常见故障的排查思路与命令速查
平时值班最常碰到的问题,基本集中在CPU、内存、磁盘、网络这四类。我把排查思路整理成一张速查表,新人拿去就能用。
| 故障现象 | 可能原因 | 排查命令 | 处理建议 |
|---|---|---|---|
| CPU使用率过高 | 业务负载突增、死循环、GC频繁 | top、pidstat -p PID 1、jstack(Java应用) | 找到占CPU高进程,确认是否预期行为,必要时限流或扩容 |
| 内存不足/OOM | 内存泄漏、堆内存设置过大、无swap | free -h、dmesg | grep -i oom、ps aux --sort=-%mem | 定位耗内存进程,调整JVM参数或升级配置 |
| 磁盘空间满 | 日志未清理、临时文件堆积 | df -h、du -sh /* 2>/dev/null | 清理日志和临时文件,增加日志轮转策略 |
| IO等待高/响应慢 | 磁盘性能瓶颈、慢SQL、大量随机读写 | iostat -x 1、iotop、数据库慢查询日志 | 优化SQL或升级SSD,必要时做读写分离 |
| 网络丢包/延迟高 | 带宽打满、网卡问题、连接数超限 | sar -n DEV 1、ping -f、ss -s | 检查带宽和连接数限制,调整内核参数或扩容 |
| 端口无法访问 | 服务未启动、防火墙拦截 | ss -lntp、systemctl status service、firewall-cmd --list-all | 确认服务状态和防火墙规则,检查监听地址 |
这张表虽然简单,但覆盖了生产环境90%以上的第一波故障。我自己的经验是,排查故障时不要一上来就怀疑内核或网络设备,先看资源监控、再看进程状态、最后才动到底层,这个顺序能少走很多弯路。很多时候所谓“疑难杂症”,最后发现就是日志文件把磁盘占满,服务写不了日志就直接假死了。
4.3 监控告警体系怎么搭才算够用
监控体系是维护阶段的另一块基石。刚上线时机器少,可能用crontab写个脚本,发现CPU高了就发个邮件,勉强够用。但系统一多,这种原始方式肯定撑不住,一定要上统一的监控平台。
我常用的组合是Prometheus + node_exporter + Grafana,再加Alertmanager做告警。node_exporter负责采集主机层面的指标,Prometheus负责存储和查询,Grafana做可视化,Alertmanager把告警推到钉钉、企业微信或者邮件。这套方案组件成熟、社区活跃,一个系统工程师足够维护得动。
监控指标方面,基础四项是必须的:CPU使用率、内存使用率、磁盘使用率和inode使用率、网络流量。另外按业务重要程度,再配上进程存活监控、端口存活监控、关键日志关键词监控。告警规则也要讲究,不要“一告警就打电话”,而要设置合理的阈值和持续时间。比如CPU使用率持续5分钟超过90%才告警,避免瞬时抖动骚扰到人。
4.4 备份与容灾:平时用不到不代表可以不做
备份是维护工作中最容易被忽视但又最关键的一环。很多人觉得云平台有快照,数据库有主从,就认为不需要额外备份了。但快照是整机级别的,只能防坏盘和误删;主从同步是逻辑复制,万一出现误执行drop database的情况,从库也会一起被删。所以,真正的数据安全必须靠独立于运行环境之外的备份。
备份方案设计要考虑三点:备份内容、备份频率、备份存储位置。业务数据至少要每天全量备份一次,重要数据库建议每天全量+binlog增量,备份文件要存到独立的存储或另一台机器上。同时要定期做恢复演练,否则备份可用性完全没保障。我个人坚持“没有经过恢复演练的备份,等同于没有备份”这个原则。曾经有位同事特别有自信,说他们的备份任务每天都跑,结果真到恢复的时候发现备份脚本从某一天开始一直静默失败,备份文件全是0字节,那场面真的很难收场。
容灾意味着要考虑“这台机器彻底没了怎么办”。在云上,可以考虑不同可用区部署双节点;在自建机房,至少要保证配置文件和代码有版本管理,数据有异地备份。容灾的等级取决于业务的RTO(恢复时间目标)和RPO(恢复点目标),先把这两个数字定义清楚,再设计容灾方案,就不会过度设计,也不会裸奔。
4.5 把重复性工作自动化,让自己从“救火队员”变成“系统优化者”
最后想说一个系统工程师成长路径上的关键转折:当你开始觉得日常维护都是重复劳动的时候,就是应该大力投入自动化的时机了。
日常巡检、日志清理、备份执行、服务重启、版本发布,这些事情都可以用脚本和工具自动化。刚开始可能只是几个简单的Shell脚本,慢慢可以引入Ansible、Python、CI/CD流水线。自动化并不一定要一步到位搭什么高大上的平台,从一个操作梳理成脚本,从脚本进化为工具,从工具再进化为平台,这条路是系统工程师最有价值的成长方向。
我自己最大的一次效率提升,就是花了两个周末把服务器初始化做成了一套脚本,从拿到机器到完成全套初始化只需要十几分钟,而且不会漏掉任何一项配置。以前手动装一台机器至少要半天,还经常忘配NTP或者忘了关SELinux,现在这些低级错误基本绝迹了。自动化不只是省时间,更重要的是消除人肉操作带来的不确定性。
5. 给准备面试和刚入行的同学几点实在建议
这道笔试题如果出现在面试里,我会建议你用“分层回答”的方式组织答案。第一层讲目标(保证业务稳定运行),第二层讲流程(需求分析、设计、部署、维护的闭环),第三层讲细节(具体命令、参数、工具),第四层讲反思(失败案例、改进措施)。大多数人只能讲到第二层,能讲到第三层的人已经少一半,能讲到第四层的人才是真正有生产经验的人。
如果刚入行,最好的学习方式不是死记硬背命令,而是自己用虚拟机完整地搭一套系统,从安装Linux开始,到部署一个Nginx+MySQL+PHP或Java应用,然后试着把它拆了再重建,直到整个过程不需要查文档。做完这一步,再尝试加监控、写自动化脚本,你会发现对系统工程师这个岗位的理解会完全不同。
设备条件有限的话,现在一台普通电脑开几个虚拟机完全没问题,或者用云平台的免费额度也不失为一个好选择。环境和工具不重要,重要的是亲手走完“搭建—出问题—排查—解决—复盘”的完整循环。这个循环走得越多,你在真正生产环境里就会越从容。
最后再分享一个小技巧:搭建任何系统时,都顺手把“为什么这样做”记录下来。比如你选择RAID 10而不是RAID 5,理由是数据安全性要求高;你关闭SELinux,理由是业务组件与它有兼容性问题。这些记录将来无论是做交接、做复盘还是面对面试官的追问,都是你最宝贵的财富。系统工程师这个岗位,归根结底拼的不是手速,而是判断力,而判断力恰恰来自一次次“知道为什么这么做”的积累。