Ứng dụng gọi bộ cân bằng tải nội bộ AWS trong cùng mạng con đã hết thời gian


7

Một số nền tảng:

Tôi đã tạo một mạng phức tạp vừa phải bằng vpc của Amazon. Đó là một mạng ba tầng trên hai vùng khả dụng. Mỗi lớp có một mạng con trong vùng-a và vùng-b. Lớp trình bày ở trên cùng, có một lớp ứng dụng ở giữa và một lớp lõi ở phía dưới.

Tất cả các nhóm bảo mật và ACL cho các mạng con hiện đang cho phép TẤT CẢ lưu lượng truy cập trong và ngoài nước để giúp tôi giảm diện tích bề mặt của vấn đề.

Bảng định tuyến của lớp trình bày đang trỏ tất cả lưu lượng truy cập đến một cổng internet. Cổng NAT nằm trong một mạng con tách biệt cũng hướng tất cả lưu lượng truy cập đến cổng internet.

Ứng dụng của tôi có hai thành phần, UI (React.js) và API (Node / Express). Chúng được triển khai như hình ảnh docker. Trước mặt là một bộ cân bằng tải cổ điển.

UI-ELB phải đối mặt với internet và nằm trong lớp trình bày, định tuyến lưu lượng truy cập từ 80/443 đến cổng 8080 và được liên kết với ứng dụng-ec2 của tôi được đặt trong mạng con lớp ứng dụng.

API của tôi có bộ cân bằng tải bên trong phía trước. API-ELB nằm trong lớp ứng dụng (trong cùng mạng con với app-ec2) và nhận lưu lượng truy cập trên cổng 80/443 và chuyển nó xuống api-ec2 trong lõi trên cổng 3000.

Cả hai bộ cân bằng tải đang giảm tải chứng chỉ trước khi truyền lưu lượng đến phiên bản của chúng.

