☰
Ubuntu开发实用功能:终端、监控、系统维护与故障恢复工作流
2026/10/18 11:21:17 网站建设 项目流程

很多人一提到 Linux Ubuntu 开发实用功能,第一反应是把常用命令背下来。我刚开始也是这么干的,抄了满满一页笔记,结果真正用的时候还是挨个查。后来换了思路:不追求命令数量,而是把终端、监控、环境管理、故障恢复这些东西串成一套工作流,效率反而提升了一大截。这篇总结适合长期在 Ubuntu 上写代码、部署、维护服务器的开发者,尤其是不想折腾桌面但想提升命令行效率的人。内容不会贴一百个命令,但会把我这两年实际用下来、踩过坑之后依然留在习惯里的功能挨个讲透。

1. 终端体验改造:从默认 bash 到 zsh + tmux 的日常组合

1.1 为什么值得把默认终端环境换掉

Ubuntu 默认的 bash 其实并不差,日常敲命令完全够用。但一旦你同时维护多个服务、经常通过 SSH 远程干活,bash 的几个短板就会越来越明显:没有可靠的会话恢复功能,网络一断,跑了一半的任务就跟着终端一起没了;历史记录是一整块,没法让不同项目窗口分开使用;命令补全也比较基础,敲一段路径或者记不清子命令的时候很累。

我最初觉得 zsh 和 tmux 这两样是“锦上添花”,直到有一次在服务器上跑一个耗时两小时的日志归并任务,SSH 连接因为网络抖动断了,任务直接消失。那之后我彻底转向了 tmux,后来又把默认 shell 切到 zsh。现在回头看,这两样东西解决的根本不是“好不好看”的问题,而是“怎么让终端像开发工具一样可复用、可恢复”的问题。

zsh 负责的是交互体验:补全、别名、历史共享、更自由的提示符。tmux 负责的是会话管理:它像给终端开了一扇门,你随时可以离开房间,回头还能原样推门进来。两者各干各的活,不冲突。对 Ubuntu 来说,安装很省事,发行版的软件源里直接有。

1.2 我实际保留下来的配置和使用方式

安装就两条命令:

sudo apt update && sudo apt install -y zsh tmux

然后切换默认 shell,执行chsh -s /usr/bin/zsh,重新登录后生效。如果换过 shell 后提示找不到配置,先执行command -v zsh确认路径,再把 chsh 参数改对。

我刚用 tmux 时最不习惯的是默认前缀键Ctrl-b,和我常用的行首跳转冲突,改成了Ctrl-a。在~/.tmux.conf里写:

set -g prefix C-a unbind C-b bind C-a send-prefix

基础会话命令不用记太多:tmux new -s work建一个叫 work 的会话,tmux detach离开,回来时用tmux attach -t work;如果不记得名字,tmux ls看列表。在会话里分屏我常用两个键:Prefix加%是左右分屏,加"是上下分屏。一个窗口放日志,一个窗口写命令,比来回切省事得多。

zsh 方面我没有用任何复杂的主题框架,只加了几个习惯:共享历史记录,让两个终端窗口敲过的命令互通;补全增强,长目录名只敲开头就能补;然后是一批高频别名。比如:

alias gs='git status' alias gf='git fetch --prune' alias la='ls -A' alias dc='docker compose'

一个小经验:如果之前在 bash 里积累了.bashrc里的别名,切到 zsh 后不会自动读取,要把内容迁到.zshrc里。很多朋友切完 shell 发现命令找不到,多半就是这一步漏了。

2. 系统监控与资源排查:从“感觉卡了”到定位具体进程

2.1 监控工具怎么选:top、htop、glances 的实际差别

很多人卡住的第一句话是“服务器最近有点慢”,但“慢”是个模糊感受。从系统角度,慢可以来自 CPU、内存、磁盘 IO、网络,甚至某个进程在锁上死等。这时候手里要有能快速缩小范围的工具。

top 是系统自带,没有额外安装成本,但输出排版比较原始,按 CPU 排序还得敲快捷键,很不直观。htop 是 top 的增强版,显示彩色、支持树状进程视图、可以直接用方向键选择进程并发送信号,适合日常上手。glances 又是一个层级,它会同时展示 CPU、内存、磁盘、网络和进程列表,还有 Web 输出模式,适合盯一段时间的整体趋势。

