Skip to main content

5.3. Networking: Reverse proxy và load balancer

Mục lục​


1. Reverse proxy giải quyết vấn đề gì​

Mỗi lần truy cập một website như kodekloud.com hay youtube.com, nội dung hiện ra trong trình duyệt đều đến từ một web server. Kịch bản đơn giản nhất chỉ có ba nhịp: người dùng yêu cầu một trang, trình duyệt gửi yêu cầu đó tới web server, web server trả về nội dung.

Nhưng những website có lưu lượng truy cập lớn thì ngày nay không được truy cập theo cách đó nữa, không truy cập trực tiếp. Có khá nhiều hạ tầng nằm ở giữa, giữa người dùng và web server.

Thêm một reverse proxy vào trước web server thì luồng đổi thành bốn nhịp: yêu cầu của người dùng đi tới reverse proxy trước, proxy gửi nó tới web server, web server trả trang về cho reverse proxy, và reverse proxy trao trang đó cho người dùng.

Việc thêm một chặng vào giữa nghe như chỉ làm chậm thêm, nên câu hỏi hợp lý là nó đem lại gì? Lợi ích thấy rõ nhất nằm ở một tình huống cụ thể. Giả sử một web server mới vừa được dựng, tốt hơn hẳn cái cũ: nhiều RAM hơn, nhiều CPU hơn, chạy nhanh hơn. Dựng xong là nói với reverse proxy chuyển một phần traffic sang đó. Bật một cái công tắc, traffic ngừng đi về web server cũ và đi về cái mới, và quá trình chuyển đổi diễn ra ngay lập tức.

Với mô hình cũ, nơi yêu cầu của người dùng đi thẳng tới web server, quá trình chuyển đổi không tức thì được. Phải sửa domain kodekloud.com để nó trỏ sang địa chỉ IP khác của web server mới, rồi đợi cho tới khi mọi người dùng được chuyển sang IP mới, việc có thể mất vài phút, vài giờ, có khi vài ngày. Hiện tượng đó gọi là DNS propagation, với DNS là viết tắt của Domain Name System.

Direct access versus a reverse proxyWithout a reverse proxyUser browserWeb serverSwitching servers needsa DNS change, minutes to daysWith a reverse proxyUser browserNGINX reverse proxylistens on port 80Web serverFlip traffic to a new web server at once, with no DNS propagationA proxy can also filter web traffic and cache pages for faster replies

Reverse proxy còn mang lại nhiều lợi ích khác, chẳng hạn lọc web traffic, hay cache trang để trả kết quả về cho người dùng nhanh hơn. Nhưng chính khả năng chuyển hướng traffic tới một web server khác ngay tức thì mới sinh ra một use case đáng chú ý khác, là load balancing.


2. Load balancer khác reverse proxy ở đâu?​

Một load balancer khá giống reverse proxy, nhưng khác ở chỗ nó chuyển hướng traffic tới nhiều web server chứ không chỉ một. Và nó chọn được server nào là tốt nhất để dùng, chọn động theo thời điểm: chẳng hạn chuyển một request tới server đang ít việc nhất. Nhờ vậy không server nào bị quá tải, và tất cả đều được dùng đều tay.

Ý tưởng cốt lõi của load balancing nằm ở đó: balance cái load, tức chia phần việc cần làm ra để mỗi server nhận một phần công bằng, phần mà nó xử lý được dễ dàng hơn.

Nghĩ tới những site như youtube.com thì thấy ngay vì sao việc này cần thiết. Không có cách nào để một web server đơn lẻ trả lời request của hàng triệu người dùng cùng lúc. Load balancing phân phối request ra hàng nghìn server, và chính điều đó khiến việc phục vụ toàn bộ số người dùng ấy trở nên khả thi mà không server nào bị dùng quá mức.

Ứng dụng thường dùng để tạo reverse proxy là NGINX, dù có những lựa chọn khác tồn tại song song như HAProxy hay Apache. NGINX là một trong những web server được dùng nhiều nhất trên thế giới, nên tên nó quen với khá nhiều người. Bản chất nó là web server, nhưng cấu hình được để làm reverse proxy, và cũng làm được load balancer.


