自动化 Amazon VPC 的网络主控场景





5.00/5 (3投票s)
本文档介绍了“基础设施即代码”概念,用于自动化 Amazon VPC 的创建。
引言
长期以来,Amazon Web Services 一直是部署和运行新项目最常用的平台之一。对于更复杂的项目,您通常会从掌握网络基础设施开始,幸运的是,Amazon 提供了关于最常用网络拓扑的权威指南。您可以在 http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenarios.html 上查看一些示例。
出于某些原因,在我见过的大多数客户案例中,他们总是手动配置设置,然后在项目 wiki 中用屏幕截图记录步骤。之后,原始知识会丢失,迁移或重新部署应用程序会导致小心翼翼的手动操作。
在本文中,我将演示如何根据项目要求,使用 Ansible 为应用程序自动化网络创建,并将网络创建逻辑与项目一起存储。通过这种方式,您的项目基础设施可以随着您的应用程序发布而发展和发布。
背景
为了演示,让我们选择一个更复杂的设置,如这里所述 带有公共和私有子网 (NAT) 的 VPC。
让我们看一下定义和网络拓扑图。
此场景的配置包括一个带有公共子网和私有子网的虚拟私有云 (VPC)。如果您想运行面向公众的 Web 应用程序,同时维护不面向公众的后端服务器,我们建议采用此场景。一个常见的例子是多层网站,Web 服务器位于公共子网中,数据库服务器位于私有子网中。您可以设置安全性和路由,以便 Web 服务器能够与数据库服务器通信。
公共子网中的实例可以直接接收来自 Internet 的入站流量,而私有子网中的实例则不能。公共子网中的实例可以直接向 Internet 发出出站流量,而私有子网中的实例则不能。相反,私有子网中的实例可以通过位于公共子网中的网络地址转换 (NAT) 网关访问 Internet。数据库服务器可以通过 NAT 网关连接到 Internet 以下载软件更新,但 Internet 无法发起连接到数据库服务器。
让我们实现一个更复杂的示例:通过将应用程序服务器放置在多个子网中来使应用程序高可用。因此,在公共子网中我们将有一个 Elastic Load Balancer,它将链接到我们创建的几个子网。
在我们的场景中,我们将使用 Ansible AWS 模块的所有功能,以及直接的 awscli 命令,因为 Ansible 尚不支持某些功能。
最初,我们的账户上将没有任何 VPC。
先决条件
由于区域的可用于性区域集不同,让我们创建两个常量,以固定可用可用区域名称进行预配,并为环境提供一个合理的名称。
--- vpc_availability_zone_t1: b vpc_availability_zone_t2: c readable_env_name: "app-{{env}}"
结构
让我们根据场景 2 图定义 VPC。为了尽可能接近 Amazon 的示例,我将使 0.0 和 2.0 子网公开,1.0 和 3.0 子网私有;正如您在 VPC 配置中所见,我们已经提供了易于阅读的命名和标签。对于两个公共网络,我们指定了默认路由表。
vpc_cidr_block: 10.0.0.0/16 vpc_subnets: - cidr: 10.0.0.0/24 az: "{{aws_region}}{{vpc_availability_zone_t1}}" resource_tags: {"Name": "{{readable_env_name}}-sb-pub-{{aws_region}}-{{vpc_availability_zone_t1}}", "Environment":"{{readable_env_name}}", "AZ" : "{{vpc_availability_zone_t1}}" } - cidr: 10.0.2.0/24 az: "{{aws_region}}{{vpc_availability_zone_t2}}" resource_tags: { "Name": "{{readable_env_name}}-sb-pub-{{aws_region}}-{{vpc_availability_zone_t2}}", "Environment":"{{readable_env_name}}", "AZ" : "{{vpc_availability_zone_t2}}" } - cidr: 10.0.1.0/24 az: "{{aws_region}}{{vpc_availability_zone_t1}}" resource_tags: {"Name": "{{readable_env_name}}-sb-priv-{{aws_region}}-{{vpc_availability_zone_t1}}", "Environment":"{{readable_env_name}}", "AZ" : "{{vpc_availability_zone_t1}}" } - cidr: 10.0.3.0/24 az: "{{aws_region}}{{vpc_availability_zone_t2}}" resource_tags: { "Name": "{{readable_env_name}}-sb-priv-{{aws_region}}-{{vpc_availability_zone_t2}}", "Environment":"{{readable_env_name}}", "AZ" : "{{vpc_availability_zone_t2}}" } vpc_internet_gateway: "yes" vpc_route_tables_public: - subnets: - 10.0.0.0/24 - 10.0.2.0/24 routes: - dest: 0.0.0.0/0 gw: igw
创建 VPC
要创建 VPC,我们使用已有的 Ansible 模块 ec2_vpc;在运行时,我们使用上述设置向其提供参数。
- name: NETWORK | Create the VPC ec2_vpc: state: present region: "{{ aws_region }}" resource_tags: Environment: "{{ readable_env_name }}" Name: "{{ readable_env_name }}-vpc-{{aws_region}}" cidr_block: "{{ vpc_cidr_block }}" subnets: "{{ vpc_subnets }}" dns_support: true dns_hostnames: true internet_gateway: "{{ vpc_internet_gateway|string }}" route_tables: "{{ vpc_route_tables_public }}" wait: yes register: vpc
此部分执行完毕后,我们将创建 VPC 和子网。
看看创建的 NAT 网关 - 正如您所见,每个公共子网都有一个 NAT 网关。
看看创建的路由表,正如您所见 - 公共子网设置了 Internet 网关,
而私有子网有一个路由条目,通过 NAT 转发出站请求。
NAT 实例的魔力
如今,Amazon 建议创建 NAT 网关,而不是 NAT 实例。要掌握 NAT 网关,我们需要
- 分配弹性 IP
- 创建 NAT 网关并将 NAT 网关与子网关联
- 等待 NAT 网关部分可用,以创建私有子网的路由表。
- 为私有子网创建路由表并为出站流量设置 NAT 网关
要分配弹性 IP,我们将使用 Ansible 中的现有模块:ec2_eip。请注意,默认情况下,每个账户每个区域最多只能有 5 个弹性 IP。
- name: NETWORK | allocate a new elastic IP without associating it to anything - 1 ec2_eip: in_vpc=yes reuse_existing_ip_allowed=yes state=present region="{{aws_region}}"
为了创建 NAT 网关,在 Ansible v1 中我们必须使用 awscli。
- name: NETWORK | Create NAT instance for subnet 1 shell: "aws ec2 create-nat-gateway --region {{aws_region}} --subnet-id {{aws_vpc_pubsubnet1_runtime}} --allocation-id {{aws_vpc_privsubnet1_allocation}}"
这里有一个小技巧:如果您需要立即使用 NAT 网关,例如创建自定义路由表,请给 AWS 一些时间使其部分初始化和注册。
现在我们可以创建自定义路由表了。
- name: NETWORK | Create new route table for the private subnet 1 shell: "aws ec2 create-route-table --region {{aws_region}} --vpc-id {{aws_vpc_id_runtime}}" - name: NETWORK | Associate route table with private subnet 1 shell: "aws ec2 associate-route-table --region {{aws_region}} --route-table-id {{routetable_private_1}} --subnet-id {{aws_vpc_privsubnet1_runtime}}" - name: NETWORK | Create NAT route record for private subnet 1 shell: "aws ec2 create-route --region {{aws_region}} --route-table-id {{routetable_private_1}} --destination-cidr-block '0.0.0.0/0' --nat-gateway-id {{nat_instance1}}"
安全组
我们可以与防火墙规则一起定义安全组的要求。请注意下面的示例,如何为 CIDR 块和特定安全组创建规则。
我想要四个安全组。
第一个用于负载均衡器,接受 HTTP 和 HTTPS。
第二个用于公共子网中的跳转服务器 (jump box),这样我就可以 SSH 到该服务器,并通过 ping、登录和排查我的应用程序实例。因此,我需要从该服务器打开到应用程序服务器的 SSH 和 ICMP。
第三个用于跳转服务器本身,因为我只想限制从我的办公室 IP 访问跳转服务器(假设网关具有公共 IP 地址 1.2.3.4)。
第四个用于我的数据库服务器,通常是 RDS。假设我们使用 MySQL - 我需要从应用程序服务器打开 3306 端口。
vpc_security_groups: - name: "{{readable_env_name}}-public-LOADBALANCER" desc: "security group for public access" rules: - proto: tcp from_port: 80 to_port: 80 cidr_ip: 0.0.0.0/0 - proto: tcp from_port: 443 to_port: 443 cidr_ip: 0.0.0.0/0 - name: "{{readable_env_name}}-public-JUMPBOX" desc: "security group that allows access from dedicated jump box in public network to internal resources" rules: - proto: tcp from_port: 22 to_port: 22 cidr_ip: "1.2.3.4/32" #Your office private ip - name: "{{readable_env_name}}-private-APP" desc: "Network for internal app servers" rules: - proto: tcp from_port: 22 to_port: 22 group_id: "{{readable_env_name}}-public-JUMPBOX" - proto: tcp from_port: 80 to_port: 80 group_id: "{{readable_env_name}}-public-LOADBALANCER" - proto: tcp from_port: 443 to_port: 443 group_id: "{{readable_env_name}}-public-LOADBALANCER" - proto: icmp from_port: -1 # icmp type, -1 = any type to_port: -1 # icmp subtype, -1 = any subtype group_id: "{{readable_env_name}}-public-JUMPBOX" - name: "{{readable_env_name}}-private-DATABASE" desc: "resources that are private" rules: - proto: tcp from_port: 3306 # MYSQL to_port: 3306 group_id: "{{readable_env_name}}-private-APP"
您不觉得从定义中就可以清晰地看出设置吗?我认为是的。
让我们检查一下创建的设置。
已创建的安全组列表。
具有入站规则的公共安全组。
具有入站规则的私有应用程序服务器组。
具有入站规则的数据库访问安全组。
具有入站规则的跳转服务器安全组。
运行预配
运行 Ansible 预配就像执行下面结构的 shell 文件一样简单。
正如您所见,我们指定了环境名称 (demo),要在其中创建 VPC 的区域 (us-east-1),并将您的安全 AWS 凭证设置为预配的环境变量。
#!/bin/sh # Static parameters WORKSPACE=$PWD BOX_PLAYBOOK=$WORKSPACE/bin/network_create.yml BOX_NAME=awsnetwork echo $BOX_NAME prudentia local <<EOF unregister $BOX_NAME register $BOX_PLAYBOOK $BOX_NAME verbose 4 set env demo set aws_region us-east-1 envset AWS_ACCESS_KEY_ID THISISVERYSECUREKEYID envset AWS_SECRET_ACCESS_KEY ANDTHISISTHISISVERYSECUREKEYACCESSKEY provision $BOX_NAME unregister $BOX_NAME EOF
想知道需要多长时间?不到 1 分钟。与手动设置相比……
PLAY RECAP ******************************************************************** 127.0.0.1 : ok=42 changed=12 unreachable=0 failed=0
最后,您将获得有关创建的网络的所有信息,因此您可以存储它,或者将其模板化到某个配置文件中。
关注点
我建议将部署和预配配方与您的应用程序代码一起存储和维护。相信我,当您知道您可以在 10-40 分钟内从头开始搭建环境时,您的客户会不那么焦虑。
如果您使用这种方法来实现不同类型的网络设置,我很乐意听取您的经验分享。如果您需要在项目中实现持续集成,欢迎您。
可与 Ansible v1 (1.9.4) 一起使用的示例可以在 https://github.com/Voronenko/ansible_vpc_ppsubnet_wnat/tree/master/v1 上查看或 fork。
可与 Ansible v2 (2.0.2.0) 一起使用的示例可以在 https://github.com/Voronenko/ansible_vpc_ppsubnet_wnat/tree/master/v2 上查看或 fork。