第 29 章 · Linux 服务器基本功
一句话:登录一台 Linux 服务器后最常做的几件事:看权限、找进程、查端口、看磁盘和日志、开防火墙、定时跑任务、管用户。 用它还是用别的:云服务器、VPS、公司的 Linux 机器都按这一章操作;只想在自己电脑上跑项目,用 WSL 或 Docker(第 12 章)就够了,不必另租一台服务器练手。 核对版本:Ubuntu 24.04.3(WSL 2.6,内核 6.6)、PowerShell 7.6,2026-10
29.1 安装与版本
Section titled “29.1 安装与版本”租来的云服务器、VPS 开机就是装好的 Linux,选镜像时选 Ubuntu 24.04 LTS 即可,本章命令以它为准。本地练习,Windows 上用 WSL 装一个 Ubuntu,以管理员身份在 PowerShell 里运行,完整步骤见第 4 章 4.4:
wsl --installmacOS 没有 WSL,用 Canonical 出的 Multipass 起一台 Ubuntu 虚拟机,再 multipass shell dev 进去:
brew install --cask multipassmultipass launch 24.04 --name devWSL 和虚拟机适合练文件、权限、进程、cron;SSH 登录、防火墙这几样要在真服务器上才完整,WSL 默认不跑 SSH 服务,外面也连不进来。
查发行版和版本,前一条看 VERSION,后一条看内核版本,在 WSL 里结尾是 microsoft-standard-WSL2:
cat /etc/os-releaseuname -r29.2 五分钟概念
Section titled “29.2 五分钟概念”- 用户与 root:每个用户有一个数字编号(UID),
root的 UID 是 0,什么都能改、什么都能删。平时用普通用户登录,要管理员权限的命令前面加sudo,输的是自己的密码。能用sudo的用户在 Ubuntu 上属于sudo组,在 RHEL、Rocky 上属于wheel组,id能看到自己在哪些组。 - 文件权限(Permission):每个文件有一个属主(owner)和一个属组(group),权限分三段:属主、属组、其他人,每段三位
r读、w写、x执行。三位换成数字是 r=4、w=2、x=1 相加,三段连起来就是chmod用的八进制写法,如750。对目录来说,x表示能进入,没有它连目录里的文件名都打不开。 - 进程与信号(Signal):每个运行中的程序是一个进程,有一个进程号(PID)。
kill实际是给进程发信号:默认发SIGTERM(15),请它收尾后退出;kill -9发SIGKILL,内核直接结束它,程序没有任何收尾机会。 - 端口与监听地址:服务启动时要绑定“地址加端口”。绑在
0.0.0.0表示所有网卡上来的请求都收,外面能连;绑在127.0.0.1只收本机来的请求,外面根本连不上,数据库、Redis 这类后端服务应该这样绑。端口号小于 1024 的要 root 权限才能绑。 - 包管理器:Ubuntu、Debian 用
apt,软件源列表在/etc/apt/下;RHEL、Rocky、Fedora 用dnf,写法差不多,如sudo dnf install nginx。 - 日志在哪:用 systemd 管理的服务,输出由 journald 收集,用
journalctl看(第 30 章);还有一部分程序自己写文本日志到/var/log/下,如/var/log/syslog、/var/log/auth.log(SSH 登录记录)、/var/log/nginx/。
ls -l 输出的第一列,如 -rwxr-x---,按下图拆成四段读:第一位是类型,后面三段各三位,分别是属主、属组和其他人的权限。三段各自换算成一个数字,合起来就是 chmod 750:属主能读写执行,属组能读和执行,其他人什么都不能做。
监听地址和防火墙是两道关:请求先过防火墙,再看有没有程序在那个地址和端口上收。下图里 PostgreSQL 只绑了 127.0.0.1,即使防火墙放行 5432,外部请求也没人接。
29.3 常用命令
Section titled “29.3 常用命令”需要 root 的命令前面已经写了 sudo。<PID>、<服务> 这类尖括号里的内容换成自己的。ss -ltnp 不加 sudo 时,最后一列只显示自己启动的进程。
| 任务 | 命令 | 说明 |
|---|---|---|
| 文件与权限 | ||
| 看权限、属主、属组 | ls -l |
第一列如 -rwxr-x---,第三、四列是属主和属组 |
| 看八进制权限 | stat -c '%a %A %U %G %n' deploy.sh |
输出 750 -rwxr-x--- deploy deploy deploy.sh |
| 按数字设权限 | chmod 750 deploy.sh |
目录常用 755,普通文件 644,私钥和密码文件 600 |
| 只加执行位 | chmod u+x deploy.sh |
u 属主、g 属组、o 其他人;o-r 是去掉其他人的读 |
| 改属主和属组 | sudo chown -R www-data:www-data /srv/app |
不是 root 时报 Operation not permitted |
| 找 7 天前的日志 | find /var/log -name '*.gz' -mtime +7 |
确认列表无误再在末尾加 -delete |
| 找大文件 | sudo find / -xdev -type f -size +100M |
-xdev 不跨到别的磁盘和挂载点 |
| 建或换软链接 | ln -sfn /srv/app/releases/20261008 /srv/app/current |
发版时切换 current 指向,ls -l current 看指向哪里 |
| 进程 | ||
| 列出所有进程 | ps aux |
USER、PID、%CPU、%MEM、COMMAND 几列最常看 |
| 按内存排序 | ps aux --sort=-%mem | head |
按 CPU 排序改成 --sort=-%cpu |
| 实时看负载 | top |
按 P 按 CPU 排、M 按内存排、q 退出;htop 更好用,要先 sudo apt install htop |
| 按名字找 PID | pgrep -af node |
-a 连完整命令行一起显示 |
| 请进程退出 | kill <PID> |
发 SIGTERM,进程收尾后退出 |
| 强制结束 | kill -9 <PID> |
最后手段,见 29.6 |
| 退出登录后照样跑 | nohup ./job.sh > job.log 2>&1 & |
长期运行的服务交给 systemd(第 30 章) |
| 端口与网络 | ||
| 哪些端口在监听、归谁 | sudo ss -ltnp |
第四列是监听地址,最后一列是进程名和 pid= |
| 只看某个端口 | sudo ss -ltnp | grep :8080 |
没有输出就是没人在听 |
| 看 HTTP 响应头 | curl -I https://example.com |
第一行是状态码,如 HTTP/2 301 |
| 看本机 IP | ip a |
ip -br a 每块网卡一行 |
| 查域名解析 | dig +short example.com |
没有就 sudo apt install dnsutils,或用 nslookup example.com |
| 磁盘 | ||
| 各分区用了多少 | df -h |
看 Use% 和 Mounted on,/ 那一行最要紧 |
| 一个目录占多少 | du -sh /var/log |
看下一级各占多少:sudo du -sh /var/log/* | sort -h |
| 有哪些磁盘 | lsblk |
新挂的云硬盘在这里出现,还没有挂载点 |
| 日志 | ||
| 看某个服务的日志 | journalctl -u nginx -n 50 |
加 -f 持续跟踪,--since "1 hour ago" 限时间 |
| 只看错误 | journalctl -p err -b |
本次开机以来的错误级别日志 |
| 跟踪文本日志 | tail -f /var/log/nginx/error.log |
Ctrl+C 停止 |
| 防火墙(ufw) | ||
| 看状态和规则 | sudo ufw status verbose |
显示 Status: inactive 就是没开 |
| 放行端口 | sudo ufw allow 80,443/tcp |
SSH 用 sudo ufw allow OpenSSH |
| 启用 | sudo ufw enable |
先放行 SSH 再启用,见 29.6 |
| 定时任务(cron) | ||
| 看自己的定时任务 | crontab -l |
没有时显示 no crontab for <用户> |
| 编辑定时任务 | crontab -e |
第一次会让你选编辑器,保存即生效 |
| 一行的写法 | 30 3 * * * /home/deploy/bin/backup.sh |
五段依次是分、时、日、月、星期,这行是每天 3:30 |
| 用户 | ||
| 建用户 | sudo adduser deploy |
交互式,会建家目录、让你设密码 |
| 加进 sudo 组 | sudo usermod -aG sudo deploy |
漏了 -a 会把用户从其他组里移出 |
| 看用户和所在组 | id deploy |
新加的组这里立刻能看到;那个用户已登录的会话要重新登录才生效 |
29.4 典型工作流
Section titled “29.4 典型工作流”新服务器第一小时
Section titled “新服务器第一小时”云厂商给了 IP 和 root 密码。有的镜像不开放 root,给的是 ubuntu 用户加密钥,那就用它登录,第 2、3 步的命令前面加 sudo。做完后:日常用普通用户、只能用密钥登录、root 不能登录、只开 22、80、443:
- 以 root 登录:
ssh root@<服务器IP>。 - 更新系统:
apt update && apt upgrade -y。 - 建普通用户,按提示设密码,再加进
sudo组:adduser deploy && usermod -aG sudo deploy。 - 在本机另开终端,把公钥装给新用户,原理和 Windows 写法见第 6 章 6.3、6.4:
ssh-copy-id deploy@<服务器IP>。 - 用新用户登录,再运行
sudo whoami,输出root就对了:ssh deploy@<服务器IP>。 - 保持这个窗口不关,新建 SSH 配置文件,写入 29.5 的片段:
sudo nano /etc/ssh/sshd_config.d/00-local.conf。 - 检查并让配置生效:
sudo sshd -t && sudo systemctl restart ssh。 - 再开一个本机终端,确认密钥登录照样能进,而
ssh root@<服务器IP>被拒绝:ssh deploy@<服务器IP>。 - 放行 SSH 和网站端口:
sudo ufw allow OpenSSH && sudo ufw allow 80,443/tcp。 - 启用防火墙,问
Proceed with operation (y|n)?时输y:sudo ufw enable。
最后打开自动安全更新,选 Yes,它写的是 /etc/apt/apt.conf.d/20auto-upgrades,Ubuntu 24.04 的镜像通常已经开好:sudo dpkg-reconfigure -plow unattended-upgrades。第 8 步登不上,回到第 6 步的窗口删掉 00-local.conf 再重启 ssh。
查“这个端口谁在听”
Section titled “查“这个端口谁在听””启动程序报端口被占,或想知道 8080 上跑的是什么:
- 找出监听 8080 的进程,最后一列形如
users:(("node",pid=1234,fd=20)):sudo ss -ltnp | grep :8080。 - 看第四列地址:
127.0.0.1:8080只有本机能连,0.0.0.0:8080或*:8080外面也能连。 - 看这个进程是谁、什么时候启动、完整命令行:
ps -o pid,user,lstart,cmd -p 1234。 - 看它在哪个目录下运行,判断是哪个项目:
sudo ls -l /proc/1234/cwd。 - 看它是不是 systemd 管的服务,第一行就是服务名:
systemctl status 1234。 - 从自己电脑上试能不能连到,连不上再查防火墙和监听地址:
curl -I http://<服务器IP>:8080。 - 确认不要了,是服务就停服务(第 30 章),不是就请它退出:
kill 1234。
写一条每天凌晨备份的 cron
Section titled “写一条每天凌晨备份的 cron”把 /srv/app/data 每天 3:30 打包到 ~/backup,只留最近一周的。先写脚本 ~/bin/backup.sh:
#!/bin/bashset -euo pipefail # 任一步出错就停,不留半截备份tar czf "$HOME/backup/app-$(date +%F).tar.gz" -C /srv/app datafind "$HOME/backup" -name 'app-*.tar.gz' -mtime +6 -delete # 删掉满 7 天的- 建目录:
mkdir -p ~/bin ~/backup。 - 写入上面的脚本:
nano ~/bin/backup.sh。 - 只让自己能读写执行:
chmod 700 ~/bin/backup.sh。 - 手动跑一次,看到当天的
app-2026-10-08.tar.gz就对了:~/bin/backup.sh && ls -lh ~/backup。 - 打开自己的定时任务:
crontab -e。 - 在文件末尾加一行,路径写全,输出和报错都进日志:
30 3 * * * /home/deploy/bin/backup.sh >> /home/deploy/backup/cron.log 2>&1。 - 确认已保存:
crontab -l。 - 第二天看 cron 有没有跑,日志里有
(deploy) CMD (/home/deploy/bin/backup.sh …)一行:journalctl -u cron --since yesterday。 - 看脚本自己的输出和报错:
tail ~/backup/cron.log。
备份放在同一台服务器上防不住磁盘坏或机器被删,再用 rsync 拷一份到别处(第 6 章)。
29.5 配置要点
Section titled “29.5 配置要点”服务器上常改的配置文件,都在 /etc 下,改之前先 sudo cp <文件> <文件>.bak 备份:
| 范围 | 路径 |
|---|---|
| SSH 服务端 | /etc/ssh/sshd_config;自己的改动放 /etc/ssh/sshd_config.d/00-local.conf,原因见第 6 章 6.5 |
| ufw 开关和规则 | /etc/ufw/,ufw allow 写进的规则在 user.rules 里,用命令改,不手改 |
| ufw 默认策略 | /etc/default/ufw,默认拒绝入站、允许出站 |
| 系统定时任务 | /etc/crontab 和 /etc/cron.d/ 下的文件,比 crontab -e 多一列用户名,如 30 3 * * * root /usr/local/bin/backup.sh |
| 用户定时任务 | /var/spool/cron/crontabs/<用户>,只用 crontab -e 改 |
| 软件源 | /etc/apt/sources.list.d/ubuntu.sources(24.04 起),旧版本是 /etc/apt/sources.list |
| 自动更新 | /etc/apt/apt.conf.d/20auto-upgrades(开关)、50unattended-upgrades(装哪些更新) |
| sudo 规则 | /etc/sudoers 和 /etc/sudoers.d/,只用 sudo visudo 改,写错了它会拒绝保存 |
sshd_config 值得改的项:
| 配置项 | 建议值 | 效果 |
|---|---|---|
PermitRootLogin |
no |
root 不能登录,已经有能 sudo 的普通用户后再设;第 6 章的 prohibit-password 允许 root 用密钥登录 |
PasswordAuthentication |
no |
只能用密钥登录,挡住全网扫描的密码爆破 |
KbdInteractiveAuthentication |
no |
关掉另一种输密码的方式,Ubuntu 默认已是 no |
PubkeyAuthentication |
yes |
允许密钥登录,默认就是 yes,写出来防止被别的文件改掉 |
AllowUsers |
deploy |
只有列出的用户能登录,多个用空格隔开 |
MaxAuthTries |
3 |
一次连接最多试 3 次,默认 6 |
最小片段,写进 /etc/ssh/sshd_config.d/00-local.conf。sshd_config 只认行首的 # 为注释,注释要单独一行;写完 sudo sshd -t 检查,sudo sshd -T | grep -i permitrootlogin 看最终生效的值:
# root 不能通过 SSH 登录,管理员操作用 sudoPermitRootLogin no# 不接受密码登录,只认密钥PasswordAuthentication no# 关掉键盘交互式认证,免得绕过上一行再问密码KbdInteractiveAuthentication no# 允许公钥登录PubkeyAuthentication yes# 只允许 deploy 这个用户登录AllowUsers deploy# 一次连接最多尝试 3 次MaxAuthTries 329.6 注意事项
Section titled “29.6 注意事项”./deploy.sh: Permission denied或cat: /etc/shadow: Permission denied。 原因:当前用户在对应那段权限里没有r、w或x,或属主不是自己。处理:ls -l看缺哪一位;脚本缺执行位chmod u+x deploy.sh;属主不对sudo chown deploy:deploy <文件>。chmod -R 777之后程序能跑了。 原因:权限问题没解决,只是让所有用户都能改这些文件,任何被攻破的进程都能改代码和配置。处理:属主改成运行服务的用户,再改回目录 755、文件 644:find /srv/app -type d -exec chmod 755 {} +,文件把-type d换-type f、755换644。- 服务器上
curl localhost:8080正常,从外面连报Connection refused。 原因:程序只监听本机,ss -ltnp里是127.0.0.1:8080。处理:改为监听0.0.0.0(如--host 0.0.0.0)并sudo ufw allow 8080/tcp;对外网站更好的做法是前面放 Nginx(第 16 章)。 sudo ufw enable之后,新开的 SSH 连接一直超时。 原因:启用前没放行 22,ufw 默认拒绝所有入站连接,已连着的窗口一般还在。处理:在还没断的窗口里sudo ufw allow OpenSSH;旧窗口也断了,就从云控制台的网页终端登录再放行;以后先allow再enable。- 脚本手动跑正常,放进 cron 报
command not found。 原因:cron 不读~/.bashrc,PATH只有/usr/bin:/bin,nvm 装的命令找不到。处理:脚本里写绝对路径(command -v node查);或在 crontab 首行写PATH=,把command -v node所在目录加到/usr/bin:/bin前。 - cron 里
date +%F那一行只执行了一半,文件名没生成。 原因:crontab 里%表示换行,后面的内容被当成命令的标准输入。处理:写成date +\%F,或者像 29.4 那样把命令放进脚本,crontab 里只写脚本路径。 df -h显示/用到100%,du加起来却少很多。 原因:文件删了,但还被进程打开着(多是日志),空间要等进程关掉它才释放。处理:sudo lsof +L1找到标着(deleted)的进程,重启它:sudo systemctl restart <服务>。- 用
kill -9停了数据库或应用,再启动时数据少了一截,或报锁文件已存在。 原因:SIGKILL不给程序收尾机会,缓冲区没落盘、锁文件没删。处理:先kill <PID>,等十几秒还在再用-9;交给 systemd 管的服务用systemctl stop(第 30 章)。 - cron 定在
0 3 * * *的任务,实际在北京时间 11 点跑。 原因:服务器时区是 UTC,cron 按系统时区算。处理:timedatectl查看,sudo timedatectl set-timezone Asia/Shanghai后sudo systemctl restart cron;或者按 UTC 换算时间来写。
29.7 深入阅读
Section titled “29.7 深入阅读”- Ubuntu Server 文档:Ubuntu 官方服务器文档,用户与权限、软件包、OpenSSH、防火墙、自动更新、备份都有分步说明。
- 防火墙 ufw(Ubuntu Server 文档):ufw 的启用、放行端口、按应用名放行、查看与删除规则,以及日志。
- Ubuntu 手册页:sshd_config:Ubuntu 24.04 上 sshd_config 每个配置项的含义和默认值,PermitRootLogin、PasswordAuthentication、AllowUsers 都在这里。
- Linux man-pages(man7.org):Linux 命令和系统调用的手册页总目录,chmod、kill、信号(signal(7))、crontab(5) 等都能查到。
- crontab.guru:输入五段的 cron 表达式,立刻用英文说明它什么时候运行,并显示接下来几次的时间。
- Ubuntu 初始服务器设置(DigitalOcean 教程):新服务器建普通用户、授予 sudo、开 ufw、配置 SSH 密钥登录的分步教程,长期随 Ubuntu 版本更新。