ssh执行远程命令的坑
要做的事情
远程主机(your.host.com)上部署有docker,期望从本地开发机ssh到远程主机,在指定的容器中执行命令,基本命令如下:
/usr/bin/ssh -i /home/users/yangjinfeng02/.ssh/.id_rsa -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -t rd@your.host.com "sudo docker exec -u rd -it 0284c4594aecc7689de407e8d2aa9106edaf646b000e3c362ad95f6dde5c9f0b bash -c 'pwd'"
出现的问题
登入shell终端,手动执行上面的命令,一切ok。但是放到crontab中无法执行,重定向输出到文件,得到如下信息
Pseudo-terminal will not be allocated because stdin is not a terminal.
问题排查与解决
注意到ssh命令中的-t参数,是用来控制给ssh分配伪终端。查看ssh帮助
-T Disable pseudo-tty allocation.
-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g., when implementing
menu services. Multiple -t options force tty allocation, even if ssh has no local tty.
注意到上述问题出错信息,结合ssh -t参数的含义,
Multiple -t options force tty allocation, even if ssh has no local tty.
可看出问题是在于分配终端引起的。
使用 ssh -t -t 执行,发现crontab下运行正常,问题得以解决。
延伸1——不分配伪终端
既然是终端分配导致的,那么根据ssh -T参数的含义,是否可以disable伪终端的分配呢?可以进行试验,执行如下命令
/usr/bin/ssh -i /home/users/yangjinfeng02/.ssh/.id_rsa -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -T rd@your.host.com "sudo docker exec -u rd -it 0284c4594aecc7689de407e8d2aa9106edaf646b000e3c362ad95f6dde5c9f0b bash -c 'pwd'"
此时不管是命令行,还是crontab里,均无法运行。提示如下错误信息
sudo: sorry, you must have a tty to run sudo
出现该问题,是因为sudo命令必须在终端中执行。如果要突破该限制,需要修改/etc/sudoers,有两种选择
-
Replace Defaults requiretty by Defaults !requiretty in your /etc/sudoers. This will impact your global sudo configuration.
-
Alternatively, you can change this configuration at a per user, per group or per command basis
Defaults!/path/to/my/bin !requiretty
Defaults:myuser !requiretty
延伸2——登录shell和非登录shell的区别
直接在终端里执行shell命令,与crontab里执行,本质区别是二者环境变量的差异。
登录shell将查找4个不同的启动文件来处理其中的命令。 bash shell处理文件的顺序如下:
1:/etc/profile
2:/etc/profile.d等待配置文件
3:$HOME/.bash_profile 会加载$HOME/.bashrc和/etc/bashrc
4:$HOME/.bash_login
5:$HOME/.profile
非登入shell则按如下顺序加载配置文件
1:/etc/bashrc
2:~/.bashrc