Skip to content

Linux 常见问题

本篇记录 Linux 系统维护中风险较高的问题。执行修改前应确认发行版、版本、启动方式和恢复入口,并保留当前会话或带外控制台。


如何安全升级 OpenSSH

推荐方案

优先使用发行版安全仓库提供的 OpenSSH 更新。发行版通常会将安全补丁回移到现有版本,版本号较旧并不等于漏洞未修复。

bash
# RHEL 8/9、Rocky Linux、AlmaLinux
sudo dnf update openssh openssh-server openssh-clients

# Ubuntu / Debian
sudo apt update
sudo apt install --only-upgrade openssh-client openssh-server

升级前后检查版本和服务状态:

bash
ssh -V
sudo sshd -t
sudo systemctl status sshd --no-pager

Ubuntu 和 Debian 的服务名通常是 ssh,RHEL 系列通常是 sshd

远程操作的安全顺序

  1. 保留当前 SSH 会话,不要提前退出。
  2. 确认云控制台、IPMI、iDRAC 或虚拟机控制台可用。
  3. 备份 /etc/ssh/sshd_config/etc/ssh/sshd_config.d/
  4. 使用 sshd -t 验证配置。
  5. 使用 reload 重新加载配置,避免无必要的服务中断。
  6. 新开一个终端验证登录成功后,再关闭旧会话。
bash
sudo cp -a /etc/ssh /etc/ssh.backup
sudo sshd -t
sudo systemctl reload sshd

不要通过下面的方式“保护”升级过程:

  • 不要开启明文 Telnet 服务;Telnet 默认使用 TCP 23 端口,认证信息不会加密。
  • 不要先停止或卸载正在工作的 OpenSSH。
  • 不要将源码编译结果直接覆盖 /usr/bin/ssh/usr/sbin/sshd
  • 不要为方便测试开启 Root 密码登录。

如果合规扫描要求高于发行版提供的版本,应优先升级操作系统。确需源码构建时,应安装到独立前缀并创建单独的测试服务和端口,验证完成后再设计切换方案。


是否应该在原系统上升级 glibc

通常不应该。glibc 被 shell、SSH、systemd、包管理器和大量基础命令依赖,直接编译覆盖 /usr 中的系统版本可能让整台机器无法启动或维护。

需求推荐方案
应用需要更新的 glibc使用与应用要求匹配的容器或基础镜像
老系统缺少安全更新升级或迁移到受支持的发行版
编译测试新版本安装到隔离目录,仅供测试程序显式加载
第三方二进制无法启动向供应商索取兼容构建,或在匹配的运行环境中执行

排查二进制依赖时可使用:

bash
ldd --version
ldd /path/to/program
readelf -l /path/to/program | grep interpreter

不要使用 --disable-sanity-checks 绕过 glibc 构建检查,也不要直接替换 /lib64/libc.so.6。如果系统库已经损坏,应从同版本安装介质进入救援环境,通过包管理器恢复原发行版软件包。


Docker 容器端口如何限制来源地址

Docker 发布端口的流量通常经过 FORWARD 和 Docker 自建链,不一定经过普通的 INPUT 链。需要额外限制时可使用 DOCKER-USER 链,但应先理解当前防火墙后端和已有规则。

bash
# 查看 Docker 用户链及命中计数
sudo iptables -L DOCKER-USER -n -v --line-numbers

Docker 在规则到达 DOCKER-USER 前通常已完成 DNAT,因此 --dport 看到的可能是容器端口,而不是宿主机发布端口。如果二者不同,应使用 conntrack 匹配原始目标:

bash
# 示例:只允许 10.108.11.178 访问宿主机发布的 TCP 1521 端口
sudo iptables -I DOCKER-USER 1 \
  -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -I DOCKER-USER 2 \
  -p tcp -s 10.108.11.178 \
  -m conntrack --ctorigdstport 1521 -j ACCEPT
sudo iptables -I DOCKER-USER 3 \
  -p tcp -m conntrack --ctorigdstport 1521 -j DROP

这只是规则结构示例。生产环境还需确认 IPv6、多个 Docker 网络、规则持久化以及 firewalld/nftables 的集成方式。修改远程防火墙前应准备回滚命令和控制台入口。

Docker 代理、镜像构建和 NVIDIA 容器运行时属于 Docker 专题,不在 Linux QA 中重复维护。


如何调整和重新加载 Swap