一个简单的选型建议:

场景推荐方式
快速看一眼负载uptime、top
交互式观察进程树htop
想看磁盘和网络整体状态glances
追一段时间的趋势glances --export csv或记录到日志
内核级排查vmstat 1、iostat -x 1

不要把监控工具当成神物,重点是你要会看“高负载到底体现在哪一项”。我习惯先用uptime看 load average,再用htop看进程排序,最后用iostat确认是不是磁盘瓶颈。

2.2 高负载排查的完整链路

一次比较典型的排查,是我接手的一台常驻开发机,CPU 不高但整个系统交互很卡。负载三个数值都在 8 以上,任务管理器里却没有明显的高 CPU 进程。这就是典型的“不是 CPU 忙,而是进程在排队或者等 IO”。

第一步看uptime,理解 load average 的含义:它代表一段时间内正在运行和等待运行的进程平均数。如果你的机器有 4 个核心,负载长期超过 4,就表示有任务在排队。生活化一点,CPU 是厨师,load average 是排队等菜的客人数量,厨师再快,排队人数太多也白搭。

第二步用htop切到进程视图,按 CPU 排序。如果 CPU 占用不高,但负载很高,别急着杀进程,先查磁盘。用iostat -x 1看%util,这个值接近 100% 时,说明磁盘已经忙得抬不起头,进程都在等 IO 完成。再进一步,用iotop看是哪个进程在读写磁盘。如果%util不高但系统很卡,再考虑是不是内存交换频繁。

第三步看内存。free -h里不要只盯 free 那一列,要看available,它包含了可回收的缓存和剩余内存,是更真实的可用值。内存不够时系统会走 swap,一旦 swap 持续读写,速度会断崖式下降。找到吃内存进程我常用:

ps aux --sort=-%mem | head -20

第四步是网络。端口和连接数异常时,ss -tunap能同时看到监听端口和连接对端地址。比如有时候服务没起来,但端口被别的内容占着,很快就能定位。

最后,别忘了journalctl -u 服务名 --since "1 hour ago" -f看服务级日志。很多所谓“莫名其妙卡顿”,其实是某个服务在高频报错,日志里全是异常记录。监控工具能告诉你现象,日志才能告诉你原因。

3. 开发环境管理:版本切换、SSH 配置和容器使用的日常提速

3.1 多版本命令切换:系统自带的 alternatives 机制

开发环境里最常见的一个烦恼,是一台机器上装了好几个版本的运行环境,系统默认调用的是旧版本,手动改 PATH 又容易污染其他工具。如果用的是 Ubuntu 这类 Debian 系发行版,可以先试系统自带的update-alternatives,不用额外装东西。

它的原理很简单:系统里有很多命令名,比如java、python、node这类,真正执行的时候会通过一个符号链接指向当前默认版本。update-alternatives就是统一管理这些链接和优先级的小工具。

举个我实际用过的例子。某台机器上装了多个 JDK,分别放在/usr/lib/jvm/下的不同目录里。先注册:

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-17/bin/java 100 sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-11/bin/java 80

这里的数字就是要优先级,数值大的默认优先。之后想临时换版本:

sudo update-alternatives --config java

系统会列出所有已注册版本,输入序号就能切换。好处是系统工具如果依赖某个固定路径,切换后也不容易夹生。Python 这边我反而更推荐“系统自带的 python3 + 虚拟环境”的组合,虚拟环境天然隔离,不用折腾全局版本,而且换机器后重建成本非常低。

3.2 SSH Key 管理与登录配置

开发机上最常用的远程连接方式就是 SSH。每次ssh 用户名@地址再手动输密码,确实能工作,但一旦要维护几台机器、偶尔还要传文件,就非常拖节奏。我建议先把密钥对和配置文件一次性弄好。

生成密钥时,现在的安全实践是优先用 ed25519:

ssh-keygen -t ed25519 -C "you@example.com"

生成后把公钥送到目标机器:

ssh-copy-id 用户名@目标地址

这一步会把你本地的公钥追加到目标机的~/.ssh/authorized_keys里,之后登录就不用输密码了。注意私钥文件权限必须是 600,太开放会被 SSH 直接拒绝。

