标签归档:linux

Linux SWAP 占用情况排查 – smem

最近发现 ArchLinux 的 32G 内存经常出现 OOM,分配 SWAP 后分多少就占多少,很奇怪,就想办法排查一下,找到了 smem.

smem 是一款能提供大量 Linux 系统内存使用报告的工具。与现有工具不同,smem 可以报告比例集大小 (PSS),这对虚拟内存系统中库和应用程序使用的内存量来说更有意义。 — smem memory reporting tool

安装

# Debian/Ubuntu
sudo apt install smem
# CentOS/RHEL
sudo yum install smem
# Archlinux
yay -S smem

查看 SWAP 占用排行

$ smem -r -k -s swap
 PID User     Command                         Swap      USS      PSS      RSS
1634 songtianlun /usr/bin/remmina -i             1.9G   623.9M   625.1M   634.9M
1260193 songtianlun /usr/bin/pycharm-profession   749.4M     2.0G     2.0G     2.0G
1166595 songtianlun /opt/goland/bin/goland        417.3M     2.8G     2.8G     2.8G
1665 songtianlun /usr/lib/firefox/firefox --   244.0M   610.8M   637.9M   801.6M
1327 songtianlun /usr/bin/plasmashell --no-r   181.6M   293.4M   300.9M   353.5M
62871 songtianlun /usr/lib/firefox/firefox -c   136.3M   121.5M   122.6M   166.4M    
2636 songtianlun /usr/lib/electron32/electro   131.4M   410.7M   417.2M   433.
...

其他用法

使用 pmap 查看特定进程的内存映射

如果您已经知道某个特定进程的 PID,并想查看它的具体内存使用情况,包括 Swap 使用量,可以使用 pmap: 使用 pmap 命令:

pmap -x 
<PID>

其中 `

` 是进程的 ID。输出将显示该进程的内存映射情况,包括每个映射的大小。 ### 使用 `grep` 直接查看 `/proc/[pid]/smaps` 您还可以通过查看 `/proc` 文件系统中的信息来获取某个特定进程的详细信息: 获取进程的 PID,假设为 `1234`,使用: “` grep -i swap /proc/1234/smaps “` 这将输出关于这个进程的 Swap 使用情况。

总结

通过使用 smempmap 等工具,可以快速确认哪些进程正在使用 Swap,从而帮助深入分析和排查问题。

References

Linux 网卡多队列配置方法

多队列是指网络接口卡具有在多个发送和接收队列上并行处理网络数据包的能力。在使用网卡多队列时,通常需要配置网络中断亲和性(IRQ Affinity),将不同的队列中断分配给特定的CPU处理,而不是由任意的CPU处理,这有助于减少CPU之间的争用并提高网络性能。本文介绍如何配置Linux系统的网卡多队列的网络中断亲和性和修改网卡多队列数。

配置网络中断亲和性

