AWS云服务的架构方案

记录一下在AWS上搭建云服务的过程。

假设我们现在要搭建一个云服务系统,这个系统要提供网站服务,用户数据存储在数据库。需要提供一个高可用性,高性能,可灵活扩展的一个方案。

方案设计如下:

1. 定义一个虚拟私有云VPC

2. 定义一个公有子网,用于放置跳板机。定义两个私有子网,放置WebServer。定义两个私有子网,放置Aurora Cluster

3. Web服务器,采用Auto Scaling Group来自动创建,制定minsize和desired capacity为2,表示最少2台web服务器运行,提供高可用性

4. 定义ELB, 指向Web服务器的Auto scaling group。ELB会检查HTTP Status来进行切换。

5. 配置Aurora Cluster,定义一个Primary Instanance和一个Read Replica。Web服务器的写操作都去到Primary Instance的Endpoint,读操作都去到Read Replica的Endpoint。如果需要进一步提高数据库的读性能,可以增加Read Replica,最多可以去到15个。(略)

6. 配置CloudFront作为内容分发,加速Web静态资源的访问速度。(略)

7. 采用CloudFormation模板来定义所需要的资源和配置,实现架构即代码(IaC)

以下是方案的架构图:

具体的实现如下:

1. 定义VPC, 绑定一个Internet Gateway,使得VPC中的公有子网可以通过IGW与互联网通讯。CloudFormation的JSON描述如下:

"VPC": {
    "Type": "AWS::EC2::VPC",
	"Properties": {
		"CidrBlock": "10.0.0.0/16"
	}
},
"InternetGateway": {
	"Type": "AWS::EC2::InternetGateway",
	"Properties": {
	}
},
"VPCGatewayAttachment": {
	"Type": "AWS::EC2::VPCGatewayAttachment",
	"Properties": {
		"VpcId": {"Ref": "VPC"},
		"InternetGatewayId": {"Ref": "InternetGateway"}
	}
}

2. 定义一个公有子网以及相应的路由表和路由,创建一个Bastion主机,放置在这个公有子网中,并定义Security Group,只允许入站SSH的22端口

"SubnetPublicSSHBastion": {
	"Type": "AWS::EC2::Subnet",
	"Properties": {
		"AvailabilityZone": "ap-east-1a",
		"CidrBlock": "10.0.1.0/24",
		"VpcId": {"Ref": "VPC"}
	}
},
"RouteTablePublicSSHBastion": {
	"Type": "AWS::EC2::RouteTable",
	"Properties": {
		"VpcId": {"Ref": "VPC"}
	}
},
"RouteTableAssociationPublicSSHBastion": {
	"Type": "AWS::EC2::SubnetRouteTableAssociation",
	"Properties": {
		"SubnetId": {"Ref": "SubnetPublicSSHBastion"},
		"RouteTableId": {"Ref": "RouteTablePublicSSHBastion"}
	}
},
"RoutePublicSSHBastionToInternet": {
	"Type": "AWS::EC2::Route",
	"Properties": {
		"RouteTableId": {"Ref": "RouteTablePublicSSHBastion"},
		"DestinationCidrBlock": "0.0.0.0/0",
		"GatewayId": {"Ref": "InternetGateway"}
	},
	"DependsOn": "VPCGatewayAttachment"
},
"SSHSecurityGroup": {
	"Type": "AWS::EC2::SecurityGroup",
	"Properties": {
		"GroupDescription": "Allow SSH",
		"VpcId": {"Ref": "VPC"}
	}
},
"AllowInboundSSH": {
	"Type": "AWS::EC2::SecurityGroupIngress",
	"Properties": {
		"GroupId": {"Ref": "SSHSecurityGroup"},
		"IpProtocol": "tcp",
		"FromPort": "22",
		"ToPort": "22",
		"CidrIp": "0.0.0.0/0"
	}
},
"BastionEC2Instance": {
	"Type": "AWS::EC2::Instance",
	"Properties": {
		"ImageId": "ami-c790d6b6",
		"InstanceType": "t3.nano",
		"KeyName": {"Ref": "KeyName"},
		"NetworkInterfaces": [{
			"AssociatePublicIpAddress": "true",
			"DeleteOnTermination": "true",
			"SubnetId": {"Ref": "SubnetPublicSSHBastion"},
			"DeviceIndex": "0",
			"GroupSet": [{"Ref": "SSHSecurityGroup"}]
		}]
	},
	"DependsOn": "VPCGatewayAttachment"
}

