TerraformとAnsibleの基礎知識
Terraformの特徴と主な機能
Terraformは、HashiCorp社が開発したInfrastructure as Code(IaC)ツールです。クラウドインフラストラクチャを宣言的なコードで管理することができ、以下のような特徴を持っています:
- 宣言的な記述方法
- HCL(HashiCorp Configuration Language)による直感的な記述
- インフラの望ましい状態を定義するだけで、現状からの差分を自動で解決
- バージョン管理システムと親和性が高い
- プロバイダーシステム
- AWS、GCP、Azureなど、多様なクラウドプラットフォームに対応
- 標準プロバイダーに加え、コミュニティプロバイダーも充実
- マルチクラウド環境の一元管理が可能
- 状態管理機能
- tfstateファイルによるインフラ状態の追跡
- リモートステート機能によるチーム開発のサポート
- 依存関係の自動解決とリソースの作成順序の最適化
- プランニング機能
terraform planによる変更内容の事前確認- 実行前のリスク検証が可能
- 意図しない変更の防止
主な使用シーン:
- クラウドインフラの新規構築
- 既存インフラのコード化(Import機能)
- 環境の複製(開発・検証・本番)
- リソースの一括管理
Ansibleの特徴と主な機能
Ansibleは、Red Hat社が提供する構成管理・自動化ツールです。エージェントレスでサーバー設定を自動化できる特徴を持ちます:
- エージェントレスアーキテクチャ
- SSHによる直接実行
- 管理対象サーバーへの追加インストール不要
- セキュリティリスクの低減
- YAMLベースの記述
- 可読性の高い設定ファイル(Playbook)
- 豊富な組み込みモジュール
- 独自モジュールの作成も可能
- べき等性の保証
- 同じPlaybookを複数回実行しても結果が変わらない
- 冪等性による安全な実行
- 障害時の再実行が容易
- 並列実行とロールバック
- 複数サーバーへの同時デプロイ
- タスクの失敗時に自動ロールバック
- 実行状況のリアルタイム確認
主な使用シーン:
- OSの初期設定
- ミドルウェアのインストールと設定
- アプリケーションのデプロイ
- 定期的なメンテナンス作業
なぜTerraformとAnsibleの組み合わせが効果的なのか
TerraformとAnsibleを組み合わせることで、インフラ構築から設定管理までをカバーする完全な自動化パイプラインを実現できます:
- それぞれの得意分野の活用
- Terraform:インフラストラクチャの構築
- Ansible:OS/ミドルウェアの設定
→ 各ツールの長所を最大限に活用
- シームレスな連携
- Terraformの出力をAnsibleの入力として利用
- 動的インベントリによる自動連携
- 一貫した自動化フロー
- 運用効率の向上 項目 Terraform Ansible 組み合わせのメリット インフラ構築 ◎ △ インフラ構築の確実な自動化 OS設定 × ◎ OSレベルでの詳細な設定が可能 実行速度 高速 中速 各工程の最適化による全体の高速化 学習コスト 中 低 段階的な習得が可能
- 実践的なメリット
- インフラのバージョン管理
- 環境の完全な再現性
- 手作業によるミスの防止
- チーム開発の効率化
このように、TerraformとAnsibleの組み合わせは、現代のクラウドインフラ運用において理想的な選択肢となります。特にAWS環境では、この2つのツールを組み合わせることで、セキュアで効率的な自動化パイプラインを構築することができます。
TerraformとAnsibleの環境構築手順
必要なツールのインストール方法
- Terraformのインストール
Ubuntu/Debianの場合:
# HashiCorpの公式GPGキーを追加 curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - # HashiCorpの公式リポジトリを追加 sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" # Terraformをインストール sudo apt update sudo apt install terraform # バージョン確認 terraform version
macOSの場合:
# Homebrewを使用してインストール brew tap hashicorp/tap brew install hashicorp/tap/terraform # バージョン確認 terraform version
- Ansibleのインストール
Ubuntu/Debianの場合:
# 必要なパッケージのインストール sudo apt update sudo apt install software-properties-common # Ansibleの公式リポジトリを追加 sudo apt-add-repository --yes --update ppa:ansible/ansible # Ansibleをインストール sudo apt install ansible # バージョン確認 ansible --version
macOSの場合:
# Homebrewを使用してインストール brew install ansible # バージョン確認 ansible --version
AWSの認証情報の設定方法
- AWS CLIのインストール
# Ubuntu/Debianの場合 sudo apt install awscli # macOSの場合 brew install awscli
- AWS認証情報の設定
方法1: AWS CLI経由での設定
aws configure
以下の情報を入力:
AWS Access Key ID: [あなたのアクセスキー] AWS Secret Access Key: [あなたのシークレットキー] Default region name: [使用するリージョン(例:ap-northeast-1)] Default output format: json
方法2: 環境変数での設定
export AWS_ACCESS_KEY_ID="あなたのアクセスキー" export AWS_SECRET_ACCESS_KEY="あなたのシークレットキー" export AWS_DEFAULT_REGION="ap-northeast-1"
方法3: 共有認証情報ファイルの作成
mkdir -p ~/.aws
~/.aws/credentialsファイルを作成:
[default] aws_access_key_id = あなたのアクセスキー aws_secret_access_key = あなたのシークレットキー
~/.aws/configファイルを作成:
[default] region = ap-northeast-1 output = json
基本的な設定ファイルの作成方法
- Terraform設定ファイル
プロジェクトディレクトリの作成:
mkdir terraform-ansible-project cd terraform-ansible-project
main.tfファイルの作成:
# プロバイダーの設定
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
# AWSプロバイダーの設定
provider "aws" {
region = "ap-northeast-1"
}
# バックエンドの設定(オプション)
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "terraform.tfstate"
region = "ap-northeast-1"
}
}
- Ansible設定ファイル
ansible.cfgファイルの作成:
[defaults] inventory = ./inventory remote_user = ec2-user private_key_file = ~/.ssh/your-key.pem host_key_checking = False stdout_callback = yaml[ssh_connection]
pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60s
基本的なインベントリファイル(inventory)の作成:
[webservers] # ここにWebサーバーのIPアドレスやDNS名を記載[dbservers]
# ここにDBサーバーのIPアドレスやDNS名を記載
[all:vars]ansible_python_interpreter=/usr/bin/python3
基本的なPlaybook(site.yml)の作成:
---
- name: Basic server setup
hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
when: ansible_os_family == "Debian"
- name: Install basic packages
package:
name:
- vim
- curl
- git
state: present
- 動作確認
Terraformの初期化と確認:
# 初期化 terraform init # 構文チェック terraform fmt terraform validate
Ansibleの構文チェック:
# Playbookの構文チェック ansible-playbook site.yml --syntax-check # インベントリの確認 ansible-inventory --list
セットアップ時の重要なポイント:
| 項目 | 注意点 |
|---|---|
| バージョン管理 | .gitignoreにシークレット情報や.terraformディレクトリを追加 |
| セキュリティ | 認証情報は環境変数か認証情報ファイルで管理 |
| バックエンド | 本番環境ではリモートバックエンドを使用 |
| SSH鍵 | EC2インスタンス用のSSH鍵を適切に管理 |
以上の手順で、TerraformとAnsibleの基本的な環境構築が完了します。次のセクションでは、これらのツールを使用して実際にAWS環境を構築していきます。
実践!TerraformとAnsibleによるAWS環境構築
VPCとサブネットの構築(Terraform)
- プロジェクト構造の作成
terraform-ansible-project/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── network.tf
└── ansible/
├── inventory/
├── group_vars/
├── roles/
└── site.yml
- VPC構築用のTerraformコード(network.tf)
# VPCの作成
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
Environment = var.environment
}
}
# パブリックサブネットの作成
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
Environment = var.environment
}
}
# プライベートサブネットの作成
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-${count.index + 1}"
Environment = var.environment
}
}
# インターネットゲートウェイの作成
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
Environment = var.environment
}
}
# NATゲートウェイの作成
resource "aws_nat_gateway" "main" {
count = length(var.public_subnet_cidrs)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-${count.index + 1}"
Environment = var.environment
}
}
# ルートテーブルの設定
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt"
Environment = var.environment
}
}
resource "aws_route_table" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.project_name}-private-rt-${count.index + 1}"
Environment = var.environment
}
}
EC2インスタンスの作成(Terraform)
- EC2インスタンス用のセキュリティグループ設定
resource "aws_security_group" "web" {
name = "${var.project_name}-web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-web-sg"
Environment = var.environment
}
}
- EC2インスタンスの作成
resource "aws_instance" "web" {
count = var.web_instance_count
ami = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
subnet_id = aws_subnet.public[count.index % length(aws_subnet.public)].id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = var.key_name
root_block_device {
volume_size = 20
volume_type = "gp3"
}
tags = {
Name = "${var.project_name}-web-${count.index + 1}"
Environment = var.environment
}
}
# EC2インスタンスの情報を出力
output "web_instance_public_ips" {
value = aws_instance.web[*].public_ip
}
ミドルウェアのインストールと設定(Ansible)
- Webサーバー用のAnsibleロール構造
roles/webserver/
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
└── vars/
└── main.yml
- Webサーバーのタスク定義(roles/webserver/tasks/main.yml)
---
- name: Update all packages
yum:
name: '*'
state: latest
become: yes
- name: Install NGINX
yum:
name: nginx
state: present
become: yes
notify: restart nginx
- name: Start and enable NGINX
service:
name: nginx
state: started
enabled: yes
become: yes
- name: Configure NGINX
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
become: yes
notify: restart nginx
- name: Install application dependencies
yum:
name:
- git
- nodejs
- npm
state: present
become: yes
- メインのPlaybook(site.yml)
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- webserver
tasks:
- name: Ensure firewalld is running
service:
name: firewalld
state: started
enabled: yes
- name: Allow HTTP traffic
firewalld:
service: http
permanent: yes
state: enabled
notify: reload firewalld
handlers:
- name: reload firewalld
service:
name: firewalld
state: reloaded
実行のポイント:
| フェーズ | コマンド | 説明 |
|---|---|---|
| Terraform実行 | terraform init | プロバイダーとモジュールの初期化 |
terraform plan | 実行計画の確認 | |
terraform apply | インフラの作成 | |
| Ansible実行 | ansible-playbook -i inventory site.yml | 設定の適用 |
構築時の重要なポイント:
- セキュリティ
- セキュリティグループは必要最小限のポートのみ開放
- SSH鍵の適切な管理
- プライベートサブネットの活用
- 可用性
- マルチAZ構成
- オートスケーリングの考慮
- ヘルスチェックの実装
- 保守性
- リソースへの適切なタグ付け
- モジュール化された構成
- 変数の適切な管理
- コスト最適化
- 適切なインスタンスタイプの選択
- 未使用リソースの削除
- 自動スケーリングの活用
TerraformとAnsibleの連携方法
Terraformの出力をAnsibleで利用する方法
- Terraform出力の定義(outputs.tf)
output "web_instance_ips" {
value = {
for instance in aws_instance.web :
instance.tags.Name => {
public_ip = instance.public_ip
private_ip = instance.private_ip
}
}
description = "Web instances IP addresses"
}
output "db_instance_ips" {
value = {
for instance in aws_instance.db :
instance.tags.Name => {
private_ip = instance.private_ip
}
}
description = "Database instances IP addresses"
}
# JSONファイルとして出力を保存
resource "local_file" "ansible_inventory" {
content = jsonencode({
webservers = {
hosts = {
for instance in aws_instance.web :
instance.tags.Name => {
ansible_host = instance.public_ip
private_ip = instance.private_ip
}
}
}
dbservers = {
hosts = {
for instance in aws_instance.db :
instance.tags.Name => {
ansible_host = instance.private_ip
}
}
}
})
filename = "../ansible/inventory/aws_hosts.json"
}
- Pythonスクリプトによる変換(convert_inventory.py)
#!/usr/bin/env python3
import json
import sys
def convert_terraform_to_ansible():
"""Terraform出力をAnsibleインベントリに変換"""
with open('ansible/inventory/aws_hosts.json', 'r') as f:
tf_output = json.load(f)
ansible_inventory = {
'all': {
'children': tf_output
}
}
# グローバル変数の設定
ansible_inventory['all']['vars'] = {
'ansible_user': 'ec2-user',
'ansible_ssh_private_key_file': '~/.ssh/my-key.pem',
'ansible_python_interpreter': '/usr/bin/python3'
}
print(json.dumps(ansible_inventory, indent=2))
if __name__ == '__main__':
convert_terraform_to_ansible()
動的インベントリの設定方法
- AWS動的インベントリスクリプトの設定
# ansible.cfg[inventory]
enable_plugins = amazon.aws.aws_ec2
[defaults]inventory = inventory/aws_ec2.yml remote_user = ec2-user private_key_file = ~/.ssh/my-key.pem host_key_checking = False
- AWS EC2動的インベントリの設定(inventory/aws_ec2.yml)
---
plugin: aws_ec2
regions:
- ap-northeast-1
keyed_groups:
- key: tags.Environment
prefix: env
- key: tags.Role
prefix: role
- key: instance_type
prefix: type
filters:
tag:Project: "my-project"
instance-state-name: running
compose:
ansible_host: public_ip_address
- タグベースのインベントリグループ化
# group_vars/all.yml --- ansible_user: ec2-user ansible_ssh_private_key_file: ~/.ssh/my-key.pem ansible_python_interpreter: /usr/bin/python3 # group_vars/role_web.yml --- nginx_port: 80 app_environment: production # group_vars/role_db.yml --- mysql_port: 3306 backup_enabled: true
べストプラクティスと注意点
- ディレクトリ構造のベストプラクティス
project/ ├── terraform/ │ ├── main.tf │ ├── variables.tf │ ├── outputs.tf │ └── modules/ │ ├── vpc/ │ ├── ec2/ │ └── rds/ ├── ansible/ │ ├── inventory/ │ │ ├── aws_ec2.yml │ │ └── group_vars/ │ ├── roles/ │ │ ├── common/ │ │ ├── web/ │ │ └── db/ │ └── site.yml ├── scripts/ │ └── convert_inventory.py └── Makefile
- 効率的な実行のためのMakefile
.PHONY: init plan apply configure destroy
init:
cd terraform && terraform init
plan:
cd terraform && terraform plan
apply:
cd terraform && terraform apply -auto-approve
python3 scripts/convert_inventory.py > ansible/inventory/current.json
configure:
cd ansible && ansible-playbook -i inventory/current.json site.yml
destroy:
cd terraform && terraform destroy -auto-approve
all: init plan apply configure
- 連携時の重要なポイント
| カテゴリ | ベストプラクティス | 注意点 |
|---|---|---|
| 状態管理 | – Terraformステートをリモート管理 – Ansibleの実行結果をログ管理 | – ステートファイルのバックアップ – 並行実行の制御 |
| セキュリティ | – IAMロールの最小権限原則 – 認証情報の安全な管理 | – シークレット情報の漏洩防止 – アクセス制御の徹底 |
| 自動化 | – CI/CDパイプラインの構築 – テスト自動化の導入 | – エラーハンドリング – ロールバック手順の整備 |
| 監視 | – リソース状態の監視 – 実行ログの集中管理 | – アラート設定 – 異常検知の基準設定 |
- トラブルシューティングのためのチェックリスト
- Terraform実行エラー:
- ステートファイルの整合性確認
- IAM権限の確認
- リソース制限の確認
- 依存関係の確認
- Ansible実行エラー:
- インベントリファイルの確認
- SSH接続の確認
- ターゲットホストの権限確認
- Python依存関係の確認
- エラー防止のためのプラクティス
# ansible/roles/web/tasks/main.yml
---
- name: Check prerequisites
block:
- name: Verify Python installation
command: python3 --version
register: python_version
changed_when: false
- name: Verify nginx installation
command: nginx -v
register: nginx_version
changed_when: false
rescue:
- name: Install required packages
package:
name:
- python3
- nginx
state: present
このように、TerraformとAnsibleを効果的に連携させることで、インフラストラクチャのプロビジョニングから設定管理まで、一貫した自動化パイプラインを構築することができます。
実際運用のためのヒント集
ステート管理の効率化テクニック
- リモートステートの設定
# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# DynamoDBによるステートロック
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
- ワークスペース管理
# 開発環境用ワークスペースの作成 terraform workspace new dev # 本番環境用ワークスペースの作成 terraform workspace new prod # ワークスペースの切り替え terraform workspace select dev
- ステート分割の実装
# ネットワークリソース用のステート
module "network" {
source = "./modules/network"
providers = {
aws = aws.network
}
}
# アプリケーションリソース用のステート
module "application" {
source = "./modules/application"
providers = {
aws = aws.application
}
}
セキュリティ対策の実装方法
- IAMポリシーの最小権限設定
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:CreateTags",
"ec2:RunInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-northeast-1",
"aws:PrincipalTag/Environment": ["prod", "dev"]
}
}
}
]
}
- セキュリティグループのベストプラクティス
# セキュリティグループモジュール
module "security_groups" {
source = "./modules/security"
# 基本ルール
ingress_rules = [
{
port = 80
cidr_blocks = ["10.0.0.0/8"]
description = "HTTP from VPC"
},
{
port = 443
cidr_blocks = ["10.0.0.0/8"]
description = "HTTPS from VPC"
}
]
# 環境変数による制御
environment = var.environment
}
- 暗号化の実装
# S3バケットの暗号化設定
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# EBSボリュームの暗号化
resource "aws_ebs_encryption_by_default" "example" {
enabled = true
}
トラブルシューティングガイド
- ログ収集と分析の設定
# ansible/roles/logging/tasks/main.yml
---
- name: Install CloudWatch Agent
amazon.aws.aws_ssm_parameter_store:
name: "AmazonCloudWatch-Agent-Config"
value: |
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/messages",
"log_group_name": "/aws/ec2/system",
"log_stream_name": "{instance_id}"
},
{
"file_path": "/var/log/nginx/access.log",
"log_group_name": "/aws/ec2/nginx",
"log_stream_name": "{instance_id}-access"
}
]
}
}
}
}
region: ap-northeast-1
- モニタリングダッシュボードの作成
resource "aws_cloudwatch_dashboard" "main" {
dashboard_name = "infrastructure-metrics"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
properties = {
metrics = [
["AWS/EC2", "CPUUtilization", "InstanceId", "*"],
["AWS/EC2", "NetworkIn"],
["AWS/EC2", "NetworkOut"]
]
period = 300
stat = "Average"
region = "ap-northeast-1"
}
}
]
})
}
- 一般的な問題と解決方法
| 問題 | 原因 | 解決方法 |
|---|---|---|
| Terraformの実行が遅い | – ステートファイルが大きい – リソースが多い | – ステートの分割 – パラレル実行の設定 |
| Ansibleのタスクが失敗 | – SSH接続の問題 – 権限不足 | – SSHキーの確認 – sudoers設定の確認 |
| リソース制限エラー | – AWSのクォータ制限 | – クォータ引き上げリクエスト – リソース最適化 |
- 運用効率化のためのTips
# 便利なMakefileコマンド集
.PHONY: validate test apply-plan cleanup
validate:
cd terraform && terraform fmt -check
cd terraform && terraform validate
cd ansible && ansible-playbook site.yml --syntax-check
test:
cd terraform && terraform plan -var-file=test.tfvars
cd ansible && ansible-playbook site.yml --check
apply-plan:
cd terraform && terraform plan -out=tfplan
cd terraform && terraform show -json tfplan > plan.json
python3 scripts/analyze_plan.py plan.json
cleanup:
find . -type f -name "*.tfstate*" -not -path "*/\.*" -delete
find . -type d -name ".terraform" -exec rm -rf {} +
- 障害復旧手順
#!/bin/bash
# recovery.sh
# バックアップからの復旧
restore_from_backup() {
local backup_date=$1
# ステートファイルの復元
aws s3 cp s3://terraform-state-backup/backup-${backup_date}.tfstate \
s3://terraform-state/terraform.tfstate
# インフラの再適用
terraform init
terraform plan
terraform apply -auto-approve
# 設定の再適用
ansible-playbook -i inventory site.yml
}
# 使用例: ./recovery.sh 2024-01-22
運用のベストプラクティス:
- 定期的なメンテナンス
- セキュリティパッチの適用
- リソースの最適化
- コストの見直し
- バックアップ戦略
- 定期的なステートファイルのバックアップ
- AMIの定期作成
- データベースのスナップショット
- ドキュメント管理
- 設計書の更新
- 運用手順書の整備
- トラブルシューティングガイドの更新
- モニタリングと監視
- メトリクスの収集
- アラートの設定
- ログの集中管理
これらの運用ヒントを実践することで、TerraformとAnsibleを使用したAWS環境の安定した運用が可能になります。