随着业务系统越来越庞大和复杂,尤其是容器类应用的快速普及,云上的网络规模和复杂多也越来越高,IP地址的分配管理虽然不是一件特别复杂的事情,但是面对快速敏捷的应用开发部署时,往往又会成为拖累整个云上价值链的瓶颈。
我们可以借助基础架构即代码(IaC)的能力,将IPAM系统与亚马逊云上的网络整合到一起,实现一个快速敏捷,可以对IP地址进行动态自动分配跟踪回收的DevOps过程,现将大致实现进行分享。
1\. IPAM系统的选择和部署
首先需要选择一个合适的IPAM系统,基本的要求就是其具备对接IaC的API能力。这里以开源的NetBox为例,Infoblox等其他常见IPAM系统均类似。
NetBox 是一个开源的网络自动化工具,用于管理和文档化网络和数据中心的各种组件,被广泛用于IP地址管理(IPAM)、数据中心基础设施管理(DCIM)、以及网络设备和连接的记录。
可以通过ECS+Fargate来快速部署NetBox,参考以下terraform代码:
```
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.24"
}
netbox = {
source = "e-breuninger/netbox"
version = "3.7.7"
}
}
}
# 部署netbox所需的VPC,也可以使用现有的VPC
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.1"
name = "netbox"
cidr = "10.254.0.0/24"
azs = ["us-east-1a", "us-east-1b"]
private_subnets = ["10.254.0.0/27", "10.254.0.32/27"]
public_subnets = ["10.254.0.64/27", "10.254.0.96/27"]
elasticache_subnets = ["10.254.0.128/27", "10.254.0.160/27"]
database_subnets = ["10.254.0.192/27", "10.254.0.224/27"]
single_nat_gateway = true
default_security_group_ingress = [{ self = true }]
default_security_group_egress = [{ self = true }]
}
# 用于存储数据库密码的secrets manager
module "db_password" {
source = "terraform-aws-modules/secrets-manager/aws"
version = "~> 1.1"
name_prefix = "netbox-db-password-"
create_random_password = true
random_password_length = 16
random_password_override_special = ""
}
# 读取数据库密码
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = module.db_password.secret_id
version_id = module.db_password.secret_version_id
}
# netbox使用的RDS数据库
module "db" {
source = "terraform-aws-modules/rds/aws"
version = "~> 6.2"
identifier = "netbox"
engine = "postgres"
family = "postgres16"
instance_class = "db.t4g.micro"
storage_type = "gp3"
allocated_storage = 20
availability_zone = module.vpc.azs[0]
username = "netbox"
password = data.aws_secretsmanager_secret_version.db_password.secret_string
db_name = "netbox"
manage_master_user_password = false
iam_database_authentication_enabled = false
skip_final_snapshot = true
create_db_option_group = false
parameters = [
{
name = "rds.force_ssl"
value = "0" # Netbox in App mode can't access /root/.postgresql/postgresql.crt so it disables SSL
},
]
db_subnet_group_name = module.vpc.database_subnet_group
vpc_security_group_ids = [module.vpc.default_security_group_id]
}
# netbox web UI使用的redis服务
resource "aws_elasticache_cluster" "redis" {
cluster_id = "netbox"
engine = "redis"
node_type = "cache.t4g.micro"
num_cache_nodes = 1
parameter_group_name = "default.redis7"
availability_zone = module.vpc.azs[0]
subnet_group_name = module.vpc.elasticache_subnet_group_name
security_group_ids = [module.vpc.default_security_group_id]
}
# 用于发布netbox web UI的ALB
module "lb" {
source = "terraform-aws-modules/alb/aws"
version = "~> 9.0"
name = "netbox"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
enable_deletion_protection = false
security_groups = [module.vpc.default_security_group_id]
security_group_ingress_rules = {
"allow-http" = {
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
from_port = 80
to_port = 80
}
}
listeners = {
http = {
port = 80
protocol = "HTTP"
forward = {
target_group_key = "netbox"
}
}
}
target_groups = {
netbox = {
create_attachment = false
target_type = "ip"
backend_protocol = "HTTP"
backend_port = 80
}
}
}
# 随机生成的netbox密码
module "secret_key" {
source = "terraform-aws-modules/secrets-manager/aws"
version = "~> 1.1"
name_prefix = "netbox-secret-key-"
create_random_password = true
random_password_override_special = "!@#\$%^&*(-_=+)"
random_password_length = 50
}
# 运行netbox的ECS集群
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws//modules/cluster"
version = "~> 5.2"
cluster_name = "netbox"
cloudwatch_log_group_retention_in_days = 1
}
# 定义netbox版本
locals {
nb_version = "v3.6.4-2.7.0"
}
# netbox配置
module "app" {
source = "terraform-aws-modules/ecs/aws//modules/service"
version = "~> 5.2"
cluster_arn = module.ecs_cluster.arn
name = "netbox"
runtime_platform = {
operating_system_family = "LINUX"
cpu_architecture = "ARM64"
}
assign_public_ip = true
enable_autoscaling = false
security_group_ids = [module.vpc.default_security_group_id]
security_group_rules = {
"allow-internet" = { # to fetch image
type = "egress"
protocol = "all"
cidr_blocks = ["0.0.0.0/0"]
from_port = 0
to_port = 0
}
}
subnet_ids = [module.vpc.public_subnets[0]]
container_definitions = {
netbox = {
readonly_root_filesystem = false
essential = true
image = "docker.io/netboxcommunity/netbox:\${local.nb_version}"
port_mappings = [
{ name = "http", containerPort = 8080, protocol = "tcp" },
]
environment = [
{
name = "DB_HOST"
value = module.db.db_instance_address
},
{
name = "DB_USER"
value = "netbox"
},
{
name = "DB_NAME"
value = "netbox"
},
{
name = "REDIS_HOST"
value = aws_elasticache_cluster.redis.cache_nodes[0].address
},
]
secrets = [
{
name = "DB_PASSWORD"
valueFrom = module.db_password.secret_arn
},
{
name = "SECRET_KEY"
valueFrom = module.secret_key.secret_arn
},
]
}
}
load_balancer = {
service = {
container_name = "netbox"
container_port = 8080
target_group_arn = module.lb.target_groups["netbox"].arn
}
}
}
# 访问netbox所需的dns地址
output "lb_dns_name" {
value = module.lb.dns_name
}
```
设置terraform所需的权限的环境变量和profile(推荐使用aws-vault等工具,略),并部署NetBox:
```
terraform init/fmt/plan/apply...
```
访问lb_dns_name,进入NetBox系统即可获取或生成后续所需的api_token
2\. 创建为VPC和Subnet预留的地址池,这一步可以通过NetBox的Web管理界面完成,也可以通过terraform模板直接定义,这里给出示例代码:
```
provider "netbox" {
server_url = "http://<之前部署的netbox服务的地址>"
api_token = "<netbox的api token>"
}
#定义三种IP地址段用途:为VPC预留的地址池,VPC分配的地址段,子网分配的地址段
#也可以根据实际用途和管理需要进行更详细的划分,比如生产环境、测试环境、DMZ区域等等
resource "netbox_ipam_role" "vpc_pool" {
name = "VPC IP Address Pool"
}
resource "netbox_ipam_role" "vpc_prefix" {
name = "VPC IP Address prefix"
}
resource "netbox_ipam_role" "subnet_prefix" {
name = "Subnet IP Address prefix"
}
#定义预留给VPC的地址段
resource "netbox_prefix" "vpc_pool" {
prefix = "10.0.0.0/8"
status = "container"
role_id = netbox_ipam_role.vpc_pool.id
description = "vpc ip pool"
}
```
3\. 使用NetBox自动为VPC和Subnet分配地址
```
#为要创建的VPC获取一个地址段
resource "netbox_available_prefix" "vpc_prefix" {
#如果已经在其他tf模板定义了vpc_pool,也可以使用指定id或者data来获取对应的id
parent_prefix_id = netbox_prefix.vpc_pool.id
prefix_length = 24
status = "active"
role_id = netbox_ipam_role.vpc_prefix.id
description = "demo_vpc"
}
#为要创建的子网获取一个地址段
resource "netbox_available_prefix" "subnet_prefix" {
parent_prefix_id = netbox_available_prefix.vpc_prefix.id
prefix_length = 25
status = "active"
role_id = netbox_ipam_role.subnet_prefix.id
description = "demo_subnet"
}
#创建VPC和子网
resource "aws_vpc" "demo_vpc" {
cidr_block = netbox_available_prefix.vpc_prefix.prefix
tags = {
Name="demo_vpc"
}
}
resource "aws_subnet" "demo_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = netbox_available_prefix.subnet_prefix.prefix
}
```
部署完成后,可在NetBox中看到获取到的地址段的具体信息:
![750cce47bdf2bb0586c24a315579a0e6.png](https://dev-media.amazoncloud.cn/a177854fba8342fab54d8e850cb071fa_750cce47bdf2bb0586c24a315579a0e6.png "750cce47bdf2bb0586c24a315579a0e6.png")
综上,借助NetBox这样的IPAM和基础架构即代码(IaC)工具,就实现了无需再人工为VPC和Subnet分配IP地址段,仅需要关心所需的IP地址段大小,分配和记录的工作均由IPAM系统自动完成的效果,这对于具有复杂网络架构的大规模的云上环境和混合云环境的网络规划运维,尤其是变更频繁的场景,具有很高的实用价值。