机器多了之后,建议把这些内容写进~/.ssh/config,给每台机器起一个容易记忆的 Host 别名。比如:

Host codeserver HostName 198.51.100.23 User ubuntu Port 2222 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30

这样以后只需要ssh codeserver就能登录,不需要带完整参数。ServerAliveInterval 是防止长连接被中途掐掉的心跳参数,远程跑任务时很实用。多个密钥需要同时使用时,可以交给 SSH agent 管理:启动后ssh-add添加私钥,必要时配上ForwardAgent在跳板机上继续用本机密钥访问内网机器,省去一堆拷贝公钥的操作。

3.3 容器环境里常用的几个操作习惯

现在开发环境几乎离不开容器。Ubuntu 上安装容器引擎后,第一件事是用普通用户执行命令,不用每次sudo。把用户加入对应用户组,重新登录后生效:

sudo usermod -aG docker $USER

不过要提一句,加入这个用户组相当于给了用户很高的系统权限,只适合在自己信任的机器上开启,别为了省事在公司生产环境的机器上随手加。

日常维护我常用三个维度:状态、日志、清理。

docker ps --filter status=running docker logs --tail 200 --follow 容器名 docker system prune -af --volumes

第一条只看正在运行的容器,第二条看最近的日志,最后一条清理所有停止的容器、没用到的网络和匿名卷。prune后面的-a会把没有被容器引用的镜像也删掉,所以跑之前最好确认镜像可以被重新拉取,否则又要重新下载。

另外,我习惯把常驻开发服务注册成用户级 systemd 服务,用systemctl --user管理,这样能在开机后自动拉起,崩溃时也能通过状态码和日志直接找原因。相比单纯依赖docker restart=always,它有更清晰的启停职责,也方便在容器之后补挂一些命令。

4. 磁盘和文件操作:定位大文件、软链接与日志瘦身

4.1 在大目录里快速找出最占空间的内容

磁盘用满这种事,几乎每个人都遇到过。真正让人头疼的不是“满了”这个事实,而是不知道哪些文件占了空间。很多人第一反应是du -sh *,在小目录里还行,一旦目录层级深、文件多,跑起来会非常慢,因为要扫描全部文件。

我常用的两个组合,一个是看某个目录下第一层各子目录的大小:

du -h --max-depth=2 /home/某用户 2>/dev/null | sort -hr | head -20

--max-depth=2控制扫描层级,2>/dev/null屏蔽没有权限访问的目录,最后按大小倒序只取前 20 条。另一个是跨过小文件,直接找大文件:

find /home -xdev -type f -size +500M -exec ls -lh {} \;

-xdev的含义是不跨文件系统。为什么要加?因为/home下很可能挂着其他数据盘或临时目录,不加这个参数会把无关的挂载点也扫一遍,浪费时间,还可能碰到权限问题产生大量报错。

使用du时有一个小技巧:先定位到最可疑的一级目录,比如/var或/home,看那一层的子目录大小,再逐层往深走。不要在一开始就用du -ah全盘扫描,看起来威风,实际上 CPU 和磁盘 IO 会被拖死,机器上有别的任务时很容易互相干扰。

4.2 软链接的几个常见坑

软件项目里经常用软链接把代码目录、数据目录映射到其他位置,比如/data盘空间大,就把家目录下的项目目录链过去。ln -s /data/project ~/project一条命令就搞定,但用起来有几个细节要注意。

第一个坑是路径写法。创建软链接时如果使用相对路径,链接目标会根据当前工作目录解析,一旦从其他目录访问这个链接就可能失效。所以我的习惯是:创建软链接时源路径一律写绝对路径。命令行里的realpath 路径能帮你把相对路径转成绝对路径。

第二个坑是时代入目录的问题。很多构建工具会调用pwd获取当前目录,但通过软链接进入的目录,pwd默认返回链接所在路径,不是真实路径。比如项目里生成临时文件的路径写死了,构建脚本可能因此找不到文件。遇到这种问题,先分清pwd -L和pwd -P,前者显示逻辑路径,后者显示物理真实路径。脚本里用到绝对路径时,尽量取真实路径。

