【Terraform入門】IaCでAWSのEC2を構築する + python環境

プログラミング

はじめに

この記事では、TerraformをつかってAWSのEC2インスタンスを構築してみます。

Terraformとは、AWS、GCP、Azureなどのインフラストラクチャをコードベースで設計できるツールです。つまり、IaC(Infrastructure as Code)を実現するツールになります。

これにより、インフラストラクチャを安全かつ効率的に管理することができ、git等でバージョン管理することにより、システムの変更履歴を簡単に追跡することができます。コマンド一発でインフラの構築、削除できるのが魅力です。

Terraformのインストールはこちら▼からおこなえます。

Install | Terraform | HashiCorp Developer
Explore Terraform product documentation, tutorials, and examples.

この記事のソースコードは以下のGitHubから手に入れることができます。

GitHub - Joichiro433/Blog-terraform-aws
Contribute to Joichiro433/Blog-terraform-aws development by creating an account on 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を利用することでコードベースでインフラの管理をすることができます。

タイトルとURLをコピーしました