Skip to content

Thiết Kế Subnet và Lập Kế Hoạch CIDR — IP Addressing ở Scale Production

Tại Sao IP Planning Quan Trọng Đến Vậy

Trong tất cả các quyết định thiết kế VPC, lập kế hoạch CIDR là thứ khó undo nhất và gây ra nhiều hệ quả nhất về lâu dài. Một hệ thống có subnet /22 tưởng như đủ rộng, sau 2 năm với GKE và autoscaling có thể cạn kiệt IP và không có cách mở rộng gracefully vì CIDR ranges liền kề đã bị subnet khác chiếm.

Nguyên tắc: Lập kế hoạch CIDR cho 5 năm tới, deploy cho ngày hôm nay.

Internal Model: Cách GCP Xử Lý Subnet

Subnet là Regional SDN Construct

Khi bạn tạo subnet, GCP thực hiện các bước sau trong control plane:

  1. Validate CIDR không chồng lấp với bất kỳ subnet nào khác trong VPC
  2. Reserve 4 địa chỉ đặc biệt trong CIDR
  3. Tạo một subnet route trong VPC route table: <cidr> → local
  4. Register subnet với DHCP service của region
  5. Propagate cấu hình mới đến Andromeda agents trong region

Sau khi propagation hoàn thành, Andromeda agents trên tất cả hosts trong region biết rằng traffic đến CIDR này thuộc về subnet này và cần được forward locally (không qua internet).

4 Địa Chỉ Reserved Trong Mỗi Subnet

Mỗi IPv4 subnet tiêu tốn 4 địa chỉ cho internal use của GCP:

Subnet: 10.0.0.0/24 (256 IPs)

10.0.0.0   → Network address (reserved, không gán được)
10.0.0.1   → Default gateway (Andromeda virtual gateway)
10.0.0.254 → Second-to-last (reserved for GCP future use)
10.0.0.255 → Broadcast address (reserved, không dùng trong SDN)

Usable IPs: 10.0.0.2 đến 10.0.0.253 = 252 IPs

Điểm kỹ thuật về gateway: 10.0.0.1 không phải là địa chỉ của một thiết bị vật lý. Đây là một virtual gateway được implement bởi Andromeda — mỗi Andromeda agent trên mỗi host đều có thể xử lý gói tin gửi đến địa chỉ gateway này. Không có SPOF (single point of failure) ở đây.

Subnet và Availability Zones

Một sự khác biệt quan trọng với AWS: subnet GCP là regional, không phải zonal. Một subnet prod-us-west1 với 10.0.0.0/20 bao phủ tất cả zones của us-west1 (a, b, c).

Region: us-west1 (10.0.0.0/20)
├── Zone us-west1-a:
│   VM1: 10.0.0.5  ← từ subnet này
│   VM2: 10.0.0.6  ← từ subnet này
├── Zone us-west1-b:
│   VM3: 10.0.3.2  ← cũng từ subnet này
└── Zone us-west1-c:
    VM4: 10.0.7.1  ← cũng từ subnet này

Khi bạn tạo VM trong us-west1-a, GCP cấp IP từ 10.0.0.0/20 — DHCP service của GCP chọn một IP chưa được cấp. Không có zonal separation trong IP allocation của cùng subnet.

Hệ quả: Bạn không thể biết VM này ở zone nào chỉ dựa trên IP address. Để biết zone, phải dùng metadata API hoặc Cloud Asset Inventory.

Primary CIDR Range

Kích Thước Tối Thiểu và Tối Đa

  • Tối thiểu: /29 = 8 IPs, 4 usable. Dùng cho proxy-only subnets hoặc small gateway subnets.
  • Tối đa: /8 trong RFC 1918 — nhưng thực tế không ai dùng một subnet /8 vì nó chiếm toàn bộ range.

Với production workloads thông thường:

  • /24 (252 usable): cho small services hoặc isolated components
  • /22 (~1000 usable): cho medium workloads không dùng GKE nhiều
  • /20 (~4000 usable): khởi điểm phổ biến, nhưng có thể thiếu với GKE
  • /16 (~65000 usable): cho large-scale deployments với nhiều GKE nodes