下述操作以Red Hat 9.2镜像系统为例,介绍如何通过ecs_mq脚本自动配置网卡多队列的网络中断亲和性。如果您的系统镜像并非Red Hat Enterprise Linux,已默认开启,则无需再配置。

  1. 远程连接Linux实例。
  2. (可选)关闭irqbalance服务。 由于irqbalance服务会动态调整IRQ Affinity,配置ecs_mq脚本会和irqbalance产生冲突,建议关闭irqbalance服务。
    systemctl stop irqbalance.service
  3. 执行以下命令,下载最新版本的网卡多队列的自动配置脚本ecs_mq
    wget https://ecs-image-tools.oss-cn-hangzhou.aliyuncs.com/ecs_mq/ecs_mq_2.0.tgz

    最新版ecs_mq修复的问题

  4. 执行以下命令,解压ecs_mq脚本。
    tar -xzf ecs_mq_2.0.tgz
  5. 执行以下命令,更换工作路径。
    cd ecs_mq/
  6. 执行以下命令,运行ecs_mq脚本。
    bash install.sh redhat 9

    说明 请您根据实际环境替换命令中的 redhat 和 9为您自己的<操作系统名称><操作系统主版本号>

  7. 执行以下命令,启动ecs_mq脚本。
    systemctl start ecs_mq

    启动脚本后,系统自动启动网络中断亲和性。

    修改网卡多队列数

  • 执行以下命令,查看主网卡eth0支持多队列的情况。
    ethtool -l eth0

    根据返回结果查看是否支持网卡多队列:

  • 如果 “Pre-set maximums” 下的 “Combined” 值大于1,表示网卡支持多队列。 您可以根据这个最大值来设置当前的队列数: 执行命令sudo ethtool -L eth0 combined N,其中N是您希望设置的队列数,且N应该小于或等于 “Pre-set maximums” 下的 “Combined” 值。
  • Current hardware settings 下的 Combined 值表示当前生效的队列数。 本例中返回表示最多支持2个队列,且当前生效的是1个队列:
    Channel parameters for eth0:
    Pre-set maximums:
    RX: 0
    TX: 0
    Other: 0
    Combined: 2 # 表示最多支持设置2个队列
    Current hardware settings:
    RX: 0
    TX: 0
    Other: 0
    Combined: 1 # 表示当前生效的是1个队列
  • 执行以下命令,设置主网卡使用2个队列功能。
    sudo ethtool -L eth0 combined 2
  • 执行以下命令,查看辅助弹性网卡eth1支持多队列的情况。
    ethtool -l eth1

    通过返回信息可以看到,辅助弹性网卡已开启支持多队列。您可以按需继续执行下述操作自定义配置辅助弹性网卡的多队列数。

    Channel parameters for eth1:
    Pre-set maximums:
    RX: 0
    TX: 0
    Other: 0
    Combined: 4 # 表示网卡硬件最多可以支持4个队列
    Current hardware settings:
    RX: 0
    TX: 0
    Other: 0
    Combined: 1 # 表示当前网卡只使用了1个队列
  • 执行以下命令,设置辅助弹性网卡使用4个队列功能。
    sudo ethtool -L eth1 combined 4

References

Linux overcommit 及 oom-killer 机制

通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程(用户态进程,不是内核线程)以腾出内存留给系统用,不致于让系统立刻崩溃。

overcommit

Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。

/proc/sys/vm/overcommit_memory 取值为[0-2],默认值为0:  

0: 启发式过度使用处理,显而易见的过度使用地址空间被拒绝。它在允许的情况下确保 严重分配失败、过度使用以减少交换使用。  
1: 始终过度使用内存,表示kernel永远不会检查是否有足够的内存可用,总是返回true.  
2: 禁止过度使用,表示kernel拒绝 >= 可用的swap+物理内存 * overcommit_ratio(默认为50)的内存分配请求.  
在大多数情况下,这意味着访问页面时不会终止进程,但会在适当时收到内存分配错误。

#16 GB Swap, 16 GB RAM, overcommit_memory=2 内存请求上限及计算方法
# free
              total        used        free      shared  buff/cache   available
Mem:       16311328     6048244      573316       42992     9689768     8963032
Swap:      16601084     3580760    13020324

# 计算方法
cat /proc/sys/vm/overcommit_ratio #默认50
Mem * overcommit_ratio (50%) + swap= 8155664 + 16601084 = 24756748 kB

16G+16G*50%/100=24G (overcommit_memory = 2)
若修改vm.overcommit_raito为100,则请求内存16G+16G*100%/100=32G

# grep -i commit /proc/meminfo
CommitLimit:    24756748 kB
Committed_AS:   14178044 kB

cat << EOF >> /etc/sysctl.conf
vm.overcommit_memory=1 #redis
vm.overcommit_ratio=50 # 默认
EOF
sysctl -p

oom killer

