ЁЯПл The SchoolтА║ЁЯПл VPCтА║ЁЯПЧя╕П рдзрдбрд╛ 12 тАФ Production VPC design: рдЯрд┐рдХрдгреНрдпрд╛рд╕рд╛рдареА рдмрд╛рдВрдзрд▓реЗрд▓рд╛ campus
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯПЧя╕П рдзрдбрд╛ 12 тАФ Production VPC design: рдЯрд┐рдХрдгреНрдпрд╛рд╕рд╛рдареА рдмрд╛рдВрдзрд▓реЗрд▓рд╛ campus

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 12 тАФ рд╢реЗрд╡рдЯрдЪрд╛ рдзрдбрд╛! ┬╖ рдорд╛рдЧрдЪрд╛: lesson-11-troubleshooting


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕, рдЖрдгрд┐ рдЖрд░рд╛рдЦрдбрд╛: vpc/demo.py рдордзрд▓реЗ design() 3 AZs ├Ч public / app / data рдорд╛рдВрдбрддреЗ, рдкреНрд░рддреНрдпреЗрдХ AZ рд▓рд╛ рдПрдХ NAT, S3 рдЖрдгрд┐ DynamoDB gateway endpoints рдЖрдгрд┐ flow logs тАФ рдЖрдгрд┐ рд╡рд╛рдвреАрд╕рд╛рдареА рдХрд┐рддреА рдЬрд╛рдЧрд╛ рдЙрд░рд▓реА рддреЗ рдореЛрдЬрддреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдЖрддрд╛ рд╢рд╛рд│рд╛ рдЕрд╕рд╛ campus рдмрд╛рдВрдзрддреЗ рдЬреНрдпрд╛рдд рддреА рд╡рд░реНрд╖рд╛рдиреБрд╡рд░реНрд╖реЗ рд░рд╛рд╣рдгрд╛рд░ рдЖрд╣реЗ. ЁЯПЧя╕П

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    igw["ЁЯЪк internet gateway"]
    subgraph a["ЁЯПв AZ a"]
        pa["public: ALB ┬╖ nat-a"] --> aa["app: no public IPs"] --> da["data: no internet route"]
    end
    subgraph b["ЁЯПв AZ b"]
        pb["public: ALB ┬╖ nat-b"] --> ab["app"] --> db["data"]
    end
    subgraph c["ЁЯПв AZ c"]
        pc["public: ALB ┬╖ nat-c"] --> ac["app"] --> dc["data"]
    end
    igw --> pa
    igw --> pb
    igw --> pc
    ep["ЁЯЪЗ S3 + DynamoDB gateway endpoints"]
    fl["ЁЯУУ flow logs on"]
    da -.-> ep

ЁЯЧ║я╕П рд░реЗрдЦрд╛рдЯрд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/vpc/lesson-diagrams.html#l12

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг servers рдиреА рднрд░рд▓реНрдпрд╛рд╡рд░ VPC рдмрджрд▓рдгреЗ рдХрдареАрдг рдЕрд╕рддреЗ. рдЖрд░рд╛рдЦрдбрд╛ рдард░рд╡рддреЛ рддреБрдордЪрд╛ blast radius (рдПрдХ AZ, рдПрдХ tier), рддреБрдордЪреЗ рдмрд┐рд▓ (NAT рдЖрдгрд┐ cross-AZ data), рддреБрдордЪреА audit рдЙрддреНрддрд░реЗ ("database рд▓рд╛ internet рдХрдбреЗ route рдирд╛рд╣реА") рдЖрдгрд┐ рдкреБрдврдЪреНрдпрд╛ рд╡рд░реНрд╖реА рддреБрдореНрд╣реА рдХрд╢рд╛рд╢реАрд╣реА рдЬреЛрдбреВ рд╢рдХрд╛рд▓ рдХреА рдирд╛рд╣реА.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

