頑張るときはいつも今

自称エンジニアのゴリラです。日々精進。

【AWS】TerraformでVPC構築からEC2を動作させてみた#3(EC2構築)

もう年が終わりますね。

私は退職前ということもあり、年納めの日は有給をいただきました(納会に参加できるわけがない)

大掃除も飽きてきたので前回の続きをしていきたいと思います

何をやるか

  • 先日の記事と同様に下記構成をTerraformで構築していきたいと思います。
    • 単一AZにパブリックサブネットとプライベートサブネットを構築
    • プライベートサブネットのEC2にはMYSQLをインストールして、パブリックサブネットのEC2からのみアクセスできるようにする
    • NAT GWをパブリックサブネットに配置して開発者がSSH接続できるようにする
  • 今日はパブリックサブネットとプライベートサブネットにそれぞれ1つずつ立てるEC2の構築です。

予備知識

毎度、めちゃくちゃ大雑把ですがメモレベルの予備知識

EC2とは

  • 従量課金制で利用することができる仮想ササーバー
    • 利用する仮想サーバの単位をインスタンスとか言ったりする
  • OSはAMI(Amazon Machine Image)から選択して起動するためOSより上のレイヤーを自分で構築していくことになる

インスタンスタイプと世代

インスタンスタイプの種類

違いについて感覚的に分かりやすかった記事

  • GPU搭載だとかインスタンスの用途別にインスタンスファミリーという形で分類されている
  • さらにインスタンスタイプの中でも何代目とかで別れている
  • 利用するインスタンスタイプと世代が決まったら、ハードウェアのリソース(メモリだとかストレージだとか)を指定してインスタンスタイプが決まる
t2.nano
            t2: 汎用ファミリーと世代(tシリーズの2世代目)
            nano: インスタンスの容量

AMI

  • EC2で利用されるOSのディスクイメージ
    • 自分で構築したインスタンスをAMIとして保存することもできる
    • Thidr PartyのAMIを売られているため、そちらを利用することも可能。(LastlineのAMI使ってみたいなー)

公式では、下記のように記載されているが要はEC2インスタンスの元となるテンプレートと捉えていいのではないでしょうか

Amazon マシンイメージ(AMI)は、ソフトウェア構成(オペレーティングシステムアプリケーションサーバー、アプリケーションなど)を記録したテンプレートです。 AMI から、クラウドで仮想サーバーとして実行される AMI のコピーであるインスタンスを起動します。 AMI には次が含まれています。

インスタンスのルートボリュームのテンプレート(オペレーティングシステムアプリケーションサーバー、アプリケーションなど)

起動許可(AMI を使用してインスタンスを起動する権限を特定の AWS アカウントに与える)

インスタンスの起動時にインスタンスにアタッチするボリュームを指定するブロックデバイスマッピング

EC2の利用形態(どのような形でお金を払うか)

クラスメソッドさんのすごく分かりやすかった記事

前述の通り、EC2は利用時間に応じてお金がかかる`従量課金`の仕組みなのですが、通常とは異なる利用形態もあります。

それがリザーブインスタンスとスポットインスタンスです。

リザーブインスタンス

  • 利用期間が長期利用を前提とした利用形態
    • 通常利用よりも割引されて利用することができる
  • 期間中にインスタンスファミリー、OSが変更できるコンパーティブルと変更ができないスタンダードがある
  • もちろんスタンダードの方が割引率が高くなる

スポットインスタンス

  • AWSさん側では、リソース不足に陥らないために多めに予備分を含めたリソースを蓄えている
  • 予備分のリソースが余剰となった際に、インスタンスに値段をつけて入札式で払い出す
  • 自分の入札価格が最高入札で有る限り、通常利用よりもコストを抑えてEC2インスタンスが利用できる

EC2が利用するストレージについて

  • EC2インスタンスが直接利用するストレージにはEBSインスタンスストアがある
  • 大雑把な違いしか分かりませんが、 EBSは不揮発性(インスタンスを止めても電源は失われない)に対して、インスタンスストアは揮発性(インスタンスストアを止めると消える)
    • インスタンスストアは一時データを保持するためのストレージと捉えればいいかも

EBS(Elastic Block Storage)

  • 名前の通り、ブロック型のストレージになります
  • EC2インスタンスとネットワーク接続されている仮想ストレージ的な位置付けになる
  • ストレージのサイズと性能で料金が変わってくる

インスタンスストア

  • 物理ホストに内蔵されたディスクストレージ
  • インスタンスを再起動すると物理ホストが切り替わるため、データは消えてしまう

Terraformの実装

設計方針

パブリックサブネットのEC2インスタンスは下記をコード化する

  • インスタンス起動時にhttpdのインストールとサービスの開始を行う
  • KeyPairは作成済みを使う

プライベートサブネットのインスタンスは下記をコード化する

  • インスタンス起動時にmysqlをインストールとサービスの開始を行う
  • Mysqlの設定はとりあえずはやらずにパブリックサブネット側のEC2からnmapを使って擬似的に再現

コードの実装

共通操作

variable "instance_type" {}
variable "key_name" {}
variable "vpc_security_group_web" {}
variable "vpc_security_group_db" {}
variable "vpc_security_group_ssh" {}


variable "public_subnet" {}
variable "private_subnet" {}



###########################################
# Resouce (Create Key Pair)               #
###########################################
locals {
    public_key_file  = "./ec2/${var.key_name}.pub"
}

resource "aws_key_pair" "instance_ssh_key_pairs" {
    key_name         = "${var.key_name}"
    public_key       = "${file(local.public_key_file)}"
}



###########################################
# Data Resource(Get most recent AMI image)#
###########################################
data "aws_ami" "recent_amazon_linux_2" {
    most_recent = true
    owners      = ["amazon"]

    filter {
        name    = "architecture"
        values  = ["x86_64"]
    }
    filter {
        name    = "root-device-type"
        values  = ["ebs"]
    }
    filter {
        name    = "name"
        values  = ["amzn2-ami-hvm-2.0.????????.?-x86_64-gp2"]
    }
    filter {
        name    = "virtualization-type"
        values  = ["hvm"]
    }
    filter {
        name    = "block-device-mapping.volume-type"
        values  = ["gp2"]
    }

}

パブリックサブネット側のEC2インスタンス

tfファイル

  • aws_instanceを使ってEC2インスタンスを作成する
  • amiにはdata resourceから取得したAMIのidを代入する
  • key_nameは上で作成したキーペアを指定する
  • vpc_security_groupsでセキュリティグループに所属させる
    • 前回の記事で作成したweb-sgssh-sgに参加
  • subne_idで作成済みのサブネットに構築する
  • user_dataインスタンス起動後に実行するコマンドを定義
###########################################
# Resource(Create Web Server)             #
###########################################

resource "aws_instance" "public_subnet_instance" {
    ami                    = "${data.aws_ami.recent_amazon_linux_2.image_id}"
    instance_type          = "${var.instance_type}"
    key_name               = "${aws_key_pair.instance_ssh_key_pairs.id}"
    vpc_security_group_ids = ["${var.vpc_security_group_web}", "${var.vpc_security_group_ssh}"]
    subnet_id              = "${var.public_subnet}"
    private_ip             = "10.0.1.10"
    tags                   = {
        Name = "Test Public Web Server"
    }

    user_data              = "${file("./ec2/httpd.sh")}"
}

httpd.sh

  • ただhttpdをインストールして起動しているだけのズボラ設定
# !/bin/bash

yum install -y httpd

systemctl start httpd.service

プライベートサブネット側のEC2インスタンス

tfファイル

  • Webサーバと設定内容はほぼ同等
    • 所属するセキュリティグループだとかサブネットが異なるぐらい
###########################################
# Resource(Create DB Server)              #
###########################################

resource "aws_instance" "private_subnet_instance" {
    ami                    = "${data.aws_ami.recent_amazon_linux_2.image_id}"
    instance_type          = "${var.instance_type}"
    key_name               = "${aws_key_pair.instance_ssh_key_pairs.id}"
    vpc_security_group_ids = ["${var.vpc_security_group_db}", "${var.vpc_security_group_ssh}"]
    subnet_id              = "${var.private_subnet}"
    private_ip             = "10.0.2.10"
    tags                   = {
        Name  = "Test Private DB Server"
    }

    user_data              = "${file("./ec2/db.sh")}"
}

db.sh

  • mysqlを起動させるだけ
    • 今回はnmapを使ってポートが開いているか最後に確認する程度
# !/bin/bash

yum -y install -y mariadb-server

systemctl start mariadb.servcice

出力値

###########################################
# Public Instance                         #
###########################################
output "public_instance_ip_address" {
    value = "${aws_instance.public_subnet_instance.public_ip}"
}
output "public_instance_id" {
    value = "${aws_instance.public_subnet_instance.id}"
}
###########################################
# Private Instance                        #
###########################################
output "private_instance_id" {
    value = "${aws_instance.private_subnet_instance.id}"
}

###########################################
# Key                                     #
###########################################
output "key_pair" {
    value = "${aws_key_pair.instance_ssh_key_pairs.key_name}"
}

呼び出し元のtfファイル

  • module_ec2_instanceで各種変数を代入している
    • vpc**_subnetの変数は、他モジュールで作成したリソースの出力値を代入
