TerraformでAIサービス向けインフラを自動構築する実装ガイド

本記事では、TerraformでAIアプリケーション実行環境のインフラを自動構築する実践的な方法を解説します。GPU対応EC2インスタンスやVPC設定、IAMロールなど、AI推論に必要なリソースをコード化し、再現性のあるインフラを数分で構築できるようになります。

Terraformを使うべき理由:AI開発とインフラの親和性

AI・機械学習プロジェクトでは、開発環境、ステージング、本番環境で異なるリソース設定が必要です。CloudFormationと異なり、TerraformはマルチクラウドAWS/GCP/Azureに対応し、状態管理が明示的で学習しやすいのが特徴です。

特にLLMやコンピュータビジョンモデルの推論では、以下のような複雑なインフラが必要になります:

  • GPU付きEC2インスタンス(g4dn型やp3型)
  • Lambda関数による非同期推論処理
  • SageMaker Endpointの管理
  • VPC内のプライベートサブネット構成
  • 推論結果を保存するS3バケット

これらを手作業で構築するのは時間がかかり、破損しやすいです。Terraformなら「Infrastructure as Code」として版管理できます。

TerraformでAI推論基盤を構築するステップ

1. 環境構築と初期化

まずはTerraformをインストールし、AWSに接続できる状態を整えます。テスト環境はmacOS 14 / Terraform 1.7.0 / AWS CLI v2で動作確認しています。

# Homebrewでインストール(macOS)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

# バージョン確認
terraform version

# AWSアカウントの認証情報を設定
aws configure

# 作業用ディレクトリを作成
mkdir terraform-ai-infra
cd terraform-ai-infra
terraform init

terraform initを実行すると、.terraformディレクトリと.terraform.lock.hclファイルが生成されます。後者は依存関係のバージョンを固定し、チーム開発で再現性を確保するために重要です。

2. VPC と ネットワーク構成の定義

AI推論には通常、プライベートサブネット内にGPUリソースを配置し、NATゲートウェイ経由でインターネットアクセスを制御します。以下は基本的なVPC構成例です。

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# VPCの作成
resource "aws_vpc" "ai_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "ai-inference-vpc"
  }
}

# パブリックサブネット
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.ai_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "ai-public-subnet"
  }
}

# プライベートサブネット(GPU インスタンス用)
resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.ai_vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "ai-private-subnet"
  }
}

# インターネットゲートウェイ
resource "aws_internet_gateway" "ai_igw" {
  vpc_id = aws_vpc.ai_vpc.id

  tags = {
    Name = "ai-igw"
  }
}

# Elastic IP(NATゲートウェイ用)
resource "aws_eip" "nat" {
  domain = "vpc"

  tags = {
    Name = "ai-nat-eip"
  }

  depends_on = [aws_internet_gateway.ai_igw]
}

# NATゲートウェイ
resource "aws_nat_gateway" "ai_nat" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public.id

  tags = {
    Name = "ai-nat-gateway"
  }

  depends_on = [aws_internet_gateway.ai_igw]
}

# パブリックルートテーブル
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.ai_vpc.id

  route {
    cidr_block      = "0.0.0.0/0"
    gateway_id      = aws_internet_gateway.ai_igw.id
  }

  tags = {
    Name = "ai-public-rt"
  }
}

# プライベートルートテーブル
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.ai_vpc.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.ai_nat.id
  }

  tags = {
    Name = "ai-private-rt"
  }
}

# ルートテーブルの関連付け
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private" {
  subnet_id      = aws_subnet.private.id
  route_table_id = aws_route_table.private.id
}

# 利用可能なAZデータソース
data "aws_availability_zones" "available" {
  state = "available"
}

3. セキュリティグループと IAM ロールの設定

GPU インスタンスはSSH接続できる必要がありますが、本番環境ではSSMセッションマネージャー経由で制限するのが推奨です。

# security_groups.tf
resource "aws_security_group" "ai_inference_sg" {
  name        = "ai-inference-sg"
  description = "Security group for AI inference instances"
  vpc_id      = aws_vpc.ai_vpc.id

  # SSHアクセス(開発環境のみ)
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.my_ip_cidr]  # 自分のIPアドレス/32 を指定
  }

  # Jupyter Notebook(内部用)
  ingress {
    from_port       = 8888
    to_port         = 8888
    protocol        = "tcp"
    security_groups = [aws_security_group.ai_inference_sg.id]
  }

  # VPC内通信を許可
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"]
  }

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

  tags = {
    Name = "ai-inference-sg"
  }
}

