0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform初心者の備忘録 〜IaCを使うときに学んだあれやこれ〜

Posted at

はじめに

グループワークにおいてawsを使う必要があったけど金かからんようにするためだけに毎回GUI操作をログインしてEC2起動して停止して、、、ってやるの面倒だったからIaC使ってみたって話。できたこと、できなかったこともまとめる。

今回の課題

  • apacheからの動画ストリーミングと動画アップロードを受け取り、保存するサーバの構築
  • Go言語でのデータベース操作のAPIサーバの構築
  • MySQLデータベースのサーバ構築

そもそもIaCってなんぞや?

聞いたことあっても自信を持ってこれ!!って説明できなかったからこの際にしっかりと。

参考:RedHat IaC (Infrastructure as Code) とは

IaC : Infrastructure as a Codeの略

IaC によるインフラストラクチャのプロビジョニングを自動化することで、開発者は、アプリケーションを開発またはデプロイするたびに、サーバー、オペレーティングシステム、ストレージ、およびその他のインフラストラクチャ・コンポーネントのプロビジョニングと管理を手動で行う必要がなくなります。インフラストラクチャを体系化すれば、プロビジョニングをテンプレート化できます。

IaCのメリット

  • コスト削減
  • デプロイメントの高速化
  • エラーの減少
  • インフラストラクチャの一貫性の向上
  • 構成ドリフトの排除

とのことなので、今回は人的エラーの減少とインフラストラクチャの一貫性の向上(コード化することで毎回同じインフラを構築すること)を目的にTerraformを使ってみよう。

というわけで、本題。

今回行うこと

Solution (1).png

これをTerraformで作ります。(SecurityGroupが一個だったり、AZが一個だったり、、色々ツッコミどころはありますが、、今回はこれで事足りるのでこれを作ります。&無料分で自分ができる最大限がこれでした。)

細かな仕様はこんな感じ

詳細仕様

  • VPC
    • CIDR:10.0.0.0/16
  • Public Subnet
    • VPC(上記のもの)
    • CIDR:10.0.3.0/24
    • IPの自動割り当て:有効化
  • IGW
    • VPC(上記のもの)
  • Route table
    • VPC(上記のもの)
    • CIDR:0.0.0.0/0
    • IGW(上記のもの)
    • Subnet(上記のもの)
  • Security Group(ガバガバなので非推奨)
    • インバウンドルール
      • 22
        • sshが好ましい。ミスって今回tcpになってました
        • CIDR:0.0.0.0/0
      • 80
        • tcp
        • CIDR:0.0.0.0/0
      • 5000
        • tcp
        • CIDR:0.0.0.0/0
      • 443
        • tcp
        • CIDR:0.0.0.0/0
      • 9090
        • tcp
        • CIDR:0.0.0.0/0
      • 3306
        • tcp
        • CIDR:0.0.0.0/0
    • アウトバウンドルール
      • 任意の場所
  • EC2
    • Apacheサーバ
      • t2.micro
      • ami:amazonlinux
      • subnet,SecurityGroup(上記のもの)
      • ボリューム:13GB
    • MySQLサーバ
      • t2.micro
      • ami:amazonlinux2
      • subnet,SecurityGroup(上記のもの)
      • ボリューム:8GB
    • Goサーバ
      • t2.micro
      • ami:amazonlinux
      • subnet,SecurityGroup(上記のもの)
      • ボリューム:8GB

sshtcpの違いはなんなのか?

sshもtcpの一種

レイヤーの違い
ssh:OSI第7層(アプリケーション層)
tcp:OSI第4層(トランスポート層)
目的の違い
tcp:信頼性のあるデータ転送を提供
ssh:安全なリモートアクセスと通信を提供

さらっと出てきてるけどCIDRについてもまとめる

CIDRとはなんぞや?

参考:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
CIDR:Classless Inter-Domain Routingの略
アドレスクラスの概念を気にしないことで、IPアドレスの割り当てや経路選択などの自由度を上げる仕組み

参考:AWS CIDRとは?

インターネット上のデータルーティング効率を向上させる IP アドレス割り当て方法です。インターネットに接続するすべてのマシン、サーバー、およびエンドユーザーデバイスには、IP アドレスと呼ばれる固有の番号が関連付けられています。デバイスは、これらの IP アドレスを使用して相互に検索し、通信します。組織は CIDR を使用して、ネットワークに IP アドレスを柔軟かつ効率的に割り当てます。

CIDRのメリット

  • IP アドレスの浪費を削減

  • データをすばやく送信

  • 仮想プライベートクラウドを作成する

  • スーパーネットを柔軟に作成

ここから先は趣旨がズレるので省きます、、

ようやくTerraformでの実装!!!

Terraformファイルの作成

今回は変数ファイルなどを使わずにそのまま書き込みます。
また、解説は要所要所のみ行います。

プロバイダーの設定

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.8.0"
}

terraform:実行環境のバージョン指定やプロバイダー指定を行うブロック
required_providers:terraformがインストールして実行できるように利用するプロバイダーを指定する
required_version:terraform自体のバージョンを指定する