vpc/demo.py рдордзрд▓реЗ design() plan_subnets(VPC, 20, [...], AZS) рдмреЛрд▓рд╛рд╡рддреЗ, рдирдК /20 subnets рдХрд┐рддреА рдкрддреНрддреЗ рдШреЗрддрд╛рдд рддреНрдпрд╛рдВрдЪреА рдмреЗрд░реАрдЬ рдХрд░рддреЗ, рдЖрдгрд┐ рдХрд┐рддреА рдЙрд░рд▓реЗ рддреЗ рдЫрд╛рдкрддреЗ. рдзрдбрд╛ 05 рдордзрд▓рд╛ lab campus рдЖрдзреАрдЪ tier рдЪреЗ рдирд┐рдпрдо рдкрд╛рд│рддреЛ: rt-data рдордзреНрдпреЗ рдлрдХреНрдд local рдЖрдгрд┐ pl-s3 рдЖрд╣реЗрдд. Snippet рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рддреЛ, рдЖрдгрд┐ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдд overlaps рдЖрд╣реЗрдд рдХрд╛ рддреЗ рддрдкрд╛рд╕рддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 vpc/demo.py design
python3 - <<'EOF'
import sys, ipaddress; sys.path.insert(0, "vpc"); from network import plan_subnets, overlaps
from demo import campus, VPC, AZS, PL
c = campus()
print("data wing тЖТ internet:", c.rts["rt-data"].lookup("93.184.216.34", PL))
print("data wing тЖТ S3:      ", c.rts["rt-data"].lookup("52.219.40.10", PL))
plan = plan_subnets(VPC, 20, ["public", "app", "data"], AZS)
print("any overlap in the plan:", any(overlaps(a[2], b[2]) for i, a in enumerate(plan) for b in plan[i+1:]))
print("last planned wing:", plan[-1])
nxt = ipaddress.ip_network("10.20.144.0/20")
print("next free /20:", nxt, "overlaps the plan?", any(overlaps(str(nxt), p[2]) for p in plan))
print("secondary CIDR 100.64.0.0/16 overlaps the campus?", overlaps(VPC, "100.64.0.0/16"))
EOF
python3 vpc/test_vpc.py

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

design рд╣реЗ рдЫрд╛рдкрддреЗ:

тФАтФА a production campus: 3 AZs ├Ч (public ┬╖ app ┬╖ data), one NAT per AZ, S3 + DynamoDB gateway endpoints, flow logs on
   9 subnets use 36,864 of 65,536 addresses тАФ 28,672 left for growth (EKS pods eat addresses fast)
   public: load balancers + NAT only ┬╖ app: desks, no public IPs ┬╖ data: databases, no route to the internet at all

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ: data wing тЖТ internet: (None, None), data wing тЖТ S3: ('pl-s3', 'vpce-s3'), any overlap in the plan: False, last planned wing: ('data', 'ap-south-1c', '10.20.128.0/20'), next free /20: 10.20.144.0/20 overlaps the plan? False рдЖрдгрд┐ secondary CIDR 100.64.0.0/16 overlaps the campus? False. Tests рд╢реЗрд╡рдЯреА 12/12 passed рдЫрд╛рдкрддрд╛рдд.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдореНрд╣реА рдЕрд╕рд╛ campus рдорд╛рдВрдбреВ рд╢рдХрддрд╛ рдЬреЛ рдПрдЦрд╛рджреА рдЗрдорд╛рд░рдд рдЧреЗрд▓реА рддрд░реА рдЯрд┐рдХрддреЛ, рдЖрдкрд▓рд╛ data wing internet рдкрд╛рд╕реВрди рджреВрд░ рдареЗрд╡рддреЛ, S3 рд╕рд╛рдареА NAT рдЯреЛрд▓ рднрд░рдд рдирд╛рд╣реА тАФ рдЖрдгрд┐ рддрд░реАрд╣реА рдкреБрдврдЪреНрдпрд╛ wing рд╕рд╛рдареА рддреНрдпрд╛рдд рдЬрд╛рдЧрд╛ рдЖрд╣реЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

рдмрд╣реБрддреЗрдХ teams рд╣реЗ рд╣рд╛рддрд╛рдиреЗ рди рдмрд╛рдВрдзрддрд╛ test рдХреЗрд▓реЗрд▓реНрдпрд╛ module рдордзреВрди рдмрд╛рдВрдзрддрд╛рдд.

рдЦрд▒реНрдпрд╛ account рд╡рд░ (Terraform рдЖрдгрд┐ AWS credentials рд▓рд╛рдЧрддрд╛рдд; values рдЙрджрд╛рд╣рд░рдгрд╛рджрд╛рдЦрд▓ рдЖрд╣реЗрдд):

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"                       # pin the version you have tested

  name = "school-prod"
  cidr = "10.20.0.0/16"
  azs  = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]

  public_subnets   = ["10.20.0.0/20",  "10.20.16.0/20", "10.20.32.0/20"]
  private_subnets  = ["10.20.48.0/20", "10.20.64.0/20", "10.20.80.0/20"]
  database_subnets = ["10.20.96.0/20", "10.20.112.0/20", "10.20.128.0/20"]
  create_database_subnet_route_table = true   # data wing: its own table, no NAT route

  enable_nat_gateway     = true
  one_nat_gateway_per_az = true

  enable_flow_log                      = true
  create_flow_log_cloudwatch_log_group = true
  create_flow_log_cloudwatch_iam_role  = true
}

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = module.vpc.vpc_id
  service_name      = "com.amazonaws.ap-south-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = concat(module.vpc.private_route_table_ids, module.vpc.database_route_table_ids)
}