查看oom killer 日志,最常见的就是MySQL 无缘无故挂掉,Out of memory: Kill process信息:

grep -i "kill" /var/log/messages #CentOS
#grep -i "kill" /var/log/kern.log #Ubuntu
...
Out of memory: Kill process 9682 (mysqld) score 9 or sacrifice child
Killed process 9682, UID 27, (mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
httpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
httpd cpuset=/ mems_allowed=0
Pid: 8911, comm: httpd Not tainted 2.6.32-279.1.1.el6.i686 #1
...

内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。

out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。

/**
 * oom_badness - heuristic function to determine which candidate task to kill
 * @p: task struct of which task we should calculate
 * @totalpages: total present RAM allowed for page allocation
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
              const nodemask_t *nodemask, unsigned long totalpages)
{
    long points;
    long adj;

    if (oom_unkillable_task(p, memcg, nodemask))
        return 0;

    p = find_lock_task_mm(p);
    if (!p)
        return 0;

    adj = (long)p->signal->oom_score_adj;
    if (adj == OOM_SCORE_ADJ_MIN) {
        task_unlock(p);
        return 0;
    }

    /*
     * The baseline for the badness score is the proportion of RAM that each
     * task's rss, pagetable and swap space use.
     */
    points = get_mm_rss(p->mm) + p->mm->nr_ptes +
         get_mm_counter(p->mm, MM_SWAPENTS);
    task_unlock(p);

    /*
     * Root processes get 3% bonus, just like the __vm_enough_memory()
     * implementation used by LSMs.
     */
    if (has_capability_noaudit(p, CAP_SYS_ADMIN))
        adj -= 30;

    /* Normalize to oom_score_adj units */
    adj *= totalpages / 1000;
    points += adj;

    /*
     * Never return 0 for an eligible task regardless of the root bonus and
     * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
     */
    return points > 0 ? points : 1;
}

上面代码里的注释写的很明白,理解了这个算法我们就理解了为啥 MySQL 躺着也能中枪了,因为它的体积总是最大(一般来说它在系统上占用内存最多),所以如果 Out of Memeory (OOM) 的话总是不幸第一个被 kill 掉。解决这个问题最简单的办法就是增加内存,或者想办法优化 MySQL 使其占用更少的内存,除了优化 MySQL 外还可以优化系统,让系统尽可能使用少的内存以便应用程序(如 MySQL) 能使用更多的内存,还有一个临时的办法就是调整内核参数,让 MySQL 进程不容易被 OOM killer 发现。

找出最有可能被 OOM Killer 杀掉的进程

# cat /data/shell/oomscore.sh 
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
    printf "%2d %5d %s\n" \
        "$(cat $proc/oom_score)" \
        "$(basename $proc)" \
        "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10

调整oom_adj

`/proc/

/oom_adj` ​值范围是[-17, 15],oom_score 值越高越容易被oom kill掉。设为 `-17`则该进程禁用 oom_killer。 比如查看进程号为187418的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3: “`bash pidof mysqld 187418 # cat /proc/187418/oom_score 18 # echo -15 > /proc/187418/oom_score_adj # cat /proc/981/oom_score 3 “` # 配置oom killer 我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统。 > 修改panic_on_oom值为1,表示请求内存不足时10秒后重启系统 “`bash cat <> /etc/sysctl.conf vm.panic_on_oom=1 kernel.panic=10 # 表示10s后重启 EOF sysctl -p “` # 内核参数 “`bash /proc/sys/vm/panic_on_oom 取值为[0-2],默认值为0: 0: OOM时系统执行OOM Killer 1: OOM时系统会panic(恐慌) 2: OOM时系统一定会触发panic(恐慌) /proc/sys/vm/oom_kill_allocating_task 取值为[0-1],默认值为0: 0: 内核将检查每个进程的分数,分数最高的进程将被kill掉 1: 那么内核将kill掉当前申请内存的进程 “` # References – [Linux overcommit 及 oom-killer 机制](https://sundayle.sundayhk.com/linux-overcommit-oom-killer/) – [理解和配置 Linux 下的 OOM Killer](https://www.vpsee.com/2013/10/how-to-configure-the-linux-oom-killer/) – [kernel overcommit accounting](https://www.kernel.org/doc/Documentation/vm/overcommit-accounting) – [Virtual memory settings in Linux – The Problem with Overcommit](http://engineering.pivotal.io/post/virtual_memory_settings_in_linux_-_the_problem_with_overcommit/)

