FDE PulseViệc làm FDE đang mở 316Mới đăng 7 ngày qua 10Chủ đề nổi bật: Đào tạo kỹ năng FDE tại Đông Nam Á

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

Load balancer L4, L7 và reverse proxy: đưa dịch vụ của bạn vào sau hạ tầng mạng của khách

Lúc dịch vụ của bạn phải chạy sau load balancer của khách, ba lỗi thường cùng lộ ra: mất IP thật của người dùng, session nhảy lung tung và health check báo sai. Cả ba đều sửa được nếu bạn biết mỗi lớp mạng nhìn thấy gì.

Đồ hoạĐường đi của request trong mạng của khách
  1. 1ClientTrình duyệt của người dùng gửi request kèm IP gốc
  2. 2Load balancer của kháchL4 chia theo địa chỉ và cổng, L7 đọc header; TLS tùy cách khách cấu hình
  3. 3Reverse proxy của bạnChỉ tin X-Forwarded-For từ dải IP của load balancer, đặt timeout
  4. 4App không giữ trạng tháiCó /healthz kiểm tra thật, không giữ session trong RAM
  5. 5Kho session dùng chungRedis hoặc database cho mọi instance dùng chung

Mỗi lớp nhìn thấy request theo cách khác nhau, nên IP, TLS và session phải được xử lý đúng ở từng lớp.

Đồ hoạ: FDE Times

Tóm tắt nhanh

  • L4 chỉ nhìn địa chỉ và cổng, L7 đọc được HTTP header. Bạn cần biết khách dùng loại nào trước khi viết dòng cấu hình đầu tiên.
  • Sau proxy, server chỉ thấy IP của proxy cuối cùng. Bạn chỉ nên tin X-Forwarded-For từ những proxy đã được xác định là đáng tin.
  • Dịch vụ dễ đặt sau load balancer khi không giữ session trong RAM, có endpoint health check thật và để load balancer đảm nhận TLS nếu khách muốn.
Chia sẻLinkedInFacebookX

Thử hình dung hôm nay là ngày thứ ba bạn làm việc ở một ngân hàng. Dịch vụ hỏi đáp tài liệu nội bộ của bạn đã chạy ổn trên máy staging. Rồi team hạ tầng gửi tin nhắn: mọi lưu lượng phải đi qua load balancer của ngân hàng, không có ngoại lệ.

Một giờ sau khi chuyển sang, log ghi cùng một địa chỉ IP cho tất cả request. Rate limit theo IP chặn luôn cả phòng ban, còn người dùng thỉnh thoảng bị đăng xuất giữa cuộc hội thoại. Code của bạn không có lỗi nào. Vấn đề là bạn chưa hiểu mạng của khách nhìn thấy request của bạn ra sao.

Tình huống này là công việc thường ngày của một FDE. Bạn không được dựng hạ tầng từ đầu mà phải đưa dịch vụ vào một hệ thống có sẵn, do người khác vận hành và có luật riêng. Muốn làm được, bạn cần phân biệt rõ ba thứ: load balancer lớp 4, load balancer lớp 7 và reverse proxy.

Load balancer của khách nhìn thấy gì?

Theo tài liệu thuật ngữ của F5, load balancer lớp 4 định tuyến bằng thông tin ở tầng transport, tức địa chỉ và cổng, và không đọc nội dung gói tin. Nó chỉ chuyển một luồng TCP tới một server và không biết bên trong là URL nào hay cookie gì.

Load balancer lớp 7 thì quyết định dựa trên các đặc điểm của HTTP header như URL hoặc cookie. Vì đọc được HTTP, nó có thể chuyển /api sang cụm này và /static sang cụm khác. Nó cũng có thể chèn thêm header trước khi gửi request vào trong.

Bạn cần quan tâm vì mỗi lớp quyết định dịch vụ của bạn nhận được gì, chẳng hạn có header X-Forwarded-For do proxy chèn vào hay không. Riêng TLS thì đừng đoán chỉ từ chữ L4 hay L7: giải mã ở load balancer hay ở dịch vụ của bạn là tùy cách khách đã cấu hình, nên bạn phải hỏi họ.