プロバイダーの詳細設定

provider "aws" {
  region     = "ap-northeast-1" # 適宜変更
  access_key = "*********"
  secret_key = "*****************"
}

region:awsのサービスを展開するリージョンを指定
access_key:IAMユーザのアクセスキー
secret_key:IAMユーザのシークレットキー

リソースブロック

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "TerraformVPC"
  }
}

resource:サービスの種類、任意の名前で指定
resource "aws_vpc" "main"はVPCをmainという名前で作成する
cidr_block:CIDR範囲を指定
tags:GUIから確認した時の名前を決める

セキュリティグループリソースブロックの作成

resource "aws_security_group" "allow_ssh_http" {
  vpc_id = aws_vpc.main.id
  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 = "TerraformSecurityGroup"
  }
}

ingress:インバウンドルールの作成
egress:アウトバウンドルールの作成

EC2リソースブロックの作成

resource "aws_instance" "Apache" {
  ami             = "ami-02a405b3302affc24" # 適宜変更
  instance_type   = "t2.micro"
  subnet_id       = aws_subnet.public.id
  security_groups = [aws_security_group.allow_ssh_http.id]
  key_name        = "mojito"

  root_block_device {
    volume_size = 13
  }
  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y httpd
              EOF
  tags = {
    Name = "Apache"
  }
}

ami:イメージの指定
instance_type:インスタンスタイプを指定
subnet_id:どこのサブネットに置くか指定
security_groups:適応するセキュリティグループの指定
root_block_device:今回はvolume_size(容量)のみ指定
user_data:インスタンス作成時に実行するスクリプトを書き込む(前提としてsudoで実行される)

outputブロック

output "Apache_public_ip" {
  description = "The public IP address of the Apache instance"
  value       = aws_instance.Apache.public_ip
}

outputterraform applyコマンド実行後に指定された出力を行う
今回の場合、ターミナルに「The public IP address of the Apache instance」と文字列と作成されたApacheのインスタンスのipアドレスが出力される

てな感じで、、出来上がったのがこちら。

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

  required_version = ">= 1.8.0"
}

provider "aws" {
  region     = "ap-northeast-1" # 適宜変更
  access_key = "***********"
  secret_key = "***************"
}

# VPCの作成
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "TerraformVPC"
  }
}

# パブリックサブネットの作成
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.3.0/24"
  map_public_ip_on_launch = true

  tags = {
    Name = "TerraformPublicSubnet"
  }
}


# インターネットゲートウェイの作成
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "TerraformIGW"
  }
}

# ルートテーブルの作成とインターネットゲートウェイへのルート追加
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

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

  tags = {
    Name = "TerraformPublicRouteTable"
  }
}

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

# セキュリティグループの作成
resource "aws_security_group" "allow_ssh_http" {
  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"]
  }

  ingress {
    from_port   = 5000
    to_port     = 5000
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 9090
    to_port     = 9090
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 3306
    to_port     = 3306
    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 = "TerraformSecurityGroup"
  }
}

# EC2インスタンス(13GBのEBSボリュームを持つt2.microインスタンス)
resource "aws_instance" "Apache" {
  ami             = "ami-02a405b3302affc24" # 適宜変更
  instance_type   = "t2.micro"
  subnet_id       = aws_subnet.public.id
  security_groups = [aws_security_group.allow_ssh_http.id]
  key_name        = "MY_KEY"

  root_block_device {
    volume_size = 13
  }

  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y httpd
              systemctl start httpd
              systemctl enable httpd
              echo "AddType video/mp4 .mp4" >> /etc/httpd/conf/httpd.conf
              systemctl restart httpd

              yum install -y git

              # githubからコードを取得
              git clone https://github.com/mrAkito/python-apache-open.git
              cd python-apache-open
              chmod +x app.py
              chmod +x changePermission.sh
              mkdir /var/www/html/uploads
              chmod 777 /var/www/html/uploads
              cd /var/www/html

              # ダミー動画ファイルのダウンロード
              curl -O https://*********

              # Flaskのインストール
              yum install -y python3
              yum install -y python3-pip
              pip3 install flask
              EOF
  tags = {
    Name = "Apache"
  }
}

