EP08 - 用 Terraform 建置 AWS RDS 服务(以 Aurora Postgres 为例)

前几天我们建立起 Gitlab,
并将它串接到 Jenkins,
今天将继续部署基础设施,
我们将使用 Terraform 建立 Amazon Relational Database Service(AWS RDS),
AWS 支援市面上大家常用的主流资料库,
对於大部分的应用都可以符合需求。

挑战至今,
我们仍未建立一个 Web 服务做部署,
未来的几天,
预计会使用 Python Django 建立一个 Portal,
而後建立 EC2,
并为其配置 Server 环境,
先进行手动部署,
最後才会将最後一哩路,
自动部署做起来。

Terrform 配置资料库

使用 KMS 将资讯加密

什麽是 KMS

AWS Key Management Service (KMS) 让您轻松建立及管理加密金钥
并控制多种 AWS 服务和应用程序的使用

AWS KMS 是安全的弹性服务
采用已通过 FIPS 140-2 验证
或正在验证中的硬体安全模组来保护您的金钥

AWS KMS 还与 AWS CloudTrail 整合
提供您所有金钥使用状况的日志
协助您满足法规和合规要求

除了我们自建的服务
可以使用 AWS KMS 做加密解密以外
比较常用的还是 AWS 上的内部服务
例如:System Session Management(SSM),允许 AWS 使用者在没有 Key 的情况下,SSH 进 EC2 并对 EC2 进行操作,设定这个操作的过程,也需要设定 KMS

Terraform 新增一个 KMS

在 maint.tf 中增加以下两行
key_usage 和 customer_master_key_spec 都有固定的写法
详情可参考 Terraform AWS KMS Key

resource "aws_kms_key" "rds" {
    description              = "It is used to encrypt and decrypt for RDS"
    key_usage                = "ENCRYPT_DECRYPT"
    customer_master_key_spec = "SYMMETRIC_DEFAULT"
    deletion_window_in_days  = 10
}

resource "aws_kms_alias" "rds" {
    name          = "alias/rds"
    target_key_id = aws_kms_key.rds.key_id
}

回到 console 中执行配置

terraform apply

完成後我们就可在 aws 上看到我们建立的 KMS
记下「金钥 ID」,等等会用到
https://ithelp.ithome.com.tw/upload/images/20210919/20141518Xx5tj7VEu2.png

将帐密储存到档案中

在建立 RDS(AWS 的资料库) 时
我们就需要设定帐号密码
如果我们直接用明码写进 terraform 中
就会进版控
在此之前我们需要下个指令先将它存到一个档案中

基於安全性规则
记得把 admin 和 password 改掉
这里并不是希望大家真的照打
而且我们等等要用到的资料库是 postgres
admin

echo -n 'admin' > plaintext-username
echo -n 'password' > plaintext-password

将帐号和密码档案加密

前几天我们建立好的 aws cli 终於要在今天派上用场
我们会透过 aws-cli 将档案加密
此次实作参考自 stackoverflow 的讨论

记得将加密过後的帐密复制出来
等等在新增 RDS 时会用到

aws kms encrypt --key-id ab123456-c012-4567-890a-deadbeef123 --plaintext fileb://plaintext-username --output text --query CiphertextBlob

aws kms encrypt --key-id ab123456-c012-4567-890a-deadbeef123 --plaintext fileb://plaintext-password --output text --query CiphertextBlob

https://ithelp.ithome.com.tw/upload/images/20210919/20141518vi1x4ugo8R.png

Terraform 新增 RDS

什麽是 RDS

Amazon Relational Database Service (Amazon RDS) 是 Amazon 提供的资料库 PaaS 服务
让使用者能够在云端中轻松设定、操作和扩展关联式资料库

它提供经济实惠且可调整大小的容量
且可自动处理硬体布建、资料库设定、修补程序和备份等耗时的管理任务

这让您有更多时间专注在应用程序,以提供其所需的快速效能、高可用性、安全性和相容性。

密码解密

这里需要把刚刚加密过後的 username 和 password 放进 payload 中
请 aws 帮忙解密

data "aws_kms_secrets" "portal" {
    secret {
        name    = "master_username"
        payload = ""
    }

    secret {
        name    = "master_password"
        payload = ""
    }
}

建立 Security group

RDS 提供多种资料库可供选
其中也有 Amazon 自己做的 Amazon Aurora
Amazon Aurora 提供相容於 MySQL 的版本
以及相容於 Postgres 的版本
我们预计使用 Amazon Aurora 相容於 Postgres 的版本
所以 inbound/outbound 是设定 Postgres 使用的 port 5432

resource "aws_security_group" "rds_portal" {
    name        = "rds-portal"
    description = "It used for RDS."
    vpc_id      = data.aws_vpc.default.id
    tags        = { Name = "RDS Postgres" }
    revoke_rules_on_delete = null
}

resource "aws_security_group_rule" "rds_portal_igress_5432" {
    type              = "ingress"
    from_port         = 5432
    to_port           = 5432
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.rds_portal.id
}

resource "aws_security_group_rule" "rds_portal_egress_22" {
    type              = "egress"
    from_port         = 5432
    to_port           = 5432
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.rds_portal.id
}