第三个坑是权限。软链接本身有自己的文件权限,但访问时要看的是源文件所在目录的权限。你把链接放在没有写权限的目录里,就算源目录权限一切都对,也可能写入失败。排查这类问题不要只盯着链接文件,先把ls -ld两个目录都看一眼。

4.3 给系统盘瘦身的三条命令

开发机的系统盘总是会在某天突然告警。除了大文件,最常见的元凶是日志、软件包缓存、系统包管理器自动带上的旧依赖。我的日常清理三板斧:

sudo apt clean sudo apt autoremove sudo journalctl --vacuum-size=200M

apt clean清掉下载的软件包缓存;apt autoremove移除不再被依赖的包;journalctl --vacuum-size=200M把系统日志总大小限制在 200M 以内,超过的部分自动清除。对长期开发机来说,这个限制不会影响排错,因为最常见的日志都在近一两天之内,太早的日志价值其实没那么大。

如果用了自动更新机制,旧内核也会越积越多。查看已安装内核时,注意保留当前正在运行的内核版本,不要为了省空间把正在用的内核删掉。方法很简单:先用uname -r查看当前内核,再去包管理器的已安装列表里手动删除旧内核。不确定的时候宁可不删,空间问题可以通过上面几条清理命令缓解,内核删错了反而更麻烦。

5. 日常故障恢复:启动、依赖与权限的几个高发问题

5.1 系统启动卡住时的抢救思路

开发机一旦启动卡住,很多人会本能地强制关机,这是最危险的动作。文件系统可能正处在写入状态,反复断电容易造成损坏。正确做法是先用系统自带的恢复入口。

Ubuntu 启动时在 GRUB 界面选择“高级选项”,通常能进入恢复模式。在恢复模式的菜单里,先看是否有文件系统检查选项,如果检测到异常就让它跑一遍修复。修复时尽量卸载分区再执行fsck,直接在挂载状态下强跑不太安全。

如果连 GRUB 菜单都见不到,思路就变成“用启动介质进入临时系统”。把系统镜像写到 U 盘后启动,进入临时环境。先确认硬盘分区名,再把根分区挂载到某个目录,必要时把/dev、/proc、/sys等虚拟文件系统绑定挂载进去,用 chroot 进入原本的系统环境,再重新生成引导配置。重点提醒一句:chroot 之前一定要备份重要数据,这类操作在不同版本上差异很大,不是死记命令就能复制的。我把这个流程当应急手册来用,而不是日常操作。

另一个很常见但容易被忽略的卡住场景,其实是系统盘完全占满。很多服务在启动时尝试写日志,磁盘没有任何剩余空间,启动流程就一直卡在“等待写入完成”。遇到启动进度条长时间不动,先想办法进恢复模式,或者用启动介质挂载系统,检查一下根分区可用空间,往往比检查硬件更实际。

5.2 依赖关系崩溃后的恢复顺序

开发机上安装软件包时,最常见的报错是“有损坏的软件包”或“依赖关系不满足”。我见过不少次,因为一个包装到一半网络中断,把整个包管理器的状态卡死了。遇到这种情况,先别急着卸载软件,也别搜索“把这几个库手动删掉”的操作。

我的习惯是按顺序来:

sudo apt update sudo apt --fix-broken install sudo dpkg --configure -a

第一行刷新软件包索引,第二行让包管理器尝试修复依赖关系,第三行把所有没有完成配置的包重新配置一遍。顺序不能反。如果某一步之后仍然报错,再用dpkg -l看有没有残留的rc状态包,这类包已经删除但配置文件还在,一般不会影响系统运行,但留着容易让后续操作弹警告。

最怕的是看到“需要卸载某依赖”就冲动地手动删包。Ubuntu 的包依赖关系很密集,一个库可能是十几个软件的共同依赖,拍脑袋删掉,后果可能比原来的错误更严重。任何修复动作跑完后,记得用apt autoremove清理一次,不要在这台机器上留着半坏的包状态。

5.3 sudoers 权限混乱后的自救

有一次我手动改/etc/sudoers文件,写错语法,保存后系统直接拒绝所有sudo操作。那一刻确实会慌,因为连修复权限的入口都被堵住了。

正确且最快的自救方式是重启进入恢复模式的 root shell。如果根分区是只读挂载,先执行mount -o remount,rw /,然后检查/etc/sudoers。修改这个文件时不要用普通编辑器直接乱改,用visudo工具,它会在保存前做语法校验,避免再次把自己锁在门外。

