Networking & VPCNetworking & VPC
Part of the Cloud knowledge base — AWS deep dive. Complements DevOps Roadmap.
Tổng quan
Networking là nơi AWS giống nhất với việc vận hành một data center của riêng bạn, bởi vì nó đúng là một data center — chỉ được ảo hóa. Virtual Private Cloud (VPC) là một lát cắt được cô lập về mặt logic của mạng AWS mà bạn hoàn toàn kiểm soát: bạn chọn dải địa chỉ IP, chia nó thành các subnet, quyết định cái gì có thể tiếp cận được từ internet và cái gì không, và đặt firewall ở mọi lớp. Gần như mọi workload bạn chạy — EC2, RDS, ECS, EKS, Lambda-trong-VPC — đều sống bên trong một VPC, nên một mô hình tư duy lung lay về networking sẽ làm suy yếu mọi thứ ở bên trên nó.
Trang này được xây từ trong ra ngoài: VPC và các subnet của nó, cách traffic đi vào và đi ra (gateway và route table), hai lớp firewall (security group và NACL), cách các VPC kết nối với nhau và với on-prem (peering, Transit Gateway, PrivateLink), cách bạn phân phối traffic (Elastic Load Balancing), cách người dùng tìm thấy bạn (Route 53 DNS), cách bạn cache trên phạm vi toàn cầu (CloudFront), và cách bạn thấy được điều đang diễn ra (Flow Logs). Nó khép lại bằng việc lập kế hoạch CIDR và một bố cục VPC tối giản, thực tế bằng Terraform.
Mô hình tư duy cốt lõi: một VPC là một mạng riêng bạn sở hữu; một subnet là một lát cắt của nó ghim vào một AZ; một route table quyết định traffic của một subnet đi đâu; và các gateway là những cánh cửa ra thế giới bên ngoài. “Public” và “private” subnet không phải là một cài đặt — chúng là hệ quả của việc route table của chúng trỏ tới đâu.
Kiến thức nền tảng
VPC và subnet
- Một VPC là một tài nguyên cấp region được định nghĩa bằng một khối CIDR (ví dụ
10.0.0.0/16→ 65.536 địa chỉ). Nó trải rộng trên tất cả các AZ trong region của nó. - Một subnet là một phần chia nhỏ của CIDR của VPC (ví dụ
10.0.1.0/24→ 256 địa chỉ) và nằm trong đúng một AZ. Để có multi-AZ bạn cần một subnet cho mỗi AZ. - AWS dành riêng 5 IP trong mỗi subnet (bốn cái đầu + cái cuối): network address, VPC router, DNS, một địa chỉ dự phòng, và broadcast. Một
/24cho bạn 251 IP dùng được, không phải 256.
Public vs private subnet — điểm khác biệt định danh:
| Public subnet | Private subnet | |
|---|---|---|
| Route table có route tới | Internet Gateway (0.0.0.0/0 → IGW) | NAT Gateway hoặc không có gì |
| Inbound từ internet | Có thể (với một public/Elastic IP) | Không bao giờ trực tiếp |
| Outbound ra internet | Trực tiếp qua IGW | Qua NAT (trong một public subnet) hoặc không có |
| Cư dân điển hình | Load balancer, bastion host, NAT gateway | App server, database, internal service |
Quy tắc kinh nghiệm: đặt càng ít càng tốt vào public subnet. Chỉ có các internet-facing load balancer và NAT gateway mới thực sự cần ở đó. App server và database thuộc về private subnet, chỉ tiếp cận được thông qua load balancer.
Gateway, route table, và IP
- Internet Gateway (IGW) — một cái cho mỗi VPC; cánh cửa ra internet công cộng. Một subnet là “public” chính xác là vì route table của nó gửi
0.0.0.0/0tới IGW. Các instance cũng cần một public hoặc Elastic IP để tiếp cận được từ inbound. - NAT Gateway — cho phép các instance trong private subnet thực hiện kết nối outbound (kéo package, gọi API) mà không tiếp cận được từ inbound. Nó sống trong một public subnet, và các private subnet route
0.0.0.0/0tới nó. Đây là một tài nguyên được quản lý, sẵn sàng cao (theo từng AZ) — và nó không miễn phí: bạn trả tiền theo giờ và theo GB được xử lý, khiến nó thường xuyên là một yếu tố cần cân nhắc về chi phí và về single-point-of-failure (triển khai một cái cho mỗi AZ để có HA). - Egress-only Internet Gateway — tương đương IPv6 của NAT: chỉ outbound cho IPv6 (vốn không có khái niệm NAT).
- Elastic IP (EIP) — một địa chỉ IPv4 công cộng tĩnh bạn sở hữu và có thể gán lại giữa các instance/NAT gateway. Địa chỉ IPv4 hiện nay chịu phí theo giờ dù có được gắn hay không, nên hãy giải phóng những cái bạn không cần.
Route table là bộ não định tuyến. Mỗi subnet được liên kết với đúng một route table; bảng này là một danh sách destination CIDR → target. Route local (CIDR của chính VPC → local) luôn hiện diện và không thể bị xóa — đó là cái cho phép các subnet nói chuyện với nhau. Mọi thứ khác bạn tự thêm: 0.0.0.0/0 → igw-... làm cho một subnet thành public; 0.0.0.0/0 → nat-... cho một private subnet outbound.
Hai lớp firewall: Security Group vs NACL
AWS cho bạn hai bộ lọc packet độc lập. Hiểu sự khác biệt là điều thiết yếu.
| Security Group (SG) | Network ACL (NACL) | |
|---|---|---|
| Hoạt động ở | Cấp instance/ENI | Cấp subnet |
| Stateful? | Stateful — traffic trả về tự động được cho phép | Stateless — phải cho phép traffic trả về một cách tường minh |
| Rule | Chỉ Allow (implicit deny) | Cả Allow và Deny, đánh giá theo số thứ tự rule |
| Mặc định | Deny tất cả inbound, allow tất cả outbound | NACL mặc định allow tất cả; NACL custom deny tất cả |
| Có thể tham chiếu | Các security group khác (và CIDR) | Chỉ CIDR |
| Đánh giá | Tất cả rule được xét cùng nhau | Rule khớp đầu tiên thắng (theo thứ tự số) |
Stateful vs stateless là cái bẫy. Một security group chỉ cần một rule inbound cho port 443; response tự động được cho phép vì SG ghi nhớ kết nối. Một NACL, vì là stateless, cần cả một rule inbound cho 443 lẫn một rule outbound cho các ephemeral return port (1024–65535) — quên cái thứ hai và các kết nối sẽ treo một cách bí ẩn.
Hướng dẫn thực tế: làm gần như tất cả công việc của bạn với security group — chúng là stateful, có thể tham chiếu lẫn nhau (ví dụ “cho phép web SG tiếp cận DB SG ở port 5432” mà không cần địa chỉ IP nào cả), và dễ suy luận hơn nhiều. Dùng NACL một cách tiết kiệm như một backstop thô ở cấp subnet, chủ yếu cho các rule deny tường minh (chặn một dải IP xấu) mà SG không thể biểu diễn.
Lập kế hoạch CIDR
Làm đúng kế hoạch địa chỉ ngay từ đầu giúp tránh việc phải tái kiến trúc đau đớn về sau.
- Chọn một dải private RFC 1918:
10.0.0.0/8,172.16.0.0/12, hoặc192.168.0.0/16. Một/16cho mỗi VPC (65k IP) là một mặc định phổ biến, thoải mái. - Đừng để chồng lấn. Các VPC mà bạn sẽ từng peer, kết nối qua Transit Gateway, hoặc liên kết với on-prem phải có CIDR không chồng lấn — chồng lấn khiến việc định tuyến trở nên bất khả thi và là lỗi lập kế hoạch số 1. Hãy dành riêng các dải khác biệt cho mỗi môi trường/region ngay từ ngày đầu.
- Chừa chỗ để phát triển. Cấp phát subnet rộng rãi; bạn không thể dễ dàng thay đổi kích thước một subnet, mặc dù bạn có thể thêm các khối CIDR phụ vào một VPC về sau.
- Một bố cục chuẩn cho một VPC
/16trải trên 3 AZ: ba public/24(một cho mỗi AZ) và ba private/24(hoặc lớn hơn, như/20, cho các workload cần nhiều IP — EKS pod ngốn địa chỉ khủng khiếp).
| Prefix | Địa chỉ | Dùng được (trừ 5) | Sử dụng điển hình |
|---|---|---|---|
/28 | 16 | 11 | Nhỏ/dự phòng |
/24 | 256 | 251 | Subnet chuẩn |
/20 | 4.096 | 4.091 | Subnet lớn (EKS, fleet lớn) |
/16 | 65.536 | — | Cả một VPC |
Khái niệm chính
Kết nối các VPC và mạng
| Lựa chọn | Kết nối | Topology | Dùng khi |
|---|---|---|---|
| VPC Peering | Hai VPC | 1:1, không bắc cầu | Một số ít VPC cần liên kết trực tiếp |
| Transit Gateway (TGW) | Nhiều VPC + on-prem | Hub-and-spoke, bắc cầu | Hàng chục VPC; bạn cần một network hub |
| PrivateLink / VPC Endpoints | VPC tiêu thụ → một service cụ thể | Riêng tư, một chiều tới một service | Tiếp cận các AWS service hoặc một SaaS một cách riêng tư, không qua internet |
| Site-to-Site VPN | VPC ↔ on-prem qua internet | Đường hầm mã hóa | Kết nối hybrid, chi phí thấp |
| Direct Connect | VPC ↔ on-prem qua đường dây riêng | Liên kết vật lý riêng tư | Độ trễ thấp nhất quán, băng thông cao tới on-prem |
VPC Peering là một liên kết trực tiếp, riêng tư giữa hai VPC. Hạn chế then chốt của nó là nó không bắc cầu (non-transitive): nếu A peer B và B peer C, A không thể tiếp cận C. Đây là điều khiến peering không thể quản lý nổi khi vượt quá một vài VPC — bạn sẽ cần một full mesh (n(n-1)/2 kết nối).
Transit Gateway giải quyết điều đó: nó là một network hub cấp region mà mọi VPC (và VPN/Direct Connect của bạn) gắn vào một lần, và nó định tuyến giữa chúng một cách bắc cầu. Với bất kỳ hệ thống nhiều VPC không tầm thường nào, TGW là câu trả lời; bạn đánh đổi một chi phí nhỏ theo từng attachment/theo GB lấy một topology đơn giản hơn rất nhiều.
PrivateLink và VPC Endpoints giữ traffic tới các AWS service (hoặc SaaS đối tác) hoàn toàn trên mạng riêng của AWS:
- Gateway endpoint (miễn phí) — chỉ dành cho S3 và DynamoDB; được thêm như một target của route table để các instance private tiếp cận các service đó mà không cần NAT gateway (một cách tiết kiệm chi phí thực sự).
- Interface endpoint (PrivateLink, theo giờ + theo GB) — một ENI với một private IP trong subnet của bạn cho hầu hết các service khác (SSM, ECR, Secrets Manager, SQS, v.v.) và cho SaaS bên thứ ba. Traffic không bao giờ chạm tới internet công cộng.
Dùng một gateway endpoint cho S3 là một tối ưu hóa kinh điển: các private subnet chỉ cần S3 khi đó có thể bỏ qua hoàn toàn NAT gateway cho traffic đó.
Elastic Load Balancing (ELB)
ELB phân phối traffic đến vào các target khỏe mạnh ở nhiều AZ. Có ba loại:
| Loại | Layer | Định tuyến theo | Dùng cho |
|---|---|---|---|
| ALB (Application) | L7 (HTTP/HTTPS) | Host, path, header, method | Web app, microservice, định tuyến container, WebSocket |
| NLB (Network) | L4 (TCP/UDP/TLS) | IP + port, độ trễ cực thấp | Hiệu năng cực đại, static IP, giao thức không phải HTTP |
| GWLB (Gateway) | L3 | Trong suốt, GENEVE | Chèn các virtual appliance (firewall, IDS/IPS) inline |
- ALB là con ngựa thồ cho HTTP: nó làm định tuyến dựa trên nội dung (gửi
/api/*tới một target group,/static/*tới cái khác), định tuyến dựa trên host (multi-tenant), TLS termination, và tích hợp với WAF và Cognito auth. Nó có một tên DNS động, không phải một IP cố định. - NLB hoạt động ở L4 cho hàng triệu request/giây ở độ trễ cực thấp, giữ nguyên client source IP, và có thể có một static/Elastic IP cho mỗi AZ — lý do để chọn nó khi client cần allowlist một IP cố định hoặc khi bạn không nói HTTP.
- GWLB là loại chuyên biệt: nó định tuyến traffic một cách trong suốt qua các network appliance bên thứ ba để kiểm tra.
Load balancer dùng health check để ngừng gửi traffic tới các target không khỏe mạnh, và target group để định nghĩa nhóm backend (instance, IP, hoặc Lambda). Luôn đặt một internet-facing LB trong các public subnet trải trên ≥2 AZ, với các target thực sự nằm trong các private subnet.
Route 53 — DNS và quản lý traffic
Route 53 là dịch vụ DNS của AWS (cái tên lấy từ port DNS kinh điển, 53). Nó làm ba việc: đăng ký (register) domain, phân giải (resolve) DNS (hosting có thẩm quyền), và định tuyến (route) một cách thông minh với health check.
Routing policy là phần thú vị:
| Policy | Hành vi | Dùng cho |
|---|---|---|
| Simple | Một record, không logic | Ánh xạ một endpoint cơ bản |
| Weighted | Chia traffic theo phần trăm | Canary/blue-green, A/B testing |
| Latency-based | Gửi người dùng tới region có độ trễ thấp nhất | App đa region toàn cầu |
| Geolocation | Định tuyến theo châu lục/quốc gia của người dùng | Tuân thủ, nội dung bản địa hóa |
| Geoproximity | Định tuyến theo khoảng cách địa lý, có bias | Kiểm soát địa lý chi tiết |
| Failover | Primary → secondary khi health check fail | DR active-passive |
| Multivalue answer | Trả về nhiều IP khỏe mạnh | Phân tán tải phía client đơn giản |
Alias record là một tính năng đặc thù của Route 53: một record A/AAAA trỏ tới một tài nguyên AWS (ALB, CloudFront, S3 website, một Route 53 record khác) bằng tên, tự động phân giải tới các IP hiện tại của nó, miễn phí khi query, và — khác với CNAME — có thể nằm ở zone apex (example.com, không chỉ www.example.com). Dùng alias record cho bất cứ thứ gì trỏ tới một AWS endpoint. Health check giám sát các endpoint và điều khiển failover cũng như việc gỡ bỏ target đã chết ở cấp DNS.
CloudFront — CDN
CloudFront là mạng phân phối nội dung toàn cầu của AWS, cache nội dung tại hơn 600 edge location gần người dùng. Nó đứng trước một origin (một S3 bucket, một ALB, hoặc bất kỳ HTTP server nào) và phục vụ các response đã cache từ edge gần nhất, cắt giảm độ trễ và giảm tải cho origin.
- Cache nội dung tĩnh và động; bạn kiểm soát TTL và cache key.
- Terminate TLS ở edge và tích hợp với AWS WAF và Shield để bảo vệ chống DDoS.
- Origin Access Control (OAC) khóa một S3 origin để nó chỉ tiếp cận được thông qua CloudFront, không trực tiếp — mẫu chuẩn để phục vụ một private bucket ra thế giới.
- CloudFront Functions và Lambda@Edge chạy code ở edge cho việc thao tác header, auth, redirect, và logic A/B.
Một thiết lập kinh điển: static site hoặc SPA trong một private S3 bucket → CloudFront với OAC đứng phía trước → Route 53 alias record ở apex → chứng chỉ ACM (được cấp ở us-east-1, một yêu cầu bắt buộc đối với CloudFront) cho HTTPS.
VPC Flow Logs — nhìn thấy traffic của bạn
VPC Flow Logs ghi lại metadata về IP traffic (source/dest IP và port, protocol, số byte, ACCEPT/REJECT) ở cấp VPC, subnet, hoặc ENI, được gửi tới CloudWatch Logs hoặc S3. Chúng không ghi lại nội dung packet, nhưng chúng không thể thiếu cho:
- Debug kết nối (“tại sao kết nối này bị REJECT?” → thường là do một SG hoặc NACL).
- Điều tra pháp y bảo mật và phát hiện những đối tượng nói chuyện bất ngờ.
- Cung cấp dữ liệu cho các công cụ như GuardDuty. Hãy bật chúng ít nhất ở cấp VPC cho bất kỳ mạng production nào.
Best Practices
-
Thiết kế kế hoạch CIDR trước khi tạo bất cứ thứ gì, và không bao giờ để chồng lấn. Các VPC có thể sẽ từng peer, tham gia một Transit Gateway, hoặc kết nối với on-prem phải có các dải khác biệt. Chồng lấn là lỗi networking duy nhất bạn không thể sửa mà không xây lại, nên hãy dành riêng các khối không chồng lấn cho mỗi môi trường và region ngay từ đầu.
-
Dùng bố cục multi-AZ, public/private subnet làm mặc định. Một public và một private subnet cho mỗi AZ trải trên ≥2 AZ là baseline cho bất kỳ VPC production nào. Nó cho bạn cả khả năng tiếp cận internet ở nơi cần và khả năng chống chịu một data-center failure với gần như không tốn thêm chi phí.
-
Chỉ đặt những gì phải public vào public subnet. Các internet-facing load balancer và NAT gateway thuộc về đó; app server và database thì không. Giữ compute và data trong private subnet, chỉ tiếp cận được qua LB, sẽ thu nhỏ đáng kể bề mặt tấn công của bạn.
-
Làm firewall bằng security group, không phải NACL. SG là stateful, có thể tham chiếu lẫn nhau, và dễ suy luận hơn nhiều. Tham chiếu SG-tới-SG (ví dụ cho phép app SG tiếp cận DB SG ở port database) thay vì hardcode IP, và dành NACL cho các rule deny thô ở cấp subnet.
-
Tuân theo least privilege trong security group. Chỉ mở đúng các port và source cụ thể mà mỗi tier cần; tránh
0.0.0.0/0trên bất cứ thứ gì trừ public HTTPS. Một database SG chỉ nên chấp nhận traffic từ SG của app tier — không bao giờ từ internet, và không bao giờ trên một dải port rộng. -
Triển khai NAT gateway theo từng AZ để có HA, và cắt giảm chi phí NAT ở nơi bạn có thể. Một NAT gateway đơn lẻ là một single point of failure ở cấp AZ và một chi phí data-transfer xuyên AZ. Chạy một cái cho mỗi AZ để chống chịu, và dùng S3/DynamoDB gateway endpoint để traffic đó hoàn toàn bỏ qua NAT.
-
Dùng VPC endpoint để giữ traffic AWS riêng tư và rẻ hơn. Interface (PrivateLink) và gateway endpoint giữ các lời gọi tới S3, ECR, SSM, Secrets Manager, và nhiều nữa trên mạng AWS — cải thiện bảo mật (không có đường qua internet) và thường giảm phí xử lý dữ liệu của NAT.
-
Chọn Transit Gateway thay vì một mesh các peering một khi bạn có nhiều hơn một vài VPC. Peering không bắc cầu và mở rộng theo
n²kết nối. Một Transit Gateway hub-and-spoke giữ cho một hệ thống nhiều VPC và hybrid đang lớn dần luôn quản lý được và định tuyến bắc cầu được. -
Chọn đúng load balancer cho giao thức. ALB cho HTTP/HTTPS với định tuyến dựa trên nội dung; NLB khi bạn cần hiệu năng cực đại, một static IP, giữ nguyên client source-IP, hoặc một giao thức không phải HTTP; GWLB chỉ cho việc chèn appliance inline. Dùng một ALB ở nơi bạn cần một IP cố định (hoặc ngược lại) buộc bạn phải xoay xở vụng về.
-
Đặt một LB trong public subnet đứng trước các app internet-facing, với target trong private subnet. Terminate TLS ở load balancer, health-check các target, và không bao giờ phơi các backend instance trực tiếp. Đây là baseline về độ tin cậy và bảo mật cho bất kỳ web workload nào.
-
Ưu tiên Route 53 alias record cho các AWS endpoint. Alias record tự động phân giải tới các IP hiện tại của tài nguyên, miễn phí khi query, và hoạt động ở zone apex nơi CNAME không thể. Dùng các routing policy (weighted, latency, failover) để làm canary, định tuyến đa region, và DR active-passive ở lớp DNS.
-
Đặt CloudFront đứng trước nội dung tĩnh và cache được. Nó cắt giảm độ trễ trên phạm vi toàn cầu, giảm tải origin của bạn, terminate TLS ở edge, và — với Origin Access Control — cho phép bạn phục vụ một private S3 bucket một cách an toàn. Nhớ rằng chứng chỉ CloudFront phải được cấp ở
us-east-1. -
Mã hóa traffic khi truyền, đầu-cuối tới đầu-cuối. Dùng HTTPS/TLS ở load balancer và CloudFront, và đừng dừng ở edge — ưu tiên TLS giữa LB và backend cho các workload nhạy cảm. AWS Certificate Manager cấp và tự động gia hạn chứng chỉ miễn phí.
-
Bật VPC Flow Logs trên mọi VPC production. Chúng là công cụ chính của bạn để chẩn đoán các lỗi kết nối (một SG/NACL REJECT hiện ra ngay lập tức) và cho việc điều tra bảo mật. Chuyển chúng tới một account trung tâm cùng với các audit log khác của bạn.
-
Bảo vệ các endpoint internet-facing bằng WAF và Shield. Gắn AWS WAF vào ALB và CloudFront để lọc các web exploit và bot phổ biến, và dựa vào Shield Standard (miễn phí) cộng với Shield Advanced ở nơi cần thiết để chống DDoS. Edge là nơi bạn muốn hấp thụ traffic độc hại, trước khi nó chạm tới origin của bạn.
-
Định nghĩa toàn bộ networking bằng code. VPC, subnet, route table, SG, và endpoint chính là loại tài nguyên liên kết chằng chịt, dễ cấu hình sai, thuộc về Terraform/CloudFormation — có thể review, diff, và tái lập được. Ghép một VPC bằng tay đảm bảo sẽ có drift và một mạng không thể xây lại được.
Worked example — a minimal multi-AZ VPC (Terraform)
Một VPC public/private trải trên hai AZ: các subnet internet-facing cho một load balancer, các private subnet cho tier app/database, và một NAT gateway cho outbound của private.
# 1. The VPC — a /16 gives ~65k addresses and room to grow
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "app-vpc" }
}
# 2. Internet Gateway — the door to the public internet
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = { Name = "app-igw" }
}
# 3. Public + private subnets across two AZs
resource "aws_subnet" "public" {
for_each = { a = "10.0.1.0/24", b = "10.0.2.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "ap-southeast-1${each.key}"
map_public_ip_on_launch = true # makes it a "public" subnet
tags = { Name = "public-${each.key}" }
}
resource "aws_subnet" "private" {
for_each = { a = "10.0.11.0/24", b = "10.0.12.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "ap-southeast-1${each.key}"
tags = { Name = "private-${each.key}" }
}
# 4. NAT gateway (in a public subnet) so private instances get outbound
resource "aws_eip" "nat" { domain = "vpc" }
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public["a"].id
tags = { Name = "app-nat" }
}
# 5. Route tables — public routes to IGW, private routes to NAT
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id # <-- this makes it public
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id # <-- outbound-only via NAT
}
}
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
for_each = aws_subnet.private
subnet_id = each.value.id
route_table_id = aws_route_table.private.id
}
Toàn bộ sự phân biệt public/private rút gọn về khối 4–5 của các route table: một subnet là public vì bảng của nó gửi 0.0.0.0/0 tới Internet Gateway, và private vì bảng của nó gửi 0.0.0.0/0 tới NAT gateway (chỉ outbound). Thêm các security group (app SG tham chiếu DB SG), một ALB trong các public subnet, và một S3 gateway endpoint trên private route table, và bạn có một mạng có hình dáng production.
Bản CLI tương đương cho một VPC nhanh đơn lẻ, để so sánh:
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query Vpc.VpcId --output text)
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone ap-southeast-1a
IGW_ID=$(aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID
Dùng CLI để kiểm tra và thử nghiệm; dùng Terraform cho bất cứ thứ gì cần tồn tại lâu dài.
Tài liệu tham khảo
- Amazon VPC User Guide
- VPC subnets
- Route tables
- NAT gateways
- Security groups
- Network ACLs
- Compare security groups and network ACLs
- VPC peering
- AWS Transit Gateway
- AWS PrivateLink and VPC endpoints
- Gateway endpoints for S3 and DynamoDB
- Elastic Load Balancing
- Application Load Balancer · Network Load Balancer
- Amazon Route 53 Developer Guide
- Route 53 routing policies
- Amazon CloudFront Developer Guide
- Restricting S3 access with Origin Access Control
- VPC Flow Logs
- VPC CIDR blocks
- Terraform AWS VPC module
Part of the Cloud knowledge base — AWS deep dive. Complements DevOps Roadmap.
Overview
Networking is where AWS feels most like running your own data center, because it is one — virtualized. The Virtual Private Cloud (VPC) is a logically isolated slice of the AWS network that you fully control: you choose the IP address range, carve it into subnets, decide what’s reachable from the internet and what isn’t, and place firewalls at every layer. Almost every workload you run — EC2, RDS, ECS, EKS, Lambda-in-a-VPC — lives inside a VPC, so a shaky mental model of networking undermines everything above it.
This page builds from the inside out: the VPC and its subnets, how traffic gets in and out (gateways and route tables), the two firewall layers (security groups and NACLs), how VPCs connect to each other and to on-prem (peering, Transit Gateway, PrivateLink), how you distribute traffic (Elastic Load Balancing), how users find you (Route 53 DNS), how you cache globally (CloudFront), and how you see what’s happening (Flow Logs). It closes with CIDR planning and a minimal, real VPC layout in Terraform.
The core mental model: a VPC is a private network you own; a subnet is a slice of it pinned to one AZ; a route table decides where a subnet’s traffic goes; and gateways are the doors to the outside world. “Public” and “private” subnets aren’t a setting — they’re a consequence of what their route table points at.
Fundamentals
VPC and subnets
- A VPC is a regional resource defined by a CIDR block (e.g.,
10.0.0.0/16→ 65,536 addresses). It spans all AZs in its region. - A subnet is a subdivision of the VPC CIDR (e.g.,
10.0.1.0/24→ 256 addresses) that lives in exactly one AZ. To be multi-AZ you need one subnet per AZ. - AWS reserves 5 IPs in every subnet (first four + last): network address, VPC router, DNS, a reserved address, and broadcast. A
/24gives you 251 usable, not 256.
Public vs private subnet — the defining difference:
| Public subnet | Private subnet | |
|---|---|---|
| Route table has a route to | Internet Gateway (0.0.0.0/0 → IGW) | NAT Gateway or nothing |
| Inbound from internet | Possible (with a public/Elastic IP) | Never directly |
| Outbound to internet | Direct via IGW | Via NAT (in a public subnet) or none |
| Typical residents | Load balancers, bastion hosts, NAT gateways | App servers, databases, internal services |
The rule of thumb: put as little as possible in public subnets. Only internet-facing load balancers and NAT gateways truly need to be there. App servers and databases belong in private subnets, reachable only through the load balancer.
Gateways, route tables, and IPs
- Internet Gateway (IGW) — one per VPC; the door to the public internet. A subnet is “public” precisely because its route table sends
0.0.0.0/0to the IGW. Instances also need a public or Elastic IP to be reachable inbound. - NAT Gateway — lets private subnet instances make outbound connections (pull packages, call APIs) without being reachable inbound. It lives in a public subnet, and private subnets route
0.0.0.0/0to it. It’s a managed, highly available (per-AZ) resource — and it is not free: you pay per hour and per GB processed, which makes it a frequent cost and single-point-of-failure consideration (deploy one per AZ for HA). - Egress-only Internet Gateway — the IPv6 equivalent of NAT: outbound-only for IPv6 (which has no NAT concept).
- Elastic IP (EIP) — a static public IPv4 address you own and can remap between instances/NAT gateways. IPv4 addresses now carry an hourly charge whether attached or not, so release ones you don’t need.
Route tables are the routing brain. Each subnet is associated with exactly one route table; the table is a list of destination CIDR → target. The local route (the VPC’s own CIDR → local) is always present and can’t be removed — that’s what lets subnets talk to each other. Everything else you add: 0.0.0.0/0 → igw-... makes a subnet public; 0.0.0.0/0 → nat-... gives a private subnet outbound.
The two firewall layers: Security Groups vs NACLs
AWS gives you two independent packet filters. Understanding the difference is essential.
| Security Group (SG) | Network ACL (NACL) | |
|---|---|---|
| Operates at | The instance/ENI level | The subnet level |
| Stateful? | Stateful — return traffic auto-allowed | Stateless — must allow return traffic explicitly |
| Rules | Allow only (implicit deny) | Allow and Deny, evaluated by rule number |
| Default | Deny all inbound, allow all outbound | Default NACL allows all; custom denies all |
| Can reference | Other security groups (and CIDRs) | CIDRs only |
| Evaluation | All rules considered together | First matching rule wins (numeric order) |
Stateful vs stateless is the trap. A security group only needs an inbound rule for port 443; the response is automatically allowed because SGs remember the connection. A NACL, being stateless, needs both an inbound rule for 443 and an outbound rule for the ephemeral return ports (1024–65535) — forget the second and connections mysteriously hang.
Practical guidance: do almost all your work with security groups — they’re stateful, can reference each other (e.g., “allow the web SG to reach the DB SG on 5432” with no IP addresses at all), and are far easier to reason about. Use NACLs sparingly as a coarse subnet-level backstop, mainly for explicit deny rules (blocking a bad IP range) that SGs can’t express.
CIDR planning
Getting your address plan right up front avoids painful re-architecture later.
- Pick a private RFC 1918 range:
10.0.0.0/8,172.16.0.0/12, or192.168.0.0/16. A/16per VPC (65k IPs) is a common, comfortable default. - Don’t overlap. VPCs you’ll ever peer, connect via Transit Gateway, or link to on-prem must have non-overlapping CIDRs — overlap makes routing impossible and is the #1 planning mistake. Reserve distinct ranges per environment/region from day one.
- Leave room to grow. Size subnets generously; you can’t easily resize a subnet, though you can add secondary CIDR blocks to a VPC later.
- A standard layout for a
/16VPC across 3 AZs: three public/24s (one per AZ) and three private/24s (or larger, like/20, for workloads that need many IPs — EKS pods devour addresses).
| Prefix | Addresses | Usable (minus 5) | Typical use |
|---|---|---|---|
/28 | 16 | 11 | Tiny/reserved |
/24 | 256 | 251 | Standard subnet |
/20 | 4,096 | 4,091 | Large subnet (EKS, big fleets) |
/16 | 65,536 | — | A whole VPC |
Key Concepts
Connecting VPCs and networks
| Option | Connects | Topology | Use when |
|---|---|---|---|
| VPC Peering | Two VPCs | 1:1, non-transitive | A handful of VPCs need direct links |
| Transit Gateway (TGW) | Many VPCs + on-prem | Hub-and-spoke, transitive | Dozens of VPCs; you need a network hub |
| PrivateLink / VPC Endpoints | Consumer VPC → a specific service | Private, one-way to a service | Reach AWS services or a SaaS privately, no internet |
| Site-to-Site VPN | VPC ↔ on-prem over internet | Encrypted tunnel | Hybrid connectivity, cheap |
| Direct Connect | VPC ↔ on-prem over dedicated line | Private physical link | Consistent low latency, high bandwidth to on-prem |
VPC Peering is a direct, private link between two VPCs. Its critical limitation is that it’s non-transitive: if A peers B and B peers C, A cannot reach C. This is what makes peering unmanageable past a few VPCs — you’d need a full mesh (n(n-1)/2 connections).
Transit Gateway solves that: it’s a regional network hub that every VPC (and your VPN/Direct Connect) attaches to once, and it routes between them transitively. For any non-trivial multi-VPC estate, TGW is the answer; you trade a small per-attachment/per-GB cost for a vastly simpler topology.
PrivateLink and VPC Endpoints keep traffic to AWS services (or partner SaaS) entirely on the AWS private network:
- Gateway endpoints (free) — only for S3 and DynamoDB; added as a route-table target so private instances reach those services without a NAT gateway (a real cost saver).
- Interface endpoints (PrivateLink, hourly + per-GB) — an ENI with a private IP in your subnet for most other services (SSM, ECR, Secrets Manager, SQS, etc.) and for third-party SaaS. Traffic never touches the public internet.
Using a gateway endpoint for S3 is a classic optimization: private subnets that only need S3 can then skip the NAT gateway entirely for that traffic.
Elastic Load Balancing (ELB)
ELB distributes incoming traffic across healthy targets in multiple AZs. Three types:
| Type | Layer | Routes on | Use for |
|---|---|---|---|
| ALB (Application) | L7 (HTTP/HTTPS) | Host, path, headers, methods | Web apps, microservices, container routing, WebSockets |
| NLB (Network) | L4 (TCP/UDP/TLS) | IP + port, ultra-low latency | Extreme performance, static IPs, non-HTTP protocols |
| GWLB (Gateway) | L3 | Transparent, GENEVE | Inserting virtual appliances (firewalls, IDS/IPS) inline |
- ALB is the workhorse for HTTP: it does content-based routing (send
/api/*to one target group,/static/*to another), host-based routing (multi-tenant), TLS termination, and integrates with WAF and Cognito auth. It has a dynamic DNS name, not a fixed IP. - NLB operates at L4 for millions of requests/sec at ultra-low latency, preserves the client source IP, and can have a static/Elastic IP per AZ — the reason to pick it when clients need to allowlist a fixed IP or you’re not speaking HTTP.
- GWLB is niche: it transparently routes traffic through third-party network appliances for inspection.
Load balancers use health checks to stop sending traffic to unhealthy targets, and target groups to define the backend pool (instances, IPs, or Lambda). Always place an internet-facing LB in public subnets across ≥2 AZs, with the actual targets in private subnets.
Route 53 — DNS and traffic management
Route 53 is AWS’s DNS service (the name is the classic DNS port, 53). It does three things: register domains, resolve DNS (authoritative hosting), and route intelligently with health checks.
Routing policies are the interesting part:
| Policy | Behavior | Use for |
|---|---|---|
| Simple | One record, no logic | Basic single-endpoint mapping |
| Weighted | Split traffic by percentage | Canary/blue-green, A/B testing |
| Latency-based | Send users to the lowest-latency region | Global multi-region apps |
| Geolocation | Route by user’s continent/country | Compliance, localized content |
| Geoproximity | Route by geographic distance, with bias | Fine-grained geographic control |
| Failover | Primary → secondary on health-check failure | Active-passive DR |
| Multivalue answer | Return several healthy IPs | Simple client-side load spreading |
Alias records are a Route 53-specific feature: an A/AAAA record that points at an AWS resource (ALB, CloudFront, S3 website, another Route 53 record) by name, resolves to its current IPs automatically, is free to query, and — unlike a CNAME — can sit at the zone apex (example.com, not just www.example.com). Use alias records for anything pointing at an AWS endpoint. Health checks monitor endpoints and drive failover and DNS-level removal of dead targets.
CloudFront — the CDN
CloudFront is AWS’s global content delivery network, caching content at 600+ edge locations close to users. It fronts an origin (an S3 bucket, an ALB, or any HTTP server) and serves cached responses from the nearest edge, cutting latency and offloading the origin.
- Caches static and dynamic content; you control TTLs and cache keys.
- Terminates TLS at the edge and integrates with AWS WAF and Shield for DDoS protection.
- Origin Access Control (OAC) locks an S3 origin so it’s reachable only through CloudFront, not directly — the standard pattern for serving a private bucket to the world.
- CloudFront Functions and Lambda@Edge run code at the edge for header manipulation, auth, redirects, and A/B logic.
A canonical setup: static site or SPA in a private S3 bucket → CloudFront with OAC in front → Route 53 alias record at the apex → ACM certificate (issued in us-east-1, a hard requirement for CloudFront) for HTTPS.
VPC Flow Logs — seeing your traffic
VPC Flow Logs capture metadata about IP traffic (source/dest IP and port, protocol, bytes, ACCEPT/REJECT) at the VPC, subnet, or ENI level, delivered to CloudWatch Logs or S3. They don’t capture packet contents, but they’re indispensable for:
- Debugging connectivity (“why is this connection REJECTed?” → usually an SG or NACL).
- Security forensics and detecting unexpected talkers.
- Feeding tools like GuardDuty. Enable them at least at the VPC level for any production network.
Best Practices
-
Design the CIDR plan before creating anything, and never overlap. VPCs that might ever peer, join a Transit Gateway, or connect to on-prem must have distinct ranges. Overlap is the one networking mistake you cannot fix without rebuilding, so reserve non-overlapping blocks per environment and region up front.
-
Use a multi-AZ, public/private subnet layout by default. One public and one private subnet per AZ across ≥2 AZs is the baseline for any production VPC. It gives you both internet reachability where needed and resilience to a data-center failure for almost no extra cost.
-
Put only what must be public in public subnets. Internet-facing load balancers and NAT gateways belong there; app servers and databases do not. Keeping compute and data in private subnets, reachable only through the LB, dramatically shrinks your attack surface.
-
Do your firewalling with security groups, not NACLs. SGs are stateful, can reference each other, and are far easier to reason about. Reference SG-to-SG (e.g., allow the app SG to reach the DB SG on the database port) instead of hardcoding IPs, and reserve NACLs for coarse subnet-level deny rules.
-
Follow least privilege in security groups. Open only the specific ports and sources each tier needs; avoid
0.0.0.0/0on anything but public HTTPS. A database SG should accept traffic only from the app tier’s SG — never from the internet, and never on a wide port range. -
Deploy NAT gateways per-AZ for HA, and cut NAT cost where you can. A single NAT gateway is an AZ-level single point of failure and a cross-AZ data-transfer cost. Run one per AZ for resilience, and use S3/DynamoDB gateway endpoints so that traffic bypasses NAT entirely.
-
Use VPC endpoints to keep AWS traffic private and cheaper. Interface (PrivateLink) and gateway endpoints keep calls to S3, ECR, SSM, Secrets Manager, and more on the AWS network — improving security (no internet path) and often reducing NAT data-processing charges.
-
Choose Transit Gateway over a mesh of peerings once you have more than a few VPCs. Peering is non-transitive and scales as
n²connections. A hub-and-spoke Transit Gateway keeps a growing multi-VPC and hybrid estate manageable and transitively routable. -
Pick the right load balancer for the protocol. ALB for HTTP/HTTPS with content-based routing; NLB when you need extreme performance, a static IP, client source-IP preservation, or a non-HTTP protocol; GWLB only for inline appliance insertion. Using an ALB where you need a fixed IP (or vice versa) forces awkward workarounds.
-
Front internet-facing apps with an LB in public subnets and targets in private subnets. Terminate TLS at the load balancer, health-check the targets, and never expose backend instances directly. This is the reliability and security baseline for any web workload.
-
Prefer Route 53 alias records for AWS endpoints. Alias records resolve to the resource’s current IPs automatically, cost nothing to query, and work at the zone apex where CNAMEs can’t. Use routing policies (weighted, latency, failover) to do canaries, multi-region routing, and active-passive DR at the DNS layer.
-
Put CloudFront in front of static and cacheable content. It cuts latency globally, offloads your origin, terminates TLS at the edge, and — with Origin Access Control — lets you serve a private S3 bucket safely. Remember CloudFront certificates must be issued in
us-east-1. -
Encrypt traffic in transit end to end. Use HTTPS/TLS at load balancers and CloudFront, and don’t stop at the edge — prefer TLS between the LB and backends for sensitive workloads. AWS Certificate Manager issues and auto-renews certificates for free.
-
Enable VPC Flow Logs on every production VPC. They’re your primary tool for diagnosing connectivity failures (an SG/NACL REJECT jumps out immediately) and for security investigation. Ship them to a central account alongside your other audit logs.
-
Protect internet-facing endpoints with WAF and Shield. Attach AWS WAF to ALBs and CloudFront to filter common web exploits and bots, and rely on Shield Standard (free) plus Shield Advanced where warranted for DDoS. The edge is where you want to absorb malicious traffic, before it reaches your origin.
-
Define all networking as code. VPCs, subnets, route tables, SGs, and endpoints are exactly the kind of interconnected, easy-to-misconfigure resources that belong in Terraform/CloudFormation — reviewable, diffable, and reproducible. Clicking a VPC together by hand guarantees drift and an un-rebuildable network.
Worked example — a minimal multi-AZ VPC (Terraform)
A public/private VPC across two AZs: internet-facing subnets for a load balancer, private subnets for app/database tiers, and a NAT gateway for private outbound.
# 1. The VPC — a /16 gives ~65k addresses and room to grow
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "app-vpc" }
}
# 2. Internet Gateway — the door to the public internet
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = { Name = "app-igw" }
}
# 3. Public + private subnets across two AZs
resource "aws_subnet" "public" {
for_each = { a = "10.0.1.0/24", b = "10.0.2.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "ap-southeast-1${each.key}"
map_public_ip_on_launch = true # makes it a "public" subnet
tags = { Name = "public-${each.key}" }
}
resource "aws_subnet" "private" {
for_each = { a = "10.0.11.0/24", b = "10.0.12.0/24" }
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = "ap-southeast-1${each.key}"
tags = { Name = "private-${each.key}" }
}
# 4. NAT gateway (in a public subnet) so private instances get outbound
resource "aws_eip" "nat" { domain = "vpc" }
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public["a"].id
tags = { Name = "app-nat" }
}
# 5. Route tables — public routes to IGW, private routes to NAT
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id # <-- this makes it public
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id # <-- outbound-only via NAT
}
}
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
for_each = aws_subnet.private
subnet_id = each.value.id
route_table_id = aws_route_table.private.id
}
The whole public/private distinction reduces to lines 4–5 of the route tables: a subnet is public because its table sends 0.0.0.0/0 to the Internet Gateway, and private because its table sends 0.0.0.0/0 to the NAT gateway (outbound-only). Add security groups (app SG referencing DB SG), an ALB in the public subnets, and an S3 gateway endpoint on the private route table, and you have a production-shaped network.
The CLI equivalent for a single quick VPC, for comparison:
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query Vpc.VpcId --output text)
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone ap-southeast-1a
IGW_ID=$(aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID
Use the CLI for inspection and experiments; use Terraform for anything that must survive.
References
- Amazon VPC User Guide
- VPC subnets
- Route tables
- NAT gateways
- Security groups
- Network ACLs
- Compare security groups and network ACLs
- VPC peering
- AWS Transit Gateway
- AWS PrivateLink and VPC endpoints
- Gateway endpoints for S3 and DynamoDB
- Elastic Load Balancing
- Application Load Balancer · Network Load Balancer
- Amazon Route 53 Developer Guide
- Route 53 routing policies
- Amazon CloudFront Developer Guide
- Restricting S3 access with Origin Access Control
- VPC Flow Logs
- VPC CIDR blocks
- Terraform AWS VPC module