Khi Nào Cần Expand Primary CIDR

GCP cho phép expand primary CIDR (không thể shrink). Lệnh:

bash
# Expand từ /20 lên /19 (double the IPs)
gcloud compute networks subnets expand-ip-range prod-us-west1 \
  --region=us-west1 \
  --prefix-length=19

Điều kiện kỹ thuật:

  1. New range phải là supernet của old range (10.0.0.0/19 bao gồm 10.0.0.0/20)
  2. Không được chồng lấp với subnet khác trong VPC
  3. Phải có không gian trống liền kề

Anti-pattern: "Để khi nào cần mới expand." Việc expand đòi hỏi không gian trống liền kề. Nếu bạn đã phân bổ ranges liền kề cho subnets khác, expand sẽ không thực hiện được. Khi đó chỉ còn cách thêm secondary range.

Secondary CIDR Ranges: Mở Rộng Địa Chỉ Mà Không Cần Subnet Mới

Secondary ranges là tính năng cho phép một subnet có nhiều CIDR ranges. Traffic đến secondary range được deliver đến VMs trong subnet đó thông qua cơ chế Alias IP.

bash
gcloud compute networks subnets update prod-us-west1 \
  --region=us-west1 \
  --add-secondary-ranges \
    gke-pods=10.1.0.0/16,\
    gke-services=10.2.0.0/20

Tại Sao Secondary Ranges Tồn Tại?

Secondary ranges giải quyết vấn đề cụ thể: GKE cần địa chỉ IP cho từng pod, nhưng pods là ephemeral và số lượng thay đổi liên tục.

Cách tiếp cận thô sơ là cấp IP từ primary range cho mỗi pod. Nhưng điều này có vấn đề:

  • Primary range cũng phục vụ VMs (nodes), không phân biệt được node IP và pod IP trong route table
  • Firewall rules không thể target chỉ pods mà không ảnh hưởng nodes cùng subnet

Secondary ranges tách biệt pod IP space khỏi node IP space. Andromeda biết rằng traffic đến secondary range phải được forward đến VM "owning" alias IP đó — cơ chế cụ thể được giải thích trong file 04 về Alias IP.

Ràng Buộc Của Secondary Ranges

  1. Tối đa 30 secondary ranges per subnet
  2. Mỗi secondary range không được chồng lấp với bất kỳ range nào khác trong VPC (cả primary lẫn secondary của các subnets khác)
  3. Không thể xóa secondary range đang được GKE cluster sử dụng mà không xóa cluster trước
  4. Secondary range không thể expand như primary range — phải thêm range mới riêng biệt

GKE IP Consumption: Nguồn Gốc Của IP Exhaustion

Đây là nơi nhiều hệ thống production gặp vấn đề nhất. GKE tiêu thụ IPs theo pattern rất khác VM thông thường.

Cấu Trúc IP Cho GKE Cluster

Một GKE cluster cần ba CIDR ranges:

1. Node CIDR (Primary subnet range):
   Mỗi node = 1 IP từ primary range
   100 nodes → 100 IPs tiêu tốn

2. Pod CIDR (Secondary range "gke-pods"):
   Mỗi node được cấp một /24 block riêng cho pods
   Với max 110 pods/node: 256 IPs per node block
   100 nodes → 100 blocks /24 = 25,600 IPs từ pod range

3. Service CIDR (Secondary range "gke-services"):
   Mỗi Kubernetes Service = 1 Cluster IP
   500 services → 500 IPs từ service range
   Thường dùng /20 hoặc /22 (1024-4096 service IPs)

Tính toán thực tế cho cluster lớn:

Cluster GKE với plan: max 200 nodes, 110 pods/node, 2000 services:

Node IPs: 200 IPs từ primary range /20 (dùng 200/4092)

Pod IPs: 200 nodes × 1 block /24 mỗi node
  = 200 blocks /24
  = 200 × 256 = 51,200 IPs cần thiết
  → Cần /16 (65,536 IPs) cho pod range để có headroom
  → /17 (32,768 IPs) KHÔNG đủ: chỉ chứa 128 blocks /24
    nhưng cần 200 blocks → sẽ fail khi scale lên 128+ nodes!