3. Cấu hình NGINX làm reverse proxy​

Các bước khá gọn. Cài NGINX trước, sau đó, trong thư mục /etc/nginx/sites-available, tạo một file cấu hình tên proxy.conf:

sudo apt install nginx
sudo vim /etc/nginx/sites-available/proxy.conf

Tên file tự chọn, không nhất thiết phải đúng tên này. Về phía NGINX, đây chỉ là thiết lập cho một website mà nó sẽ host, vì về mặt kỹ thuật nó vẫn hoạt động như một web server ở phần cốt lõi. Người dùng nối tới nó thì nhận được nội dung website. Khác biệt nằm ở chỗ khi một người dùng yêu cầu một trang web, thay vì tự sinh trang đó ở bên trong, NGINX được dạy phải đi lấy nội dung từ một web server khác. Chính điều đó biến nó thành reverse proxy.

Nội dung cần thêm vào file cấu hình:

server {
listen 80;

location / {
proxy_pass http://1.1.1.1;
include proxy_params;
}
}

Đọc từng phần:

  • Trong khối server, dòng listen 80 nói NGINX chờ kết nối đi vào ở port 80.
  • Dòng location / khó hiểu hơn một chút. Về bản chất, nó nói NGINX chỉ áp directive proxy_pass bên dưới cho những request khớp mô tả location này. Thử so các URL của một domain: sau tên domain example.com, mọi URL đều tiếp tục bằng một dấu gạch chéo. Vì không có URL nào mà không thêm dấu gạch chéo sau tên domain, directive location / khớp mọi request tới bất cứ địa chỉ web nào mà người dùng muốn thăm.
  • Dòng proxy_pass thì gần như tự giải thích: request đã khớp ở phần location sẽ được proxy tới web server ghi ở đây.

Có lúc chỉ cần proxy một phần website chứ không phải toàn bộ. Lúc đó đổi directive location thành:

