【问题标题】:Issues trying to SSH into a fresh EC2 instance with Paramiko尝试使用 Paramiko SSH 进入新 EC2 实例的问题
【发布时间】:2011-08-26 21:38:12
【问题描述】:

我正在编写一个脚本,该脚本使用 boto 启动一个新的 EC2 实例,并使用 Paramiko SSH 客户端在该实例上执行远程命令。无论出于何种原因,Paramiko 客户端无法连接,我收到错误消息:

Traceback (most recent call last):
  File "scripts/sconfigure.py", line 29, in <module>
    ssh.connect(instance.ip_address, username='ubuntu', key_filename=os.path.expanduser('~/.ssh/test'))
  File "build/bdist.macosx-10.3-fat/egg/paramiko/client.py", line 291, in connect
  File "<string>", line 1, in connect
socket.error: [Errno 61] Connection refused

我可以使用相同的密钥文件和用户手动 ssh。有人在使用 Paramiko 时遇到问题吗?我的完整代码如下。谢谢。

import boto.ec2, time, paramiko, os
# Connect to the us-west-1 region
ec2 = boto.ec2.regions()[3].connect()
image_id = 'ami-ad7e2ee8'
image_name = 'Ubuntu 10.10 (Maverick Meerkat) 32-bit EBS'
new_reservation = ec2.run_instances(
    image_id=image_id,
    key_name='test',
    security_groups=['web'])

instance = new_reservation.instances[0]

print "Spinning up instance for '%s' - %s. Waiting for it to boot up." % (image_id, image_name)
while instance.state != 'running':
    print "."
    time.sleep(1)
    instance.update()

print "Instance is running, ip: %s" % instance.ip_address

print "Connecting to %s as user %s" % (instance.ip_address, 'ubuntu')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(instance.ip_address, username='ubuntu', key_filename=os.path.expanduser('~/.ssh/test'))
stdin, stdout, stderr = ssh.exec_command('echo "TEST"')
print stdout.readlines()
ssh.close()

【问题讨论】:

  • +1 好问题!我从来没有对我的 EC2 账户做过任何事情,这会给我一些动力。
  • 您是否尝试过将端口指定为 ssh.connect() 中的第二个参数?
  • 感谢您的建议,但我有。我相信我已经想通了。即使根据 boto,实例状态为“正在运行”,但它实际接受任何 SSH 连接的时间似乎存在延迟。在尝试建立连接之前添加一个 time.slep(25) 似乎就足够了,但我要做更多的测试。
  • 太棒了!一旦你让它工作,也许你可以回答你自己的问题,这样下一个遇到这个问题的人就可以了!
  • 完全正确。做 time.sleep(60) 将解决问题。但是您的代码启发了我对 paramiko 部分的了解。谢谢老兄。

标签: python amazon-ec2 paramiko boto


【解决方案1】:

我似乎已经通过反复试验弄明白了这一点。即使根据 boto,实例状态为“正在运行”,但实际允许 SSH 连接的时间仍有延迟。在“ssh.connect(...)”之前添加一个“time.sleep(30)”似乎对我有用,尽管这可能会有所不同。

【讨论】:

  • 可能“正在运行”意味着映像本身正在启动?所以你必须等待操作系统启动,然后 sshd 启动。
  • 是的,这是最有意义的。
  • 我和你一样(45秒:p),这是我发现的唯一方法,即使我不喜欢睡觉(x)
  • 你不需要这样做。您可以尝试连接多次并捕获异常。我成功实现了this pattern 与 paramiko 合作。
  • 天哪,这令人沮丧!谢谢@rr。 !!我正在使用 boto 的 sshclient_from_instance,它显然依赖于 paramiko。我只是认为这不是一个好的错误消息和 API 设计。它不应该那么神秘——除非这个用例很少见,也许吧?
【解决方案2】:

检查它的 ssh 可用的方法是确保它的两个状态检查都通过。在 Web UI 上,它看起来像这样:

而使用boto3(最初的问题使用boto,但它是5年前的),我们可以这样做:

session = boto3.Session(...)
client = session.client('ec2')
res = client.run_instances(...) # launch instance
instance_id = res['Instances'][0]['InstanceId']

while True:
    statuses = client.describe_instance_status(InstanceIds=[instance_id])
    status = statuses['InstanceStatuses'][0]
    if status['InstanceStatus']['Status'] == 'ok' \
            and status['SystemStatus']['Status'] == 'ok':
        break
    print '.'
    time.sleep(5)
print "Instance is running, you are ready to ssh to it"

【讨论】:

  • 这个可行,我试过了。注意:您可以在这两种状态从“正在初始化”切换到“确定”之前通过 ssh 连接到节点。因此,如果您的代码不耐烦并想尽快 ssh,则另一种技术可能会更好。
【解决方案3】:

为什么不改用boto.manage.cmdshell

cmd = boto.manage.cmdshell.sshclient_from_instance(instance,
                                                   key_path,
                                                   user_name='ec2_user')

(代码取自 ec2_launch_instance.py 中的第 152 行)

有关可用的cmdshell 命令,请查看cmdshell.py 中的SSHClient 类。

【讨论】:

    【解决方案4】:

    我最近遇到了这个问题。 “正确”的方法是先启动 close() 然后重新打开连接。但是在旧版本中,close() 被破坏了。

    使用此版本或更高版本,它应该被修复: https://github.com/boto/boto/pull/412

    “正确”的方法:

    newinstance = image.run(min_count=instancenum, max_count=instancenum, key_name=keypair, security_groups=security_group, user_data=instancename, instance_type=instancetype, placement=zone)
    time.sleep(2)
    newinstance.instances[0].add_tag('Name',instancename)
    
    print "Waiting for public_dns_name..."
    counter = 0
    while counter < 70:
        time.sleep(1)
        conn.close()
        conn = boto.ec2.connection.EC2Connection(ec2auth.access_key,ec2auth.private_key)
        startedinstance = conn.get_all_instances(instance_ids=str(newinstance.instances[0].id))[0]
        counter = counter + 1
        if str(startedinstance.instances[0].state) == "running":
            break
        if counter == 69:
            print "Timed out waiting for instance to start."
    print "Added: " + startedinstance.instances[0].tags['Name'] + " " + startedinstance.instances[0].public_dns_name
    

    【讨论】:

    • 我不明白你为什么每次都通过循环创建一个新连接。此代码 (github.com/garnaat/paws/blob/master/ec2_launch_instance.py) 每次都使用相同的连接执行类似的循环。我从来没有遇到过这种模式的问题。
    • 问题是,初始连接被缓存了。因此,除非您关闭并重新打开连接,否则它不会重新检查更新。这是我在测试中发现的。还没有和博托人谈过这件事。
    【解决方案5】:

    我最近查看了这段代码,我有一个代码建议, 您可以尝试“wait_until_running()”,而不是运行while循环来检查实例是否正在运行。

    以下是示例代码...

    client = boto3.resource(
        'ec2',
        region_name="us-east-1"
    )
    
    Instance_ID = "<your Instance_ID>"
    instance = client.Instance(Instance_ID)
    instance.start()
    instance.wait_until_running()
    

    然后尝试编写 ssh 连接代码。

    【讨论】: