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

ЁЯз▒ рдзрдбрд╛ 03 тАФ Subnets рдЖрдгрд┐ AZs: campus рдЪреЗ wings, рдкреНрд░рддреНрдпреЗрдХ рдПрдХрд╛ рдЗрдорд╛рд░рддреАрдд

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 03 ┬╖ рдорд╛рдЧреЗ: lesson-02-cidr-ips ┬╖ рдкреБрдвреЗ: lesson-04-route-tables


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

рдзрдбреЗ 01тАУ02, рдЖрдгрд┐ wings: plan_subnets() рд╣реЗ 10.20.0.0/16 рдЪреЗ рдирдК /20 subnets рдХрд╛рдкрддреЗ тАФ рддреАрди tiers (public, app, data) ├Ч рддреАрди AZs тАФ рдЖрдгрд┐ vpc/network.py рдордзреАрд▓ reserved_addresses() / usable() рджрд╛рдЦрд╡рддрд╛рдд рдХреА AWS рдкреНрд░рддреНрдпреЗрдХ subnet рдордзреНрдпреЗ 5 рдкрддреНрддреЗ рд╕реНрд╡рддрдГрдХрдбреЗ рдареЗрд╡рддреЛ.

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

Campus рдПрдХрдЪ рдЙрдШрдбрд╛ рд╣реЙрд▓ рдЕрд╕рдгреНрдпрд╛рдЗрддрдХрд╛ рд▓рд╣рд╛рди рдирд╛рд╣реА. рдореНрд╣рдгреВрди рддреБрдореНрд╣реА wings ЁЯз▒ рдмрд╛рдВрдзрддрд╛: рдореБрдЦреНрдп рджрд░рд╡рд╛рдЬрд╛рдЬрд╡рд│ рдПрдХ public wing, рдЬрд┐рдереЗ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рдХрд╛рдо рдХрд░рддрд╛рдд рддреЛ рдПрдХ app wing, рдЖрдгрд┐ рдЬрд┐рдереЗ рдкрд░реАрдХреНрд╖реЗрдЪреЗ рдкреЗрдкрд░ рдХреБрд▓реБрдкрд╛рдд рдареЗрд╡рд▓реЗрд▓реЗ рдЕрд╕рддрд╛рдд рддреЛ рдПрдХ data wing.

Campus рдЪреНрдпрд╛ рдЬрдорд┐рдиреАрд╡рд░ рдПрдХрдореЗрдХрд╛рдВрдкрд╛рд╕реВрди рджреВрд░ рдЕрд╢рд╛ рддреАрди рдЗрдорд╛рд░рддреА ЁЯПвЁЯПвЁЯПв рдЖрд╣реЗрдд. рд╡рд╛рджрд│рд╛рдореБрд│реЗ рдПрдХ рдЗрдорд╛рд░рдд рдмрдВрдж рдЭрд╛рд▓реА, рддрд░реА рдЙрд░рд▓реЗрд▓реНрдпрд╛ рджреЛрди рдХрд╛рдо рдХрд░рдд рд░рд╛рд╣рддрд╛рдд. рдореНрд╣рдгреВрди рддреБрдореНрд╣реА рдкреНрд░рддреНрдпреЗрдХ wing рддреАрди рд╡реЗрд│рд╛ рдмрд╛рдВрдзрддрд╛, рдкреНрд░рддреНрдпреЗрдХ рдЗрдорд╛рд░рддреАрдд рдПрдХрджрд╛. рдПрдЦрд╛рджрд╛ wing рдХрдзреАрдЪ рджреЛрди рдЗрдорд╛рд░рддреАрдВрдордзреНрдпреЗ рдкрд╕рд░рдд рдирд╛рд╣реА.

рдкреНрд░рддреНрдпреЗрдХ wing рдордзреНрдпреЗ рд░рдЦрд╡рд╛рд▓рджрд╛рд░ 5 рдЦреЛрд▓реНрдпрд╛рдВрдЪреЗ рдирдВрдмрд░ рд╕реНрд╡рддрдГрд╕рд╛рдареА рдареЗрд╡рддреЛ: wing рдЪреА рд╕реНрд╡рддрдГрдЪреА рдирд╛рд╡рд╛рдЪреА рдкрд╛рдЯреА, corridor рдЪреА рдкрд╛рдЯреА, phone book, рдПрдХ рдЬрд╛рджрд╛, рдЖрдгрд┐ рд╢реЗрд╡рдЯрдЪрд╛ рдирдВрдмрд░. рддреЗ 5 рдирдВрдмрд░ рддреБрдореНрд╣реА рдХрдзреАрдЪ рдПрдЦрд╛рджреНрдпрд╛ desk рд▓рд╛ рджреЗрдК рд╢рдХрдд рдирд╛рд╣реА.

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