RockLinux 安装 Docker

Docker Engine 可以在 Rocky Linux 服务器上运行原生 Docker 风格的容器工作负载。在运行完整的 Docker Desktop 环境时,有时会首选这种方式。

添加 Docker 仓库

使用 dnf 工具将 Docker 仓库添加到你的 Rocky Linux 服务器。输入:

sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

安装所需的软件包

通过运行以下命令安装最新版本的 Docker Engine、containerd 和 Docker Compose:

sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin

启动并启用 Docker (dockerd)

使用 systemctl 配置 Docker 在重启时自动启动,并同时立即启动它。输入:

sudo systemctl --now enable docker

可选:允许非 root 用户管理 docker

将非 root 用户添加到 docker 组,以允许用户无需 sudo 即可管理 docker

这是一个可选步骤,但如果你是系统的主要用户,或者想允许多个用户管理 docker 但不想授予他们 sudo 权限,这会很方便。

输入:

# 添加当前用户
sudo usermod -a -G docker $(whoami)

# 添加特定用户
sudo usermod -a -G docker custom-user

要使新组生效,你必须注销并重新登录。使用 id 命令验证组是否已添加。

注释

docker-ce               : 此软件包提供用于构建和运行 docker 容器的底层技术 (dockerd)
docker-ce-cli          : 提供命令行界面 (CLI) 客户端 docker 工具 (docker)
containerd.io          : 提供容器运行时 (runc)
docker-compose-plugin  : 提供 'docker compose' 子命令的插件

References

使用 earlyoom 提前终止 Linux 高内存占用进程

当我在服务器上运行一个不太重要的进程时,它的内存使用量会随实际情况不断发生变化,当它的内存超过某个阈值时,我想要 kill 掉它并重启该进程。为了满足我这个需求,我了解到了 earlyoom 这个程序。

earlyoom 是一个用于 Linux 的内存不足 (OOM) 守护进程。它在系统内存和交换空间不足时提前终止内存占用最大的进程,从而避免系统陷入完全无响应的状态。它的全称是 “Early OOM Daemon”,其中 OOM 代表 “Out Of Memory”。本文将介绍 earlyoom 的安装、配置和使用方法。

什么是 earlyoom?

在 Linux 系统中,默认的 OOM 机制只有在内存和交换空间完全耗尽后才会触发,这通常会导致系统变得非常缓慢甚至完全无响应。earlyoom 的设计初衷是通过更早地检测内存和交换空间不足的情况,提前终止高内存占用的进程,从而保持系统的响应速度。

earlyoom 每秒最多检查 10 次内存和交换空间的使用情况。如果可用内存和可用交换空间均低于 10%,则终止内存占用最大的进程。这个阈值可以通过命令行参数进行配置。

earlyoom 的主要功能和特点

  1. 监控内存使用情况:earlyoom 会持续监控系统的可用内存和交换内存。通过定期检查内存使用情况,它能够在内存过低时及时采取措施。
  2. 主动释放内存:当系统内存降至预设的阈值以下时,earlyoom 会主动终止内存占用最高的进程。这比传统的 Linux 内核 OOM 杀手更早地介入,减少系统变得完全无响应的风险。
  3. 可配置性:用户可以通过命令行参数或配置文件自定义 earlyoom 的行为,包括设定内存和交换内存的阈值,选择是否终止内存占用最高的进程或是直接重启系统。
  4. 轻量级和高效:earlyoom 设计为一个轻量级的守护进程,占用系统资源非常少,适合在各种环境下运行。
  5. 日志和通知:earlyoom 会记录所有的内存监控和进程终止操作,并可以配置为在内存过低时发送通知,方便管理员及时了解系统状态。