location /images {

Giờ chỉ những URL nằm dưới location /images được proxy, còn những URL như example.com hay example.com/admin thì không. Xem location như một bộ lọc: nó nói NGINX chỉ proxy request nếu địa chỉ web được thăm có chứa phần ghi trong directive này, tính từ sau tên domain.

Đích của proxy_pass không bắt buộc là địa chỉ IP, dùng hostname hay tên domain cũng được:

proxy_pass http://webserver1.example.com;

Và nếu web server được proxy tới không chờ kết nối ở port 80 thì phải tự ghi rõ port. Chẳng hạn server chờ kết nối ở port 8081:

proxy_pass http://1.1.1.1:8081;

Giữ lại IP thật của người dùng​

Đứng từ góc nhìn của web server, khách thăm website chính là cái proxy, vì proxy mới là bên nối tới nó. Tùy thiết lập, đôi khi lại cần phá cái ảo giác đó đi. Chẳng hạn cần thu thập số liệu xem người dùng nào thăm website, vậy làm sao nói với server rằng request này đang được gửi nhân danh một người dùng, và người dùng đó có một IP cần được log lại?

Việc này chỉ cần một dòng, cũng chính là dòng include proxy_params đã có trong cấu hình ở trên. Nó nạp vào các chỉ thị nằm ở một file khác tên proxy_params, đọc được tại /etc/nginx/proxy_params:

cat /etc/nginx/proxy_params

# Output:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Toàn bộ việc nó làm là đặt một số header cho các request sẽ được chuyển hướng tới web server. Nhờ vậy server nhận được thêm dữ liệu trong request, chẳng hạn địa chỉ IP thật của người dùng đã yêu cầu trang, protocol họ dùng, và vài thông tin tương tự.


4. Kích hoạt site và nạp lại cấu hình​

File cấu hình đã lưu, nhưng NGINX vẫn chưa dùng nó. File vừa tạo nằm trong thư mục /etc/nginx/sites-available, và đúng như cái tên, thư mục đó chỉ liệt kê những cấu hình website có sẵn. NGINX còn một thư mục nữa tên sites-enabled, và nó chỉ áp dụng định nghĩa cho những file tìm được trong thư mục đó.

How NGINX picks the site config to load/etc/nginx/sites-availabledefaultproxy.confloadbalancer.conf/etc/nginx/sites-enabledproxy.conf symlinkdefault link removed hereOnly files found in sites-enabled are applied, the rest stay available

Thực hành chuẩn là soft link file sang. Muốn bật một cấu hình website từ sites-available thì tạo soft link của nó vào sites-enabled:

sudo ln -s /etc/nginx/sites-available/proxy.conf /etc/nginx/sites-enabled/

Cũng nên tắt cấu hình website default đang active. Tắt nó chỉ cần xóa file khỏi thư mục sites-enabled, và file đó tên đúng là default:

sudo rm /etc/nginx/sites-enabled/default

Xóa như vậy không mất nội dung gì, vì file này cũng là một soft link, còn file thật thì nằm trong thư mục /etc/nginx/sites-available. Cơ chế soft link nói chi tiết ở Part 2.1: Đăng nhập Linux, tra cứu trợ giúp và làm việc với file.

Tới đây nên kiểm tra file cấu hình xem có lỗi gì không:

sudo nginx -t

# Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

-t là tham số bảo NGINX test file cấu hình. Không ra hai dòng như trên thì thông báo lỗi sẽ chỉ rõ những dòng cần sửa. Output chỉ nhắc /etc/nginx/nginx.conf, nhưng file cấu hình đó nạp các file khác, trong đó có proxy.conf vừa tạo, nên proxy.conf cũng được test, dù nó không xuất hiện trong output.

Áp dụng thiết lập mới bằng cách reload NGINX:

sudo systemctl reload nginx

Xong. NGINX giờ hoạt động như một reverse proxy, chuyển hướng mọi request tới web server ở 1.1.1.1.


5. Cấu hình NGINX làm load balancer​

Cấu hình lại thành load balancer cũng không khác nhiều. Trước tiên bỏ link tới cấu hình cũ đi, rồi tạo một file cấu hình mới trong /etc/nginx/sites-available:

sudo rm /etc/nginx/sites-enabled/proxy.conf
sudo vim /etc/nginx/sites-available/loadbalancer.conf

Nội dung có thể trông như sau:

upstream mywebservers {
server 1.2.3.4;
server 5.6.7.8;
}

server {
listen 80;

location / {
proxy_pass http://mywebservers;
}
}

Hiểu file này dễ nhất là đọc ngược từ dưới lên, vì khối ở dưới đã quen từ phần reverse proxy. Nó gần như giống hệt, chỉ khác một chỗ: proxy_pass được trỏ tới mywebservers thay vì địa chỉ IP hay hostname của một web server. Về cơ bản mywebservers là một cái tên không tồn tại ngoài thế giới thật, nhưng nghĩa của nó được định nghĩa ở khối phía trên, trong phạm vi nội bộ của file này.

Và đoạn ở trên giờ có nghĩa. Với directive upstream, một tập hợp server được định nghĩa, và tập hợp đó được đặt tên mywebservers. Tên này tự chọn, đổi thành gì cũng được miễn khớp với tên ghi ở proxy_pass. Bên dưới chỉ là danh sách địa chỉ IP của các web server.

Với nội dung hiện tại, phương pháp load balancing là round robin. Nghĩa là mỗi khi người dùng yêu cầu gì đó từ load balancer, request đó được chuyển hướng tới một trong các server: có lúc là 1.2.3.4, có lúc là 5.6.7.8.

Cách này ổn với một website lưu lượng không quá lớn. Nhưng với lưu lượng lớn thì việc chuyển hướng ngẫu nhiên có thể không còn lý tưởng: hoàn toàn có khả năng server 1.2.3.4 nhận hơn năm mươi phần trăm số request và bị quá tải so với mức nó chịu được.

Muốn NGINX phân phối request tới server đang ít việc nhất thì thêm một dòng:

upstream mywebservers {
least_conn;
server 1.2.3.4;
server 5.6.7.8;
}

Từ khóa least_conn khiến NGINX chọn server có ít connection đang hoạt động nhất trong danh sách.

One upstream group behind the load balancerUser requestsNGINX load balancerupstream mywebserversleast_connserver 1.2.3.4 weight=3gets most of the trafficserver 5.6.7.8default weight of 1server 10.20.30.40marked as backupRound robin by default, least_conn sends to the quietest serverweight, down and backup change how the group is used

6. Tinh chỉnh upstream với weight, down và backup​

Một kịch bản khác hay gặp: có những web server khỏe hơn những server còn lại, nên chúng xử lý được nhiều request hơn những server yếu. Trường hợp này gán cho mỗi server một con số gọi là weight:

upstream mywebservers {
least_conn;
server 1.2.3.4 weight=3;
server 5.6.7.8;
}

Thêm từ khóa này thì NGINX gửi nhiều request hơn hẳn tới server có IP 1.2.3.4. Không khai weight thì giá trị mặc định bằng 1, nên server 5.6.7.8 đang mang weight mặc định là 1. Với thiết lập trên, trong mỗi bốn request thì khoảng ba request đi tới 1.2.3.4 và một request đi tới 5.6.7.8.

Cần bảo trì một server thì đánh dấu nó là không khả dụng bằng từ khóa down:

server 1.2.3.4 weight=3 down;

Một server nhận được nhiều thiết lập cùng lúc: ở đây nó vừa mang weight bằng 3 vừa bị đánh dấu down. NGINX sẽ không chuyển hướng request tới server đó nữa, nên người vận hành bảo trì được, restart được, rồi bỏ từ khóa đi khi server sẵn sàng nhận request lại.

Kịch bản thứ ba: hai server đủ sức xử lý web traffic, nhưng nếu một server hỏng thì mọi thứ sẽ chậm với chỉ một server còn lại. Chặn trước tình huống đó bằng từ khóa backup:

upstream mywebservers {
least_conn;
server 10.20.30.41;
server 10.20.30.42;
server 10.20.30.40 backup;
}

Dòng cuối thêm 10.20.30.40 vào tập hợp web server nhưng đánh dấu nó là backup, nghĩa là traffic chưa được load balance tới nó. Chỉ khi 10.20.30.41 hoặc 10.20.30.42 chết thì 10.20.30.40 mới trở thành active và traffic bắt đầu được chuyển hướng tới nó. Nó ngồi ngoài lề, chỉ được dùng khi cần thiết.

💡 Hình dung: cả khối upstream giống bảng phân công của một tổ trưởng. weight là mức việc giao cho từng người theo sức, down là cho một người nghỉ phép, còn backup là người dự bị ngồi ngoài, chỉ vào sân khi có người trong tổ nghỉ bất ngờ.

Cuối cùng, nếu các web server này không chờ kết nối ở port chuẩn 80 thì ghi rõ port cần dùng:

server 1.2.3.4:8081 weight=3;

Ở đây server 1.2.3.4 được khai là đang chờ kết nối đi vào ở port 8081.

Sau khi file cấu hình cho load balancer đã xong, các bước còn lại giống hệt phần reverse proxy: link file sang thư mục sites-enabled, test cấu hình, rồi reload NGINX.

sudo ln -s /etc/nginx/sites-available/loadbalancer.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Thói quen đáng giữ khi vận hành: không bao giờ reload NGINX trước khi nginx -t trả về syntax is ok. Một file cấu hình sai khiến NGINX không khởi động lại được, và lúc đó cả website lẫn đường proxy đều đứng.

Mục cuối của domain Networking bàn tới đồng bộ thời gian và cấu hình SSH, ở Part 5.4: Đồng bộ thời gian và SSH server/client.


Nguồn tham khảo​

Nguồn gốc: Khóa "Prep Course - Linux Foundation Certified System Administrator (LFCS) Certification" — phần "5. Networking" (cấu hình reverse proxy và load balancer cho web server bằng NGINX), nền tảng KodeKloud. Giảng viên: Jeremy Morgan và Alexandru Andrei.