RDSを仕事で本格的に触る機会が増えたので自分でも作ってみました。

概要

接続は

https://qiita.com/inugami_sayo/items/16b0cec096c22fdd905b

で作成したEC2から行います。 terraformについては基本的には公式リファレンスを参考にしつつ作成しています。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs

この記事はそのメモやまとめです。 すでに設定済みの項目がいくつかある。 試行錯誤しながらやったので、手順として不要かもしれない。あくまでメモ用。

やってみる

Chatgptに聞いてみる

流行りのchatgptにどんなコードがいいか聞いてみます。 お遊びするのに課金したのでGPT-4です 質問は以下で聞いてみました。

terraform を利用してAWS RDSを作成したいと思っています。
RDSには以下の条件があります。
・Private subnetに配置
・エンジンはmysql 8.0.33を利用
・削除保護は無効
上記の条件を満たすようにterraformのコードを書いてください。

出力されたコードはこんな感じでした。

provider "aws" {
  region = "us-west-2" // 任意のリージョンを設定してください
}

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

resource "aws_subnet" "private" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "us-west-2a" // 任意のAZを設定してください
  map_public_ip_on_launch = false
}

resource "aws_security_group" "allow_mysql" {
  name        = "allow_mysql"
  description = "Allow inbound traffic from MySQL clients"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 3306
    to_port     = 3306
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] // 適切なCIDRブロックを設定してください
  }
}

resource "aws_db_instance" "mysql" {
  allocated_storage      = 20
  storage_type           = "gp2"
  engine                 = "mysql"
  engine_version         = "8.0.33"
  instance_class         = "db.t2.micro" // 任意のインスタンスタイプを設定してください
  name                   = "mydb"
  username               = "user"
  password               = "xxxx" //マスクしました
  parameter_group_name   = "default.mysql8.0"
  skip_final_snapshot    = true
  deletion_protection    = false
  availability_zone      = "us-west-2a"
  vpc_security_group_ids = [aws_security_group.allow_mysql.id]

  subnet_group {
    name       = "my_database_subnet_group"
    subnet_ids = [aws_subnet.private.id]
  }

  tags = {
    Name = "my-database"
  }
}

見た感じ悪くはなさそうですが、いくつか修正点がありますのでそこは手で修正します。 またvpcやsubnetはすでに自分で作成済みのものを利用します。

chatgptの出力結果を修正

vscodeのチェックの段階ですでにいくつか間違っている箇所があったので修正していきます。

name                   = "mydb"
↓
db_name              = "mydb"
subnet_group
↓
db_subnet_group_name

サブネットグループは作成していなかったので合わせて作成するようにします。

ここで問題発生 1

サブネットグループを作成するに当たり、まずはコンソール画面でどんなパラメーターがあるか確認してみます。 VPCを指定し、そこからAZを2つ指定することが必須のようだったので、すでに作成されているsubnetを指定しようとしても、AZaのものしか選択肢に出てこない……。AZa/AZcで作っておいたはずなのになぜ……?

普段使い用に作成していたsubnetがAZa/AZcと分けて作ったつもりだったのですが、 AZの指定せずにapplyして作っていたので、AZa/AZaの用に意味のない別れ方としていました。

明示的にAZを指定してこちらの想定しているAZにsubnetが作成されるように修正しました。

resource "aws_subnet" "subnet_private_AZc" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "10.0.11.0/24"
  availability_zone = "ap-northeast-1c" //明示的にAZを指定

  tags = {
    Name = "aws-my-workspace-private-subnet-c"
  }
}

RDSを作る

最終的には以下のようなterraform資材になりました。

resource "aws_security_group" "allow_mysql" {
  name        = "allow_mysql"
  description = "Allow inbound traffic from MySQL clients"
  vpc_id      = "vpc-xxxx"

  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = ["sg-xxxx"]
  }
}

resource "aws_db_subnet_group" "default" {
  name       = "main"
  subnet_ids = ["subnet-xxxx", "subnet-xxxx"] //private subnet
}

resource "aws_db_instance" "mysql" {
  allocated_storage      = 10
  storage_type           = "gp2"
  engine                 = "mysql"
  engine_version         = "8.0.33"
  instance_class         = "db.t2.micro"
  db_name                = "mydb"
  username               = "user"
  password               = "xxxx"
  parameter_group_name   = "default.mysql8.0"
  skip_final_snapshot    = true
  deletion_protection    = false
  availability_zone      = "ap-northeast-1a"
  vpc_security_group_ids = [aws_security_group.allow_mysql.id]
  db_subnet_group_name   = aws_db_subnet_group.default.id

  tags = {
    Name = "my-database"
  }
}