earlyoom 的使用场景

  1. 避免系统冻结:在 Linux 系统中,当内存资源耗尽时,内核的 OOM (Out of Memory) Killer 会启动,尝试释放内存。默认情况下,OOM Killer 会在系统内存极度不足时才启动,这可能导致系统变得非常缓慢或完全无响应,用户无法进行操作。earlyoom 通过在内存和交换空间较低时更早地杀死进程,防止系统冻结,提高系统的稳定性和响应性。
  2. 提高系统的可用性:对于一些关键的服务器或应用场景,确保系统的稳定性至关重要。earlyoom 可以在内存紧张之前采取措施,避免系统进入不可操作的状态,尤其是在资源使用高峰期。
  3. 开发和测试环境:在开发和测试环境中,可能会出现由于内存泄漏或其他原因导致内存迅速耗尽的情况。使用 earlyoom 可以模拟和测试系统在低内存条件下的表现,确保应用程序能够在这些条件下稳定运行。

如何安装 earlyoom

从源码编译

首先,克隆 earlyoom 的 GitHub 仓库并进入项目目录:

git clone https://github.com/rfjakob/earlyoom.git
cd earlyoom

然后,编译项目:

make

可选:运行集成的自测试:

make test

最后,使用 systemdinit.dearlyoom 注册为系统服务:

sudo make install              # systemd
sudo make install-initscript   # non-systemd

使用包管理器安装

Debian 和 Ubuntu

对于 Debian 10+ 和 Ubuntu 18.04+,可以直接安装 earlyoom 包:

sudo apt install earlyoom

Fedora 和 RHEL 8

对于 Fedora 和 RHEL 8(需要 EPEL):

sudo dnf install earlyoom
sudo systemctl enable --now earlyoom

Arch Linux

对于 Arch Linux:

sudo pacman -S earlyoom
sudo systemctl enable --now earlyoom

其他发行版的安装方法请参阅 repology 页面

如何使用 earlyoom

启动 earlyoom 可执行文件:

./earlyoom

它会显示内存和交换空间的使用情况,并在内存和交换空间不足时终止进程。以下是一个示例输出:

earlyoom v1.8
mem total: 23890 MiB, user mem total: 21701 MiB, swap total: 8191 MiB
sending SIGTERM when mem avail <= 10.00% and swap free <= 10.00%,
        SIGKILL when mem avail <=  5.00% and swap free <=  5.00%
mem avail: 20012 of 21701 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
mem avail: 20031 of 21721 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
mem avail: 20033 of 21723 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
[...]

命令行选项

earlyoom 支持多种命令行选项,以下是常用选项的说明:

earlyoom v1.8
Usage: ./earlyoom [OPTION]...

  -m PERCENT[,KILL_PERCENT] 设置可用内存最小百分比 (默认 10%)。
                            低于此值发送 SIGTERM 信号,低于 KILL_PERCENT 时发送 SIGKILL 信号 (默认 PERCENT/2)。
  -s PERCENT[,KILL_PERCENT] 设置可用交换空间最小百分比 (默认 10%)。
  -M SIZE[,KILL_SIZE]       设置可用内存最小值 (以 KiB 计)。
  -S SIZE[,KILL_SIZE]       设置可用交换空间最小值 (以 KiB 计)。
  -n                        启用 d-bus 通知。
  -N /PATH/TO/SCRIPT        进程被杀死后执行脚本。
  -g                        杀死整个进程组。
  -d, --debug               启用调试信息。
  -v                        显示版本信息并退出。
  -r INTERVAL               内存报告间隔(以秒计,默认 1),设为 0 可禁用。
  -p                        设置 EarlyOOM 的 niceness 为 -20 和 oom_score_adj 为 -100。
  --ignore-root-user        不杀死 root 用户的进程。
  --sort-by-rss             按 RSS 大小选择进程而非 oom_score。
  --prefer REGEX            优先杀死符合正则表达式的进程。
  --avoid REGEX             避免杀死符合正则表达式的进程。
  --ignore REGEX            忽略符合正则表达式的进程。
  --dryrun                  干跑模式(不杀死任何进程)。
  --syslog                  使用 syslog 记录日志。
  -h, --help                显示帮助信息。

