57
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Terraformを使用してWordPressを構築するハンズオン

Last updated at Posted at 2021-12-11

はじめに

DMM WEBCAMP Advent Calendar 2021の12日目 :christmas_tree: を担当させていただきます。
前日の記事: @keijitsumori さんの[Rails] param is missing or the value is empty error と向き合う

普段DMM WEBCAMPでメンターをしている@koseiinfratopです!
よろしくお願いします:santa_tone1:

背景

  • 近年、さまざまなインターネットサービスがクラウド化しているので自分も主要なクラウドサービス(AWS, GCP, Azure, さくら)を使ってみたいと思いWordPressを構築してみました。各種サービスのサイトのGUIをぽちぽちしてインスタンス等を立ち上げて、立ち上がったインスタンスにSSHし各種設定。。。。

QiitaやZenn等の情報共有サイトを見てもインフラの構築系記事は大体がGUIをベースとした記事が多いと思います。

それでもいいのですが、、、最近ではインフラの構築をコードで宣言するサービスが人気を集めています。
その1サービスがTerraformです。
今回はTerraformを使用してWordPressを構築するハンズオンを紹介しようと思います。

事前準備と注意事項

  • 本記事ではTerraformの使い方・記述方法については割愛しています。
    Terraformの基本知識については5分で分かるTerraform(Infrastructure as Code)を、

    Terraformの構文についてはAWSでTerraform入門をご覧になってください。

  • 本記事はAWS,さくらのクラウド, GCP等のクラウドサービスを使用したことがある人を対象としています。

0. 今回作成するインフラの構成図と使用するサービス、ファイル構成

構成図

WardPress構成図-Page-2.drawio.png

使用するサービス

  • Terraform
  • AWS
  • Terraformがインストールされている環境(今回は標準でTerraformがインストールされているAWSのCloud9を使用しています。)

ファイル構成

  • Terraformのアーキテクチャはまだベストプラクティスがないかつ、今回のゴールはコードを使用してインフラを構築することに焦点を当てているためこのようなファイル構成になっています。
ec2-user:~/environment $ ls -1
init.sh
main.tf
mariadb_config.sh
nginx_config.sh
nginx_default
prepareWordPress.sql
README.md
terraform.tfstate
terraform.tfstate.backup
terraform.tfvars
variable.tf
wordpress_config.sh

------------------------------------------------------

ec2-user:~/environment $ ls ~/.ssh -1
id_rsa.pub
id_rsa

1. 構築

1-0 SSH鍵を用意する

  • 作成したEC2にSSHでログインできるようにするために
    ssh-keygen -b 4096コマンドを実行してSSH鍵を作成する。

1-0.1 ワークスペースを初期化する

  • 下記コマンドを実行してワークスペースを初期化する。
  • terraformを使用する上で必須コマンドとなる。
    terrform init

1-1 providerの宣言

  • terraformではAWS,GCP,Azure等の様々なクラウドサービスにインフラを構築することができます。
    まずはどのサービスを使用してインフラを構築するのかを宣言します。
main.tf
provider "aws" {   
  region     = "ap-northeast-1"
  access_key = var.access_key
  secret_key = var.secret_key
}

解説

  • provider "aws": 今回はAWSにインフラを構築するので"aws"と記述しています。
  • region = "ap-northeast-1": 東京リージョンにインフラを構築するので"ap-northeast-1"と記述しています。
  • access_key, secret_key: クレデンシャル情報値を記述します。

    * access_keyとsecret_keyの取得についてはこちらをご覧ください。
GUIだと。。。

a. AWSのサイトにログインしヘッダーのリージョンを"東京"に選択する。
スクリーンショット 2021-12-10 5.06.27.pngスクリーンショット 2021-12-10 5.07.08.png

b. IAMダッシュボードにアクセスし、access_keyとsecret_keyを取得するためにユーザの作成

1-2 VPCの宣言

  • 仮想ネットワークの作成

main.tfに追記

# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc
resource "aws_vpc" "main" {
  cidr_block       = "10.1.0.0/16"
  instance_tenancy = "default"

  enable_dns_hostnames = "false"
  enable_dns_support   = "true"
  tags = {
    Name = "WordPress_vpc"
  }
}

#### 解説

  • instance_tenancy: デフォルトでは"default"値なので"default"と記述する。
  • enable_dns_hostnames: VPC がパブリック IP アドレスを持つインスタンスへのパブリック DNS ホスト名の割り当てをサポートするかどうかを決定している。今回は不必要なので"false"と記述する。
  • enable_dns_support: デフォルトでは"true"値なので"true"と記述する。
