プロビジョナーの罠ついに Terraform のマニフェストが完成しました。terraform apply を実行し、インスタンスが立ち上がるのを眺めます。そして...沈黙。ターミナルは5分間停止したままになり、最終的におなじみのエラーでクラッシュします。
Error: timeout - last error: dial tcp 10.0.1.5:22: connect: connection refused
これは通常、Terraform を実行している環境(ローカル PC、GitHub Actions エージェント、CI/CD パイプラインなど)が SSH ポートに到達できないことを意味します。イライラする障害ですが、多くの場合、解決は簡単です。冷静にデバッグを進めるための手順を紹介します。
TL;DR: 60秒のクイックチェック- セキュリティグループ: 特定の IP に対してポート 22 が開放されていますか?インバウンドルールを確認してください。- IP の選択: パブリックインターネットからプライベート IP に接続しようとしていませんか? host を public_ip に変更してください。- 起動の遅延: AWS コンソールで「実行中」となっていても、sshd の初期化が完了していない場合があります。- ユーザー名: Amazon Linux AMI に対して ubuntu を使用していませんか?(正しいユーザー名は ec2-user です)。### 1. ルーティングの不一致: パブリック IP vs. プライベート IPエラーに含まれる IP(例: 10.0.1.5)をよく確認してください。ローカルマシンから Terraform を実行している場合、そのアドレスにはトラフィックをルーティングできません。それはプライベートな内部 IP です。Terraform は最初に見つけた IP(通常は内部 IP)を自動的に取得することがよくあります。
remote-exec プロビジョナーには直接の通信経路が必要です。VPN を使用しているか、踏み台ホスト(Bastion host)を経由していない限り、インスタンスのパブリック IP を使用する必要があります。
解決策:connection ブロックでパブリック IP 属性を使用するように明示します。
resource "aws_instance" "web" {
# ... 設定 ...
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/deploy_key")
host = self.public_ip # 自動選択に任せず明示的に指定
}
inline = ["sudo apt-get update"]
}
}
2. セキュリティグループ: 見えない壁正しい IP を使用していても、timeout が発生する場合はファイアウォールがパケットを暗黙的にドロップしていることを示唆しています。一方、connection refused はサーバーに到達したものの接続を拒否されたことを意味します。ほとんどのクラウドプロバイダーは、インバウンドトラフィックをデフォルトで「すべて拒否」に設定しています。
次のステップ:セキュリティグループまたはネットワークセキュリティグループが TCP ポート 22 を許可しているか確認します。テスト目的では 0.0.0.0/0 を使用することもありますが、本番環境では特定の IP 範囲(例: 203.0.113.5/32)に制限してください。
resource "aws_security_group" "ssh_access" {
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["ここに自分のIPを入力/32"]
}
}
3. レースコンディション: 起動時間の現実クラウドの API は高速ですが、Linux カーネルの起動はそれよりも遅いです。AWS の t3.micro は 20 秒以内に「実行中」と報告されることがありますが、cloud-init や sshd が完全に起動するにはさらに 40〜60 秒かかることがよくあります。Terraform が早すぎるタイミングで接続を試みると、リトライ回数を使い果たしてしまう可能性があります。
解決方法:OS が起動する余裕を持たせるために、接続タイムアウトを長く設定します。標準的なイメージの場合、通常 5〜10 分に設定すれば安全です。
connection {
type = "ssh"
user = "ec2-user"
host = self.public_ip
timeout = "10m"
}
4. ローカルファイアウォール (UFW/Firewalld)セキュリティが強化された一部の AMI では、内部ファイアウォールが有効になっています。クラウドレベルのセキュリティグループが全開放されていても、Ubuntu の ufw や RHEL の firewalld がポート 22 をブロックしている可能性があります。
手動で確認して、Terraform 固有の問題でないことを切り分けます。
ssh -i ~/.ssh/key.pem user@1.2.3.4
この手動コマンドが失敗する場合、問題は Terraform のコードではなく、ネットワークまたは OS の設定にあります。
上級者のテクニック: プロビジョナーを使わないHashiCorp はプロビジョナーを「最終手段」と考えています。プロビジョナーは Terraform のステート(状態)に含まれないため、動作が不安定になりやすいからです。ネットワークの不安定さが頻繁に問題になる場合は、セットアップのロジックを user_data に移行してください。
Cloud-init はマシン上でローカルに実行されます。ローカル PC からの SSH トンネルを必要としないため、インスタンスのブートストラップにおいて 10 倍信頼性が高まります。
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
EOF
}