рдордЧ рдзрдбрд╛ 11 рдордзрд▓реНрдпрд╛ tools рдиреЗ рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рд╛: database рдкрд╛рд╕реВрди internet gateway рдкрд░реНрдпрдВрддрдЪрд╛ Reachability Analyzer path not reachable рдЕрд╕рд╛рдЪ рдкрд░рдд рдпрд╛рдпрд▓рд╛ рд╣рд╡рд╛.

ЁЯОУ Campus рдЖрддрд╛ рддреБрдордЪрд╛ рдЖрд╣реЗ

рдХреБрдВрдкрдг рдШрд╛рддрд▓реЗрд▓рд╛ campus тЖТ рдШрд░ рдирдВрдмрд░ тЖТ рддреАрди рдЗрдорд╛рд░рддреАрдВрдордзрд▓реЗ wings тЖТ corridor рдЪреНрдпрд╛ рдкрд╛рдЯреНрдпрд╛ тЖТ рдореБрдЦреНрдп рджрд░рд╡рд╛рдЬрд╛ тЖТ рдкрд╛рд╣реБрдгреНрдпрд╛рдВрдЪреНрдпрд╛ рдпрд╛рджреНрдпрд╛ рдЖрдгрд┐ wing рдЪреА рджрд╛рд░реЗ тЖТ рд╕рдВрдЧреНрд░рд╣рд╛рд▓рдпрд╛рдХрдбреЗ рдмреЛрдЧрджрд╛ тЖТ phone book тЖТ рднрд╛рдЧреАрджрд╛рд░рд╛рдХрдбреЗ corridor тЖТ bus station тЖТ рдиреЛрдВрджрд╡рд╣реА рдЖрдгрд┐ рдорд╛рд░реНрдЧрд╛рд╡рд░реВрди рдЪрд╛рд▓рдгреЗ тЖТ рдЯрд┐рдХрдгреНрдпрд╛рд╕рд╛рдареА рдмрд╛рдВрдзрд▓реЗрд▓рд╛ campus. рддреБрдореНрд╣реА рдлрдХреНрдд VPC рд╢рд┐рдХрд▓рд╛ рдирд╛рд╣реАрдд тАФ рддреБрдореНрд╣реА campus рдЪрд╛ рдЖрд░рд╛рдЦрдбрд╛ рдХрд░реВ рд╢рдХрддрд╛, рддреЛ рдЬреЛрдбреВ рд╢рдХрддрд╛ рдЖрдгрд┐ debug рдХрд░реВ рд╢рдХрддрд╛. ЁЯПлЁЯОУ

рд╢рд╛рд│реЗрддреАрд▓ рдкреБрдврдЪреА рджрд╛рд░реЗ: NAT рд╢рд╛рд│рд╛ private wings рдЬреНрдпрд╛рддреВрди рдмрд╛рд╣реЗрд░ рдЬрд╛рддрд╛рдд рддреЗ рдмрд╛рдЬреВрдЪреЗ рджрд╛рд░ рдмрд╛рдВрдзрддреЗ; EC2 рд╢рд╛рд│рд╛ рд╣реЗ wings desks рдиреА рднрд░рддреЗ; Networking рд╢рд╛рд│рд╛ IP, TCP рдЖрдгрд┐ DNS рдордзреНрдпреЗ рдЕрдзрд┐рдХ рдЦреЛрд▓рд╡рд░ рдЬрд╛рддреЗ.

git checkout main
python3 vpc/demo.py     # one last run, for fun

ЁЯПЧя╕П Lesson 12 тАФ Production VPC design: a campus built to last

ЁЯУН You are here: Lesson 12 of 12 тАФ the final lesson! ┬╖ Previous: lesson-11-troubleshooting


ЁЯУж What's in this branch

The complete course, plus the blueprint: design() in vpc/demo.py lays out 3 AZs ├Ч public / app / data, one NAT per AZ, S3 and DynamoDB gateway endpoints and flow logs тАФ and counts how much room is left to grow.

ЁЯзТ Explain like I'm 5

Now the school builds the campus it will live in for years. ЁЯПЧя╕П

ЁЯЧ║я╕П Diagram

flowchart TB
    igw["ЁЯЪк internet gateway"]
    subgraph a["ЁЯПв AZ a"]
        pa["public: ALB ┬╖ nat-a"] --> aa["app: no public IPs"] --> da["data: no internet route"]
    end
    subgraph b["ЁЯПв AZ b"]
        pb["public: ALB ┬╖ nat-b"] --> ab["app"] --> db["data"]
    end
    subgraph c["ЁЯПв AZ c"]
        pc["public: ALB ┬╖ nat-c"] --> ac["app"] --> dc["data"]
    end
    igw --> pa
    igw --> pb
    igw --> pc
    ep["ЁЯЪЗ S3 + DynamoDB gateway endpoints"]
    fl["ЁЯУУ flow logs on"]
    da -.-> ep

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/vpc/lesson-diagrams.html#l12