earlyoom 的命令行使用非常直观,可以通过不同的参数来配置它的行为。

注意事项:以上命令中正则表达式参数需要使用进程的comm名称

  • --prefer--avoid--ignore 参数中使用的正则表达式需要根据进程名称的前 15 个字节进行匹配,因为 comm 名称字段最大长度为 15 字节,超过部分会被截断。
  • 使用 --ignore 参数时需要特别小心,因为忽略某些关键进程可能会导致其他进程被杀死,从而影响系统的正常运行。

相关阅读:什么是进程的 comm 名称?Linux 进程 comm 名称详解

earlyoom 常用命令行使用示例

earlyoom 是一个内存管理工具,它可以在系统内存和交换空间低于设定阈值时提前杀死进程。以下是一些常用的命令行使用示例:

  1. 基本使用

     earlyoom

    使用默认配置,设置内存和交换空间阈值为 10%。当内存和交换空间都低于 10% 时,earlyoom 会发送 SIGTERM,如果继续低于阈值,则发送 SIGKILL

  2. 自定义内存和交换空间阈值

     earlyoom -m 20,10 -s 15,7

    设置可用内存阈值为 20%,当内存低于 20% 时发送 SIGTERM,低于 10% 时发送 SIGKILL。同时,设置交换空间阈值为 15%,低于 15% 时发送 SIGTERM,低于 7% 时发送 SIGKILL

  3. 指定内存和交换空间的绝对值

     earlyoom -M 1048576,524288 -S 512000,256000

    设置内存阈值为 1 GiB (1048576 KiB),低于此值时发送 SIGTERM,低于 512 MiB (524288 KiB) 时发送 SIGKILL。交换空间阈值为 500 MiB (512000 KiB),低于此值时发送 SIGTERM,低于 250 MiB (256000 KiB) 时发送 SIGKILL

  4. 启用调试模式

     earlyoom -d

    启用调试信息输出,帮助排查问题。

  5. 启用通知

     earlyoom -n

    启用通过 D-Bus 发送通知。需要 systembus-notify 工具运行在用户会话中,以显示通知。

  6. 执行脚本

     earlyoom -N /path/to/script.sh

    在每次进程被杀死时执行指定的脚本。脚本可以获取被杀死进程的相关信息,如 PID、进程名、命令行等。

  7. 增加优先级

     earlyoom -p

    增加 earlyoom 的优先级,将其 niceness 设置为 -20,并将 oom_score_adj 设置为 -100。需要通过系统服务配置来达到相同效果。

  8. 模拟运行

     earlyoom --dryrun

    模拟运行 earlyoom,不实际杀死任何进程,用于测试配置是否正确。

  9. 忽略某些进程

     earlyoom --ignore '^gnome-shell$'

    --ignore 参数用于完全忽略名称匹配指定正则表达式(REGEX)的进程。这意味着这些进程无论内存使用情况如何,都不会被 earlyoom 杀死。使用此选项时需要谨慎,因为忽略某些进程可能会导致其他进程被优先选择进行杀死,从而影响系统的稳定性。

  10. 优先杀死某些进程

     earlyoom --prefer '^Isolated Web Co$'

    --prefer 参数用于优先杀死名称匹配指定正则表达式(REGEX)的进程。当某个进程的名称与给定的正则表达式匹配时,会在其 oom_score 中增加 300 分,这使得该进程更有可能被优先选择进行杀死。

  11. 避免杀死某进程

     ```sh
     earlyoom --avoid '^gunicorn$'
    
    
     `--avoid` 参数用于避免杀死名称匹配指定正则表达式(REGEX)的进程。当某个进程的名称与给定的正则表达式匹配时,会在其 `oom_score` 中减少 300 分,这使得该进程不太可能被选择进行杀死。

earlyoom 退出状态码

  • 0: 成功执行。
  • 1: 其他错误 – 检查错误消息以获取详细信息。
  • 2: 开关冲突 – 使用了不兼容的选项。
  • 4: 无法切换到 /proc 目录。
  • 5: 无法打开 /proc 目录。
  • 7: 无法打开 /proc/sysrq-trigger
  • 13: 未知选项 – 传递了无效的选项。
  • 14: 其他选项的参数错误。
  • 15: 内存阈值参数错误。
  • 16: 交换空间阈值参数错误。
  • 102: 无法打开 /proc/meminfo 文件。
  • 103: 无法读取 /proc/meminfo 文件。
  • 104: /proc/meminfo 文件中找不到特定条目。
  • 105: 解析 /proc/meminfo 文件内容时转换数字失败。

配置文件

如果你将 earlyoom 作为系统服务运行,可以通过 /etc/default/earlyoom 文件调整其配置。例如:

EARLYOOM_ARGS="-m 5 -r 60 --avoid '(^|/)(init|Xorg|ssh)$' --prefer '(^|/)(java|chromium)$'"

修改配置文件后,重新启动服务以应用更改:

systemctl restart earlyoom

测试 earlyoom

可以通过制造内存泄漏(切记不要在生产环境测试)来测试 earlyoom 的功能:

tail /dev/zero

查看日志

如果 EarlyOOM 作为 systemd 服务运行,可以通过以下命令查看最近的日志:

systemctl status earlyoom

或者查看详细日志:

sudo journalctl -u earlyoom -g "(sending|killing)" --since today

示例输出:

8月 05 00:53:43 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1389844 uid 0 "Isolated Web Co": badn>
8月 05 02:25:59 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1419894 uid 0 "Isolated Web Co": badn>
8月 05 03:52:30 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1448534 uid 0 "Isolated Web Co": badn>
8月 05 05:18:24 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1476938 uid 0 "Isolated Web Co": badn>
8月 05 06:48:10 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1505535 uid 0 "Isolated Web Co": badn>
8月 05 08:13:19 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1535267 uid 0 "Isolated Web Co": badn>
8月 05 09:40:01 32.3.18.8 earlyoom[3147772]: sending SIGTERM to process 1563244 uid 0 "Isolated Web Co": badn>
8月 05 10:32:45 32.3.18.8 earlyoom[1609353]: Will avoid killing process names that match regex '(init|sshd|su>
8月 05 10:32:45 32.3.18.8 earlyoom[1609353]: sending SIGTERM when mem <= 10.00% and swap <= 10.00%,

启用通知

从 1.6 版本开始,earlyoom 可以通过 d-bus 发送通知。需要启用 -n 选项,并运行 systembus-notify

此外,earlyoom 可以在每次终止进程后执行脚本,使用 EARLYOOM_PIDEARLYOOM_UIDEARLYOOM_NAME 环境变量提供进程信息。使用 -N /path/to/script 启用此功能。

发送通知到企业微信示例脚本:

#!/bin/bash

# 企业微信 webhook 地址
WEBHOOK_URL='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx'

# 生成通知内容
CONTENT="进程被EarlyOOM杀死\n\n"
CONTENT+="PID: $EARLYOOM_PID\n"
CONTENT+="进程名: $EARLYOOM_NAME\n"
CONTENT+="命令行: $EARLYOOM_CMDLINE\n"
CONTENT+="UID: $EARLYOOM_UID"

# 发送通知
curl -X POST "$WEBHOOK_URL" \
     -H "Content-Type: application/json" \
     -d "{
           \"msgtype\": \"text\",
           \"text\": {
               \"content\": \"$CONTENT\"
           }
     }"

结论

earlyoom 是一个简单而有效的工具,可以帮助 Linux 用户避免由于内存不足而导致的系统无响应问题。通过提前检测和终止高内存占用的进程,earlyoom 保持系统的稳定性和响应速度。如果你经常遇到系统内存不足的问题,推荐试试 earlyoom

References

#GNU_Linux #Debian #earlyoom

虚拟磁盘扩容后扩展 LVM vg

新空间分区

在使用 fdisk 工具将新增的磁盘空间分配到 LVM PV 上时,您需要按照以下步骤操作:

  1. 运行 fdisk -l 命令查看系统中当前可用的磁盘和分区信息。
  2. 运行 fdisk /dev/sdX 命令(其中 sdX 表示要调整大小的磁盘),以编辑该磁盘的分区表。
  3. 使用命令 p 查看磁盘分区表,确定要修改的分区号码。
  4. 使用命令 n 创建一个新的分区。按照提示输入分区类型、起始和结束位置等信息即可。请注意,新分区的起始位置必须与现有的 LVM PV 的终止位置相同。
  5. 使用命令 t 更改新分区的类型。选择类型 8e,以便将其设置为 LVM 分区类型。
  6. 使用命令 w 将新的分区表写入磁盘,并退出 fdisk 工具。
  7. 运行 pvresize /dev/sdXn 命令(其中 sdXn 表示新创建的 LVM PV 分区设备文件名)以将新分区添加到现有的 LVM 物理卷中。

完成以上步骤后,您可以使用 lvextend 命令扩展逻辑卷的大小,并使用 resize2fs 命令调整文件系统大小以反映新的逻辑卷大小。

扩展 vg

# 查看 vg
vgs
# 将物理卷添加到存储池
vgextend <vg name> /dev/sdd

创建 lv

# 类似这样创建 lv
lvcreate -L 10G -n lv_var_log vg0
mkfs.xfs /dev/mapper/vg0-lv_var_log

Arch Linux 高分屏缩放方案

linux 下高分屏适配当前在 macos/windows/Linux 中的排位尚属末尾,采用整数倍缩放效果尚好(例如 100%、200%),但是若是使用非整数倍缩放(125%、175%)常常会体会到窗口模糊。

这一问题当前没有比较好的解决方案,但是可以使用字体缩放的方式绕过,效果尚可。

如果你在使用 GNOME 桌面,可以通过安装 gnome tweaks 软件来调整字体缩放率:

这样就可以在高分屏下获得一个比较好的效果,手边这台 MacBook air 中的一台采用 GNOME 的 arch linux 虚拟机,使用全分辨率的情况下,整个显示效果还算可以接受:

Reference

PVE 批量创建、配置虚拟机 IP、网卡、启停及销毁

# 从模版批量派生,100 虚拟机仅需2 min
for i in {001..128}; do qm clone 336 11$i --name stl-ceph-bare-node$i --pool stl-ceph-cluster2; done

# 使用 cloud-init 批量配置 ip,需在模版提前装好 cloud-init 软件包并添加设备
for i in {1..128}; do num=`echo $i | awk '{printf("%03d",$0)}'`; qm set 10${num}  --ipconfig1 ip=10.24.88.$i/24,gw=10.24.88.254; done

# 批量断开网络接口
for i in {002..128}; do qm set 10$i --net0 virtio,link_down=1; done

# 批量开机
for i in {001..128}; do qm start 10$i; done

# 批量销毁
for i in {001..111}; do qm destroy 10$i; done