关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

怎样避免云服务器mysqld被oom-killer杀死

发布时间:2023-04-01 14:51:02
1491984010112764.jpg

前几天我的小小的 WordPress 服务器被攻击了,被五六台机器从不同的 IP 发起 xmlrpc 攻击,大约每秒 80 次请求的样子,虽然不算猛烈,但我的虚拟机本身太小了,单 CPU 仅 1GB 内存,于是频频出现内存紧张的情况,oom-killer 会自动选择合适的进程牺牲掉,但它怎么就那么不开眼,每次都选中了最重要的 mysqld 进程。解决的思路很简单,就是减小 mysqld 进程的 oom_score_adj 值,因为 oom-killer 通过比较每个进程的 oom_score 来挑选要出局的进程,数值越大就越容易被选中,而手工调整 oom_score 是通过 oom_score_adj 来实现的,命令如下:

  1. $ echo "-100" > /proc/<pid>/oom_score_adj

但是每次 reboot 或者 mysql 重启都需要重新设置一遍实在麻烦,怎样才能实现自动化呢?我的做法如下,虽然道理很简单,但实现过程中有好几个坑,包含了几个有用的知识点,所以有点共享价值。

要点一:

如何修改 systemd 的服务脚本,首先,我想在启动 mysql 服务的时候就自动把 oom_score_adj 调整好,理论上应该通过修改启动脚本完成,问题是我用了 CentOS 7,systemd 的启动脚本与以前有很大的不同。还是让我们先找到它吧:

  1. # systemctl status mariadb
  2. ● mariadb.service - MariaDB database server
  3.    Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)

显然/usr/lib/systemd/system/mariadb.service 就是我们感兴趣的,可以直接修改,但不建议这么做,因为将来升级 mariadb 有可能会覆盖掉你的改动。更好的方式是这样的:创建一个新目录 “/etc/systemd/system/mariadb.service.d/”,把需要改动的内容放在该目录下的”.conf”文件里,文件名可以随便,但必须以”.conf”结尾。这个文件起的作用是对 systemd 的 service 文件做出补充,systemd 的 service 文件包括三个主要部分:[Unit], [Service]和[Install],其中[Service]段定义了服务启停的方法,[Service]段中以下三个字段是最常用的:
ExecStart 用于定义启动服务的命令,
ExecStartPre 是启动服务之前执行的命令,
ExecStartPost 是启动服务之后执行的命令。
我们修改 oom_score_adj 的任务应该放在 ExecStartPost 部分,需要注意的是:命令必须使用全路径,因为 systemd 不提供设置好的 PATH 环境变量。

  1. $ mkdir /etc/systemd/system/mariadb.service.d
  2. $ cd /etc/systemd/system/mariadb.service.d
  3. $
  4. $ cat > avoid_oom.conf
  5. # avoid oom-killer
  6. [Service]
  7. ExecStartPost=/bin/sudo /usr/local/bin/oom_mysql.sh

我们只增加了一行有实质内容,即让 mariadb.service 在启动之后调用自定义的脚本 oom_mysql.sh,我们在这个脚本中调整 mysqld 进程的 oom_score_adj 值,脚本如下 (因为 mariadb 会把 mysqld 的 PID 记录在/var/run/mariadb/mariadb.pid 之中,所以在脚本中我们寻找 mysqld 进程号便简单了):

  1. $ cat /usr/local/bin/oom_mysql.sh
  2. #!/bin/bash
  3.  
  4. MYSQL_PIDFILE="/var/run/mariadb/mariadb.pid"
  5.  
  6. pgrep mysql > /dev/null
  7. if [ ! $? ]; then
  8.         exit 1
  9. fi
  10.  
  11. if [ -f "$MYSQL_PIDFILE" ]; then
  12.         MYSQL_PID=`cat $MYSQL_PIDFILE`
  13.         echo "-100" > /proc/$MYSQL_PID/oom_score_adj
  14. else
  15.         exit 1
  16. fi
  17.  
  18. exit 0

要点二:

如何设置不用输入密码而且没有 tty 的 sudo

为什么我要通过 sudo 来执行以上脚本呢?
ExecStartPost=/bin/sudo /usr/local/bin/oom_mysql.sh
为什么不象下面这样直接执行呢?
ExecStartPost=/usr/local/bin/oom_mysql.sh

这是因为 mariadb.service 是以 mysql 用户的身份启动的,mysql 用户不具备写入/proc 的权限,而在 oom_mysql.sh 脚本中我们需要写入 /proc/oom_score_adj

  1. mysql:x:27:27:MariaDB Server:/var/lib/mysql:/sbin/nologin

为了使 mysql 用户能够写入/proc//oom_score_adj,我们利用了 sudo。修改 sudo 配置的命令是 visudo,如果不加 -f 参数,它默认修改配置文件/etc/sudoers,但直接改动/etc/sudoers 不太稳妥,万一改坏了什么地方就有麻烦,所以推荐的方法是:把你要增加的 sudoers 配置放在/etc/sudoers.d/ 目录下的文件中,文件名可以随意。

  1. $ visudo -f /etc/sudoers.d/4mysql
  2. Defaults:mysql !requiretty
  3. mysql  ALL=(ALL)       NOPASSWD: /usr/local/bin/oom_mysql.sh

我们把要增加的配置放进了 4mysql 文件里,内容只有两行,需要解释一下:因为 mysql 用户很特别,它是不能登录的,因为它的 shell 是/sbin/nologin,而且在自动启动 mariadb.service 的时候显然也不能依赖人工输入口令去执行 sudo,所以它的 sudoers 配置需要一种特殊的权限–即无需输入口令就可以执行 sudo 命令,这就是上述第二行 “mysql ALL=(ALL) NOPASSWD:” 的意思;第一行表示 mysql 用户执行 sudo 可以无需 tty(终端),因为 RHEL 和 CentOS 默认情况下 sudo 需要 tty 才能执行,否则会发生下列错误:

  1. sudo: sorry, you must have a tty to run sudo

至此,我们的工作就完成了,最后重启 mariadb.service 即可:

  1. $ systemctl daemon-reload
  2. $ systemctl restart mariadb

再检查 mysqld,发现它的 oom_score_adj 已经设置为-100 了,正是我们所期望的:

  1. $ cat /proc/10839/oom_score_adj
  2. -100



/template/Home/8a/PC/Static