作者:任仲禹,爱可生数据库工程师,擅长故障分析和性能优化。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 1200 字,预计阅读需要 4 分钟。
1背景
某客户通过 OAT 初始化服务器时,走到 precheck 步骤时,报错如下:
ERROR - check current session hard limit of open_files (ulimit -H -n): 4096 != 655350 ... EXPECT 655350 ... FAIL
错误大意:OAT 需要服务器的 ulimit -H -n
命令返回值是 655350,实际得到的是 4096。
具体如下图,虚拟机磁盘和内存规格过低导致的检查报错(信息可忽略)。
2排查过程
① 检查当前服务器的 ulimit 值
使用 SSH 登录到目标服务器检查。
结果:【不符合预期】
使用 su
切换到服务器 admin 用户检查。
结果:【符合预期】
这里就有问题,OAT 在 prepare 之前,已经有步骤对服务器的内核参数完成了调整,如下图所示。
这里的 oceanbase_limits.conf
是 OAT 生成的,属于全局配置生效。
理论上不应该出现 SSH 到服务器与 SU 到服务器查出 ulimit 结果不一致的情况。
② 检查 OAT 对于 ulimit 的检查机制
OAT 检查该步骤是运行的程序是 init_server_with_tag.py
。
[2024-09-27T16:34:52.775+0800] INFO - Running: ['airflow', 'tasks', 'run', 'init_server_with_tag', 'precheck', 'manual__2024-09-27T08:34:21.675314+00:00', '--job-id', '39950', '--raw', '--subdir', 'DAGS_FOLDER/init_server_with_tag.py', '--cfg-path', '/tmp/tmp5ni__moh']
登录到 OAT 容器,查看程序最终的调用如下。
# 脚本 task_engine/dags/init_server_with_tag.py
def precheck():
ctx = get_current_context()
common.server_precheck(ctx, logger=logger)
# 脚本 task_engine/plugins/common.py
def server_precheck(ctx, logger):
init_tag = ctx['params']['init_tag']
role = _get_server_role(init_tag)
envs = _get_custom_user_env(ctx['params'])
with ServerRemoteExecute(server_id=ctx['params']['server_id']) as client:
precheck_sh = SHELL_PATH / 'precheck.sh'
ret_code, _ = client.execute_script(
precheck_sh, args=('-m', role), control_master=False, logger=logger,
env={'LC_ALL': 'en_US.UTF-8', 'OB_IP': client.server['ip'], **envs}
)
if ret_code != 0:
raise RuntimeError('server precheck failed, please see the summary info above for details')
# 脚本 task_engine/shells/precheck.sh
check_limit() {
limit_type_list=(-H/hard -S/soft)
for limit in "${EXPECT_LIMITS[@]}"
do
limit_option=$(echo $limit | awk -F'/' '{print $1}')
expect_limit=$(echo $limit | awk -F'/' '{print $2}')
limit_description=$(echo $limit | awk -F'/' '{print $3}')
limit_item=$(echo $limit | awk -F'/' '{print $4}')
for limit_type in "${limit_type_list[@]}"
do
limit_type_option=$(echo $limit_type | awk -F'/' '{print $1}')
limit_type_description=$(echo $limit_type | awk -F'/' '{print $2}')
get_limit_cmd="ulimit $limit_type_option $limit_option"
# check new session
current_limit=$(runuser - "$EXPECT_USER" -c "$get_limit_cmd")
if ! compare_ulimit "$current_limit" "$expect_limit"; then
echo_fail "check permanent $limit_type_description limit of $limit_description ($get_limit_cmd): $current_limit != $expect_limit ... EXPECT $expect_limit"
echo_hint "modify /etc/security/limits.d/oceanbase_limits.conf\n echo \"* $limit_type_description $limit_item $expect_limit\" >> /etc/security/limits.d/oceanbase_limits.conf"
else
echo_pass "check $limit_type_description limit of new session $limit_description ($get_limit_cmd): $current_limit"
fi
# check current session
current_limit=$($get_limit_cmd)
if ! compare_ulimit "$current_limit" "$expect_limit"; then
echo_fail "check current session $limit_type_description limit of $limit_description ($get_limit_cmd): $current_limit != $expect_limit ... EXPECT $expect_limit"
echo_hint "excute: ulimit $limit_type_option $limit_option $expect_limit"
else
echo_pass "check $limit_type_description limit of $limit_description ($get_limit_cmd): $current_limit"
fi
done
done
}
这里 OAT 获取 ulimit 结果不符合预期的原因已清楚:
通过检查脚本,OAT 是通过 ServerRemoteExecute
模块(使用 SSH 方式)去目标服务器上执行 prepare.sh
脚本做 *ulimit 检查(与预期的 ulimit 值进行对比)。
但是,通过 SSH 连到服务器上执行 ulimit 命令的执行结果不符合预期,甩出报错。
③ 疑问:为何 SSH 连接时 ulimit 值不正确?
先使用 strace
命令查看下 su
时的系统调用。
strace -o /root/l1 su - admin
SU 的结果
SU 时,将调用 /usr/lib64/security/pam_limits.so
文件,继而获取到如下两个文件的 ulimit 配置:/etc/security/limits.conf
和 /etc/security/limits.d/oceanbase_limits.conf
。
所以,su - admin
命令执行的结果【符合预期】。
通过系统日志 /var/log/secure
可以看到,SU 操作加载了 pam 插件。
Oct 14 17:44:44 10-186-58-85 su: pam_unix(su-l:session): session opened for user admin by root(uid=0)
从结果反推下,为什么 SSH 不去读取 /usr/lib64/security/pam_limits.so
文件?
根据关键字,猜测跟 SSH 的 PAM 插件有关,继续检查 ssh_config
配置文件如下。
UsePAM no
目标服务器的 PAM 确实是关闭的。 通过系统日志 /var/log/secure
可以看到,SSH 操作没有加载 pam 插件。
Oct 14 17:50:40 10-186-58-85 sshd[25117]: Accepted publickey for root from 10.186.58.85 port 19118 ssh2: RSA SHA256:+TtbeuvInWm90vrJG7cHHm2G2a2FULFE0Uq+imx2m30
引申:PAM 的作用?
这里 ChatGPT 了一下,解释非常清楚如下图,总结一句就是:
关闭 PAM,用户 SSH 到服务器时,将不会读取 *limits.conf
的配置,继而导致获取到默认配置而使 OAT 报错。
3解决方法
将配置文件 /etc/ssh/sshd_config
中的 UsePAM 修改为 yes ,重启 SSHD 服务即可。
启用 PAM 插件后,再次 SSH 可以看到系统日志如下多了加载 pam_unix(sshd:session)
的操作。
Oct 14 17:51:56 10-186-58-85 sshd[26147]: Accepted publickey for root from 10.186.58.85 port 19480 ssh2: RSA SHA256:+TtbeuvInWm90vrJG7cHHm2G2a2FULFE0Uq+imx2m30
Oct 14 17:51:56 10-186-58-85 sshd[26147]: pam_unix(sshd:session): session opened for user root by (uid=0)
4结论
OAT 初始化服务器 precheck 报 ulimit 值不符合预期错误是由于目标服务器 sshd_config 不合理导致。 sshd_config 中 禁用了 PAM 插件,使得 OAT 读取不到目标服务器优化后的 ULIMIT 配置文件。
本文关键字:#OceanBase# #OAT# #ulimit# #PAM#
✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource
🔗 商业支持:https://www.actionsky.com/sqle
扫描下方的微信扫描小程序码,进行在线咨询预约:
此外,您也可以直接联系我们的商业支持团队获取更多信息,联系方式如下:
400-820-6580 / 13916131869 / 18930110869