flowchart TB
    vpc["ЁЯПл VPC 10.20.0.0/16"]
    subgraph az1["ЁЯПв ap-south-1a"]
        p1["public 10.20.0.0/20"]
        a1["app 10.20.48.0/20"]
        d1["data 10.20.96.0/20"]
    end
    subgraph az2["ЁЯПв ap-south-1b"]
        p2["public 10.20.16.0/20"]
        a2["app 10.20.64.0/20"]
        d2["data 10.20.112.0/20"]
    end
    subgraph az3["ЁЯПв ap-south-1c"]
        p3["public 10.20.32.0/20"]
        a3["app 10.20.80.0/20"]
        d3["data 10.20.128.0/20"]
    end
    vpc --> az1
    vpc --> az2
    vpc --> az3
    r["ЁЯкк every subnet: 5 reserved<br/>.0 ┬╖ .1 router ┬╖ .2 DNS ┬╖ .3 future ┬╖ last"]
    d1 -.-> r

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдПрдХ AZ рдмрдВрдж рдкрдбреВ рд╢рдХрддреЛ, рдЖрдгрд┐ subnet рджреБрд╕рд▒реНрдпрд╛ AZ рдордзреНрдпреЗ рд╣рд▓рд╡рддрд╛ рдпреЗрдд рдирд╛рд╣реА. рддреАрди рдЗрдорд╛рд░рддреАрдВрдордзреНрдпреЗ wings рдЕрд╕рд▓реЗ рдХреА рдПрдХ рдЗрдорд╛рд░рдд рдЧреЗрд▓реНрдпрд╛рд╡рд░ рддреБрдордЪреА рдлрдХреНрдд рдПрдХ рддреГрддреАрдпрд╛рдВрд╢ рдХреНрд╖рдорддрд╛ рдЬрд╛рддреЗ, рд╕рдЧрд│реА рдирд╛рд╣реА. рдЖрдгрд┐ tiers рдореБрд│реЗ рдкреНрд░рддреНрдпреЗрдХ wing рд▓рд╛ рд╡реЗрдЧрд│реНрдпрд╛ рдкрд╛рдЯреНрдпрд╛ (рдзрдбрд╛ 04) рдЖрдгрд┐ рд╡реЗрдЧрд│реА рджрд╛рд░реЗ (рдзрдбрд╛ 06) рдорд┐рд│реВ рд╢рдХрддрд╛рдд.

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

vpc/network.py рдордзреАрд▓ plan_subnets(vpc_cidr, new_prefix, tiers, azs) ip_network(vpc_cidr).subnets(new_prefix=...) рдордзреВрди рдЬрд╛рддреЛ рдЖрдгрд┐ рдХреНрд░рдорд╛рдиреЗ рдкреНрд░рддреНрдпреЗрдХ (tier, AZ) рд▓рд╛ рдПрдХ block рджреЗрддреЛ. reserved_addresses() block рдордзреАрд▓ 0, 1, 2, 3 рдЖрдгрд┐ тИТ1 рдпрд╛ рдЬрд╛рдЧрд╛ рдкрд░рдд рдХрд░рддреЛ; usable() рдореНрд╣рдгрдЬреЗ block рдЪрд╛ рдЖрдХрд╛рд░ рд╡рдЬрд╛ RESERVED = 5. vpc/demo.py рдордзреАрд▓ subnets() рдирдК wings рдЪрд╛ рдЖрд░рд╛рдЦрдбрд╛ рдЫрд╛рдкрддреЛ.

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

python3 vpc/demo.py subnets
python3 - <<'EOF'
import sys; sys.path.insert(0, "vpc"); from network import plan_subnets, usable, reserved_addresses
for tier, az, c in plan_subnets("10.20.0.0/16", 22, ["public", "app"], ["ap-south-1a", "ap-south-1b"]):
    print(tier, az, c, usable(c))
print(reserved_addresses("10.20.96.0/28"), usable("10.20.96.0/28"))
EOF

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

subnets рдирдК рдУрд│реА рдЫрд╛рдкрддреЛ, public ap-south-1a 10.20.0.0/20 4,091 usable рдкрд╛рд╕реВрди data ap-south-1c 10.20.128.0/20 4,091 usable рдкрд░реНрдпрдВрдд, рдЖрдгрд┐ рдордЧ:

тФАтФА in EVERY subnet AWS keeps 5 addresses тАФ 10.20.0.0/24: 10.20.0.0, 10.20.0.1, 10.20.0.2, 10.20.0.3, 10.20.0.255
   so a /24 gives 251 usable addresses, not 256 ┬╖ a /28 (the smallest) gives 11

