【问题标题】:SSH from Heroku into remote server with Mysql Db使用 Mysql Db 从 Heroku SSH 到远程服务器
【发布时间】:2014-12-14 23:31:47
【问题描述】:

我正在查看一个 Rails 应用程序,它需要建立从 Heroku 到远程 Mysql 数据库的远程 SSH 会话(SSH 隧道?)作为背景 ActiveRecord 会话的一部分。目标是通过此通道将不同时间的数据迁移到应用程序中。通过 Web 连接到远程 mysql 数据库不是一种选择。

几个问题:

  1. Heroku 是否允许通过 SSH 关闭他们的 Dyno?
  2. 会是什么 这样做的缺点?
  3. 我需要担心 SSH 会话持久性(工作可能需要 1 小时)?
  4. 最后,如何配置 Rails 和 Heroku 以启用远程连接 database.yml mysql 端点?

【问题讨论】:

  • 只需添加,我愿意在后台作业中创建 SSH 隧道。不确定这是否更容易。

标签: ruby-on-rails ruby-on-rails-4 heroku


【解决方案1】:

好吧,经过几天的研究,我现在可以回答我自己的问题了。简而言之,是的。

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

在你的 database.yml 中:

legacy_production:
  adapter: "mysql2"
  host: "127.0.0.1"
  username: "root"
  password: "password"
  database: "legacydb"
  port: 3307
  secure_auth: false

【讨论】:

  • 我认为这种方法失败的地方在于 gateway.open() 调用——如果另一个进程已经打开了端口,你会得到一个错误,并且 AFAIK 没有办法检测到 -先验的。我开始认为正确的方法是在这种情况下简单地捕获并忽略 EADDRINUSE 异常。更多讨论请参见stackoverflow.com/questions/27536729/…。
  • @fearless_fool,是的......这个解决方案(而是测试)并不是为了说明/支持多个进程(因此我添加了后台作业)。在后台作业中,只有一个进程会打开网关。此外,在 Heroku 上测试成功,这是我最初的问题。
  • 我的应用程序还从后台任务访问远程数据库,不同之处在于我在不同进程中有多个任务。我已经轻轻测试了rescue EADDRINUSE 方法,它似乎很好地满足了我的需求。感谢您让我完成了 80% 的工作!
【解决方案2】:

我尝试了@user1322092 的方法,但当多个客户端尝试访问数据库时遇到了麻烦,因为每个客户端都会尝试打开连接(除第一个之外的所有客户端都会出错)。

我创建了一个可怕的解决方案,涉及在测功机上生成一个 SSH 进程并将其用于所有通信。血腥细节在https://stackoverflow.com/a/27361295/558639。除了非常笨拙之外,每次启动进程都会导致延迟,无论您是否正在访问远程数据库。

更新

所以这里有一个更好的方法,看起来效果很好:只需捕获(并忽略)Errno::EADDRINUSE 错误。代码如下:

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

【讨论】:

  • 因为我在迁移数据,所以过程是顺序的;但是,我发现我的第一个 SSH 隧道连接是成功的,但第二个会抛出 'mysql connection has gone away'...grrrrr .. 所以我运行了heroku ps:restart,我猜想这会导致下一个连接成功。这也是你的问题吗?
  • @user1322092:我只在本地机器上进行测试。我收到了不同的错误消息(抱歉,我不记得确切的消息),但可能是相同的原因:尝试重新打开已经打开的 ssh 隧道。我怀疑根本原因是隧道是每台机器的,如果你有单独的进程试图创建隧道,除了第一个进程之外的所有进程都会收到错误。也许有一种方法可以检测隧道是否已经在运行......
猜你喜欢
  • 2017-12-27
  • 2016-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-03
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多