はじめに
この記事では、TerraformをつかってAWSのEC2インスタンスを構築してみます。
Terraformとは、AWS、GCP、Azureなどのインフラストラクチャをコードベースで設計できるツールです。つまり、IaC(Infrastructure as Code)を実現するツールになります。
これにより、インフラストラクチャを安全かつ効率的に管理することができ、git等でバージョン管理することにより、システムの変更履歴を簡単に追跡することができます。コマンド一発でインフラの構築、削除できるのが魅力です。
Terraformのインストールはこちら▼からおこなえます。
この記事のソースコードは以下のGitHubから手に入れることができます。
作成するAWSの構成と準備
構成
今回、Terraformで作成するインフラの概要を下図に示します。
この構成では、VPC (Virtual Private Cloud) によりプライベートな仮想ネットワークを立ち上げます。
その Pubolic subnet の中に EC2 (Elatic Compute Cloud) を配置します。このEC2にはpythonを使用できるようにMinicondaをインストールしておきます。
また、セキュリティのために、Security Group によるEC2のアクセス制限を設定します。
準備
AWSをTerraformから操作するためにAWS CLIの登録が必要です。方法は ▼こちらの記事で述べています。
まずは、EC2でssh接続ができるように秘密鍵・公開鍵を作成します。それらは、~/.ssh
ディレクトリに保存します。
$ ssh-keygen -t rsa -f ~/.ssh/demo-key -N ''
# 出力
Generating public/private rsa key pair.
Your identification has been saved in /Users/jo/.ssh/demo-key.
Your public key has been saved in /Users/jo/.ssh/demo-key.pub.
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx@xxxxx-xxx
The key's randomart image is:
+---[RSA 3072]----+
| ..... . ..|
| . . . o.+|
| o o . . .+|
| * = . o|
| + S + = ..o|
| B + + = = +|
| * O o o . oo |
| E = + . ...=|
| . .o .. oo|
+----[SHA256]-----+
そして、公開鍵: demo-key.pub の権限を読み取り専用に変更します。
$ sudo chmod 400 ~/.ssh/demo-key.pub
以上で秘密鍵・公開鍵の作成は完了です。
Terraformコード
これから用意するのは、インフラ構築のためのterraformコード 4ファイルと、Minicondaインストール用のシェルスクリプト 1ファイルです。ディレクトリ構造は次のようになります。
.
├─ ec2.tf
├─ provider.tf
├─ vpc.tf
├─ output.tf
└─ setup.sh
プロバイダの設定
provider.tf
# Provider
provider "aws" {
profile = "default"
region = "ap-northeast-1"
}
Provider
- TerrafromはAWSだけでなく、GCP、Azureにも対応しています。そのAPIの違いを吸収する役割がプロバイダです。今回はAWS上で構築するので、プロバイダをAWSに設定しています
EC2の設定
ec2.tf
# AMI
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
# EC2 Instance
resource "aws_instance" "demo" {
ami = data.aws_ami.ubuntu.id
vpc_security_group_ids = [aws_security_group.demo.id]
subnet_id = aws_subnet.public.id
key_name = aws_key_pair.demo.id
instance_type = "t3.micro"
user_data = file("./setup.sh")
tags = {
Name = "demo"
}
}
# Elastic IP
resource "aws_eip" "demo" {
instance = aws_instance.demo.id
vpc = true
}
# Key Pair
resource "aws_key_pair" "demo" {
key_name = "demo-key"
public_key = file("~/.ssh/demo-key.pub")
}
AMI
- データソースを使うと、外部データを参照することができます。ここでは、ubuntu20.04の最新のAMIを取得しています。
EC2 Instance
- データソースで記載したubuntuのAMIを渡します。
- ssh等でEC2にアクセスするにはセキュリティグループが必要です。後ほど
vpc.tf
で記載するセキュリティグループのidをvpc_security_group_idsに渡します。リスト形式で渡すため、値を [ ] で囲みます。 - user_dataにシェルスクリプトを渡すことで、インスタンス作成時にその内容の処理が走ります。
Elastic IP
- EIPをつかって、パブリックIPアドレスを固定します。
Key Pair
ssh-keygen
コマンドで作成した公開鍵を指定します。
VPCの設定
vpc.tf
# VPC
resource "aws_vpc" "demo" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true # DNSサーバーによる名前解決を有効化
enable_dns_hostnames = true # パブリックDNSホスト名を自動的に割り当て
tags = {
Name = "demo"
}
}
# Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.demo.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "demo"
}
}
# Internet Gateway
resource "aws_internet_gateway" "demo" {
vpc_id = aws_vpc.demo.id
tags = {
Name = "demo"
}
}
# Route Table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.demo.id
tags = {
Name = "public_demo"
}
}
resource "aws_route" "demo" {
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.demo.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "demo" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# Security Group
resource "aws_security_group" "demo" {
vpc_id = aws_vpc.demo.id
name = "demo"
tags = {
Name = "demo"
}
}
# インバウンドルール(ssh接続用)
resource "aws_security_group_rule" "in_ssh" {
security_group_id = aws_security_group.demo.id
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
from_port = 22
to_port = 22
protocol = "tcp"
}
# インバウンドルール(pingコマンド用)
resource "aws_security_group_rule" "in_icmp" {
security_group_id = aws_security_group.demo.id
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
from_port = -1
to_port = -1
protocol = "icmp"
}
# アウトバウンドルール(全開放)
resource "aws_security_group_rule" "out_all" {
security_group_id = aws_security_group.demo.id
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
from_port = 0
to_port = 0
protocol = "-1"
}
VPC
- VPCは他のネットワークから論理的に切り離されている仮想ネットワークです。EC2などのリソースを配置します。
Subnet
- VPCをさらに分割し、インターネットからアクセス可能なパブリックサブネットを定義します。
Internet Gateway
- VPCは隔離されたネットワークであり単体でインターネットと通信ができないため、インターネットゲートウェイを作成し、VPCとインターネット間で通信ができるようにします。
Route Table
- VPCとインターネット間で通信するためには、インターネットゲートウェイの他にルートテーブルが必要です。ネットワークにデータを流すため、ルーティング情報を管理します。
- ルートはルートテーブルの1レコードに対応します。デフォルトゲートウェイ: 0.0.0.0/0 を destination_cidr_block に指定します。
- どのルートテーブルをつかってルーティングするかを aws_route_table_association で定義します。
Security Group
- sshでEC2インスタンスにアクセスできる様にポート22を開放します。
Outputの定義
output.tf
# Output
output "public_ip" {
value = aws_eip.demo.public_ip
}
Output
- outputにはapply後に標準出力される内容を記載します。今回はパブリックIPアドレスを出力します。
シェルスクリプトの定義
setup.sh
#!/bin/bash
cd /opt
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
sh /opt/Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda3
echo 'PATH=/opt/miniconda3/bin:$PATH' >> /home/ubuntu/.bashrc && source /home/ubuntu/.bashrc
- インスタンス作成時に走る内容を記載します。Minicondaをインストールしてpythonを使えるようにしています。
Terraformでインフラ構築
リソースの作成
まずは terraform init
コマンドでリソース作成に必要なバイナリファイルをダウンロードします。
$ terraform init
# 出力
Initializing the backend...
Initializing provider plugins...
Terraform has been successfully initialized!
terraform apply
コマンドを実行するとAWS上にリソースが作成されます。途中、確認が求められるので yes
を入力します。
$ terraform apply
しばらくすると、リソースの作成が完了し、EC2インスタンスのパブリックIPアドレスが出力されます。
# 出力
Apply complete! Resources: 13 added, 0 changed, 0 destroyed.
Outputs:
public_ip = "35.78.167.186"
AWSコンソールでもEC2インスタンスの作成が確認できます。
ssh接続の確認
パブリックIPアドレスの接続を確認してみます。今回は public_ip = "35.78.167.186"
でした。
まずはpingの確認です。
$ ping 35.78.167.186
# 出力
PING 35.78.167.186 (35.78.167.186): 56 data bytes
64 bytes from 35.78.167.186: icmp_seq=0 ttl=42 time=18.675 ms
64 bytes from 35.78.167.186: icmp_seq=1 ttl=42 time=15.546 ms
64 bytes from 35.78.167.186: icmp_seq=2 ttl=42 time=15.935 ms
64 bytes from 35.78.167.186: icmp_seq=3 ttl=42 time=16.440 ms
次はssh接続の確認をします。初めての接続の場合は確認が求められるので yes
を入力します。
$ ssh -i ~/.ssh/demo-key ubuntu@35.78.167.186
# 出力
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.13.0-1021-aws x86_64)
...
ubuntu@ip-10-0-1-137:~$
ssh接続することができました。最後にssh接続したEC2インスタンスのubuntuでpythonが使用できるか確認します。
ubuntu@ip-10-0-1-137:~$ python
# 出力
Python 3.9.7 (default, Sep 16 2021, 13:09:58)
[GCC 7.5.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
EC2インスタンスにpythonがインストールされていますね。
リソースの削除
作成したリソース一式は、terraform destroy
コマンドで削除することができます。途中、確認が求められるので yes
を入力します。
$ terraform destoroy
# 出力
Destroy complete! Resources: 13 destroyed.
AWSコンソールでEC2インスタンスの削除が確認できます。
このように、Terraformを利用することでコードベースでインフラの管理をすることができます。