рддреБрдордЪрд╛ snippet рдЪрд╛рд░ /22 wings рдЫрд╛рдкрддреЛ тАФ 10.20.0.0/22, 10.20.4.0/22, 10.20.8.0/22, 10.20.12.0/22, рдкреНрд░рддреНрдпреЗрдХрд╛рдд 1019 рд╡рд╛рдкрд░рддрд╛ рдпреЗрдгрд╛рд░реЗ тАФ рдЖрдгрд┐ рдордЧ ['10.20.96.0', '10.20.96.1', '10.20.96.2', '10.20.96.3', '10.20.96.15'] 11.

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

рддреБрдореНрд╣реА campus рдЪреЗ рдкреНрд░рддреНрдпреЗрдХ рдЗрдорд╛рд░рддреАрд╕рд╛рдареА wings рдХрд╛рдкреВ рд╢рдХрддрд╛, рдЖрдгрд┐ 4,096 рдкрддреНрддреНрдпрд╛рдВрдЪреЗ 4,091 тАФ рдЖрдгрд┐ 16 рдЪреЗ 11 тАФ рдХрд╛ рд╣реЛрддрд╛рдд рд╣реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдорд╛рд╣реАрдд рдЖрд╣реЗ.

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

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

Teams subnets code рдордзреВрди рдХрд╛рдкрддрд╛рдд, рдореНрд╣рдгрдЬреЗ рдкреНрд░рддреНрдпреЗрдХ environment рдордзреНрдпреЗ рдЖрд░рд╛рдЦрдбрд╛ рд╕рд╛рд░рдЦрд╛рдЪ рд░рд╛рд╣рддреЛ.

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

aws ec2 describe-availability-zones --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output table
aws ec2 create-subnet --vpc-id vpc-0123456789abcdef0 --cidr-block 10.20.48.0/20 --availability-zone ap-south-1a
aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-0123456789abcdef0 \
    --query 'Subnets[].[CidrBlock,AvailabilityZone,AvailableIpAddressCount]' --output table

Terraform рдордзреНрдпреЗ cidrsubnet("10.20.0.0/16", 4, n) lab рд╕рд╛рд░рдЦреЗрдЪ /20 blocks рджреЗрддреЛ: n = 0 тЖТ 10.20.0.0/20, n = 3 тЖТ 10.20.48.0/20, n = 8 тЖТ 10.20.128.0/20.

locals { azs = ["ap-south-1a", "ap-south-1b", "ap-south-1c"] }

resource "aws_subnet" "app" {
  count             = 3
  vpc_id            = aws_vpc.school.id
  availability_zone = local.azs[count.index]
  cidr_block        = cidrsubnet(aws_vpc.school.cidr_block, 4, 3 + count.index)
  tags              = { Name = "app-${count.index}" }
}

тПня╕П рдкреБрдвреЗ

рдирдК wings, рдЖрдгрд┐ рдЕрдЬреВрди рдПрдХрд╣реА рдкрд╛рдЯреА рдирд╛рд╣реА. Packet рд▓рд╛ рдХреБрдареЗ рдЬрд╛рдпрдЪреЗ рддреЗ рдХрд╕реЗ рдХрд│рддреЗ? Route tables тАФ corridor рдЪреНрдпрд╛ рдкрд╛рдЯреНрдпрд╛.

git checkout lesson-04-route-tables

ЁЯз▒ Lesson 03 тАФ Subnets & AZs: wings of the campus, each in one building

ЁЯУН You are here: Lesson 03 of 12 ┬╖ Previous: lesson-02-cidr-ips ┬╖ Next: lesson-04-route-tables


ЁЯУж What's in this branch

Lessons 01тАУ02, plus the wings: plan_subnets() carves 10.20.0.0/16 into nine /20 subnets тАФ three tiers (public, app, data) ├Ч three AZs тАФ and reserved_addresses() / usable() in vpc/network.py show the 5 addresses AWS keeps in every subnet.

ЁЯзТ Explain like I'm 5

The campus is too big to be one open hall. So you build wings ЁЯз▒: a public wing near the front gate, an app wing where the teachers work, and a data wing where the exam papers are locked away.

The campus stands on land with three buildings ЁЯПвЁЯПвЁЯПв far apart from each other. If a storm closes one building, the other two keep working. So you build each wing three times, once in every building. A wing never stretches across two buildings.

In every wing, the caretaker keeps 5 room numbers for himself: the wing's own name plate, the corridor sign, the phone book, one spare, and the last number. You can never give those 5 to a desk.