3. 创建一个自定义的AMI映像,基于ubuntu 18,安装Flask和Gunicorn,用Flask来搭建一个简单的Web服务index.py,返回当前服务器的hostname,代码如下:

from flask import Flask
import socket
app = Flask(__name__)

@app.route('/')
def index():
    return '<h1>The server name is '+socket.gethostname()+'</h1>'
    
if __name__ == '__main__':
    from werkzeug.contrib.fixers import ProxyFix
    app.wsgi_app = ProxyFix(app.wsgi_app)
    app.run()

通过Gunicorn来部署Flask应用

gunicorn -w 1 -b 0.0.0.0:5000 -D index:app

可以参考以下博客在Ubuntu 18环境下设置开机自动运行脚本,来自动运行Gunicorn

https://blog.csdn.net/time_future/article/details/85805298

以上步骤完成后,就可以在AWS控制台中,在EC2 Instance上选择创建AMI,之后我们就可以用这个自定义的AMI来启动EC2 Instance

4. 定义一个Launch Configuration,指定Web Server需要用到的Instance类型,AMI ID,以及Security Group。

"HTTPSecurityGroup": {
	"Type": "AWS::EC2::SecurityGroup",
	"Properties": {
		"GroupDescription": "Allow HTTP",
        "SecurityGroupIngress": [
            {
                "IpProtocol": "tcp",
                "FromPort": "22",
                "ToPort": "22",
                "SourceSecurityGroupId": {"Ref": "SSHSecurityGroup"}
            },
            {
                "IpProtocol": "tcp",
                "FromPort": "5000",
                "ToPort": "5000",
                "SourceSecurityGroupId": {"Ref": "ApplicationLoadBalancerSecurityGroup"}
            }
        ],
        "VpcId": {"Ref": "VPC"}
	}
},
"LaunchConfiguration": {
	"Type": "AWS::AutoScaling::LaunchConfiguration",
	"Properties": {
		"InstanceMonitoring": false,
		"SecurityGroups": [{"Ref": "HTTPSecurityGroup"}],
		"ImageId": "ami-0f2685ba13c898c26",
		"KeyName": {"Ref": "KeyName"},
		"AssociatePublicIpAddress": false,
		"InstanceType": {"Ref": "InstanceType"}
	}
}

5. 定义一个Auto scaling group,自动启动所需的Web服务器。指定最小的服务器数目为2,这样可以保证容错性。DesiredCapacity可以根据服务器的负荷或者ELB的网络连接数等指标来动态设定,以满足业务增长的需求。关联两个可用区的各一个子网到这个Auto scaling group,AWS会自动平均分配服务器到这两个可用区中。

"SubnetPrivateWebserver1": {
	"Type": "AWS::EC2::Subnet",
	"Properties": {
		"AvailabilityZone": "ap-east-1a",
		"CidrBlock": "10.0.2.0/24",
		"VpcId": {"Ref": "VPC"}
	}
},
"SubnetPrivateWebserver2": {
	"Type": "AWS::EC2::Subnet",
	"Properties": {
		"AvailabilityZone": "ap-east-1b",
		"CidrBlock": "10.0.3.0/24",
		"VpcId": {"Ref": "VPC"}
	}
},
"AutoScalingGroup": {
	"Type": "AWS::AutoScaling::AutoScalingGroup",
	"Properties": {
        "TargetGroupARNs" : [{"Ref" : "ALBTargetGroup"}],
		"LaunchConfigurationName": {"Ref": "LaunchConfiguration"},
		"DesiredCapacity": 2,
		"MinSize": 2,
		"MaxSize": 2,
		"VPCZoneIdentifier": [
			{"Ref": "SubnetPrivateWebserver1"},
			{"Ref": "SubnetPrivateWebserver2"}
		]
	},
    "DependsOn": "VPCGatewayAttachment"
}