Tôi có cả bộ cân bằng tải của mình được liên kết dưới dạng bí danh trong Route53 và được tham chiếu trong các ứng dụng bằng url đẹp của chúng ( https://app.website.com ). Mỗi bộ cân bằng tải vượt qua các kiểm tra sức khỏe đã xác định và báo cáo tất cả các trường hợp ec2 đang sử dụng.

Cuối cùng, trên API tôi đã kích hoạt cors bằng gói cors nodejs.

Đây là một sơ đồ nhanh và bẩn của mạng của tôi.

Vấn đề:

APP-ELB thành công đưa tôi đến ứng dụng. Tuy nhiên, khi ứng dụng cố gắng gửi yêu cầu GET đến API-ELB, trước tiên, ứng dụng sẽ gửi yêu cầu TÙY CHỌN hết thời gian với mã lỗi 408.

Nơi mà nó trở nên kỳ lạ

Một số điều kỳ lạ nhất tôi gặp phải khi gỡ lỗi là:

  1. Tôi có thể SSH vào phiên bản app-ec2 và có thể chạy thử thành công với API-ELB. Tôi đã thử rất nhiều, và tất cả đều hoạt động. Một vài ví dụ là: curl -L https://api.website.com/system/healthcheckcurl -L -X OPTIONS https://api.website.com/system/healthcheck. Nó luôn trả về thông tin mong muốn.
  2. Tôi đã chuyển toàn bộ ứng dụng ra khỏi mạng của mình thành một vpc mặc định công khai và nó hoạt động như mong muốn.
  3. Tôi có api-ec2 viết tất cả các yêu cầu mạng đến bàn điều khiển. Mặc dù nó hiển thị các yêu cầu kiểm tra sức khỏe, nhưng nó không hiển thị bất kỳ yêu cầu nào từ ứng dụng-ec2. Điều này khiến tôi tin rằng lưu lượng truy cập thậm chí không đạt đến api.

Thực sự điều lớn nhất khiến tôi mất hoàn toàn là việc cuộn tròn api elb nội bộ hoạt động, nhưng yêu cầu axios cho cùng một url chính xác thì không. Điều này không có ý nghĩa với tôi cả.

Những gì tôi đã thử

Ban đầu tôi đã dành rất nhiều thời gian để chơi với các quy tắc ACL và các nhóm bảo mật nghĩ rằng tôi đã làm gì đó sai. Cuối cùng, tôi chỉ nói, "vặn nó", và mở mọi thứ lên để thử và đưa phần đó ra khỏi phương trình.

Tôi đã dành nhiều thời gian để chơi với Cors trên api của tôi. Cuối cùng hạ cánh trên cấu hình tôi có bây giờ, đó là cuộc app.use(cors())gọi lại mặc định được cung cấp bởi gói nút cors. Tôi cũng đã bao gồm những app.options('*', cors())gì được khuyến nghị trong tài liệu.

Tôi đã google mọi thứ dưới ánh mặt trời, nhưng cụ thể là tôi có cần xác định một số tiêu đề tùy chỉnh đặc biệt với khuỷu tay không? Nhưng dường như không thể tìm thấy bất cứ điều gì. Thêm vào đó, khi tôi chuyển ứng dụng của mình ra khỏi mạng, nó hoạt động rất tốt.

Tôi chắc chắn rằng tôi đã thử nhiều thứ khác, nhưng những thứ này dường như là thích hợp nhất. Tôi đang thiếu gì? Tôi nhận ra đây có thể là một vấn đề rất mơ hồ và rộng lớn, và một bài viết rất lớn, nhưng tôi đánh giá cao bất kỳ cái nhìn sâu sắc và thời gian của bạn trong việc đọc nó!


Đó là hai mạng con cho mỗi vùng: một lớp trình bày, một lớp ứng dụng và một lớp lõi. Đó là ba. Bạn có thể làm rõ điều đó? Tôi có bảng tuyến đường cho lớp trình bày và ổ đĩa NAT định tuyến tất cả lưu lượng truy cập thông qua một cổng internet. Tôi có cả lớp trình bày và lớp lõi định tuyến tất cả lưu lượng truy cập thông qua ổ đĩa nat. Điều đó có vẻ tự mâu thuẫn. Nếu lớp trình bày đang định tuyến qua NAT (ổ đĩa?) (Cổng?), Thì nó cũng không định tuyến qua cổng Internet. Lớp nào của bạn nằm trên mạng con nào và tuyến đường mặc định cho mỗi mạng con là gì?
Michael - sqlbot

1
... Cụ thể, ELB đối diện bên ngoài của bạn phải nằm trên các mạng con có tuyến đường mặc định trỏ đến Cổng Internet, điều này hầu như luôn có nghĩa là không chính xác khi đặt nó trên cùng một mạng con như các trường hợp mà nó đang cân bằng lưu lượng truy cập. Các phiên bản đó sẽ nằm trên một mạng con có tuyến mặc định là NAT Gateway ... và chính NAT Gateway, sẽ không nằm trên bất kỳ mạng con nào giống như các phiên bản mà nó đang cung cấp dịch vụ bên ngoài nhưng có thể giống nhau mạng con là ELB.
Michael - sqlbot

Vâng, xin lỗi về điều đó, bắt đầu nhận được một số từ trộn lẫn. Tôi có hai mạng con (một trong khu vực a và một trong khu vực b) trong mỗi ba lớp. Lớp trình bày được định tuyến thông qua cổng internet, cũng như nat. Một điều tôi đã không đề cập đến là NAT nằm trong mạng con riêng biệt của nó. Sau đó, ứng dụng và tuyến đường lõi thông qua cổng nat.
David Meents

1
Bạn có thể muốn xem xét kỹ lưỡng câu hỏi và làm rõ cho phù hợp - đó là một câu hỏi hay, nhưng có một số kết luận khó thực hiện và nhiều điều cần kiểm tra. Khi bạn nói "Ứng dụng - UI đang hết thời gian yêu cầu tùy chọn khi thực hiện cuộc gọi api" , ai sẽ thấy lỗi này? Người gọi bên ngoài? curl -X OPTIONS 127.0.0.1...trên ứng dụng-ec2? Chỉ OPTIONSlà bị hỏng? ELB là "Cổ điển" không phải là "Ứng dụng", đúng không? Có phải tất cả các trường hợp có thể truy cập Internet chính xác thông qua NAT, vd curl ipv4.icanhazip.com? (Vâng, tôi yêu cầu một lý do có vẻ mơ hồ.)
Michael - sqlbot

1
Trừ khi tôi hoàn toàn nhầm, các ứng dụng Reac.js chạy trong trình duyệt và cần liên hệ với máy chủ API, máy chủ frontend của bạn chỉ cung cấp các tệp html và js và không định tuyến / ủy quyền yêu cầu đối với API
Tensibai

Câu trả lời:


7

Vì vậy, những gì bạn có thực sự là thế này:

Lược đồ kiến ​​trúc của OP

Vì API ELB của bạn nằm trong vùng riêng tư, nó không thể được truy cập từ internet.
Giao diện của bạn trong React.js chỉ chạy trong trình duyệt của Người dùng chứ không phải trên các máy chủ UI, những máy chủ đó chỉ cung cấp các tệp tĩnh.

Bạn có hai tùy chọn, định cấu hình máy chủ lối vào của mình để chuyển hướng các lệnh gọi API đến ELB API hoặc chỉ cập nhật API ELB để đối mặt với internet.

Cạm bẫy thông thường của các ứng dụng JavaScript là quên chúng chạy bên trong trình duyệt của người dùng chứ không phải trên các máy chủ lối vào như một ứng dụng JEE.


1

Điều này nghe có vẻ như một vấn đề định tuyến không đối xứng hoặc n-path. Đây là những gì có thể xảy ra:

Máy A tại địa chỉ IP 192.168.1.1 thực hiện yêu cầu [SYN] thông qua LB tại 192.168.1.10. LB sau đó ủy nhiệm tải trọng cho Máy B ở 192.168.1.2, do đó, tải trọng hiện có nguồn: 192.168.1.1 và có đích: 192.168.1.2 (trước đây là 192.168.1.10).

Vậy điều gì xảy ra bây giờ khi 192.168.1.2 phản hồi bằng [SYN, ACK]? Điều gì sẽ xảy ra là Máy B sẽ phản hồi với Máy A thông qua bộ cân bằng tải- thường là do một tuyến hoặc cổng mặc định trên máy chủ định tuyến lưu lượng qua LB. Tuy nhiên, trong trường hợp này, máy nằm trên cùng một mạng con, do đó tuyến / cổng không được sử dụng và bảng định tuyến bị máy chủ bỏ qua. Điều này có nghĩa là khi máy chủ phản hồi, [SYN, ACK] xuất hiện cho Máy A đến từ một IP khác với IP mà Máy A đã thực hiện yêu cầu với - nó đang mong đợi IP nguồn 192.168.1.10 (LB) nhưng đang nhìn thấy [SYN, ACK] đến từ 192.168.1.2 (máy B) và do đó LB không thể thiết lập kết nối với máy B trong trường hợp này vì phản hồi đã đi đến thiết bị sai.

Lý do điều này hoạt động cho lưu lượng truy cập bên ngoài là do tuyến đường mặc định của bạn - các phản hồi cho mọi người khác được chuyển qua ELB. ELB thấy rằng họ đã bắt đầu một kết nối và tự động chặn phản hồi và hoán đổi nguồn 192.168.1.2 trở lại 192.168.1.10.

Vì vậy, đối với một giải pháp cho vấn đề này, bạn có thể thực hiện cân bằng tải một vũ trang (còn được gọi là cân bằng tải trên thanh). Điều này sẽ làm là sử dụng NAT nguồn trên giao diện bên trong của bộ cân bằng tải (vì vậy giả sử bạn có giao diện bên ngoài 192.168.1.10 trên bộ cân bằng tải của bạn và 192.168.1.11 trên giao diện bên trong). Điều này sẽ làm cho tất cả lưu lượng truy cập dường như đến từ 192.168.1.11 từ góc độ của Máy B sẽ giải quyết vấn đề kết nối của bạn.

Tuy nhiên, có vẻ như AWS ELB của bạn không hỗ trợ SNAT , do đó, bạn sẽ cần phải đặt máy chủ và ELB của mình trên các mạng con khác nhau hoặc sử dụng thứ gì đó hỗ trợ SNAT như Phiên bản ảo của F5 có các hương vị theo giờ hoặc BYOL . Hãy coi chừng các giới hạn kết nối với SNATing - nếu bạn cần khoảng hơn 30k kết nối đồng thời, bạn sẽ gặp phải tình trạng cạn kiệt cổng SNAT và cần bắt đầu sử dụng nhóm SNAT. .

Do đó, giải pháp tốt nhất của bạn (về chi phí và để ngăn chặn các sự cố trong tương lai) sẽ là đảm bảo máy khách và máy chủ nằm trên các mạng con khác nhau.

Cách tốt nhất để xác nhận là sử dụng tcpdump trên máy chủ kết nối và / hoặc máy chủ back-end và tìm kiếm các phản hồi đến trực tiếp từ / từ máy chủ back-end thay vì đi qua bộ cân bằng tải. Sau đó, bạn có thể tải tệp kết xuất của mình vào WireShark để tìm hiểu chính xác những gì đang diễn ra.


ELB không chuyển tiếp gói. Nó tạo ra các kết nối TCP mới và chuyển tiếp tải trọng. Tuyến đường không đối xứng là một vấn đề không thể có.
Michael - sqlbot

F5 cũng vậy và họ vẫn gặp phải vấn đề định tuyến không đối xứng. Ngay cả với kiến ​​trúc proxy đầy đủ và kết nối TCP mới, riêng biệt, bộ cân bằng tải F5 theo mặc định sẽ là địa chỉ nguồn của máy khách kết nối, do đó sự cố vẫn xảy ra chính xác như được mô tả ở trên. Tôi giả sử ELB hoạt động theo cách tương tự. Tôi biết A10 cũng hành xử theo cách tương tự.
James Shewey

Họ không, ELB có IP riêng biệt. The
Robo

1
AWS ELB có thể hoạt động như một proxy ngược, không chỉ là bộ cân bằng tải TCP. Như OP cho biết ELB đang thực hiện giảm tải SSL, nó không thể là bộ cân bằng TCP và phải là proxy ngược HTTP. Câu trả lời của bạn không áp dụng cho ngữ cảnh và ELB không bao giờ được sử dụng cho các gói gửi đi (chúng hoàn toàn không phải là bộ định tuyến). Hơn nữa, nếu bạn cố gắng ủy quyền với F5 với 2 giao diện và đặt cùng một mạng con trên mỗi giao diện thì bạn thực sự đang tạo ra một vấn đề, giải quyết nó bằng SNAT chỉ là một giải pháp tồi.
Tensibai

1
Chà, ở đây cái mà máy B từ ví dụ của bạn nhìn thấy là IP ELB, IP của máy khách sẽ nằm trong tiêu đề X-Forwarded-Port. Máy khách có thể là một máy chủ không phải là vấn đề. Khi ở chế độ HTTP, ELB không hoạt động như một F5 khi chấm dứt SSL. (ngay cả trong chế độ TCP, nó vẫn là một bộ cân bằng tải nginx, không có gì có thể so sánh từ xa cả). Những gì tôi nghĩ rằng bạn đang bị treo là "ủy quyền", chúng tôi thực sự đang nói về các gói ủy quyền và không chuyển tiếp các gói. Tôi có thể gửi cho bạn một tcpdump của ELB trong cùng mạng con với 2 máy nếu bạn muốn, nó hoạt động.
Tensibai
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.