はじめに
グループワークにおいてawsを使う必要があったけど金かからんようにするためだけに毎回GUI操作をログインしてEC2起動して停止して、、、ってやるの面倒だったからIaC使ってみたって話。できたこと、できなかったこともまとめる。
今回の課題
- apacheからの動画ストリーミングと動画アップロードを受け取り、保存するサーバの構築
- Go言語でのデータベース操作のAPIサーバの構築
- MySQLデータベースのサーバ構築
そもそもIaCってなんぞや?
聞いたことあっても自信を持ってこれ!!って説明できなかったからこの際にしっかりと。
参考:RedHat IaC (Infrastructure as Code) とは
IaC : Infrastructure as a Codeの略
IaC によるインフラストラクチャのプロビジョニングを自動化することで、開発者は、アプリケーションを開発またはデプロイするたびに、サーバー、オペレーティングシステム、ストレージ、およびその他のインフラストラクチャ・コンポーネントのプロビジョニングと管理を手動で行う必要がなくなります。インフラストラクチャを体系化すれば、プロビジョニングをテンプレート化できます。
IaCのメリット
- コスト削減
- デプロイメントの高速化
- エラーの減少
- インフラストラクチャの一貫性の向上
- 構成ドリフトの排除
とのことなので、今回は人的エラーの減少とインフラストラクチャの一貫性の向上(コード化することで毎回同じインフラを構築すること)を目的にTerraformを使ってみよう。
というわけで、本題。
今回行うこと
これを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
- 22
- アウトバウンドルール
- 任意の場所
- インバウンドルール
- 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
- Apacheサーバ
sshとtcpの違いはなんなのか?
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
}
output
:terraform apply
コマンド実行後に指定された出力を行う
今回の場合、ターミナルに「The public IP address of the Apache instance」と文字列と作成されたApacheのインスタンスのipアドレスが出力される
てな感じで、、出来上がったのがこちら。
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_profile
とbashrc
の違いが影響あったっぽい。(要勉強)
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
source ~/.bash_rc
- AWS RDSを使ってデータベース側を実装しようとしたけど、terraform側でシングルAZでRDSとEC2の連結ができなかった。
というわけでなんやかんやあーだこーだあったけどTerraformで都度都度簡単に同じインフラを組めるのとコードが作業ログの一部になってるのが便利だったって話。pythonの実行とかGoの実行とか気になるから近いうちに再挑戦しようかな、、、ネットワークの知識の復習になったのは意外と嬉しい!「ここ間違ってるよ!」ってあったら優しく教えてくれると嬉しいです。