ЁЯЧ║я╕П Diagram

flowchart TB
    vpc["ЁЯПл VPC 10.20.0.0/16"]
    subgraph az1["ЁЯПв ap-south-1a"]
        p1["public 10.20.0.0/20"]
        a1["app 10.20.48.0/20"]
        d1["data 10.20.96.0/20"]
    end
    subgraph az2["ЁЯПв ap-south-1b"]
        p2["public 10.20.16.0/20"]
        a2["app 10.20.64.0/20"]
        d2["data 10.20.112.0/20"]
    end
    subgraph az3["ЁЯПв ap-south-1c"]
        p3["public 10.20.32.0/20"]
        a3["app 10.20.80.0/20"]
        d3["data 10.20.128.0/20"]
    end
    vpc --> az1
    vpc --> az2
    vpc --> az3
    r["ЁЯкк every subnet: 5 reserved<br/>.0 ┬╖ .1 router ┬╖ .2 DNS ┬╖ .3 future ┬╖ last"]
    d1 -.-> r

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

тЭУ What

ЁЯдФ Why

Because one AZ can fail, and a subnet cannot move to another AZ. Wings in three buildings mean a lost building costs you a third of your capacity, not all of it. And because the tiers let each wing get different signs (lesson 04) and different doors (lesson 06).

ЁЯФз How (in this repo)

plan_subnets(vpc_cidr, new_prefix, tiers, azs) in vpc/network.py walks ip_network(vpc_cidr).subnets(new_prefix=...) and hands out one block per (tier, AZ) in order. reserved_addresses() returns positions 0, 1, 2, 3 and тИТ1 of a block; usable() is the block size minus RESERVED = 5. subnets() in vpc/demo.py prints the nine-wing plan.

ЁЯзк Try it

python3 vpc/demo.py subnets
python3 - <<'EOF'
import sys; sys.path.insert(0, "vpc"); from network import plan_subnets, usable, reserved_addresses
for tier, az, c in plan_subnets("10.20.0.0/16", 22, ["public", "app"], ["ap-south-1a", "ap-south-1b"]):
    print(tier, az, c, usable(c))
print(reserved_addresses("10.20.96.0/28"), usable("10.20.96.0/28"))
EOF

тЬЕ Verify тАФ what you should see

subnets prints nine lines, from public ap-south-1a 10.20.0.0/20 4,091 usable to data ap-south-1c 10.20.128.0/20 4,091 usable, then:

тФАтФА in EVERY subnet AWS keeps 5 addresses тАФ 10.20.0.0/24: 10.20.0.0, 10.20.0.1, 10.20.0.2, 10.20.0.3, 10.20.0.255
   so a /24 gives 251 usable addresses, not 256 ┬╖ a /28 (the smallest) gives 11

Your snippet prints four /22 wings тАФ 10.20.0.0/22, 10.20.4.0/22, 10.20.8.0/22, 10.20.12.0/22, each with 1019 usable тАФ then ['10.20.96.0', '10.20.96.1', '10.20.96.2', '10.20.96.3', '10.20.96.15'] 11.

ЁЯПБ What you just proved

You can carve a campus into wings per building, and you know why 4,096 addresses become 4,091 тАФ and 16 become 11.

тЪая╕П Common mistakes

ЁЯПн In production

Teams carve subnets from code, so the plan is the same in every environment.

On a real account (needs the AWS CLI and credentials; IDs are examples):

aws ec2 describe-availability-zones --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output table
aws ec2 create-subnet --vpc-id vpc-0123456789abcdef0 --cidr-block 10.20.48.0/20 --availability-zone ap-south-1a
aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-0123456789abcdef0 \
    --query 'Subnets[].[CidrBlock,AvailabilityZone,AvailableIpAddressCount]' --output table

In Terraform, cidrsubnet("10.20.0.0/16", 4, n) gives the same /20 blocks as the lab: n = 0 тЖТ 10.20.0.0/20, n = 3 тЖТ 10.20.48.0/20, n = 8 тЖТ 10.20.128.0/20.

locals { azs = ["ap-south-1a", "ap-south-1b", "ap-south-1c"] }

resource "aws_subnet" "app" {
  count             = 3
  vpc_id            = aws_vpc.school.id
  availability_zone = local.azs[count.index]
  cidr_block        = cidrsubnet(aws_vpc.school.cidr_block, 4, 3 + count.index)
  tags              = { Name = "app-${count.index}" }
}

тПня╕П Next

Nine wings, and no signs yet. How does a packet know where to walk? Route tables тАФ the corridor signs.

git checkout lesson-04-route-tables
тЖР Previouscidr ipsNext тЖТroute tables

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