另一个容易踩的坑是权限。/etc/sudoers的权限应该是所有者可读可写,其他用户只读,尤其不能给普通用户写权限。我在恢复模式里第一件事就是确认权限没有变成 777,再用chmod 440 /etc/sudoers修正。正常情况下,这个文件不会频繁变动,更不要把用户直接加进 root 组来绕开 sudoers,那样虽然能临时解决问题,但会让整个系统的权限边界变得形同虚设。

6. 自动化脚本封装:把重复操作变成一条命令

6.1 用 shell 函数替代每天重复的初始化动作

开发工作里有很多“每天重复”的动作:新建项目、初始化目录、配置虚拟环境、建 git 仓库、打开编辑器。这种循环机械操作至少十分钟一次,用脚本函数化之后,效率提升非常明显。

我习惯把这类函数写进 shell 配置里,比如在.zshrc中定义一个创建前端项目的函数:

mknproj() { local project_dir="$1" if [[ -z "$project_dir" ]]; then echo "用法: mknproj 项目名" return 1 fi mkdir -p "$project_dir/src" cd "$project_dir" git init echo "# ${project_dir}" > README.md }

函数体里做了三件事:检查参数是否为空,创建目录结构并进入,初始化 git 仓库。为什么强调变量加引号?因为项目名可能包含空格或特殊字符,不加引号会被 shell 解析成多个词,目录就建错地方了。

如果需要更严谨的脚本逻辑,我建议把可复用的部分抽出来放到独立脚本文件,而不是全堆在配置文件里。脚本开头写下:

#!/usr/bin/env bash set -euo pipefail

set -e表示遇到错误就退出,-u表示使用未定义变量时报错,-o pipefail让管道命令失败时整个管道返回非零状态。这三项是脚本工程化的地基,能避免大多数“看起来执行了但结果不对”的怪问题。

6.2 用 systemd 用户定时器替代简单 cron

很多人一想到定时任务就是 crontab,但对于用户级的开发任务,我更喜欢 systemd 的用户级 timer。它在启动依赖、失败记录和日志查看上都更透明,而且不需要后台常驻脚本,写起来也不复杂。

先定义服务文件~/.config/systemd/user/backup.service:

[Unit] Description=Backup development directory snapshot [Service] Type=oneshot ExecStart=/home/myuser/bin/backup-snapshot.sh

再定义对应的 timer 文件:

[Unit] Description=Run backup snapshot every day at 02:00 [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=default.target

启用并立即观察状态:

systemctl --user daemon-reload systemctl --user enable --now backup.timer systemctl --user list-timers

Persistent=true的意思是如果定时时间点机器关机了,等下次开机后立即补一次任务。这个特性是普通 crontab 默认不具备的。每次跑完任务,用systemctl --user status backup.service就能看到退出码和最近日志,排错比翻 cron 的邮件输出舒服很多。

6.3 把故障排查也变成一条命令

当系统出现异常时,我会先花几十秒收集基础信息。以前是手打一长串命令,后来写了一个小脚本,把高频排查信息集中输出:

#!/usr/bin/env bash set -euo pipefail echo "===== 系统时间与运行时长 =====" uptime echo "===== 磁盘使用 =====" df -hT -x tmpfs -x devtmpfs echo "===== 内存概览 =====" free -h echo "===== 负载进程前五 =====" ps aux --sort=-%cpu | head -6 echo "===== 近期错误日志 =====" journalctl -p err --since "1 hour ago" --no-pager | tail -40

脚本放到/usr/local/bin/sysreport,执行chmod +x后,遇到异常先跑一次,输出的内容足够覆盖 80% 的初步判断。这个脚本不需要 root 权限,只读信息,很安全。我还会把常用服务的状态检查追加进去,这样每次排查都有一份固定格式的“系统快照”,和别人沟通时也方便描述。

写这类脚本时,最重要的一点是不要把所有机器都当成同一套环境。不同机器上可能缺少某些命令,脚本运行时如果某个命令不存在,会直接报错退出。用command -v 命令名 >/dev/null先判断一下,或者至少在使用非常规工具前给出明确提示,能让脚本在不同机器之间迁移省很多事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询