тЭУ What

ЁЯдФ Why

Because a VPC is hard to change once full of servers. The plan decides your blast radius (one AZ, one tier), your bill (NAT and cross-AZ data), your audit answers ("the database has no route to the internet") and whether you can connect to anything next year.

ЁЯФз How (in this repo)

design() in vpc/demo.py calls plan_subnets(VPC, 20, [...], AZS), adds up the addresses the nine /20 subnets take, and prints what is left. The lab campus from lesson 05 already follows the tier rules: rt-data has only local and pl-s3. The snippet proves it, and checks the plan for overlaps.

ЁЯзк Try it

python3 vpc/demo.py design
python3 - <<'EOF'
import sys, ipaddress; sys.path.insert(0, "vpc"); from network import plan_subnets, overlaps
from demo import campus, VPC, AZS, PL
c = campus()
print("data wing тЖТ internet:", c.rts["rt-data"].lookup("93.184.216.34", PL))
print("data wing тЖТ S3:      ", c.rts["rt-data"].lookup("52.219.40.10", PL))
plan = plan_subnets(VPC, 20, ["public", "app", "data"], AZS)
print("any overlap in the plan:", any(overlaps(a[2], b[2]) for i, a in enumerate(plan) for b in plan[i+1:]))
print("last planned wing:", plan[-1])
nxt = ipaddress.ip_network("10.20.144.0/20")
print("next free /20:", nxt, "overlaps the plan?", any(overlaps(str(nxt), p[2]) for p in plan))
print("secondary CIDR 100.64.0.0/16 overlaps the campus?", overlaps(VPC, "100.64.0.0/16"))
EOF
python3 vpc/test_vpc.py

тЬЕ Verify тАФ what you should see

design prints:

тФАтФА a production campus: 3 AZs ├Ч (public ┬╖ app ┬╖ data), one NAT per AZ, S3 + DynamoDB gateway endpoints, flow logs on
   9 subnets use 36,864 of 65,536 addresses тАФ 28,672 left for growth (EKS pods eat addresses fast)
   public: load balancers + NAT only ┬╖ app: desks, no public IPs ┬╖ data: databases, no route to the internet at all

Your snippet prints data wing тЖТ internet: (None, None), data wing тЖТ S3: ('pl-s3', 'vpce-s3'), any overlap in the plan: False, last planned wing: ('data', 'ap-south-1c', '10.20.128.0/20'), next free /20: 10.20.144.0/20 overlaps the plan? False and secondary CIDR 100.64.0.0/16 overlaps the campus? False. The tests end with 12/12 passed.

ЁЯПБ What you just proved

You can lay out a campus that survives a lost building, keeps its data wing off the internet, pays no NAT toll for S3 тАФ and still has room for the next wing.

тЪая╕П Common mistakes

ЁЯПн In production

Most teams build this from a tested module rather than by hand.

On a real account (needs Terraform and AWS credentials; values are examples):

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"                       # pin the version you have tested

  name = "school-prod"
  cidr = "10.20.0.0/16"
  azs  = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]

  public_subnets   = ["10.20.0.0/20",  "10.20.16.0/20", "10.20.32.0/20"]
  private_subnets  = ["10.20.48.0/20", "10.20.64.0/20", "10.20.80.0/20"]
  database_subnets = ["10.20.96.0/20", "10.20.112.0/20", "10.20.128.0/20"]
  create_database_subnet_route_table = true   # data wing: its own table, no NAT route

  enable_nat_gateway     = true
  one_nat_gateway_per_az = true

  enable_flow_log                      = true
  create_flow_log_cloudwatch_log_group = true
  create_flow_log_cloudwatch_iam_role  = true
}

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = module.vpc.vpc_id
  service_name      = "com.amazonaws.ap-south-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = concat(module.vpc.private_route_table_ids, module.vpc.database_route_table_ids)
}

Then prove it with the tools from lesson 11: a Reachability Analyzer path from a database to the internet gateway should come back not reachable.

ЁЯОУ The campus is yours

A fenced campus тЖТ house numbers тЖТ wings in three buildings тЖТ corridor signs тЖТ the front gate тЖТ guest lists and wing doors тЖТ a tunnel to the archive тЖТ the phone book тЖТ a corridor to the partner тЖТ the bus station тЖТ the logbook and the walk тЖТ a campus built to last. You didn't just learn VPC тАФ you can plan, connect and debug the campus. ЁЯПлЁЯОУ

Next doors in the school: the NAT school builds the side gate private wings go out through; the EC2 school fills these wings with desks; the Networking school goes deeper into IP, TCP and DNS.

git checkout main
python3 vpc/demo.py     # one last run, for fun
тЖР PrevioustroubleshootingFinished! Take the quiz тЖТcheck what stuck

This page is the lesson's README from the lesson-12-production-design branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.