GUIだと。。。

1-3 サブネットの宣言

  • サブネットを3つ作成する
  • main.tfに追記
main.tf
# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#map_public_ip_on_launch
resource "aws_subnet" "public-a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.1.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = "true"

  tags = {
    Name = "WP-PublicSubnet-1a"
  }
}

resource "aws_subnet" "private-a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.1.2.0/24"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "WP-PrivateSubnet-1a"
  }
}

resource "aws_subnet" "private-d" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.1.10.0/24"
  availability_zone = "ap-northeast-1d"

  tags = {
    Name = "WP-PrivateSubnet-1d"
  }
}

#### 解説

  • vpc_id: "1-2 VPCの作成"で作成したVPCと紐付ける
GUIだと。。。
  • こちらからサブネットを作成する。
    スクリーンショット 2021-12-11 2.35.27.png

1-4 ゲートウェイの宣言

  • インターネットに出るためのゲートウェイの作成
  • main.tfに追記
main.tf
#document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "WP-InternetGateway"
  }
}

#### 解説

vpc_id: "1-2 VPCの作成"で作成したVPCと紐付ける。

GUIだと。。。
  • 特に設定する必要はない。

1-5 VPCルーティングテーブルを作成する

  • ラスト リゾート ゲートウェイを作成する。
    • main.tfに追記
main.tf
# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table
resource "aws_route_table" "public-table" {
  vpc_id = aws_vpc.main.id

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

  tags = {
    Name = "vpc-public-table"
  }
}

#### 解説

  • vpc_id: "1-2 VPCの作成"で作成したVPCと紐付ける。
  • route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.gw.id } : 全てのトラフィックは"aws_internet_gateway.gw.id"のインターネットゲートウェイに送信するようにする。
GUIだと。。。
  • 特に設定する必要はない。

1-6 "VPCルーティングテーブルを作成する" で作成したルーティングテーブルをメインルーティングテーブルに関連付ける。

#document:  https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association
resource "aws_main_route_table_association" "public-a" {
  vpc_id         = aws_vpc.main.id
  route_table_id = aws_route_table.public-table.id
}

#### 解説

  • vpc_id: "1-2 VPCの作成"で作成したVPCと紐付ける。
  • route_table_id: "1-5 スタティックルートの作成" で作成したルーティングをルーティングテーブルに追加するためにaws_route_table.public-table.idと記述する。
GUIだと。。
  • 特に設定する必要はない。
実際のルーティングテーブル ![スクリーンショット 2021-12-10 6.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541740/b2c54ac8-08ad-b24a-dd95-64224fc4ee59.png) 下記画像のレコードは"1-2 VPCの作成"で作成したVPCと紐付けているためデフォルトで追加されている。 ![スクリーンショット 2021-12-10 6.22.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541740/dfe215c0-6fc9-8b47-b352-4b76f5b9166d.png)

1-7 セキュリティグループの宣言

  • webserverとdbserver用のセキュリティグループを作成する。
  • main.tfに追記
# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
resource "aws_security_group" "app" {
  name        = "WP-Web-DMZ"
  description = "WordPress Web App Security Group"
  vpc_id      = aws_vpc.main.id

  tags = {
    Name = "WP-Web-DMZ"
  }
}

resource "aws_security_group" "db" {
  name        = "WP-DB"
  description = "WordPress Mysql Security Group"
  vpc_id      = aws_vpc.main.id
}

#### 解説

  • vpc_id: "1-2 VPCの作成"で作成したVPCと紐付ける。
GUIだと。。。
  • こちらからセキュリティグループを作成する

スクリーンショット 2021-12-10 6.52.05.png

1-8 セキュリティルールを宣言する

  • インバウンドルール・アウトバウンドルールを適宜宣言し、必要なセキュリティグループに紐付ける。
  • main.tfに追記
main.tf
document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule

resource "aws_security_group_rule" "ssh" {
  type        = "ingress"
  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]

  security_group_id = aws_security_group.app.id
}

resource "aws_security_group_rule" "http" {
  type        = "ingress"
  from_port   = 80
  to_port     = 80
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]

  security_group_id = aws_security_group.app.id
}


resource "aws_security_group_rule" "mysql" {
  type      = "ingress"
  from_port = 3306
  to_port   = 3306
  protocol  = "tcp"

  source_security_group_id = aws_security_group.app.id

  security_group_id = aws_security_group.db.id
}