建立资料库子网路群组

啥?子网路群组?
没错,无论是预设的网段
还是未来我们打算自己建立 VPC 并规划网段
我们都可以指定资料库都只在哪个网段
而不是所有网段都套用

会有这设定
是因为我们可以透过路由规则的设计
让有些网段可以公开对外
而有些网段无法直接对外连接
资料库通常来说就蛮需要此设计
透过路由规则的设计
让他只存在於不能对外的 subnet
不过这部分如果我铁人赛有继续写
再看看有没有机会把这个坑补完

resource "aws_db_subnet_group" "rds_subnet_group" {
    name       = "database_subnet_group"
    subnet_ids = sort(data.aws_subnet_ids.subnet_ids.ids)

    tags = {
        "Name" = "Database subnet group"
    }
}

建立参数组

因为 RDS 皆为 PaaS 服务
我们无法直接接触作业系统层级的设定
过去有实际建立资料库的夥伴
或多或少也曾经调整过资料库的参数
RDS 特别针对不同的资料库以及各版本做出参数组供参考
在建立资料库前
我们需要特别建立参数组
并在建立资料库时指令参数组
未来如果需要调整资料库设定
则直接调整参数组的数值即可

我们使用的 resource 是 aws_rds_cluster_parameter_group
而不是 aws_db_parameter_group 喔

resource "aws_rds_cluster_parameter_group" "cluster_aurora_postgres_13" {
    name        = "aurora-db-postgres13-cluster-parameter-group"
    family      = "aurora-postgresql13"
    description = "aurora-db-postgres13-cluster-parameter-group"
}

建立资料库

定义好参数
设定好帐密和 inbound/outbound
终於我们要来建立资料库了

其实 aws rds 有两种形式
一种是单纯建立 instance
可以理解成帮你建立一个 EC2 并装好资料库的 Server

另一种则是建立丛集
丛集内可建立多个 instance
aws 会透过内部机制帮你对多个丛集做读写/同步
我们是使用 Aurora
因此不得不建立 Cluster
并在 Cluster 里面再另外建立 instance
AWS API 文件中也写得很明确

Not applicable. Aurora cluster volumes automatically grow as the amount of data in your database increases, though you are only charged for the space that you use in an Aurora cluster volume.

如果你直接用建立 instance 的语法
而不是建立 cluster 的方式
会出 allocated storage 相关的错误
这个雷我已经帮大家踩过了
希望大家不用再踩一次了

resource "aws_rds_cluster" "ithome_ironman_cluster" {
    cluster_identifier              = "ithome-ironman-cluster"
    engine                          = "aurora-postgresql"
    engine_version                  = "13.3"
    availability_zones              = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
    db_subnet_group_name            = aws_db_subnet_group.rds_subnet_group.name
    db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.cluster_aurora_postgres_13.name
    database_name                   = "ironman2021" 
    master_username                 = data.aws_kms_secrets.portal.plaintext["master_username"]
    master_password                 = data.aws_kms_secrets.portal.plaintext["master_password"]
    skip_final_snapshot             = true
    vpc_security_group_ids          = [aws_security_group.rds_portal.id]
    tags                            = {}
}

resource "aws_rds_cluster_instance" "ithome_ironman_rds_instance" {
    count              = 1
    identifier         = "aurora-portal-cluster-${count.index}"
    cluster_identifier = aws_rds_cluster.ithome_ironman_cluster.id
    instance_class     = "db.t3.medium"
    engine             = aws_rds_cluster.ithome_ironman_cluster.engine
}

执行配置

cluster 与资料库的建置需要七、八分钟的时间
可以喝个水或上个厕所休息一下再回来

terraform apply

建立完後
就可以在 aws console 看到结果
https://ithelp.ithome.com.tw/upload/images/20210920/20141518jr14dAUSpS.png

提醒

在 checkin code 的时候
不要不小心将产生的 plaintext-username 和 plaintext-password 进版控了

参考资料:

  1. Amazon KMS
  2. Terraform AWS KMS Key
  3. AWS: Using a KMS-encrypted master password to create a RDS instance
  4. Amazon RDS
  5. AWS API CreateDBInstance
  6. storage type error with terraform and aurora postgresql

<<:  绝对路径及相对路径

>>:  day 12 - API组装实作

Day23 Plugin 从零开始到上架 - ActivityAware 介绍

ActivityAware 如果插件需要与UI 进行互动,例如请求权限或更改Android UI ,...

Day 05:是说,这个选项可以接什麽东西?autocomplete 与 auto-pair

更新 我把从第一天到现在每天的 Home 目录都放上 GitHub 了,README.md 里面有...

DAY07 - API架构分享

其实大家可能都有自己的API架构方式,不过我这边就是分享我目前在Web端撰写API时,在架构上和开发...

[Day11] VS Code + Git - 使用 Git Graph

Git Graph操作 点击 commit 名称就可以展开细节,再点一次收回 右边栏位绿字为新增的项...

[Day18] 第十八章-API资料如何呈现在前端的页面上(blade跟view呈现)

前言 昨天我们算是把api service完成了 那我们今天来写一些简单的前端 以及使用balde的...