Reverse proxy không phải là load balancer

Hai khái niệm này hay bị dùng lẫn. Reverse proxy nhận request từ client rồi chuyển tới một server xử lý được nó, còn load balancer phân phối request cho một nhóm server. Vì thế reverse proxy vẫn có ích ngay cả khi phía sau chỉ có một server.

Kể cả khi khách đã có load balancer, bạn vẫn nên có một reverse proxy của riêng mình, ví dụ NGINX, đứng ngay trước app. Đó là nơi bạn kiểm soát được: đọc lại IP thật, đặt timeout và trả health check. Bạn không phải xin team hạ tầng sửa thiết bị của họ mỗi lần cần đổi cấu hình.

NGINX hợp với vai trò này vì kiến trúc của nó. Blog kỹ thuật của NGINX giải thích rằng cách thiết kế ứng dụng mạng phổ biến là gán mỗi kết nối một thread hoặc process, và cách đó tốn chi phí chuyển ngữ cảnh.

NGINX chọn kiến trúc event-driven: master process lo phần việc cần đặc quyền như đọc cấu hình và bind cổng, còn các worker process làm phần việc chính.

Làm thử từ đầu đến cuối: lấy lại IP thật của người dùng

Quay lại chuyện ở ngân hàng. Một request bây giờ đi theo đường: trình duyệt nhân viên → load balancer L7 của ngân hàng → NGINX của bạn → app. Khi kết nối đi qua bất kỳ proxy nào, server chỉ thấy IP của proxy cuối cùng, nên log của bạn chỉ toàn IP của load balancer.

Cách giải quyết là header X-Forwarded-For: proxy chèn vào header này IP gốc của client trước khi chuyển request vào trong. App đọc header đó để biết ai thật sự đang gọi.

Nhưng MDN cảnh báo rằng nếu dùng header này cho bảo mật, như rate limit hay kiểm soát truy cập theo IP, bạn chỉ được dùng những IP do proxy tin cậy thêm vào. Lý do là client có thể tự gửi kèm một header giả.

Giả sử team hạ tầng cho biết load balancer của họ nằm trong dải 10.20.0.0/24. Cấu hình NGINX của bạn sẽ trông như sau:

# Chỉ tin X-Forwarded-For khi request đến từ load balancer của khách
set_real_ip_from 10.20.0.0/24;
real_ip_header   X-Forwarded-For;
real_ip_recursive on;

upstream app {
    server 127.0.0.1:8000;
}

server {
    listen 80;

    location /healthz {
        proxy_pass http://app;
    }

    location / {
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Host              $host;
        proxy_pass http://app;
    }
}

Dòng set_real_ip_from quan trọng nhất trong cả đoạn cấu hình. Không có nó, ai gửi X-Forwarded-For: 1.2.3.4 cũng có thể giả làm người khác và vượt qua rate limit của bạn.

Có nó, NGINX chỉ tin giá trị đến từ dải IP của load balancer. Phần bài tập ở cuối sẽ chỉ cách kiểm tra điều này bằng curl.

Vì sao session bị nhảy, và vì sao sticky không cứu được bạn

Tiếp theo là lỗi đăng xuất. Giả sử bạn chạy 3 instance và cấu hình cân bằng theo source IP. Tài liệu kiến trúc của HAProxy mô tả cách này như sau: cùng một IP luôn đến cùng một server, miễn là số server không thay đổi.

Hãy xem hai điều kiện đó hỏng như thế nào. Nếu bạn băm theo IP mà lớp của bạn nhìn thấy, mọi request đều mang IP của load balancer, nên cả 3 instance biến thành 1 instance đang quá tải. Nếu tuần sau bạn tăng lên 4 instance, phép băm chia lại, nhiều người dùng bị chuyển sang server khác và mất session đang nằm trong RAM.

Cách bền vững hơn là không giữ session trong app. Trong mô hình scale ngang, các server đứng sau load balancer, còn session nằm ở một kho dữ liệu chung mà mọi app server đều truy cập được.

