如何使用 Terraform 使用 EC2 和 RDS 设置基本 VPC
本指南将帮助您使用 Terraform(基础设施即代码)设置具有虚拟机 (EC2) 和数据库 (RDS) 的基本 AWS VPC。
我将把这个话题分解如下:
-
大纲
-
VPC
-
EC2
*EC2 安全组
- RDS
*RDS 安全组
- 结论
大纲
我们将在 AWS 上创建以下内容:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--jpYNxJGI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/i/gc4m221kunlarpjlzoen.png)
带有 1 个Route table
的VPC
将Internet Gateway
连接到承载EC2 instance
的public subnet
。
两个private subnets
配置为 1 个子网组,托管 1RDS instance
。
使用security groups
安排访问控制,一个用于 EC2 公有子网,一个用于 RDS 私有子网。
我们有 2 个 RDS 子网的原因是因为这是部署要求,如果没有配置 2 个子网,您将无法启动 RDS 实例。
理想情况下,您希望对 EC2 和 RDS 实例都执行load balancing
。在 EC2 端,您必须为另一个 EC2 实例添加另一个子网,并将它们与负载均衡器连接。如果其中一个子网由于某种原因出现故障,您的站点仍然可以正常运行。
但是,对于本文,我们将重点关注用于开发和测试目的的最低设置。
VPC
要创建 VPC,我们将模块配置如下:
resource "aws_vpc" "_" {
cidr_block = var.vpc_cidr
enable_dns_support = var.enable_dns_support
enable_dns_hostnames = var.enable_dns_hostnames
}
resource "aws_internet_gateway" "_" {
vpc_id = aws_vpc._.id
}
resource "aws_route_table" "_" {
vpc_id = aws_vpc._.id
dynamic "route" {
for_each = var.route
content {
cidr_block = route.value.cidr_block
gateway_id = route.value.gateway_id
instance_id = route.value.instance_id
nat_gateway_id = route.value.nat_gateway_id
}
}
}
resource "aws_route_table_association" "_" {
count = length(var.subnet_ids)
subnet_id = element(var.subnet_ids, count.index)
route_table_id = aws_route_table._.id
}
请注意;为简洁起见,我删除了标记块,但您应该标记所有可能的资源,以便轻松跟踪您的部署成本,并能够在tfstate
出现任何问题时找到所有内容。
路由表配置为与aws_route_table_association
资源中的 Internet 网关_关联_。我们在var.subnet_ids
中提供的任何子网都可以访问路由表配置和互联网网关。
我这样称呼 VPC 模块:
module "vpc" {
source = "../../modules/vpc"
resource_tag_name = var.resource_tag_name
namespace = var.namespace
region = var.region
vpc_cidr = "10.0.0.0/16"
route = [
{
cidr_block = "0.0.0.0/0"
gateway_id = module.vpc.gateway_id
instance_id = null
nat_gateway_id = null
}
]
subnet_ids = module.subnet_ec2.ids
}
vpc_cidr = "10.0.0.0/16"
表示我们正在创建一个具有 65,536 个可能 IP 地址的 VPC。有关 CIDR 表示法的解释,请参见此处的。
路由表通过以下方式连接到 EC2 子网;subnet_ids = module.subnet_ec2.ids
. 该子网通过cidr_block
配置可以完全访问互联网;"0.0.0.0/0"
。
EC2
要创建 EC2 实例,我们只需要配置我们想要的机器并将其放置在我们的路由表所在的子网中。
在我们的EC2 module
中,我们配置以下内容:
locals {
resource_name_prefix = "${var.namespace}-${var.resource_tag_name}"
}
resource "aws_instance" "_" {
ami = var.ami
instance_type = var.instance_type
user_data = var.user_data
subnet_id = var.subnet_id
associate_public_ip_address = var.associate_public_ip_address
key_name = aws_key_pair._.key_name
vpc_security_group_ids = var.vpc_security_group_ids
iam_instance_profile = var.iam_instance_profile
}
resource "aws_eip" "_" {
vpc = true
instance = aws_instance._.id
}
resource "tls_private_key" "_" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "aws_key_pair" "_" {
key_name = var.key_name
public_key = tls_private_key._.public_key_openssh
}
这将创建:
-
AWS EC2 实例
-
具有与该实例关联的弹性 IP
-
通过 SSH 访问实例的公钥/私钥(PEM 密钥)。
然后我们可以调用它:
module "ec2" {
source = "../../modules/ec2"
resource_tag_name = var.resource_tag_name
namespace = var.namespace
region = var.region
ami = "ami-07ebfd5b3428b6f4d" # Ubuntu Server 18.04 LTS
key_name = "${local.resource_name_prefix}-ec2-key"
instance_type = var.instance_type
subnet_id = module.subnet_ec2.ids[0]
vpc_security_group_ids = [aws_security_group.ec2.id]
vpc_id = module.vpc.id
}
我们需要提供 EC2 模块的四个主要内容(除其他外):
1)将EC2实例附加到子网;subnet_id = module.subnet_ec2.ids[0]
,
- 附加安全组;
vpc_security_group_ids = [aws_security_group.ec2.id]
,安全组的作用类似于防火墙。
3)为其提供需要部署的VPC;vpc_id = module.vpc.id
- AMI 标识符,这里是更多关于如何查找 Amazon 机器映像 (AMI) 标识符的信息。
EC2安全组
到目前为止,我们有一个 VPC 设置、一个 EC2 实例及其子网,并且我们已经配置了对 EC2 子网正在使用的安全组的引用。
安全组就像您的子网的防火墙一样,允许进入ingress
的内容和允许离开子网的egress
的内容:
resource "aws_security_group" "ec2" {
name = "${local.resource_name_prefix}-ec2-sg"
description = "EC2 security group (terraform-managed)"
vpc_id = module.vpc.id
ingress {
from_port = var.rds_port
to_port = var.rds_port
protocol = "tcp"
description = "MySQL"
cidr_blocks = local.rds_cidr_blocks
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
description = "Telnet"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
description = "HTTP"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
description = "HTTPS"
cidr_blocks = ["0.0.0.0/0"]
}
# Allow all outbound traffic.
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
我们允许交通从港口进入; 22 (SSH)、80 (HTTP)、443 (HTTPS),我们允许所有端口上的所有流量流出。如果您想进一步收紧这一点,请分析您的应用程序用于出站流量的端口以提高安全性。
对于入口,您可以通过提供允许在端口 22 上连接的特定 IP 地址来进一步收紧这一点。
RDS
我们几乎完成了设置,只需要配置我们的数据库子网和具有安全组的实例。
locals {
resource_name_prefix = "${var.namespace}-${var.resource_tag_name}"
}
resource "aws_db_subnet_group" "_" {
name = "${local.resource_name_prefix}-${var.identifier}-subnet-group"
subnet_ids = var.subnet_ids
}
resource "aws_db_instance" "_" {
identifier = "${local.resource_name_prefix}-${var.identifier}"
allocated_storage = var.allocated_storage
backup_retention_period = var.backup_retention_period
backup_window = var.backup_window
maintenance_window = var.maintenance_window
db_subnet_group_name = aws_db_subnet_group._.id
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
multi_az = var.multi_az
name = var.name
username = var.username
password = var.password
port = var.port
publicly_accessible = var.publicly_accessible
storage_encrypted = var.storage_encrypted
storage_type = var.storage_type
vpc_security_group_ids = ["${aws_security_group._.id}"]
allow_major_version_upgrade = var.allow_major_version_upgrade
auto_minor_version_upgrade = var.auto_minor_version_upgrade
final_snapshot_identifier = var.final_snapshot_identifier
snapshot_identifier = var.snapshot_identifier
skip_final_snapshot = var.skip_final_snapshot
performance_insights_enabled = var.performance_insights_enabled
}
这里有很多选项,让我们抓取tfvars
文件,看看其中大部分变量包含什么:
# RDS
rds_identifier = "mysql"
rds_engine = "mysql"
rds_engine_version = "8.0.15"
rds_instance_class = "db.t2.micro"
rds_allocated_storage = 10
rds_storage_encrypted = false # not supported for db.t2.micro instance
rds_name = "" # use empty string to start without a database created
rds_username = "admin" # rds_password is generated
rds_port = 3306
rds_maintenance_window = "Mon:00:00-Mon:03:00"
rds_backup_window = "10:46-11:16"
rds_backup_retention_period = 1
rds_publicly_accessible = false
rds_final_snapshot_identifier = "prod-trademerch-website-db-snapshot" # name of the final snapshot after deletion
rds_snapshot_identifier = null # used to recover from a snapshot
rds_performance_insights_enabled = true
关于我在这里使用的配置的一些注意事项;
-
实例大小和加密: 对于生产,请确保您使用大于
db.t2.micro
的实例大小,以便您可以在存储层上使用加密。 -
维护窗口: 此日期和时间设置用于修补您的实例。
-
公共访问: 确保出于显而易见的原因将公共访问设置为关闭,但如果您的实例托管在私有子网中,则无论如何都应该是这种情况。
4)**备份:**关于备份的两件事:
4.1)提供最终的快照标识符在销毁环境时很有用,它将自动创建具有给定名称的快照。如果您不提供此变量,您将无法使用terraform destroy
命令删除 RDS 实例,您必须手动执行此操作(!)。
4.2) RDS 支持自动备份,请确保正确设置保留期限(以天为单位)。
-
查询跟踪: 要启用对查询和性能统计的深入跟踪,请将
performance_insights_enabled
设置为true
。这对于分析慢查询以及通常的查询性能非常有用。 -
数据库密码: 生成数据库的密码,这可以用这个资源来完成:
resource "random_string" "password" {
length = 16
special = false
}
RDS安全组
最后,我们需要为 RDS 提供安全组配置,以便 EC2 可以与我们的数据库通信。
resource "aws_security_group" "_" {
name = "${local.resource_name_prefix}-rds-sg"
description = "RDS (terraform-managed)"
vpc_id = var.rds_vpc_id
# Only MySQL in
ingress {
from_port = var.port
to_port = var.port
protocol = "tcp"
cidr_blocks = var.sg_ingress_cidr_block
}
# Allow all outbound traffic.
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = var.sg_egress_cidr_block
}
}
以上允许ingress
来自端口 3306 和egress
的一切。
结论
我希望本指南对您有所帮助,请在下面发表评论,让我知道您喜欢什么,不喜欢什么,建议等等。
下周我将介绍(开放)API 安全性以及配置建议,以及 AWS API 防火墙解决方案AWS WAF。
谢谢阅读!
更多推荐
所有评论(0)