问题
I'm looking at an Rails app that would require establishing a remote SSH session (SSH Tunnel?) from Heroku to a remote Mysql database as part of an background, ActiveRecord session. The goal would be to migrate data at different times via this channel into the app. Connecting to the remote mysql database over the web would not be an option.
Few questions:
- Does Heroku permit SSHing off their Dyno?
- What would be the downsides of doing so?
- Would I have to be concerned about SSH session persistence (job could take 1 hour)?
- Lastly, how can I configure Rails and Heroku to enable remote connections for a database.yml mysql endpoint?
回答1:
Well, I'm now able to answer my own question after digging around for a few days. In short, yes.
class LegacyAccount < ActiveRecord::Base
self.table_name = "account"
def self.test_connection
# net-ssh-gateway gem
gateway = Net::SSH::Gateway.new("ip_address","ssh_user_name",
password: "password",
port: "port if different from 22",
verbose: :debug
)
port = gateway.open('127.0.0.1', 3306, 3307)
establish_connection :legacy_production
result = LegacyAccount.first
puts "Record: #{result.to_yaml}"
gateway.close(port)
gateway.shutdown!
result
end
end
and in your database.yml:
legacy_production:
adapter: "mysql2"
host: "127.0.0.1"
username: "root"
password: "password"
database: "legacydb"
port: 3307
secure_auth: false
回答2:
I tried @user1322092's approach and got into trouble when multiple clients tried to access the db, since each one would attempt to open a connection (all but the first would get an error).
I created a monstrous solution involving spawning an SSH process on the dyno and using that for all communications. Gory details are in https://stackoverflow.com/a/27361295/558639. Aside from being really kludgy, it incurs a delay every time a process starts up, whether or not you're accessing the remote database.
update
So here's a better approach that appears to work well: just trap (and ignore) Errno::EADDRINUSE errors. Here's the code:
require 'net/ssh/gateway'
class Mole
TUNNEL_HOST = <redacted>
TUNNEL_USER = <redacted>
AWS_HOST = <redacted>
TUNNEL_PORT_NUMBER = 3307
attr_reader :tunnel_gateway, :tunnel_port
# Call this to open an SSH tunnel from the current machine. If the
# the tunnel was already opened (e.g. by another process) , you'll
# get a warning message
def open_tunnel
@tunnel_gateway = Net::SSH::Gateway.new(TUNNEL_HOST, TUNNEL_USER)
begin
@tunnel_port = @tunnel_gateway.open(AWS_HOST, 3306, TUNNEL_PORT_NUMBER)
rescue Errno::EADDRINUSE => e
$stderr.puts("Warning: #{e.class}: #{e.message}")
end
end
# You won't normally call this, because the tunnel is a system wide
# resource; you don't know when other processes want to release it.
def close_tunnel
r = @tunnel_gateway.close(@tunnel_port) if @tunnel_gateway
@tunnel_gateway.shutdown!
r
end
end
来源:https://stackoverflow.com/questions/26457941/ssh-from-heroku-into-remote-server-with-mysql-db