Ở chỗ khách, kho đó thường là Redis hoặc database họ đã có sẵn. Cũng trong tình huống ở ngân hàng, hai cách cho kết quả như sau:

Tình huống Sticky theo IP, session trong RAM Session ở kho dùng chung
Mọi request mang IP của load balancer Cả 3 instance dồn thành 1 instance quá tải Instance nào cũng đọc được session của người dùng
Tăng từ 3 lên 4 instance Phép băm chia lại, nhiều người bị đăng xuất Người dùng không bị đăng xuất

Năm bước trước buổi deployment

  1. Hỏi team hạ tầng ba câu. Load balancer là L4 hay L7, TLS kết thúc ở đâu, và dải IP của proxy là gì. Ba câu trả lời này quyết định gần hết cấu hình của bạn.
  2. Quyết định ai lo TLS. Load balancer có thể nhận phần mã hóa và giải mã để máy chủ tập trung vào việc chính.

Nếu khách đã làm vậy, bạn đọc X-Forwarded-Proto để biết request gốc có phải HTTPS không, thay vì tự cấu hình chứng chỉ. 3. Viết /healthz có kiểm tra thật. Theo tài liệu của AWS, Elastic Load Balancing kiểm tra sức khỏe các target và chỉ gửi request tới target còn khỏe.

Endpoint luôn trả 200 trong khi app mất kết nối database sẽ khiến người dùng tiếp tục bị gửi vào một instance đã hỏng. 4. Đưa session và dữ liệu tạm ra kho dùng chung. Khi đó khách thêm hay bớt instance cũng không ai bị đăng xuất. 5. Thống nhất timeout. Hỏi timeout của thiết bị là bao lâu.

Request gọi LLM có thể chạy lâu hơn mức mặc định, và bị cắt giữa chừng là lỗi rất khó tìm.

Những lỗi hay gặp

Lỗi phổ biến nhất là tin mọi X-Forwarded-For mà không giới hạn nguồn, rồi dùng nó để làm rate limit. Lỗi thứ hai là bật sticky session theo IP mà không biết mọi request trông như đến từ cùng một địa chỉ.

Lỗi thứ ba là cho rằng có load balancer rồi thì không cần reverse proxy, nên mỗi lần đổi cấu hình lại phải chờ ticket của team khác. Lỗi thứ tư khó thấy hơn: health check chỉ kiểm tra process còn chạy, không kiểm tra app có phục vụ được hay không.

Với developer Việt Nam muốn chuyển sang FDE, đây là kỹ năng nên ghi rõ trong CV. Đừng chỉ viết “biết NGINX”. Hãy mô tả một lần bạn đưa dịch vụ vào sau proxy có sẵn, giữ lại được IP thật và để dịch vụ chạy nhiều instance mà không mất session.

Khi đọc JD có “customer environment” hay “on-prem deployment”, bạn có thể đoán người ta sẽ hỏi đúng những chuyện này.

Bài tập: thử lừa chính proxy của bạn

Dựng NGINX với cấu hình ở trên trước một app nhỏ chỉ làm một việc là in IP client ra log. Đặt set_real_ip_from bằng dải IP của một máy bạn coi là load balancer.

Từ một máy nằm ngoài dải đó, chạy curl -H "X-Forwarded-For: 1.2.3.4" http://<proxy>/. Nếu log ghi 1.2.3.4, proxy của bạn đang bị lừa. Nếu log ghi IP thật của máy gửi, cấu hình đã đúng.

Sau đó xóa dòng set_real_ip_from, chạy lại lệnh và so sánh hai dòng log. Mạng của khách là thứ bạn không sửa được, nhưng một bài kiểm tra mười phút như thế này cho bạn biết dịch vụ của mình có đứng vững trong đó hay không.

7 nguồn
Đọc tiếp trên lộ trình · Chặng 5: Triển khaiThực hành: cho người dùng tải file lớn qua presigned URL và phục vụ nội dung tĩnh bằng CDNMỗi gigabyte đi qua server ứng dụng tốn compute, bộ nhớ và băng thông mà bạn phải trả tiền, trong khi một chìa khóa tạm thời có thể làm thay việc đó.