provider "aws" {
    region  = "${var.region}"
    profile = "${var.profile}"
}

module "module_vpc" {
  source = "./vpc"
}

module "module_security_group" {
  source = "./sg"
  vpc_id = "${module.module_vpc.vpc_id}"
}

module "module_ec2_instance" {
  source                 = "./ec2"
  instance_type          = "t3.micro"
  key_name               = "terraform-test"
  vpc_security_group_web = "${module.module_security_group.web_sg_id}"
  vpc_security_group_db  = "${module.module_security_group.db_sg_id}"
  vpc_security_group_ssh = "${module.module_security_group.ssh_sg_id}"
  public_subnet          = "${module.module_vpc.public_subnet_id}"
  private_subnet         = "${module.module_vpc.private_subnet_id}"
}


output "nat_gateway_ip_address" {
  value = "${module.module_vpc.nat_gateway_ip}"
}

output "ec2_public_instance_ip" {
  value = "${module.module_ec2_instance.public_instance_ip_address}"
}

output "ec2_public_instance_id" {
  value = "${module.module_ec2_instance.public_instance_id}"
}

output "ec2_private_instance_id" {
  value = "${module.module_ec2_instance.private_instance_id}"
}

output "ec2_ssh_key_pair_name" {
  value = "${module.module_ec2_instance.key_pair}"
}

実行結果

  • terraform plan からterraform applyを実行し、必要な情報が出力されていることを確認
(省略)
Outputs:
ec2_private_instance_id = (インスタンスID)
ec2_public_instance_id = (インスタンスID)
ec2_public_instance_ip = (EC2のパブリックIPアドレス)
ec2_ssh_key_pair_name = terraform-test
nat_gateway_ip_address = (NAT GatewauのパブリックIPアドレス)
  • まずは、パブリックサブネットのEC2インスタンスSSH接続
    • 作成したキーペアを指定してアクセスできている
shinoAir% ssh ec2-user@[IPアドレス] -i ~/.ssh/terraform-test
The authenticity of host '[IPアドレス]' can't be established.
ECDSA key fingerprint is SHA256:***********.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[IPアドレス]' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
16 package(s) needed for security, out of 32 available
Run "sudo yum update" to apply all updates.
  • Webサーバが動作できているようです。
waragaishinoAir% curl 52.194.254.19
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
(省略)
shinoAir% ssh ec2-user@[NATゲートウェイ] -i ~/.ssh/terraform-test
ssh: connect to host [NATゲートウェイ] port 22: Operation timed out
  • ということで、パブリックサブネット側のEC2を踏み台にして接続できるようにセキュリティグループを1つ追加してリトライ
    • 先ほどのssh-sgは、パブリックサブネット専用に変更
[ec2-user@ip-10-0-1-10 ~]$ ssh -i ~/.ssh/terraform-test ec2-user@10.0.2.10
The authenticity of host '10.0.2.10 (10.0.2.10)' can't be established.
ECDSA key fingerprint is SHA256:****************.
ECDSA key fingerprint is MD5:*********.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.2.10' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
  • プライベートサブネット側の3306/tcpが開いているかプライベート側から確認
    • 文字化けしてるけど、接続はできているっぽいですね
[ec2-user@ip-10-0-1-10 ~]$ nc 10.0.2.10 3306
R
5.5.64-MariaDBueySq^bQ� �P|Bp2C<BxU}9mysql_native_password
^C

諸々確認できたので、今回作成したリソースは`terraform destory`で削除しておわり!!

まとめ

  • 今回でとりあえず1シリーズは終了です。
    • VPC
    • セキュリティグループ
  • EC2

  • 次はRDSとかS3を使って、シンプルな構成をもう一度作成していきたいと思います

蛇足

今週は4回トレーニングにいけました。ど素人の重さしか挙げれていないです。

  • 火曜日(三頭、二頭)

    • トライセプスエクステンション 30KG 10回 3セット
    • ディップス 10回 3セット
    • プレスダウン 21.5kg 10回 3セット
    • ランニング 30分
  • 木曜日(胸)

  • 金曜日(脚、肩)

    • スクワット 60kg 10回 3セット
    • レッグプレス 90kg 10回 3セット
    • レッグカール 35kg 10回 3セット
    • レッグエクステンション 60kg 10回 3セット
    • ダンベルショルダープレス 14kg 10回 3セット
    • サイドレイズ 8kg 10回 3セット
    • ランニング 40分
    • 坂道ウォーキング 10分
  • 日曜日(予定, 背中)

    • デッドリフト 60kg 10回 3セット
    • ベントオーバーロウ 35kg 10回 3セット
    • ラットプルダウン 42kg 10回 3セット
    • 懸垂 6~7回 ?セット
    • ランニング 40分