本指南将帮助您使用 Terraform(基础设施即代码)设置具有虚拟机 (EC2) 和数据库 (RDS) 的基本 AWS VPC。

我将把这个话题分解如下:

  • 大纲

  • VPC

  • EC2

*EC2 安全组

  • RDS

*RDS 安全组

  • 结论

大纲

我们将在 AWS 上创建以下内容:

[vpc-图](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 tableVPCInternet Gateway连接到承载EC2 instancepublic 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
}

这将创建:

  1. AWS EC2 实例

  2. 具有与该实例关联的弹性 IP

  3. 通过 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],

  1. 附加安全组;vpc_security_group_ids = [aws_security_group.ec2.id],安全组的作用类似于防火墙。

3)为其提供需要部署的VPC;vpc_id = module.vpc.id

  1. 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

关于我在这里使用的配置的一些注意事项;

  1. 实例大小和加密: 对于生产,请确保您使用大于db.t2.micro的实例大小,以便您可以在存储层上使用加密。

  2. 维护窗口: 此日期和时间设置用于修补您的实例。

  3. 公共访问: 确保出于显而易见的原因将公共访问设置为关闭,但如果您的实例托管在私有子网中,则无论如何都应该是这种情况。

4)**备份:**关于备份的两件事:

4.1)提供最终的快照标识符在销毁环境时很有用,它将自动创建具有给定名称的快照。如果您不提供此变量,您将无法使用terraform destroy命令删除 RDS 实例,您必须手动执行此操作(!)。

4.2) RDS 支持自动备份,请确保正确设置保留期限(以天为单位)。

  1. 查询跟踪: 要启用对查询和性能统计的深入跟踪,请将performance_insights_enabled设置为true。这对于分析慢查询以及通常的查询性能非常有用。

  2. 数据库密码: 生成数据库的密码,这可以用这个资源来完成:

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。

谢谢阅读!

Logo

CI/CD社区为您提供最前沿的新闻资讯和知识内容

更多推荐