vm.swappiness 控制内核使用 Swap 的倾向,取值为 0200。它不是“剩余内存百分比”,也不表示设置为 0 后完全禁用 Swap。

bash
# 查看当前值
sysctl vm.swappiness

# 临时调整
sudo sysctl vm.swappiness=10

# 写入独立配置文件并加载
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/90-swappiness.conf
sudo sysctl --system

重新加载所有 Swap 前必须确认可用内存足够容纳当前已使用的 Swap,否则 swapoff -a 可能失败,甚至触发 OOM:

bash
free -h
swapon --show
sudo swapoff -a
sudo swapon -a

如何确认是否发生 OOM

OOM Killer 通常终止进程,不一定导致操作系统重启。优先查询当前和上一次启动的内核日志:

bash
# 当前启动
sudo journalctl -k -g 'oom|out of memory|killed process' --no-pager

# 上一次启动
sudo journalctl -k -b -1 -g 'oom|out of memory|killed process' --no-pager

# 不使用 systemd-journald 的系统
dmesg -T | grep -Ei 'oom|out of memory|killed process'

定位问题时重点记录:

  • 被终止的进程名和 PID
  • anon-rssfile-rss、Swap 使用量
  • 进程是否位于容器或 systemd cgroup 中
  • 是否配置了内存限制
  • OOM 前后的业务流量和内存趋势

容器或服务可能触发 cgroup OOM,而不是整机 OOM。可以进一步查看:

bash
systemctl status my-service
systemctl show my-service -p MemoryCurrent -p MemoryMax
docker inspect my-container --format '{{.HostConfig.Memory}}'

仅看到 last reboot 记录不能证明 OOM 导致重启,还需要结合上一次启动的内核日志、硬件日志和云平台事件判断。


GRUB 损坏时如何恢复

GRUB 修复命令取决于启动模式、发行版、磁盘和分区布局,不能固定使用 /dev/sda

修复前确认环境

在 Live 或 Rescue 环境中先收集信息:

bash
lsblk -f
blkid
sudo fdisk -l

# 存在该目录通常表示当前救援环境以 UEFI 启动
test -d /sys/firmware/efi && echo UEFI || echo BIOS

还需要确认:

  • 根文件系统、/boot 和 EFI System Partition 分别位于哪里
  • 是否使用 LVM、软件 RAID 或磁盘加密
  • 原系统是 RHEL 系列还是 Debian/Ubuntu
  • 当前启动的是 BIOS 还是 UEFI 模式

挂载并进入系统

下面仅展示通用结构,设备名必须替换为实际值:

bash
sudo mount /dev/mapper/vg-root /mnt
sudo mount /dev/vda2 /mnt/boot

# UEFI 系统还需挂载 EFI System Partition
sudo mount /dev/vda1 /mnt/boot/efi

for path in /dev /dev/pts /proc /sys /run; do
  sudo mount --bind "$path" "/mnt$path"
done

sudo chroot /mnt /bin/bash

选择对应修复流程

  • RHEL 系列 BIOS:通常重新安装 grub2-pc 相关组件、执行 grub2-install,再生成 /boot/grub2/grub.cfg
  • RHEL 系列 UEFI:应恢复 EFI 版 GRUB 和 shim 软件包,并生成对应配置,不应对整块磁盘执行 BIOS 式安装。
  • Ubuntu/Debian BIOS:通常重新安装 grub-pc 并执行 grub-installupdate-grub
  • Ubuntu/Debian UEFI:应挂载 EFI 分区,重新安装 grub-efi-* 和 shim,再执行 update-grub

具体命令应以发行版对应版本的救援文档为准。操作完成后退出 chroot,并按挂载顺序反向卸载:

bash
exit
sudo umount -R /mnt

sudo 如何保留必要的环境变量

sudo -E 会请求保留调用者环境,但最终是否保留由 /etc/sudoersenv_resetenv_keep 和安全策略决定。它不会保证所有变量都被传递。

优先只传递当前命令需要的变量:

bash
sudo HTTP_PROXY="$HTTP_PROXY" HTTPS_PROXY="$HTTPS_PROXY" command-name

需要长期允许特定变量时,应使用 visudo 配置精确白名单,例如:

text
Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY"

不要为了传递环境变量而习惯性执行 sudo -E /bin/bash,这会启动继承较多用户环境的 Root Shell,扩大误操作和环境注入风险。