Service IPs: 2000 services
  → /19 (2048 IPs) hoặc /20 (4096 IPs)

Điều quan trọng ít được biết: GKE phân bổ một /24 block cho mỗi node, không phải cho mỗi pod. Node chỉ có 10 pods vẫn giữ nguyên 256 IPs block. IPs không dùng trên node đó bị "waste" cho đến khi node bị terminate. Đây là lý do cần /16 cho pod range dù chỉ có 200 nodes.

Multi-Cluster: Mỗi Cluster Cần Secondary Range Riêng

GKE không cho phép nhiều clusters chia sẻ cùng pod CIDR:

Cluster 1: pods từ 10.1.0.0/16, services từ 10.2.0.0/20
Cluster 2: pods từ 10.4.0.0/16, services từ 10.5.0.0/20
Cluster 3: pods từ 10.8.0.0/16, services từ 10.9.0.0/20

Với ba clusters, bạn cần 3 pod ranges và 3 service ranges. Mỗi subnet cần đủ secondary ranges cho tất cả clusters trong region đó.

Proxy-Only Subnets: Trường Hợp Đặc Biệt

Với Envoy-based Load Balancers (Internal Application LB, Internal TCP/UDP Proxy LB), GCP yêu cầu proxy-only subnets — subnets đặc biệt chỉ phục vụ cho load balancer proxies, không chứa VM workloads.

bash
gcloud compute networks subnets create prod-proxy-us-west1 \
  --network=prod-vpc \
  --region=us-west1 \
  --range=10.3.0.0/24 \
  --purpose=REGIONAL_MANAGED_PROXY \
  --role=ACTIVE

GCP cấp IPs trong proxy-only subnet cho các Envoy proxy instances. Với traffic cao, số proxy instances có thể nhiều — /24 (252 IPs) là minimum, nhưng với high-traffic production environment nên dùng /23 hoặc lớn hơn.

Nếu bạn deploy Internal Application LB mà không có proxy-only subnet, LB creation sẽ fail với error rõ ràng.

Xung Đột CIDR: Các Patterns Nguy Hiểm

Pattern 1: Chồng Lấp Với On-Premises

Nhiều tổ chức có on-premises dùng 10.0.0.0/8 (toàn bộ Class A range). Khi kết nối GCP qua VPN/Interconnect:

On-premises: 10.0.0.0/8
GCP VPC: 10.0.0.0/16

Cloud Router sẽ nhận route quảng bá từ cả hai phía:
  → 10.0.0.0/16 từ GCP subnet
  → 10.0.0.0/8 từ on-premises BGP peer (supernet)

Kết quả: Longest prefix match → traffic đến 10.0.0.x đi GCP VPC
         nhưng 10.1.0.x - 10.255.x.x đi on-premises
         Nếu on-premises cũng có devices ở 10.0.0.x → conflict!

Giải pháp: Khi on-premises đã dùng 10.0.0.0/8, GCP VPC nên dùng 172.16.0.0/12 hoặc 192.168.0.0/16. Nếu bắt buộc phải dùng 10.x.x.x, cần dùng range mà on-premises KHÔNG dùng và cấu hình route filtering cẩn thận ở Cloud Router.

Pattern 2: Secondary Range Collision Với Subnet Khác

Subnet A (us-west1):
  primary: 10.0.0.0/20
  secondary: gke-pods = 10.1.0.0/16

Subnet B (us-central1):
  primary: 10.4.0.0/20
  secondary: gke-pods = 10.1.0.0/16  ← CONFLICT!

Secondary ranges phải unique trong toàn bộ VPC, không chỉ trong subnet. GCP sẽ từ chối tạo Subnet B với error rõ ràng, nhưng nếu bạn không track centrally, error này có thể bất ngờ khi scale sang region mới.

Pattern 3: RFC 1918 Nhầm Lẫn Với Ranges Đặc Biệt

Một số ranges không được dùng cho subnet thông thường:

100.64.0.0/10  → Shared Address Space (RFC 6598) — dùng bởi một số providers
169.254.0.0/16 → Link-local
192.0.2.0/24   → Documentation (TEST-NET)
198.51.100.0/24 → Documentation
203.0.113.0/24 → Documentation

Các ranges GCP reserved:
35.199.192.0/19 → Cloud DNS / Service Directory
35.199.224.0/19 → Serverless VPC Access
35.235.240.0/20 → Identity-Aware Proxy

Nếu bạn dùng 35.199.192.0/19 cho subnet thực — bạn vừa conflict với GCP internal routing cho Cloud DNS. Hãy tránh tất cả ranges không phải RFC 1918.

Chiến Lược CIDR Allocation Cho Enterprise

Với tổ chức lớn (nhiều teams, environments, regions):

Tier 1: Environment Split

Total: 10.0.0.0/8

Production:     10.0.0.0/9    (10.0.0.0 - 10.127.255.255)
Non-production: 10.128.0.0/9  (10.128.0.0 - 10.255.255.255)

Tier 2: Region Allocation (Trong Production)

us-west1:         10.0.0.0/13   (10.0.0.0 - 10.7.255.255, 524K IPs)
us-central1:      10.8.0.0/13
us-east1:         10.16.0.0/13
europe-west1:     10.24.0.0/13
europe-west2:     10.32.0.0/13
asia-northeast1:  10.40.0.0/13
asia-southeast1:  10.48.0.0/13
[reserved]:       10.56.0.0/13 - 10.127.255.255  (cho regions mới)

Tier 3: Per-Region Allocation

us-west1 (10.0.0.0/13 = 524K IPs):
  Primary subnet (nodes):     10.0.0.0/20  (4096 node IPs)
  Proxy-only subnet:          10.0.16.0/24 (LB proxies)
  Private services subnet:    10.0.17.0/24 (CloudSQL, etc)

  Secondary ranges:
    cluster1-pods:     10.1.0.0/16  (65K pod IPs)
    cluster1-services: 10.2.0.0/20  (4K service IPs)
    cluster2-pods:     10.3.0.0/16
    cluster2-services: 10.4.0.0/20

  Reserved:  10.5.0.0 - 10.7.255.255 (expansion)

Terraform: Quản Lý CIDR Như Infrastructure Code

hcl
# network_config.tf
locals {
  vpc_cidrs = {
    prod = {
      "us-west1" = {
        nodes    = "10.0.0.0/20"
        proxy    = "10.0.16.0/24"
        gke_clusters = {
          "cluster-1" = {
            pods     = "10.1.0.0/16"
            services = "10.2.0.0/20"
          }
          "cluster-2" = {
            pods     = "10.3.0.0/16"
            services = "10.4.0.0/20"
          }
        }
      }
      "europe-west1" = {
        nodes    = "10.24.0.0/20"
        proxy    = "10.24.16.0/24"
        gke_clusters = {
          "cluster-1" = {
            pods     = "10.25.0.0/16"
            services = "10.26.0.0/20"
          }
        }
      }
    }
  }
}

Approach này: CIDR được version-controlled, thay đổi được review qua PR, lịch sử allocation rõ ràng trong git history.

Phát Hiện IP Exhaustion Sớm

Monitoring cần thiết để tránh bị bất ngờ:

Alert khi utilization > 70% của bất kỳ subnet nào:

bash
# Query số IPs đang dùng
gcloud compute networks subnets describe prod-nodes-us-west1 \
  --region=us-west1 \
  --format="json" | jq '.ipCidrRange, .fingerprint'

# Kiểm tra số VM đang dùng subnet
gcloud compute instances list \
  --filter="networkInterfaces[0].subnetwork:prod-nodes-us-west1" \
  --format="value(name)" | wc -l

Với GKE nodes, xem thêm:

bash
# Số nodes trong cluster
kubectl get nodes --no-headers | wc -l

# Pod CIDR đang dùng per node
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'

Khi utilization đạt 70%, bắt đầu kế hoạch mở rộng ngay — không đợi đến 90%.

References