resource "aws_security_group_rule" "all" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.app.id
}

#### 解説

security_group_id: "1-7 セキュリティグループの作成"で作成したセキュリティグループ内で紐付けたいセキュリティグループに紐づける。

  • source_security_group_id: type(今回なら"ingress")に応じてアクセスを許可するセキュリティグループID。(今回ならセキュリティグループ"app"が適用されているインスタンスからの通信であれば許可する。)
  • from_port = 0    to_port = 0          protocol = "-1": 全てのトラフィックへの通信を許可する。
GUIだと。。。

下記画像のようにインバウンド・アウトバウンドルールに適宜追加していく。
スクリーンショット 2021-12-10 6.52.05.png

実際に作成されたセキュリティグループの1例![スクリーンショット 2021-12-10 7.08.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541740/b9b1cc6f-6e53-6a62-2789-3d2b09c24981.png)

1-9 DBのサブネットグループを宣言する

  • DB用のサブネットを定義する。
  • main.tfに追記
main.tf
# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group
resource "aws_db_subnet_group" "main" {
  name        = "wp-dbsubnet"
  description = "WordPress DB_Mysql_ Subnet"
  subnet_ids  = [aws_subnet.private-a.id, aws_subnet.private-d.id]

  tags = {
    Name = "WordPressDB"
  }
}

#### 解説

  • subnet_ids: "1-3 サブネットの作成"で作成したサブネットの中からDB用のサブネットを記述する。
GUIだと。。。
  • こちらからDBサブネットグループを作成する。

スクリーンショット 2021-12-10 11.36.20.png

実際に作成されたDBサブネットグループ ![スクリーンショット 2021-12-11 3.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541740/a2f3ce15-3ad0-6369-5f4e-7a8296f58c4f.png) スクリーンショット 2021-12-11 3.08.47

1-10 DBのインスタンスを宣言する

  • main.tfに追記
main.tf
resource "aws_db_instance" "db" {
  identifier        = "wp-mysql-db" #lowcase
  allocated_storage = 5
  engine            = "mysql"
  engine_version    = "8.0.23"
  instance_class    = "db.t2.micro"

  storage_type            = "gp2"
  username                = var.username
  password                = var.password
  backup_retention_period = 0
  skip_final_snapshot     = true
  apply_immediately       = true
  vpc_security_group_ids  = [aws_security_group.db.id]
  db_subnet_group_name    = aws_db_subnet_group.main.name

  lifecycle {
    ignore_changes = [password]
  }
}

#### 解説

  • identifier: DBの一意な識別子を割り当てる。*全て小文字でなければいけない。

    (省略した場合はAWS側で一意な識別子が割り当てられる。)
  • apply_immediately : RDSへの設定変更を即時反映するかどうかの設定。即時反映したいのでtrue
    を記述する。
  • skip_final_snapshot: インスタンス削除時にスナップショットの取得をスキップするかどうかの設定。コスト節約のためtrueを記述する。
  • lifecycle { ignore_changes = [password] }: ライフサイクルの設定。Terraformで管理しない項目を記述する。passwordの変更はTerraformとして無視する。セキュリティの観点からインスタンス構築後、手動でパスワードを変更するため。
GUIだと。。。

スクリーンショット 2021-12-10 11.51.34.png

1-11 SSH接続するための公開鍵を宣言する。

  • "1-0 SSH鍵を用意する"で作成したキーペアの公開鍵を指定する。
  • main.tfに追記
main.tf
document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair
resource "aws_key_pair" "deployer" {
  key_name   = "test-key"
  public_key = file("~/.ssh/id_rsa.pub")
}

#### 解説

  • key_name: キーペア名を記述する。
  • public_key: Terraformの'file'メソッドを使用して"1-0 SSH鍵を用意する"で事前に用意したキーペアの公開鍵ファイルパスを記述する。
GUIだと。。。
  • EC2を起動するときに画像ようなモーダルが出てくるところで設定する。

スクリーンショット 2021-12-10 12.31.39.png

1-12 EC2を宣言する

  • ubuntuを使用してEC2を作成する。
  • main.tfに追記