これをapplyします。 RDS自体は問題なく作成されましたが、EC2に入りmysqlをインストールするところで問題が発生しました。

ここで問題発生 2

EC2でsudo yum install mysqlを実行してmysqlをインストールしようとしたところ……。

[ec2-user@ip-10-0-10-62 ~]$ sudo yum install mysql
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Could not retrieve mirrorlist https://amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.list error was
12: Timeout on https://amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.list: (28, 'Failed to connect to amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com port 443 after 5000 ms: Timeout was reached')

AL2のS3にあるRepositoryに接続できない旨のエラーが表示された。 どうやらEC2がPrivate subnetにありインターネットに出られないのが原因のようだ。

https://chariosan.com/2020/07/24/ec2-amazonlinux2_yum_error/

せっかくなのでS3エンドポイント(ゲートウェイエンドポイント)を使ってこの問題を解決させようと思います。 必要なものとしては

  • s3へのアクセスを許可したIAMポリシー
  • 上記IAMポリシーをアタッチしたIAMロール
  • 上記IAMロールをアタッチしたインスタンスプロファイル
  • 上記インスタンスプロファイルをEC2にアタッチさせる
  • s3用のVPCエンドポイント でした。 これらについては

https://zenn.dev/sakai_nako/articles/terraform-aws-stuck-on

https://cross-black777.hatenablog.com/entry/2015/12/04/233206

を参考にしました。 できあがったterraform資材はこちらです

data "aws_iam_policy_document" "EC2toS3" {
  statement {
    actions = ["sts:AssumeRole"]
    effect  = "Allow"

    principals {
      type        = "Service"
      identifiers = ["s3.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "EC2toS3" {
  name               = "EC2toS3"
  assume_role_policy = data.aws_iam_policy_document.EC2toS3.json
}

resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.vpc.id
  service_name = "com.amazonaws.ap-northeast-1.s3"
  route_table_ids = ["rtb-xxxx"] //ec2と同じVPCのルートテーブルを指定
}

EC2側にもインスタンスプロファイルの作成とそのアタッチのため少し手を加えました。

# instance profile for s3
resource "aws_iam_instance_profile" "EC2toS3" {
  name = aws_iam_role.EC2toS3.name
  role = aws_iam_role.EC2toS3.name
}

# bastion ec2
resource "aws_instance" "bastion" {
  ami                    = data.aws_ami.amazon-linux-2.id # Amazon Linux 2
  instance_type          = "t3.nano"
  subnet_id              = aws_subnet.subnet_private_AZa.id # private subnet
  vpc_security_group_ids = [aws_security_group.bastion_ec2.id]
  iam_instance_profile   = aws_iam_instance_profile.EC2toS3.name //ここを追加
  tags = {
    Name = "bastion-ec2"
  }
  lifecycle {
    ignore_changes = [
      ami,
    ]
  }
}

これでapplyします。 これによってPrivateなEC2でもyumでs3上にあるAL2のRepositoryからパッケージを拾ってくることができるはずです。

[ec2-user@ip-10-0-10-62 ~]$ sudo yum install mysql
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Resolving Dependencies
--> Running transaction check
---> Package mariadb.x86_64 1:5.5.68-1.amzn2.0.1 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=================================================================================================================================================================================================================
 Package                                       Arch                                         Version                                                       Repository                                        Size
=================================================================================================================================================================================================================
Installing:
 mariadb                                       x86_64                                       1:5.5.68-1.amzn2.0.1                                          amzn2-core                                       8.8 M

Transaction Summary
=================================================================================================================================================================================================================
Install  1 Package

Total download size: 8.8 M
Installed size: 49 M
Is this ok [y/d/N]: 

繋がった!

RDSへ接続する

mysqlがインストールできたことですし、PrivateなEC2上からRDSに接続してみます。

[ec2-user@ip-10-0-10-62 ~]$ mysql -u user -p -h xxxxxx.ap-northeast-1.rds.amazonaws.com
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 21
Server version: 8.0.33 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> select version();
+-----------+
| version() |
+-----------+
| 8.0.33    |
+-----------+
1 row in set (0.00 sec)

MySQL [(none)]> 

入れた!versionも指定したものになっています。

ハマったところとか

  • 学習用のsubnetがよくみると自分の意図していなかったsubnetに立っていた。今後は作成後きちんとコンソールでみることにする。
  • EC2がPrivateにあるせいで参考手順とは別のところで躓くことがある。

次やりたいこと

  • RDSのB/G
  • Aurora作ってみる
  • 3層構造のアプリケーションを作ってみる