You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MacOS脚本scp文件夹至Ubuntu EC2实例遇连接拒绝求助

EC2 SCP连接被拒(脚本执行失败但手动操作正常)

问题背景

我通过Bash脚本结合Terraform创建Ubuntu EC2实例后,立刻执行scp传输本地文件夹,但脚本运行时始终提示Connection refused;手动执行相同的scp命令却完全正常。已确认安全组开放22端口,文件路径和密钥均无误。

脚本代码

#!/bin/bash

# This block should parse instance DNS name from terraform output
terraform apply -auto-approve | tee output.txt
publicdns=$(grep 'instance_dns_name = "' output.txt | cut -b 26-)
public_dns_name=${publicdns%?}
echo $public_dns_name # I added this to show that DNS name in variable is correct

scp -v -r -i ~/dev_folder/test_task_tf/tf_key.pem ~/dev_folder/test_task_tf/docker_dir ubuntu@$public_dns_name:~/ 

错误输出

ec2-xx-xx-xx-xxx.eu-central-1.compute.amazonaws.com
Executing: program /usr/bin/ssh host ecx-xx-xx-xx-xxx.eu-central-1.compute.amazonaws.com, user ubuntu, command sftp
OpenSSH_9.0p1, LibreSSL 3.3.6
debug1: Reading configuration data /Users/maxivanov/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: include /etc/ssh/ssh_config.d/\* matched no files
debug1: /etc/ssh/ssh_config line 54: Applying options for \*
debug1: Authenticator provider $SSH_SK_PROVIDER did not resolve; disabling
debug1: Connecting to ec2-xx-xx-xx-xxx.eu-central-1.compute.amazonaws.com port 22.
ssh: connect to host ec2-xx-xx-xx-xxx.eu-central-1.compute.amazonaws.com port 22: Connection refused
scp: Connection closed

问题原因

  1. 核心原因:实例未完全就绪
    Terraform的apply命令执行完成仅代表EC2实例的云资源创建成功,但实例内部的操作系统初始化、SSH服务(sshd)启动需要额外时间。脚本在terraform apply结束后立刻发起scp连接,此时实例的22端口还未对外开放,导致连接被拒;手动执行时你已经等待了足够时间,实例SSH服务已正常运行,因此连接成功。

  2. 次要原因:DNS解析方式不可靠
    用grep+cut手动解析Terraform输出的方式容易受输出格式变化(如引号转义、字符位置变动)影响,虽然当前echo显示DNS正确,但这种方式存在潜在隐患。


解决方案

1. 改用Terraform官方命令获取实例DNS

使用terraform output命令直接提取输出值,-raw参数可获取纯文本格式的结果,无需处理转义字符或截取字符串:

public_dns_name=$(terraform output -raw instance_dns_name)

2. 添加SSH就绪等待逻辑

在scp前通过循环检查实例22端口是否开放,直到端口就绪再执行传输(最多等待5分钟,每10秒检查一次):

修改后的完整脚本

#!/bin/bash

# 执行Terraform创建实例
terraform apply -auto-approve

# 用terraform output直接获取实例DNS
public_dns_name=$(terraform output -raw instance_dns_name)
echo "实例DNS:$public_dns_name"

# 等待SSH端口就绪
MAX_WAIT=300
WAIT_INTERVAL=10
elapsed=0

echo "等待实例SSH端口就绪..."
while ! nc -z "$public_dns_name" 22; do
    if [ $elapsed -ge $MAX_WAIT ]; then
        echo "超时:实例SSH端口未在${MAX_WAIT}秒内就绪"
        exit 1
    fi
    sleep $WAIT_INTERVAL
    elapsed=$((elapsed + WAIT_INTERVAL))
done

# 执行scp传输
scp -v -r -i ~/dev_folder/test_task_tf/tf_key.pem ~/dev_folder/test_task_tf/docker_dir ubuntu@$public_dns_name:~/

额外检查点

  • 如果本地没有nc工具,可替换循环中的检查命令为:
    while ! ssh -o ConnectTimeout=2 ubuntu@$public_dns_name echo "SSH ready" 2>/dev/null; do
    
  • 检查DNS变量是否包含隐藏字符:执行echo "$public_dns_name" | od -c,确保输出无多余换行符、空格或控制字符。

内容的提问来源于stack exchange,提问作者kavo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 19:00:18