main.tf
# document: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance
resource "aws_instance" "app" {
  ami                         = var.ami
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.public-a.id
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.app.id]

  private_ip = "10.1.1.100"

  key_name = aws_key_pair.deployer.id

  provisioner "file" {
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host = self.public_ip
    }
    source = "prepareWordPress.sql"
    destination = "/home/ubuntu/prepareWordPress.sql"
  }

  
  
  provisioner "remote-exec" {
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host = self.public_ip
    }
    scripts =[
      "init.sh",
      "nginx_config.sh",
    ]
  }
  
  provisioner "remote-exec" {
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host = self.public_ip
    }
    inline = [
    "sudo apt update -y",
    "sudo apt install default-mysql-client -y",
    "sudo mysql -u ${var.username} -p${var.password} -h ${aws_db_instance.db.address} < /home/ubuntu/prepareWordPress.sql"
    ]
  }
  
  provisioner "file" {
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host = self.public_ip
    }
    source = "nginx_default"
    destination = "/home/ubuntu/default"
  }
  
  provisioner "remote-exec" {
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host = self.public_ip
    }
    script = "wordpress_config.sh"
    
  }
  
  

  tags = {
    Name = "WordPressServer"
  }
}

#### 解説

associate_public_ip_address = true
  • VPC内のインスタンスにパブリックIPアドレスを関連付けるためにtrueを記述する。
 private_ip = "10.1.1.100"
  • このEC2で使用するプライベートIPを明示的に指定するために固定IPを記述している。
key_name = aws_key_pair.deployer.id
  • "1-11 SSH接続するための公開鍵を宣言する。"で宣言した公開鍵をEC2に登録するために記述している。
provisioner "file" {
    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("~/.ssh/id_rsa")
      host        = self.public_ip
    }
    source      = "prepareWordPress.sql"
    destination = "/home/ubuntu/prepareWordPress.sql"
  }
  • EC2にファイルを転送している。
    内部ではscpコマンドを使用してファイル転送をしている。
scpコマンドで表した場合

scp -i ~/.ssh/id_rsa prepareWordPress.sql ubuntu@self.public_ip:/home/ubuntu/prepareWordPress.sql

host = self.public_ip

>> - 宛先のアドレス(`host`)にEC2のパブリックIP(`self.public_ip`)を指定している。

provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
scripts = [
"init.sh",
"nginx_config.sh",
]
}

> - EC2で実行したいシェルスクリプトを記述している。
> 内部ではSSH接続をして、`scripts`に記述しているスクリプトファイルを実行している。

###### GUIだと。。。
- <a href="https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LaunchInstanceWizard:" target="_blank" rel="noopener">こちら</a>からEC2を作成する。

## 1-13 EC2のグローバルIPを確認する
- main.tfに追記

```:main.tf
output "WordPressServer_EC2__PublicIP" {
  value = aws_instance.app.public_ip
}

解説

  • 後の動作確認のためにEC2に割り振られたグローバルIPをターミナルに出力するために記述する。

~補足~ 後の項目"1-17 定義したファイルを実行しWordPressを構築する"でterraform applyコマンドを実行し正常に終了したらターミナルにグローバルIPが出力される。

1-14 スクリプトファイル、ダンプファイル、Terraformで使用している変数系のファイルを作成する

  • こちらに関しての説明は割愛させていただきます。
  • 詳しい変数値などはこちらをご覧ください。

1-15 作成したファイルのコードフォーマット

  • 下記コマンドを実行してコード整形を行う。

terraform fmt

1-16 記述した設定ファイルの変更内容を確認する

  • 下記コマンドを実行して定義内容が意図した通りに反映されるかを確認する。

terraform plan

1-17 定義したファイルを実行しWordPressを構築する

  • 下記コマンドを実行してWordPressを構築する。

terraform apply

動作確認

  • ターミナルに出力されたグローバルIPを使用してhttp://グローバルIP/wordpressにアクセスする。
  • 下記の画像のような画面が表示される。
    スクリーンショット 2021-12-10 14.11.47.png
    スクリーンショット 2021-12-10 14.13.01.png
  • 言語を設定し、DBの設定等を行うとwordpressのサイトを表示できるようになる。

最後に

  • このようにGUIを一切使用せずにインフラ構築を行うことができます。
  • 今回はメンテナンス等は全く気にせずコードを書きましたが、メンテナンスを意識したコードに随時書き換えていこうと思います。
  • 皆さんもぜひ、プログラミングでインフラを構築してみてください!!!

インフラがおもしろくない可能性これ、、、一切なーい!!!

参考資料

https://reboooot.net/post/what-is-terraform/
https://kazuhira-r.hatenablog.com/entry/2020/06/20/172541
https://registry.terraform.io/
https://qiita.com/toshihirock/items/6a46fcba165a0b34a1f4
https://www.lac.co.jp/lacwatch/service/20200903_002270.html
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-dns.html

57
11
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
57
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?