装Oracle 19c RAC翻车翻在ssh互信上,说出去我自己都不太信。环境是Oracle Linux 8.3,双节点,19c的GI和DB一起装。前期检查全过了,hosts、防火墙、时钟、依赖包都没问题,ssh互信也严格按官方文档配的,ssh rac1 date、ssh rac2 date都是秒回。结果跑到OUI的“SSH Connectivity”那一步,直接弹了个INS-06006,说Passwordless SSH connectivity is not established between nodes。我当场血压就上来了:明明能免密登录,凭什么说没建立?
前后折腾了两个多小时,最后发现根子不在ssh,而在scp。准确地说,是 RHEL 8.3 / Oracle Linux 8.3 自带的 OpenSSH 8.0 改变了scp的底层协议,Oracle 19c的OUI还没跟上这个变化。解决办法反而简单粗暴:临时把scp替换成一个强制走老协议(legacy SCP)的包装脚本,装完再换回来。这篇就把整个排查过程和这个“骚操作”的完整步骤写出来,给还在坑里的兄弟一个参考,省得你们再走一遍弯路。
1. 问题现场:INS-06006报错与第一次判断失误
1.1 环境与报错现场
我这边环境是这样的:
| 节点 | IP | 操作系统 | 软件版本 |
|---|---|---|---|
| rac1 | 192.168.10.11 | Oracle Linux 8.3 | Oracle 19.16 RAC |
| rac2 | 192.168.10.12 | Oracle Linux 8.3 | Oracle 19.16 RAC |
Grid Infrastructure的安装包已经解压,runInstaller也正常启动了,前面几步都很顺。到“Cluster Configuration”这一步,选完节点,填了oracle用户密码,勾上“Configure SSH Connectivity”之后,界面卡了大概十几秒,然后弹出一个红色错误框:
INS-06006: Passwordless SSH connectivity is not established between the following node(s): rac1, rac2
这个报错的意思很直白:你配置的免密ssh有问题。但我明明在安装前用oracle用户做过互信测试,两边都能免密登录,而且我用ssh -o BatchMode=yes这种严格模式跑过命令,也没问题。所以看到报错的第一反应是怀疑OUI抽风了,点了Retry,结果还是一样。
1.2 日志里最像线索的那句话
OUI把详细的日志写在$ORACLE_HOME/cfgtoollogs/oui/和/tmp/installActions*.log下。我翻了最新的一份日志,在里面抓到了关键信息:
INFO: scp -r -q /tmp/ssh_connectivity_check_xxx oracle@rac2:/tmp/ INFO: scp: Connection closed虽然日志里没有更细的stderr,但“Connection closed”这个现象很典型。更微妙的是,我在rac1本地手动执行同样的scp命令,居然也是失败的——但手动执行的时候它报的是别的错,比如:
scp: Connection closed by remote host而反过来,用ssh直接登录、执行命令、复制文件内容,都完全正常。这就说明问题不是出在ssh隧道本身,而是OUI在检查互信时用的那条scp命令,跟系统环境不兼容。
后来我还试过在rac1上把文件用scp推到rac2,报错一模一样。但用sftp登录又能正常连接。当时我就意识到,这不是权限或者防火墙的问题,而是scp这个命令本身在环境里坏了——准确点说,是scp的行为和Oracle OUI预期的不一样。
2. 排查路径:互信没问题的前提下,问题还能出在哪
2.1 常规三板斧:权限、解析、防火墙
遇到INS-06006,相信大多数DBA的第一反应和我一样:先检查互信配置本身。因为网上随便一搜,这个报错百分之八十的答案都是这几个方向:
- authorized_keys权限不对:
.ssh目录要700,authorized_keys文件要600,owner必须是oracle用户。我用ls -l确认了,没问题。 - /etc/hosts解析错乱:两个节点的公私IP、hostname对应关系必须写对,不能出现别名解析到其他地址。我检查了,也没问题。
- 防火墙/SELinux拦截:Oracle RAC要求的端口,包括22端口,都要放通。SELinux如果没关,至少要放行ssh相关服务。我用
setenforce 0临时关了SELinux,防火墙也确认过22端口是通的,问题依旧。 - ssh严格检查HostKey导致交互确认:我检查了
.ssh/known_hosts和~/.ssh/config,都设置好了,ssh -o StrictHostKeyChecking=no也试过,还是没用。
这板斧抡完,基本可以排除“配错互信”这个最常见原因了。但我依然没有把方向和scp联系起来,直到我抱着试试看的心态,在rac1上手动执行了一下OUI日志里的那条scp命令,才发现真正的异常在scp本身。
2.2 真凶现身:OpenSSH 8.0的scp协议切换
事情的转机是我无意中执行了scp -V和strings /usr/bin/scp | grep -i openssh,然后发现系统里的OpenSSH版本是8.0p1。当时我对8.0的印象还停留在“修了某个漏洞”的层面,完全没意识到scp的默认行为已经被改了。
这里就要说一个新旧对比的知识点了。在OpenSSH 8.0之前,scp默认走的是传统的SCP协议(基于RCP,通过ssh通道执行远端scp来完成传输)。但从8.0开始,OpenSSH官方把scp的默认实现切换成了SFTP协议。这个切换本意是让scp更安全、更健壮,因为传统SCP协议在处理文件名特殊字符、权限保持等方面有不少问题。
可问题就出在“默认”这两个字上。老版本的scp命令被大量脚本、工具依赖,它们都按传统SCP协议的行为去调用scp,根本不认识也不需要SFTP这套新机制。Oracle 19c的OUI在检测SSH互信时,用的就是老式调用方式,它在本地生成一个临时文件,然后调用scp把这个文件推到远程节点的/tmp目录下,再通过ssh取回校验。整个过程对scp的行为预期是“传统SCP协议”,结果OpenSSH 8.0给它的却是SFTP协议实现,两边对不上,于是scp在传输还没开始时就被远程端或本地端判定为连接异常,直接关闭。
2.3 为什么OUI会栽在scp上
既然OpenSSH 8.0的scp默认走SFTP,那为什么我们自己手动敲scp有时又没问题呢?其实不是“没问题”,而是SFTP模式下传输普通文件也没问题,但如果调用方传入了适合传统SCP协议、但不符合SFTP预期行为的参数组合(比如OUI日志里的-r -q加上-B批量模式),scp在解析参数、初始化连接、切换目录等环节就可能出现不兼容,表现就是各种“Connection closed”或者状态判断超时。
Oracle的OUI在做互信检查时,逻辑非常朴素:能通过scp把文件推到另一台机器并读回来,才判定互信是通的。它并不关心底层用什么协议实现,只要scp这条命令能成功执行。而在OpenSSH 8.0的环境里,scp默认走SFTP,OUI调用时又没有也不可能去加-O参数,于是两边就像两个说不同方言的人,互相听不懂,握手失败。
知道根因之后,上网一搜,果然有MOS文档和相关讨论提到:Oracle 19c RAC安装包在较新的Linux发行版上偶发INS-06006,原因就是OpenSSH 8.0之后scp默认协议变了。官方建议要么升级OpenSSH到8.7以上(有些版本修复了兼容性),要么给Oracle相关的OPatch补丁。可我在现场哪有时间慢慢下载补丁、读README、打补丁?于是就产生了这个临时的“骚操作”思路。
3. 临时替换scp的骚操作:两行脚本绕过互信检测
3.1 核心思路:用wrapper强行走老协议
思路其实很简单:既然OUI调用scp的时候不会主动加-O参数,而OpenSSH 8.0的scp支持-O参数来强制使用传统SCP协议,那我就在OUI能看到的scp命令前面套一层“包装”——用一个脚本替换掉系统里的scp,脚本内部自动给原始scp命令追加-O参数,再透传OUI传来的其他参数。
这句话拆开就是:
- 保留原始scp:把
/usr/bin/scp复制改名为/usr/bin/scp.bak。 - 创建包装脚本:新建一个文本文件
/usr/bin/scp,内容只有一行核心逻辑:exec /usr/bin/scp.bak -O "$@"。 - 让系统PATH生效:因为/usr/bin在系统PATH里,所有调用
scp命令的地方都会自动落到这个包装脚本上,包括Oracle OUI。
这样做的好处是不需要重启、不需要改Oracle程序、不需要动sshd配置,整个过程五分钟搞定。缺点是它改了系统命令,虽然我们做了备份,但装完RAC之后一定得记得恢复,否则后续系统里其他程序调用scp的行为也会被改变。
3.2 具体实施步骤
我是在rac1和rac2两个节点上同时操作的。因为OUI检查互信是双向的,rac1会往rac2推文件,rac2也可能往rac1推文件,所以两边都换成包装脚本才稳妥。具体命令如下:
# 在rac1和rac2上分别执行 # 第一步:备份原始scp sudo cp /usr/bin/scp /usr/bin/scp.bak # 第二步:生成包装脚本,覆盖原scp sudo bash -c 'cat > /usr/bin/scp << "EOF" #!/bin/bash # Temporary scp wrapper for Oracle 19c RAC Install # Force legacy SCP protocol to workaround INS-06006 exec /usr/bin/scp.bak -O "$@" EOF' # 第三步:添加执行权限 sudo chmod 755 /usr/bin/scp # 第四步:验证包装脚本生效 which scp # 输出应该是 /usr/bin/scp scp -O 2>&1 | head -5 # 正常会显示usage信息,且不会报 unknown option -- O这里有几个细节值得说明:
- 为什么包装脚本里调用的是scp.bak而不是scp:如果包装脚本里写
exec /usr/bin/scp -O "$@",那就会无限递归调用自己,直接把系统调死。所以必须先备份成.bak,包装脚本指向.bak。 - 为什么用
exec:exec会直接在当前shell进程里替换成目标命令,不额外fork子进程,这样脚本本身占用的资源最小,也不会影响信号转发,更接近原生scp的行为。 - 为什么不在/usr/local/bin下建wrapper:理论上自定义命令放/usr/local/bin更规范,但Oracle的OUI在调用scp时,使用的是
/usr/bin/scp这个绝对路径(从日志可以看出来),它不一定受PATH影响。所以最保险的做法是直接替换/usr/bin/scp。如果你确定OUI走PATH,那在/usr/local/bin放wrapper也行,但为了稳妥,我选择替换/usr/bin/scp。
3.3 验证生效与OUI重新检查
替换完包装脚本,我先手动验证了一下scp是否恢复了传统协议下的正常工作:
# 在rac1上执行 ssh rac1 "echo test > /tmp/from_rac1.txt" scp /tmp/from_rac1.txt rac2:/tmp/from_rac1.txt # 如果能正常退出,且rac2上出现过这个文件,说明scp走传统协议成功了 # 再反向测一下 ssh rac2 "echo test > /tmp/from_rac2.txt" scp /tmp/from_rac2.txt rac1:/tmp/from_rac2.txt这两条命令通过后,我又模拟了OUI的检查方式,在rac1上远程执行scp推到rac2:
ssh rac1 "scp /tmp/from_rac1.txt rac2:/tmp/from_rac1_via_ssh.txt"这步也成功了。这说明包装脚本确实让scp回归到了传统协议,而且远程调用scp也没问题(因为OUI在某些场景下会通过ssh在远程节点上再执行scp)。
随后我回到OUI界面,点击“Retry”,这一次SSH Connectivity检查顺利通过,进入了下一步。
注意:整个过程不要关闭OUI,也不要重启runInstaller。只要在OUI保持在Error对话框的状态下改好环境,点Retry重新检查即可,不需要重新启动安装流程。
4. 装完记得收尾:恢复原始scp与后续检查
4.1 恢复操作与验证
包装脚本只是权宜之计,绝对不能留在生产环境里长期使用。原因是-O参数本质上是在强制使用OpenSSH已经逐步淘汰的传统SCP协议,未来某个版本很可能会被彻底移除。而且我们手动替换了rpm管理的系统命令文件,虽然不影响rpm数据库记录,但一旦系统升级openssh-clients包,rpm可能会拿新文件覆盖我们的包装脚本,也可能因为校验和不对而拒绝更新,这在运维上是隐患。
所以Grid Infrastructure和Database安装完成后,第一时间把scp恢复原样:
# 在rac1和rac2上分别执行 # 移除包装脚本 sudo rm -f /usr/bin/scp # 恢复原始scp sudo mv /usr/bin/scp.bak /usr/bin/scp # 验证恢复成功 ls -l /usr/bin/scp # 文件应该是原来的二进制,而不是文本脚本 # 可以再用file命令确认 file /usr/bin/scp # 输出类似 ELF 64-bit ... ,而不是 ASCII text # 最后用rpm校验一下,看是否和rpm包里一致 rpm -V openssh-clientsrpm -V如果不输出任何内容,说明系统里的文件与rpm包安装的一致,这就代表scp已经被干净地恢复了。
4.2 恢复后还需要注意什么
安装完RAC之后,除了恢复scp,还有几个地方需要复查,因为前面排查问题时可能动过:
- SELinux状态:如果之前在排查INS-06006时临时用
setenforce 0关闭了SELinux,恢复scp之后要记得重新开启,或者至少按Oracle的要求配置好相关布尔值。我这边是确认了问题与SELinux无关后,立即恢复成Enforcing模式。 - 防火墙规则:如果为了排查把防火墙规则放得很宽或者临时停止,需要按RAC需求精确放行,避免留下安全漏洞。
- oracle用户的.bashrc/profile:如果在排查过程中往里面加了奇怪的alias或者SSH_AUTH_SOCK相关变量,记得清理掉,避免影响后面crsctl等命令的自动启动行为。
- known_hosts记录:有些DBA在排查时会清空known_hosts,如果后来在克隆节点或者扩展集群时发现host key校验失败,别慌,重新执行一次
ssh-keyscan更新即可。
4.3 什么时候可以直接打补丁而不是临时绕过
我知道有人会觉得临时替换scp不够“正规”,会问:为什么不直接打Oracle官方的补丁?我的回答是:看场景。
如果你的环境允许停机、有时间下载补丁、也有测试环境验证,那当然可以走正规路线,比如升级OpenSSH到修复版本,或者给Oracle GI打对应的PSU/OCW补丁,让OUI使用新的scp调用方式。但如果你是在生产环境窗口里安装RAC,窗口时间紧,前面还耗了几个小时排查,最怕的就是安装卡住。这时候临时替换scp绕过去,让安装流程先走通,是最务实的救场手段。
需要强调的是:临时替换只解决安装阶段的互信检测问题,不解决实际运行时的高可用性问题。RAC安装完成后,集群本身的节点间通信走的是Oracle私网协议,不再依赖系统scp命令。所以这个workaround对RAC运行没有影响,这也是我敢在生产环境用它的原因。
5. INS-06006常见原因排查速查表
5.1 常见原因优先级排序
经过这次经历,我整理了一份INS-06006的排查优先级清单,以后再遇到可以直接按顺序查:
| 优先级 | 排查点 | 说明 |
|---|---|---|
| 1 | authorized_keys权限 | .ssh目录700,authorized_keys文件600,owner为oracle用户 |
| 2 | /etc/hosts解析 | 公私IP、hostname一一对应,不能有多余别名指向错误地址 |
| 3 | sshd配置 | PermitRootLogin、PasswordAuthentication、UsePAM等参数不能太激进 |
| 4 | 防火墙/SELinux | 22端口及各集群端口是否放行,SELinux是否阻止ssh传输 |
| 5 | OpenSSH版本与scp兼容性 | 8.0及以上版本scp默认走SFTP,OUI可能不兼容 |
| 6 | known_hosts校验 | 两台节点有没有互换过host key,或known_hosts里有旧记录 |
| 7 | 时钟同步 | 节点间时钟偏差过大导致Kerberos或证书校验失败 |
前四项是我在遇到这次问题之前就会优先查的,后三项尤其第5项,是这次踩坑后新增的经验。
5.2 对应的排查命令与对策
针对上表,我把自己实际用过的排查命令也列出来:
# 1. 查互信权限 ls -ld ~oracle/.ssh ~oracle/.ssh/authorized_keys chmod 700 ~oracle/.ssh chmod 600 ~oracle/.ssh/authorized_keys # 2. 查hosts cat /etc/hosts getent hosts rac1 getent hosts rac2 # 3. 查sshd配置 sshd -T | grep -E 'permitrootlogin|passwordauthentication|usepam|port' # 4. 查防火墙和SELinux systemctl status firewalld getenforce sudo semanage port -l | grep ssh # 5. 查scp实际调用是否异常 scp /tmp/testfile rac2:/tmp/testfile # 如果报 Connection closed,考虑OpenSSH 8.0兼容性 # 6. 查看OUI日志定位具体失败命令 grep -i "scp\|ssh\|INS-06006" /tmp/installActions*.log | tail -20 # 7. 查看节点间时钟偏差 chronyc tracking | grep -E "System time|Stratum"其中第5条最关键:如果手动执行scp都失败,而ssh登录正常,那基本就是scp协议兼容性问题。这时候不要犹豫,直接按上面第三节的包装脚本方案处理,或者去升级OpenSSH/打Oracle补丁。
我在实际排查中还发现一个小技巧:如果不想替换/usr/bin/scp,也可以在不影响全局的前提下,临时把scp命令路径指向一个自定义脚本,方法是修改oracle用户的环境变量PATH,在登录shell里加一行PATH=/tmp/wrapper:$PATH,这样OUI在某些版本下会优先调用/tmp/wrapper下的scp。但如果OUI像这次一样使用绝对路径/usr/bin/scp,环境变量PATH这招就不灵了,还是老老实实改/usr/bin/scp更可靠。
另外,排查过程中如果判断出是SFTP协议的问题,还可以试一下调整sshd配置,在/etc/ssh/sshd_config里确保Subsystem sftp指向正确的sftp-server路径。因为OpenSSH 8.0的scp默认走SFTP时,需要sshd能正常启动SFTP子系统。如果这个子系统路径被别人改过或者缺失,scp的SFTP模式就会连接关闭。这虽然不是本次问题的根因,但也是排查时要排除的一个点。
最后再分享一个个人习惯:做这种临时替换系统命令的操作前,一定先把原始文件备份到一个安全的地方,并且记下操作时间、修改内容、预计恢复时间。我一般会在/root下留一份备份,再顺手把操作命令写成一个简单的README放在/tmp下,防止几天后自己都忘了改过什么。安装窗口结束后,第一时间按README恢复,恢复后再跑一遍rpm -V确认系统文件干净。运维这行,小心驶得万年船。