TerraformとAWSで実現する安全なDocker環境: EC2インスタンスの自動セットアップガイド

## はじめに

クラウド環境でのアプリケーション開発において、Docker環境の構築は重要なステップです。本記事では、AWSのEC2インスタンス上にTerraformを使用して自動的にDockerとDocker Composeをセットアップする方法を詳しく解説します。セキュリティを重視したプライベートサブネットの利用や、Systems Manager (SSM)によるアクセス管理など、実践的なクラウドインフラ構築のノウハウを学べます。

## なぜEC2インスタンスでDockerを使うのか？

1. **柔軟性**: EC2インスタンス上でDockerを利用することで、様々なアプリケーションやサービスを柔軟にデプロイできます。
2. **スケーラビリティ**: 需要に応じてインスタンスのサイズを変更したり、複数のインスタンスを追加したりすることが容易です。
3. **コスト効率**: 必要な時だけリソースを使用し、不要な時は停止することでコストを最適化できます。
4. **セキュリティ**: プライベートサブネットとNAT Gatewayを使用することで、セキュアな環境を構築できます。

## Terraformを使ったEC2インスタンスのセットアップ手順

### 環境準備

- Terraformのインストール
- AWS CLIのセットアップと認証情報の設定

### Terraformの設定

プロジェクトディレクトリに移動し、必要なファイルを準備します：

```bash
cd s08_ec2_docker
cp terraform.tfvars.example terraform.tfvars
```

`terraform.tfvars`ファイルを編集して、プロジェクト固有の設定を行います：

```hcl
aws_region         = "ap-northeast-1"
project_name       = "my-docker-compose-project"
vpc_cidr           = "10.0.0.0/16"
private_subnet_cidr = "10.0.1.0/24"
ami_id             = "ami-0d52744d6551d851e"
instance_type      = "t3.medium"
key_name           = "your-ssh-key-name"
```

### インフラストラクチャのデプロイ

以下のコマンドを実行して、インフラストラクチャをデプロイします：

```bash
terraform init
terraform plan
terraform apply
```


## 主要なTerraformコードの解説

### VPCの作成

```hcl
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${var.project_name}-vpc"
  }
}
```

このコードブロックでは、指定したCIDRブロックを持つVPCを作成し、DNSホスト名とDNSサポートを有効にしています。

### EC2インスタンスの作成

```hcl
resource "aws_instance" "app_server" {
  ami                    = var.ami_id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.private.id
  vpc_security_group_ids = [aws_security_group.ec2.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name
  key_name               = var.key_name

  user_data = <<-EOF
              #!/bin/bash
              # Docker and Docker Compose installation script
              ...
              EOF

  tags = {
    Name = "${var.project_name}-ec2"
  }
}
```

このリソースブロックでは、指定したAMIとインスタンスタイプを使用してEC2インスタンスを作成します。`user_data`セクションでは、インスタンス起動時にDockerとDocker Composeをインストールするスクリプトを定義しています。

### セキュリティグループの設定

```hcl
resource "aws_security_group" "ec2" {
  name        = "${var.project_name}-ec2-sg"
  description = "Security group for EC2 instance"
  vpc_id      = aws_vpc.main.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

  tags = {
    Name = "${var.project_name}-ec2-sg"
  }
}
```

このセキュリティグループは、EC2インスタンスに適用されます。すべての送信トラフィックを許可し、VPC内からの443ポートへの着信トラフィックのみを許可しています。


## セキュリティ対策の重要ポイント

1. **プライベートサブネットの使用**: EC2インスタンスはプライベートサブネットに配置され、直接のインターネットアクセスを防いでいます。

2. **NAT Gatewayの利用**: プライベートサブネット内のインスタンスがインターネットにアクセスするためのNAT Gatewayを設定しています。

   ```hcl
   resource "aws_nat_gateway" "main" {
     allocation_id = aws_eip.nat.id
     subnet_id     = aws_subnet.public.id

     tags = {
       Name = "${var.project_name}-nat-gw"
     }

     depends_on = [aws_internet_gateway.main]
   }
   ```

3. **Systems Manager (SSM)の使用**: SSHポートを開放せずにインスタンスにアクセスするため、SSMを利用しています。

   ```hcl
   resource "aws_vpc_endpoint" "ssm" {
     vpc_id              = aws_vpc.main.id
     service_name        = "com.amazonaws.${var.aws_region}.ssm"
     vpc_endpoint_type   = "Interface"
     private_dns_enabled = true

     subnet_ids         = [aws_subnet.private.id]
     security_group_ids = [aws_security_group.ec2.id]
   }
   ```
## SSH接続の設定と使用方法

開発やトラブルシューティングの際に、EC2インスタンスにSSH接続する必要がある場合があります。以下に、SSH接続を行うための手順と注意点を説明します。

### セキュリティグループの設定

SSH接続を許可するには、セキュリティグループにSSHポート（22）のインバウンドルールを追加する必要があります。セキュリティ上の理由から、特定のIPアドレスからのみアクセスを許可することをおすすめします。

```hcl
resource "aws_security_group" "ec2" {
  # ...既存の設定...

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["YOUR_IP_ADDRESS/32"]  # あなたのIPアドレスを指定
  }

  # ...
}
```

### SSH接続の実行

インスタンスが起動し、セキュリティグループが適切に設定されたら、以下のコマンドでSSH接続を行うことができます：

```bash
ssh -i "C:\Users\makim\.ssh\streamlit-terraform-keypair-tokyo-PEM.pem" ubuntu@i-0f9cd7c06ab7abeea
```

このコマンドの各部分を解説します：

- `-i "C:\Users\makim\.ssh\streamlit-terraform-keypair-tokyo-PEM.pem"`: 秘密キーファイルのパスを指定します。
- `ubuntu`: EC2インスタンスのデフォルトユーザー名です（AMIによって異なる場合があります）。
- `i-0f9cd7c06ab7abeea`: EC2インスタンスのIDです。これはパブリックDNS名やパブリックIPアドレスに置き換えることもできます。

### 注意点とベストプラクティス

- **セキュリティ**: SSHポートを公開することはセキュリティリスクを伴います。可能な限り、Systems Manager Session Managerなどのマネージドサービスを使用することをおすすめします。
- **一時的なアクセス**: 開発やデバッグ目的でSSHアクセスが必要な場合は、作業が終わったら速やかにセキュリティグループのSSHルールを削除してください。
- **キーの管理**: SSHキーペアは安全に管理し、決して公開リポジトリにコミットしないようにしてください。


## まとめ

TerraformとAWSを組み合わせることで、セキュアで効率的なDocker環境を自動的に構築できることがわかりました。この方法は、開発環境から本番環境まで幅広く適用可能で、クラウドインフラストラクチャの管理を大幅に簡素化します。

今後のステップとして、CI/CDパイプラインとの統合や、より複雑なマルチコンテナアプリケーションのデプロイなど、さらなる拡張が考えられます。クラウドネイティブな開発手法を取り入れることで、より効率的で柔軟なアプリケーション開発が可能になるでしょう。

## リポジトリ

https://github.com/Sunwood-ai-labs/aws-terraform-sandbox


## 参考リソース

- [Terraform公式ドキュメント](https://www.terraform.io/docs/index.html)
- [AWS EC2ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html)
- [Docker公式ドキュメント](https://docs.docker.com/)
- [AWS Systems Managerユーザーガイド](https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html)

クラウドインフラストラクチャの自動化に興味がある方は、ぜひこの方法を試してみてください。セキュアで効率的なDocker環境構築のスキルは、現代のソフトウェア開発において非常に価値があります。