# EC2インスタンス(8GBのEBSボリュームを持つt2.microインスタンス)
resource "aws_instance" "MySQL" {
  ami             = "ami-08c07d6e7c1274e2a"
  instance_type   = "t2.micro"
  subnet_id       = aws_subnet.public.id
  security_groups = [aws_security_group.allow_ssh_http.id]
  key_name        = "MY_KEY"

  root_block_device {
    volume_size = 8
  }

  user_data = <<-EOF
              #!/bin/bash
              sudo yum update -y
              sudo yum remote -y mariadb
              sudo yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-11.noarch.rpm
              sudo yum install -y --enablerepo=mysql80-community mysql-community-server
              sudo yum install -y --enablerepo=mysql80-community mysql-community-devel
              yum list installed | grep mysql
              sudo touch /var/log/mysqld.log
              sudo systemctl start mysqld
              sudo systemctl enable mysqld
              # MySQLのパスワード設定をオフにする
              echo "[mysqld]" | sudo tee -a /etc/my.cnf
              echo "skip-grant-tables" | sudo tee -a /etc/my.cnf
              echo "bind-address = 0.0.0.0" | sudo tee -a /etc/my.cnf
              sudo systemctl restart mysqld
              # MySQLにデータベースを作成
              mysql -u root -e "CREATE DATABASE DATABASE;"
              mysql -u root SolutionDB -e "
              CREATE TABLE IF NOT EXISTS 
              (中略)
              "
              sudo sed -i '/skip-grant-tables/d' /etc/my.cnf
              sudo systemctl restart mysqld
              echo "MYSQL_PASSWORD=$(sudo grep 'temporary password' /var/log/mysqld.log |sudo awk '{print $NF}')" >> .bash_profile
              mysql -u root -p $MYSQL_PASSWORD -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'NEWPASSWORD';"
              # 全ての権限を持つユーザを作成
              mysql -u root -p NEWPASSWORD -e "CREATE USER 'USER'@'%' IDENTIFIED BY 'DATABASE_PASSWORD';"
              mysql -u root -p NEWPASSWORD -e "GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' IDENTIFIED BY 'solution2024_MySQL'; FLUSH PRIVILEGES;"
              mysql -u root -p NEWPASSWORD -e "GRANT ALL PRIVILEGES ON SolutionDB.* TO 'admin'@'%'; FLUSH PRIVILEGES;"
              sudo systemctl restart mysqld
              # スワップファイルの作成
              sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
              sudo chmod 600 /swapfile
              sudo mkswap /swapfile
              sudo swapon /swapfile
              EOF
  tags = {
    Name = "MySQL"
  }
}

# EC2インスタンス(8GBのEBSボリュームを持つt2.microインスタンス)
resource "aws_instance" "Go" {
  ami             = "ami-02a405b3302affc24" # 適宜変更
  instance_type   = "t2.micro"
  subnet_id       = aws_subnet.public.id
  security_groups = [aws_security_group.allow_ssh_http.id]
  key_name        = "MY_KEY"

  root_block_device {
    volume_size = 8
  }

  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum remove golang
              curl -O https://dl.google.com/go/go1.22.0.linux-amd64.tar.gz
              cd
              tar -xvf go1.22.0.linux-amd64.tar.gz
              mv go /usr/local
              cd /home/ec2-user
              echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
              source ~/.bash_rc

              yum install -y git

              git clone https://github.com/mrAkito/SolutionDev
              cd SolutionDev
              go mod tidy
              echo "DB_HOST=${aws_instance.MySQL.public_ip}" > .env
              echo "DB_USER=USER" >> .env
              echo "DB_PASSWORD=DATABASE_PASSWORD" >> .env
              echo "DB_NAME=DATABASE" >> .env
              echo "DB_PORT=3306" >> .env
              sudo shutdown -r now
              EOF
  tags = {
    Name = "GoAPI"
  }
}

output "Apache_public_ip" {
  description = "The public IP address of the Apache instance"
  value       = aws_instance.Apache.public_ip
}

output "Go_public_ip" {
  description = "The public IP address of the Go instance"
  value       = aws_instance.Go.public_ip
}

output "MySQL_public_ip" {
  description = "The public IP address of the MySQL instance"
  value       = aws_instance.MySQL.public_ip
}

Terraformの実行

上記のファイルが出来上がったら、Terraformコマンドを実行してインフラを構築していく!

構築編

terraform init

構成ファイルを含むディレクトリで行うことでTerraformでの環境構築に必要な情報を作成してくれる。

terraform plan

この構成ファイルで構築するインフラを構築前に確認することができる。クラウドベンダーだとミスるとお金かかるやつもあるから必須。

terraform apply

terraform planで確認したインフラを構築することができる。

破棄編

terraform destroy

このコマンドでterraform applyで作成したインフラを全削除することができる。

できなかったこと、わからなかったことまとめ

  • pythonのインストール、flaskのインストールまで終わったがその後pythonプログラムを起動させようとしたがInternal Server Errorが起きて起動できなかった。
    • バックエンドプロセスになってなかったからシェルが切れたタイミングで止まってしまったのかもしれない。nohupコマンドとかscreenを使ったらもしかしたらできた??
  • goをインストールしたが下記コマンドをuser_dataで打ち込んで起動してもgo -versionを使ってもパスが通っていないことが確認された。bash_profilebashrcの違いが影響あったっぽい。(要勉強)
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
source ~/.bash_rc
  • AWS RDSを使ってデータベース側を実装しようとしたけど、terraform側でシングルAZでRDSとEC2の連結ができなかった。

というわけでなんやかんやあーだこーだあったけどTerraformで都度都度簡単に同じインフラを組めるのとコードが作業ログの一部になってるのが便利だったって話。pythonの実行とかGoの実行とか気になるから近いうちに再挑戦しようかな、、、ネットワークの知識の復習になったのは意外と嬉しい!「ここ間違ってるよ!」ってあったら優しく教えてくれると嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?