# IAM ロール
resource "aws_iam_role" "ai_inference_role" {
  name = "ai-inference-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

# IAMポリシー(S3とCloudWatch Logsへのアクセス)
resource "aws_iam_role_policy" "ai_inference_policy" {
  name = "ai-inference-policy"
  role = aws_iam_role.ai_inference_role.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.ai_results.arn,
          "${aws_s3_bucket.ai_results.arn}/*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
        Resource = "arn:aws:logs:*:*:*"
      }
    ]
  })
}

# IAM インスタンスプロファイル
resource "aws_iam_instance_profile" "ai_inference" {
  name = "ai-inference-profile"
  role = aws_iam_role.ai_inference_role.name
}

4. GPU 対応 EC2 インスタンスの起動

AI推論用途ではg4dn型またはp3型のGPUインスタンスを選択します。ここではコスト効率の良いg4dn.xlrgeを使用する例を示します。

# ec2.tf
data "aws_ami" "ubuntu_gpu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical(Ubuntu公式)

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_instance" "ai_inference" {
  ami           = data.aws_ami.ubuntu_gpu.id
  instance_type = "g4dn.xlarge"  # NVIDIA T4 GPU搭載

  subnet_id                   = aws_subnet.private.id
  iam_instance_profile        = aws_iam_instance_profile.ai_inference.name
  vpc_security_group_ids      = [aws_security_group.ai_inference_sg.id]
  associate_public_ip_address = false

  root_block_device {
    volume_size           = 100  # GB
    volume_type           = "gp3"
    delete_on_termination = true
  }

  # ユーザーデータスクリプト(初期セットアップ)
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    s3_bucket_name = aws_s3_bucket.ai_results.bucket
  }))

  tags = {
    Name = "ai-inference-instance"
  }

  depends_on = [aws_nat_gateway.ai_nat]
}

# Elastic IPの割り当て(SSM接続用)
resource "aws_eip" "ai_instance" {
  instance = aws_instance.ai_inference.id
  domain   = "vpc"

  tags = {
    Name = "ai-inference-eip"
  }

  depends_on = [aws_internet_gateway.ai_igw]
}

user_data.shでは、GPU ドライバーと Python 推論フレームワークのインストールを自動化します:

#!/bin/bash
set -e

# NVIDIA GPU ドライバーのインストール
ubuntu-drivers autoinstall

# NVIDIA CUDA Toolkitのインストール
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
dpkg -i cuda-keyring_1.1-1_all.deb
apt-get update
apt-get install -y cuda-toolkit-12-4

# Python と PyTorch のインストール
apt-get install -y python3.11 python3.11-venv python3-pip
python3 -m pip install --upgrade pip

# PyTorch with CUDA support
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# 推論用ライブラリ
pip install fastapi uvicorn boto3 pydantic

# CloudWatch Logs エージェント
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
dpkg -i -E ./amazon-cloudwatch-agent.deb

echo "GPU instance setup completed"

5. S3 バケットとストレージ設定

推論結果を保存するS3バケットを作成し、ライフサイクルポリシーで古いデータを自動削除します。

# s3.tf
resource "aws_s3_bucket" "ai_results" {
  bucket = "ai-inference-results-${data.aws_caller_identity.current.account_id}"

  tags = {
    Name = "ai-results"
  }
}

# バージョニングの有効化
resource "aws_s3_bucket_versioning" "ai_results" {
  bucket = aws_s3_bucket.ai_results.id

  versioning_configuration {
    status = "Enabled"
  }
}

# ライフサイクルルール(30日後に削除)
resource "aws_s3_bucket_lifecycle_configuration" "ai_results" {
  bucket = aws_s3_bucket.ai_results.id

  rule {
    id     = "delete-old-results"
    status = "Enabled"

    expiration {
      days = 30
    }

    noncurrent_version_expiration {
      noncurrent_days = 7
    }
  }
}

# サーバ
    
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →