在前一页中创建了安全资源后,我们现在将能够部署所需的资源来托管我们的应用程序。 我们将创建一个RDS MySQL数据库、一个负载均衡器、一个ASG、一个EFS文件系统、一个S3存储桶,以及下面所示的许多其他资源:
在terraform
文件夹中打开main.tf
, 附加以下内容并保存文件
# RDS
resource "aws_db_subnet_group" "mission_db_group" { # 创建RDS数据库子网组
name = "${var.namespace}-db-group"
subnet_ids = values(aws_subnet.private)[*].id # 使用私有子网
tags = {
Name = "${var.namespace}-db-group"
}
}
resource "random_password" "default" { # 生成随机密码
length = 25
special = false
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "aws_secretsmanager_secret" "db" { # 在Secrets Manager中创建密钥
name_prefix = "${var.namespace}-secret-db-"
description = "Password to the RDS"
recovery_window_in_days = 7 # 删除保护期为7天
}
resource "aws_secretsmanager_secret_version" "db" { # 存储密钥版本
secret_id = aws_secretsmanager_secret.db.id
secret_string = random_password.default.result # 存储随机生成的密码
}
resource "aws_db_instance" "wp_mysql" { # 创建RDS MySQL实例
identifier = "${var.namespace}-db"
allocated_storage = 20 # 分配20GB存储空间
engine = local.rds.engine # MySQL数据库引擎
engine_version = local.rds.engine_version
instance_class = local.rds.instance_class # 实例类型
db_name = local.rds.db_name # 数据库名称
username = local.rds.username # 数据库用户名
password = aws_secretsmanager_secret_version.db.secret_string # 使用生成的密码
db_subnet_group_name = aws_db_subnet_group.mission_db_group.name # 使用创建的子网组
vpc_security_group_ids = [aws_security_group.db.id] # 应用安全组
multi_az = true # 启用多可用区部署
skip_final_snapshot = true # 删除时跳过最终快照
tags = {
Name = "${var.namespace}-db"
}
}
# EFS
resource "aws_efs_file_system" "mission_app" { # 创建EFS文件系统
creation_token = "${var.namespace}-efs"
encrypted = true # 启用加密
tags = {
Name = "${var.namespace}-efs"
}
}
resource "aws_efs_mount_target" "mission_app_targets" { # 创建EFS挂载点
count = length(local.vpc.azs) # 在每个可用区创建挂载点
file_system_id = aws_efs_file_system.mission_app.id
subnet_id = aws_subnet.private_ingress[count.index].id # 部署在私有入口子网
security_groups = [aws_security_group.nfs.id] # 应用NFS安全组
}
resource "aws_instance" "staging_app" { # 创建暂存应用服务器
lifecycle {
prevent_destroy = false
ignore_changes = [iam_instance_profile, tags, tags_all] # 忽略这些属性的变更
}
ami = data.aws_ami.linux.image_id # 使用Amazon Linux AMI
instance_type = local.vm.instance_type
subnet_id = aws_subnet.private_ingress[0].id # 部署在第一个私有入口子网
user_data_replace_on_change = true
user_data = templatefile("${path.module}/userdata/staging-efs.sh", { # 使用用户数据脚本配置实例
region = data.aws_region.current.name,
efs_id = aws_efs_file_system.mission_app.id
db_name = aws_db_instance.wp_mysql.db_name
db_username = aws_db_instance.wp_mysql.username
db_password = aws_db_instance.wp_mysql.password
db_host = aws_db_instance.wp_mysql.address
DOMAIN_NAME = aws_cloudfront_distribution.mission_app.domain_name
demo_username = local.demo.admin.username
demo_password = local.demo.admin.password
demo_email = local.demo.admin.email
})
iam_instance_profile = aws_iam_instance_profile.app.name # 使用应用IAM配置文件
availability_zone = data.aws_availability_zones.available.names[0] # 部署在第一个可用区
vpc_security_group_ids = [aws_security_group.app.id] # 应用安全组
metadata_options { # 配置实例元数据选项
http_endpoint = "enabled"
http_put_response_hop_limit = 1
http_tokens = "required" # 要求使用IMDSv2
instance_metadata_tags = "enabled"
}
root_block_device { # 配置根卷
delete_on_termination = true
encrypted = true # 启用加密
}
tags = {
Name = format("${var.namespace}-staging_app-%s", element(data.aws_availability_zones.available.names, 0))
}
depends_on = [aws_s3_object.mission_app-private_key, aws_s3_object.mission_app-public_key] # 依赖S3对象创建
}
resource "aws_ami_copy" "mission_app_ami" { # 复制AMI
name = "Amazon Linux 2 Image"
description = "A copy of ${data.aws_ami.linux.image_id} - ${data.aws_ami.linux.description}"
source_ami_id = data.aws_ami.linux.image_id
source_ami_region = data.aws_region.current.name
tags = {
Name = "${var.namespace}-ami"
Description = data.aws_ami.linux.description
"Creation Date" = data.aws_ami.linux.creation_date
"Deprecation Time" = data.aws_ami.linux.deprecation_time
}
}
# Application Load Balancer
resource "aws_lb" "mission_app" { # 创建应用负载均衡器
name = "${var.namespace}-alb"
internal = false # 面向公网
load_balancer_type = "application"
subnets = values(aws_subnet.private)[*].id # 部署在私有子网
tags = {
Name = "${var.namespace}-lb"
}
security_groups = [aws_security_group.app.id] # 应用安全组
}
# Launch configuration
resource "aws_launch_template" "mission_app_lc" { # 创建启动模板
name_prefix = "${var.namespace}-mission_app_iac_lc-"
image_id = aws_ami_copy.mission_app_ami.id # 使用复制的AMI
instance_requirements { # 实例要求配置
memory_mib {
min = local.vm.instance_requirements.memory_mib.min
}
vcpu_count {
min = local.vm.instance_requirements.vcpu_count.min
}
allowed_instance_types = ["m*"] # 允许使用m系列实例
instance_generations = local.vm.instance_requirements.instance_generations
}
ebs_optimized = true # 启用EBS优化
vpc_security_group_ids = [aws_security_group.app.id] # 应用安全组
iam_instance_profile {
name = aws_iam_instance_profile.web_hosting.name # 使用Web托管IAM配置文件
}
block_device_mappings { # 块设备映射配置
device_name = "/dev/xvda"
ebs {
delete_on_termination = true
encrypted = true # 启用加密
}
}
monitoring {
enabled = true # 启用详细监控
}
metadata_options { # 配置实例元数据选项
http_endpoint = "enabled"
http_put_response_hop_limit = 1
http_tokens = "required" # 要求使用IMDSv2
instance_metadata_tags = "enabled"
}
user_data = base64encode(templatefile("${path.module}/userdata/staging-wordpress.sh", { # 使用用户数据脚本配置WordPress
region = data.aws_region.current.name,
efs_id = aws_efs_file_system.mission_app.id,
s3_bucket = aws_s3_bucket.mission_app.bucket
}))
update_default_version = true # 更新默认版本
}
resource "aws_autoscaling_group" "mission_app_asg" { # 创建自动扩展组
name = "${var.namespace}-asg-mission_app"
min_size = 2 # 最小实例数
max_size = 8 # 最大实例数
vpc_zone_identifier = values(aws_subnet.private_ingress)[*].id # 部署在私有入口子网
target_group_arns = [aws_lb_target_group.mission_app.arn] # 关联目标组
mixed_instances_policy { # 混合实例策略
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.mission_app_lc.id
version = "$Latest" # 使用最新版本
}
}
}
lifecycle {
create_before_destroy = true # 先创建后销毁
}
tag {
key = "Name"
value = "${var.namespace}-asg-mission_app"
propagate_at_launch = true # 标签传播到实例
}
depends_on = [aws_instance.staging_app, aws_db_instance.wp_mysql] # 依赖暂存实例和数据库
}
resource "aws_autoscaling_policy" "mission_app_scale_out_policy" { # 创建扩容策略
name = "${var.namespace}-out-policy"
scaling_adjustment = 4 # 每次增加4个实例
adjustment_type = "ChangeInCapacity"
cooldown = 300 # 冷却时间300秒
autoscaling_group_name = aws_autoscaling_group.mission_app_asg.name
}
# ASG Cloudwatch policy
resource "aws_cloudwatch_metric_alarm" "mission_app_scale_out_alarm" { # 创建扩容告警
alarm_name = "${var.namespace}-out-alarm"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "60" # 60秒评估一次
statistic = "Average"
threshold = "30" # CPU使用率超过30%触发
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.mission_app_asg.name
}
alarm_description = "This metric monitors ec2 cpu utilization for Mission App ASG"
alarm_actions = [aws_autoscaling_policy.mission_app_scale_out_policy.arn] # 触发扩容策略
}
resource "aws_autoscaling_policy" "mission_app_scale_in_policy" { # 创建缩容策略
name = "${var.namespace}-in-policy"
scaling_adjustment = -1 # 每次减少1个实例
adjustment_type = "ChangeInCapacity"
cooldown = 180 # 冷却时间180秒
autoscaling_group_name = aws_autoscaling_group.mission_app_asg.name
}
# ASG Cloudwatch policy
resource "aws_cloudwatch_metric_alarm" "mission_app_scale_in_alarm" { # 创建缩容告警
alarm_name = "${var.namespace}-in-alarm"
comparison_operator = "LessThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "60" # 60秒评估一次
statistic = "Average"
threshold = "20" # CPU使用率低于20%触发
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.mission_app_asg.name
}
alarm_description = "This metric monitors ec2 cpu utilization for Mission App ASG"
alarm_actions = [aws_autoscaling_policy.mission_app_scale_in_policy.arn] # 触发缩容策略
}
resource "aws_lb_target_group" "mission_app" { # 创建负载均衡器目标组
name = "${var.namespace}-lb-tg"
port = 443 # HTTPS端口
protocol = "HTTPS"
vpc_id = aws_vpc.default.id
stickiness { # 配置会话粘性
type = "lb_cookie"
cookie_duration = 1800 # Cookie有效期1800秒
enabled = true
}
health_check { # 配置健康检查
healthy_threshold = 3
unhealthy_threshold = 10
timeout = 5
interval = 10
path = "/"
port = 443
protocol = "HTTPS"
}
}
resource "aws_lb_listener" "mission_app_http" { # 创建HTTP监听器
load_balancer_arn = aws_lb.mission_app.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.mission_app.arn
}
}
resource "aws_lb_listener_rule" "redirect_cloudfront_to_http" { # 创建CloudFront到HTTP的重定向规则
listener_arn = aws_lb_listener.mission_app_http.arn
priority = 100 # 优先级100
action {
type = "forward"
target_group_arn = aws_lb_target_group.mission_app.arn
}
condition {
http_header {
http_header_name = "X-Request"
values = [aws_lb.mission_app.dns_name]
}
}
}
resource "aws_lb_listener_rule" "redirect_http_to_https" { # 创建HTTP到HTTPS的重定向规则
listener_arn = aws_lb_listener.mission_app_http.arn
priority = 200 # 优先级200
action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301" # 永久重定向
}
}
condition {
host_header {
values = [aws_lb.mission_app.dns_name]
}
}
}
# Certificates
resource "tls_private_key" "mission_app" { # 创建私钥
algorithm = "RSA"
}
resource "tls_self_signed_cert" "mission_app" { # 创建自签名证书
private_key_pem = tls_private_key.mission_app.private_key_pem
validity_period_hours = 8760 # 有效期一年
# 在证书过期前3小时内运行Terraform时生成新证书
early_renewal_hours = 3
# 服务器SSL证书的合理用途
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
dns_names = [ # 证书的DNS名称
aws_lb.mission_app.dns_name
]
subject { # 证书主题信息
common_name = aws_lb.mission_app.dns_name
organization = "Amazon Web Services"
organizational_unit = "WWPS ProServe"
country = "USA"
locality = "San Diego"
}
}
resource "aws_acm_certificate" "mission_app_private" { # 导入证书到ACM
private_key = tls_private_key.mission_app.private_key_pem
certificate_body = tls_self_signed_cert.mission_app.cert_pem
}
resource "aws_s3_bucket" "mission_app" { # 创建S3存储桶
bucket_prefix = "${var.namespace}-mission-app-"
tags = {
Description = "Used to store items"
}
}
resource "aws_s3_bucket_public_access_block" "mission_app" { # 配置S3存储桶公共访问
bucket = aws_s3_bucket.mission_app.bucket
block_public_acls = true # 阻止公共访问
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_object" "mission_app-private_key" { # 上传私钥到S3
bucket = aws_s3_bucket.mission_app.bucket
key = "server.key"
content = tls_private_key.mission_app.private_key_pem
}
resource "aws_s3_object" "mission_app-public_key" { # 上传公钥到S3
bucket = aws_s3_bucket.mission_app.bucket
key = "server.crt"
content = tls_self_signed_cert.mission_app.cert_pem
}
# Application Load Balancer (finishing touch)
resource "aws_lb_listener" "mission_app_https" { # 创建HTTPS监听器
load_balancer_arn = aws_lb.mission_app.arn
port = "443"
protocol = "HTTPS"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.mission_app.arn
}
certificate_arn = aws_acm_certificate.mission_app_private.arn # 使用ACM证书
}
resource "aws_cloudfront_distribution" "mission_app" { # 创建CloudFront分发
enabled = true
comment = "${var.namespace} - frontend site for mission app"
origin { # 配置源站
domain_name = aws_lb.mission_app.dns_name
origin_id = aws_lb.mission_app.dns_name
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1.2"]
}
custom_header {
name = "X-Request"
value = aws_lb.mission_app.dns_name
}
}
default_cache_behavior { # 配置默认缓存行为
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = aws_lb.mission_app.dns_name
viewer_protocol_policy = "redirect-to-https" # 重定向到HTTPS
cache_policy_id = aws_cloudfront_cache_policy.default.id
}
# https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
restrictions { # 配置地理限制
geo_restriction {
restriction_type = "none" # 不限制
locations = []
}
}
viewer_certificate { # 配置查看器证书
cloudfront_default_certificate = true # 使用默认证书
}
}
resource "aws_cloudfront_cache_policy" "default" { # 创建CloudFront缓存策略
name = "${var.namespace}-default-policy"
comment = "Default Policy"
default_ttl = 50 # 默认TTL 50秒
max_ttl = 100 # 最大TTL 100秒
min_ttl = 1 # 最小TTL 1秒
parameters_in_cache_key_and_forwarded_to_origin { # 配置缓存键参数
cookies_config {
cookie_behavior = "all" # 包含所有Cookie
}
headers_config {
header_behavior = "whitelist"
headers {
items = ["X-Request"] # 白名单头部
}
}
query_strings_config {
query_string_behavior = "all" # 包含所有查询字符串
}
}
}
再运行一次terraform init
运行命令 terraform apply 来应用部署
上面大概要20min创建完毕
Terraform 为以下 AWS 服务创建了资源,在控制台上检查: