5

I currently have a Ruby (Rails) application that needs to make a lot of short SSH connections. This works fine using the Ruby Net::SSH library, except that the app has to log in and negotiate keys every time I want to make a command, which is too slow.

Is there a way to enable Control Master with Ruby Net::SSH? In testing on the command line, this makes logins (after the first one) very fast, since the connection is already open (keys are negotiated etc.).

If there is no way to do this with Net::SSH, can anybody suggest an alternative library that could do it?

I imagine this must be a common requirement, so hopefully someone can help.

Thanks!

Harry
  • 4,660
  • 7
  • 37
  • 65

2 Answers2

5

Why not just keeping the connection open ? The ssh calls are dummy as I don't know the api but it serves its purpose:

def ssh_execute(user, command)
  Thread.current[:user_connections] ||= {}

  unless Thread.current[:user_connections][user.id]
    Thread.current[:user_connections][user.id] = Net::SSH.connect(...)
  end

  ssh = Thread.current[:user_connections][user.id]
  ssh.run_command(command)
end

You will get one ssh connection per thread or if your application is deployed with passenger each process will have one connection and will reuse it.

Is it what you want ?

Schmurfy
  • 1,715
  • 9
  • 17
  • This might be a solution - is there a way to keep the connection open so that I can come back to it? E.g. the user may open a connection to do something that requires SSH, then go and do something else for a while (so the thread will have completed), then come back to do another SSH command. As I understand it Control Master will essentially keep this connection authenticated, but will your solution allow for a persistent session like this? – Harry Jan 09 '12 at 21:11
  • I never really used ControlMaster so I have no idea how it works but by using a code like above the connection should be left open since threaded servers usually have a pool of threads and do not create new thread for every requests. If you need a user specific connection just use a hash instead of storing the connection directly as I did above. – Schmurfy Jan 10 '12 at 10:56
  • Thanks, this seems to work - you mentioned using a hash for a user specific connection (this is definitely a requirement, as each user will connect to their own server only) - would this be saved in a user hash (e.g. User.ssh_connection = Net::SSH.connect(...)) – Harry Jan 10 '12 at 13:46
  • I updated my code example to show what I meant by using a hash. – Schmurfy Jan 11 '12 at 10:32
  • did this solution work for you? my site needs to make an ssh connection on almost every page so control master would be incredible. – bumpkin Jul 12 '14 at 00:55
  • i tried and get "(undefined method "run_command" for #)" – bumpkin Jul 12 '14 at 01:17
  • @user2004922 can you put your code in a gist or similar so I can have a look at it ? – Schmurfy Jul 16 '14 at 14:28
2

You can specify exactly what kind of encryption protocol you want as to minimize the handshake, but well, yeah SSH can be a bit slow if you need to scale it up.

A really interesting take on this is http://saltstack.org/ it has implemented it's own "ssh" alternative on top of 0mq, making it super-fast to run parallel commands on multiple servers, apparently not having the same problems of ssh-related performance issues such as with chef etc. Maybe you could build upon salt do do this?

sunkencity
  • 3,482
  • 1
  • 22
  • 19
  • This looks like an interesting solution, but there doesn't seem to be much support for Ruby (looks like it's mostly python). I'm trying to run the commands from the controller of a Rails app, so i'd like it to be something that I can do directly in Ruby to keep things clean. Also, to clarify, the problem is not having a lot of servers, but running a lot of commands on the same server - hence why being able to use Control Master will solve the problem. – Harry Jan 09 '12 at 11:41