Điều gì đứng trước hầu hết mọi dịch vụ web
Chào mừng
Nếu bạn nhập example.com vào trình duyệt, bạn hầu như không bao giờ tiếp cận trực tiếp với máy chủ thực sự chạy ứng dụng. Bạn tiếp cận với một máy chủ chuyển tiếp yêu cầu đến máy chủ thực sự thực hiện điều đó. Máy chủ chuyển tiếp đó có một cái tên: reverse proxy (proxy ngược).
Bài học này dạy bạn reverse proxy làm gì, tại sao hầu hết mọi dịch vụ web công khai đều ẩn sau một reverse proxy, & ba nhiệm vụ mà lớp biên xử lý đồng thời là gì.
Vào cuối bài, bạn sẽ hiểu:
- Sự khác biệt giữa client (khách hàng), proxy (máy chủ trung gian) & origin (máy chủ gốc)
- Tại sao một proxy đặt trước origin giúp bảo vệ, mở rộng quy mô & cho phép bạn thay thế các thành phần mà không ai nhận ra
- Ba nhiệm vụ mà một reverse proxy (proxy ngược) xử lý đồng thời: che giấu origin, kết thúc TLS & phân phối tải giữa các bản sao (replicas)
- Cách một yêu cầu di chuyển từ trình duyệt đến proxy, đến upstream (máy chủ phía sau) & quay lại, từng bước một
Vào cuối bài học, bạn sẽ có khả năng lập luận tự tin về vị trí đặt proxy, sự tách biệt các mối quan tâm ở biên (edge) & tại sao một tầng proxy không trạng thái (stateless) có thể nhân bản với chi phí thấp trong khi một origin đơn lẻ thì không.
Forward Proxy (Proxy thuận) vs Reverse Proxy (Proxy ngược)
Hai hướng của Proxy
Cả hai loại proxy đều đứng giữa hai bên & chuyển tiếp lưu lượng. Chúng khác nhau ở bên nào mà chúng đại diện.
Proxy thuận (Forward proxy): đứng trước một nhóm máy khách (clients). Các máy khách biết về proxy; một máy chủ bên ngoài chỉ nhìn thấy địa chỉ của proxy, không phải địa chỉ của máy khách. Các proxy egress doanh nghiệp, bộ lọc nội dung & proxy SOCKS phù hợp với mô hình này.
Proxy ngược (Reverse proxy): đứng trước một nhóm máy chủ (servers). Thế giới bên ngoài (các máy khách) giao tiếp với địa chỉ của proxy; các máy khách không hề biết rằng có một máy chủ thực sự ẩn sau đó. Gần như mọi dịch vụ web công khai đều sử dụng một proxy ngược.
Mẹo ghi nhớ: proxy thuận che giấu các máy khách khỏi các máy chủ. Proxy ngược che giấu các máy chủ khỏi các máy khách.
Tại sao cần quan tâm đến hướng nào? Công việc, các chế độ lỗi & ranh giới bảo mật đều khác nhau. Proxy thuận lo lắng về việc người dùng của nó liên lạc với ai; proxy ngược lo lắng về việc ai liên lạc với các máy chủ của nó.
Một máy khách gửi lưu lượng qua cả hai loại cùng lúc sẽ di chuyển theo đường: client -> forward_proxy -> internet -> reverse_proxy -> origin.
Tại Sao Nhiều Thành Phần Đặt Tại Lớp Biên
Lớp Biên (Edge Layer) Chứng Minh Giá Trị Của Nó
Một reverse proxy đảm nhận ba nhiệm vụ cùng một lúc. Bất kỳ nhiệm vụ nào trong số đó cũng đủ để biện minh cho sự tồn tại của lớp này; việc thực hiện cả ba nhiệm vụ tại cùng một địa chỉ giải thích tại sao hầu hết các kiến trúc web trong môi trường sản xuất đều có hình dạng giống nhau ở phần đầu.
Nhiệm vụ 1: Ẩn nguồn gốc (origin). Proxy trả lời trên một IP công khai. Các backend nằm trên các IP riêng mà internet không thể truy cập. Kẻ tấn công muốn nhắm vào nguồn gốc trước tiên phải xâm phạm proxy.
Nhiệm vụ 2: Kết thúc TLS. Proxy giữ chứng chỉ cho example.com & giải mã các yêu cầu HTTPS đến. Các backend giao tiếp bằng HTTP thuần túy (hoặc TLS nội bộ đơn giản hơn) với proxy qua một phân đoạn mạng đáng tin cậy. Việc xoay vòng, gia hạn chứng chỉ & chính sách mật mã đều được quản lý tại một nơi duy nhất.
Nhiệm vụ 3: Phân phối tải. Proxy chọn backend nào sẽ xử lý mỗi yêu cầu. Các backend phía sau một proxy tạo thành một nhóm (pool); proxy chọn một backend cho mỗi yêu cầu dựa trên chiến lược (round-robin, ít kết nối nhất, băm theo một tiêu đề). Việc tăng dung lượng có nghĩa là thêm một backend vào nhóm, thay vì thông báo cho mọi client một địa chỉ mới.
Mỗi nhiệm vụ là một chương trình nhỏ. Chúng cùng nhau giải thích tại sao một tầng đơn giản như 'Caddy phía trước ứng dụng Python' lại mang nhiều trọng lượng thiết kế hơn chính ứng dụng Python đó.
Thiết kế cho cả ba nhiệm vụ
Đội ngũ của bạn vận hành một API nhỏ trên một tiến trình Python duy nhất lắng nghe trên cổng 8000 của một VM duy nhất. Lưu lượng đã tăng đủ lớn để một VM không thể theo kịp, & một cuộc đánh giá bảo mật đã chỉ ra rằng VM có IP công khai & tự chạy chứng chỉ TLS của nó.
Bạn quyết định đặt một proxy ngược phía trước. Phác thảo bố cục: DNS trỏ đến đâu, chứng chỉ nằm ở đâu, tải đến các backend như thế nào, & điều gì thay đổi đối với VM ban đầu?
Thay Thế Một Thành Phần Mà Không Ai Nhận Ra
Sự Gián Tiếp Mang Lại Sự Tự Do
Một câu nói kinh điển trong khoa học máy tính cho rằng: mọi vấn đề đều có thể được giải quyết bằng cách thêm một lớp trung gian (ngoại trừ vấn đề là có quá nhiều lớp trung gian). Proxy ngược (reverse proxy) là một trong những lớp trung gian hữu ích nhất trong các hệ thống phân tán.
Những gì bạn nhận được:
- Backend có thể thay thế được. Chuyển ứng dụng từ Python sang Go? Di chuyển từ một trung tâm dữ liệu sang trung tâm dữ liệu khác? Triển khai phiên bản mới với thời gian ngừng hoạt động bằng không? Mỗi trường hợp này đều diễn ra phía sau một địa chỉ công khai ổn định. Người dùng không thấy bất kỳ thay đổi nào.
- Mở rộng độc lập. Lớp proxy mở rộng dựa trên băng thông & CPU ở lớp TLS. Lớp backend mở rộng dựa trên khối lượng công việc của ứng dụng. Mỗi lớp phát triển theo trục riêng vì chúng chạy trên các máy khác nhau.
- Chứa đựng sự cố. Một lần triển khai lỗi ở backend sẽ không làm sập địa chỉ công khai. Proxy vẫn hoạt động; bạn đẩy bản sửa lỗi hoặc hoàn tác; thế giới kết nối lại khi backend phục hồi.
- Các mối quan tâm xuyên suốt ở một nơi duy nhất. Giới hạn tần suất (rate limiting), chặn theo địa lý (geo-blocking), ghi log yêu cầu, viết lại tiêu đề (header rewriting), bộ nhớ đệm (caching), nén phản hồi: tất cả đều nằm ở proxy. Mã nguồn backend vẫn tập trung vào ứng dụng.
Không có proxy, mỗi một trong những vấn đề này phải nằm bên trong tiến trình ứng dụng. Có proxy, chúng nằm ở một lớp do một nhóm duy nhất phụ trách.
Chi phí: một lớp vận hành thêm. Các nhóm trưởng thành chấp nhận chi phí này vì chính lớp proxy chạy không trạng thái (stateless) & mở rộng theo chiều ngang; thay thế một proxy bằng hai không cần phối hợp.
Triển khai Blue/Green thông qua Proxy
Đội ngũ của bạn đang chạy phiên bản 1 của API trên ba máy chủ ảo (VM) backend (pool xanh dương) phía sau một reverse proxy. Bạn muốn triển khai phiên bản 2 với khả năng hoàn tác (roll back) trong dưới ba mươi giây nếu có sự cố xảy ra.
Bạn khởi chạy ba máy chủ ảo (VM) backend mới (pool xanh lá) chạy phiên bản 2, đặt song song với pool xanh dương, nhưng chưa định tuyến bất kỳ lưu lượng nào đến chúng.
Từ Trình Duyệt Đến Máy Chủ Phía Sau & Quay Lại
Theo Dõi Một Yêu Cầu Từ Đầu Đến Cuối
Theo dõi một yêu cầu HTTPS GET duy nhất của https://api.example.com/users/42 đi qua một proxy ngược (reverse proxy) đặt trước một nhóm máy chủ phía sau (backend pool).
Nhảy 1: Phân giải DNS. Trình duyệt hỏi bộ phân giải (resolver) về api.example.com. Bộ phân giải trả về địa chỉ IP công khai của proxy (ví dụ: 203.0.113.10). Trình duyệt mở kết nối TCP đến 203.0.113.10:443.
Nhảy 2: Bắt tay TLS (TLS handshake). Proxy trình bày chứng chỉ của nó cho api.example.com. Trình duyệt xác thực chứng chỉ, hai bên thống nhất khóa phiên (session key), & kênh mã hóa được mở ra.
Nhảy 3: Yêu cầu HTTP bên trong TLS. Trình duyệt gửi GET /users/42 HTTP/1.1\nHost: api.example.com\n.... Proxy giải mã các byte của yêu cầu.
Bước 4: Chọn backend. Proxy tham chiếu pool upstream cho api.example.com & chọn một backend (ví dụ 10.0.0.21:8000) dựa trên chiến lược cân bằng tải của nó.
Bước 5: Yêu cầu upstream. Proxy mở (hoặc tái sử dụng) một kết nối HTTP thuần túy đến 10.0.0.21:8000 & chuyển tiếp yêu cầu. Proxy có thể viết lại các header trong quá trình này: thêm X-Forwarded-For: <client-ip>, đặt Host: chính xác, loại bỏ các header hop-by-hop như Connection.
Bước 6: Xử lý backend. Ứng dụng backend đọc yêu cầu, truy vấn cơ sở dữ liệu của nó, và xây dựng phản hồi JSON.
Bước 7: Phản hồi upstream. Backend gửi phản hồi trở lại proxy dưới dạng HTTP thuần túy.
Bước 8: Phản hồi tại biên. Proxy có thể viết lại hoặc nén phản hồi, mã hóa nó lại qua phiên TLS, & gửi nó đến trình duyệt.
Bước 9: Chu kỳ sống của kết nối. Phiên TLS thường vẫn mở cho yêu cầu tiếp theo (HTTP/2 đa đường nhiều yêu cầu trên một kết nối). Kết nối từ proxy đến backend thường được pool để tái sử dụng.
Mọi dịch vụ web công khai đều tuân theo một biến thể nào đó của cấu trúc này. Hiểu các bước giúp bạn suy luận về nơi độ trễ tích lũy, nơi ghi log nên thuộc về, & nơi một sự cố có thể ẩn nấp.
Thời gian đã đi đâu?
Một người dùng phàn nàn rằng API cảm giác chậm. Bạn đo và phát hiện yêu cầu mất 850 ms từ đầu đến cuối. Nhật ký máy chủ ở phía backend cho thấy ứng dụng đã xử lý yêu cầu trong 40 ms. Nhật ký proxy cho thấy proxy đã dành 50 ms cho phần công việc của nó (bắt tay TLS + định tuyến + ghi phản hồi).
Thiết kế biên tối giản cho một dịch vụ mới
Tổng hợp
Bạn đã học được sự khác biệt giữa proxy chuyển tiếp (forward) và proxy ngược (reverse), ba nhiệm vụ mà proxy ngược xử lý đồng thời, lý do việc ẩn địa chỉ nguồn (origin) mang lại lợi ích mỗi khi bạn cần thay đổi cấu hình, & cách một yêu cầu di chuyển từng bước (hop) qua biên.
Bây giờ hãy áp dụng kiến thức đó.
Một nhóm nhỏ đang lên kế hoạch ra mắt một dịch vụ mới có tên notes.example.com. Người dùng sẽ đọc & ghi các ghi chú cá nhân. Nhóm sẽ chạy hai máy ảo (VM) backend khi ra mắt & dự kiến mở rộng lên mười máy trong năm tới. Họ muốn có HTTPS cho người dùng, triển khai dần các phiên bản mới, & không công khai địa chỉ IP của backend.
Khóa Học Tiếp Theo Sẽ Đi Đến Đâu
Khóa Học Tiếp Theo Sẽ Đi Đến Đâu
Bài học này đã thiết lập cấu trúc của lớp biên. Bốn bài học tiếp theo trong khóa học này sẽ xây dựng dựa trên nó:
- Mở rộng ngang không trạng thái (Stateless Horizontal Scaling): lý do tại sao tầng proxy (và các backend phía sau nó) có thể nhân bản một cách rẻ, & công thức để xác định số lượng bản sao (replica) dưới điều kiện tăng đột biến (surge).
- Tách biệt Ingress / Egress: lý do tại sao một máy proxy duy nhất xử lý cả lưu lượng đến & đi cuối cùng sẽ gặp lỗi theo những cách bất ngờ, & cách tách các lớp này ra.
- Chế độ lỗi & Phạm vi ảnh hưởng: cách một thay đổi cấu hình đơn lẻ có thể lan truyền thành sự cố gián đoạn dịch vụ, & cách viết các mục hành động không đổ lỗi để ngăn chặn sự tái diễn.
- Khả năng quan sát & Sức chứa: những gì cần đo lường ở biên để bạn phát hiện ra sự cố trước khi người dùng nhận thấy.
Mỗi bài học đều độc lập. Khi kết hợp lại, chúng cung cấp cho bạn một mô hình nhận thức hoạt động của một đội máy quy mô web.
Bài học đồng hành: geometry_of_proxies_and_origins diễn giải lại mọi thứ trong bài học này dưới dạng đồ thị có hướng & khám phá những gì lý thuyết đồ thị cho bạn biết về một đường dẫn yêu cầu.
Làm tốt lắm. Hãy tiếp tục tiến lên.