6. 定义负载均衡器ALB,这个ALB关联两个公共子网,定义这两个子网的路由,以及Security Group,ALB将Web访问请求转发到后端的Web Server的5000端口

"SubnetLB1": {
	"Type": "AWS::EC2::Subnet",
	"Properties": {
		"AvailabilityZone": "ap-east-1a",
		"CidrBlock": "10.0.4.0/24",
		"VpcId": {"Ref": "VPC"}
	}
},
"SubnetLB2": {
	"Type": "AWS::EC2::Subnet",
	"Properties": {
		"AvailabilityZone": "ap-east-1b",
		"CidrBlock": "10.0.5.0/24",
		"VpcId": {"Ref": "VPC"}
	}
},
"RouteTableLB": {
	"Type": "AWS::EC2::RouteTable",
	"Properties": {
		"VpcId": {"Ref": "VPC"}
	}
},
"RouteTableAssociationLBSubnet1": {
	"Type": "AWS::EC2::SubnetRouteTableAssociation",
	"Properties": {
		"SubnetId": {"Ref": "SubnetLB1"},
		"RouteTableId": {"Ref": "RouteTableLB"}
	}
},
"RouteTableAssociationLBSubnet2": {
	"Type": "AWS::EC2::SubnetRouteTableAssociation",
	"Properties": {
		"SubnetId": {"Ref": "SubnetLB2"},
		"RouteTableId": {"Ref": "RouteTableLB"}
	}
},
"RouteWebServerToInternet": {
	"Type": "AWS::EC2::Route",
	"Properties": {
		"RouteTableId": {"Ref": "RouteTableWebServer"},
		"DestinationCidrBlock": "0.0.0.0/0",
		"GatewayId": {"Ref": "InternetGateway"}
	},
	"DependsOn": "VPCGatewayAttachment"
},
"ApplicationLoadBalancerSecurityGroup": {
	"Type": "AWS::EC2::SecurityGroup",
	"Properties": {
		"GroupDescription": "elb-sg",
		"VpcId": {"Ref": "VPC"},
		"SecurityGroupIngress": [{
			"CidrIp": "0.0.0.0/0",
			"FromPort": 80,
			"IpProtocol": "tcp",
			"ToPort": 80
		}]
	}
},
"ApplicationLoadBalancer": {
	"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
	"Properties": {
		"Subnets": [
			{"Ref": "SubnetLB1"},
			{"Ref": "SubnetLB2"}
		],
        "SecurityGroups": [{"Ref": "ApplicationLoadBalancerSecurityGroup"}]
	},
    "DependsOn": "VPCGatewayAttachment"
},
"ALBListener": {
    "Type": "AWS::ElasticLoadBalancingV2::Listener",
    "Properties": {
        "DefaultActions": [{
            "Type": "forward",
            "TargetGroupArn": {"Ref": "ALBTargetGroup"}
        }],
        "LoadBalancerArn": {"Ref": "ApplicationLoadBalancer"},
        "Port": "80",
        "Protocol": "HTTP"
    }
},
"ALBTargetGroup": {
    "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
    "Properties": {
        "HealthCheckIntervalSeconds" : 10,
        "HealthCheckTimeoutSeconds" : 5,
        "HealthyThresholdCount" : 2,
        "Port" : 5000,
        "Protocol" : "HTTP",
        "UnhealthyThresholdCount" : 5,
        "VpcId" : {"Ref" : "VPC"}
    }
}

在CloudFormation上面Create Stack,用以上的模板,即可成功创建。创建完成后,访问ALB的DNS Name,即可看到当前提供Web服务的Hostname,多刷新几次,可以看到Hostname也会切换,证明了Web的访问可以成功通过ALB切换到不同的EC2进程中。

要远程登录到Web Server,可以通过跳板机来登录,命令如下ssh -p 22 -i ./id_rsa.pub -fNL 3307:mysql_ip:3306 root@root_ip

猜你喜欢

转载自blog.csdn.net/gzroy/article/details/105880929