Reverse proxy là một loại máy chủ proxy nhận các yêu cầu HTTP(S) và chuyển tiếp chúng một cách trong suốt đến một hoặc nhiều máy chủ backend. Reverse proxy đóng vai trò quan trọng vì nhiều ứng dụng web hiện đại xử lý các yêu cầu HTTP thông qua các máy chủ ứng dụng backend. Những máy chủ này không được thiết kế để người dùng truy cập trực tiếp và thường chỉ hỗ trợ các chức năng HTTP ở mức cơ bản.

Bạn có thể sử dụng reverse proxy để ngăn chặn việc truy cập trực tiếp vào các máy chủ ứng dụng nằm ở phía sau. Reverse proxy còn được dùng để phân phối tải (load balancing) từ các yêu cầu đến nhiều máy chủ ứng dụng khác nhau, giúp cải thiện hiệu suất và tăng khả năng chịu lỗi. Ngoài ra, chúng có thể bổ sung các tính năng mà máy chủ ứng dụng không hỗ trợ, chẳng hạn như caching, nén dữ liệu hoặc mã hóa SSL.
Trong bài hướng dẫn này, bạn sẽ cấu hình Apache làm Reverse Proxy. Chúng ta sẽ sử dụng tiện ích mở rộng mod_proxy để chuyển hướng các connection đến một hoặc nhiều máy chủ backend đang chạy trên cùng một mạng. Hướng dẫn này sử dụng một backend đơn giản viết bằng framework Flask, nhưng bạn có thể dùng bất kỳ máy chủ backend nào bạn muốn.
Yêu cầu trước khi thực hiện
Để làm theo hướng dẫn này, bạn cần:
- Một máy chủ Ubuntu 16.04 được thiết lập theo hướng dẫn cấu hình máy chủ ban đầu, bao gồm một người dùng không phải root có quyền
sudovà tường lửa đã được cấu hình. - Cài đặt Apache 2 trên máy chủ theo Bước 1 của hướng dẫn hướng dẫn cài đặt bộ LAMP (Linux, Apache, MySQL, PHP) trên hệ điều hành Ubuntu 16.04.
Bước 1: Kích hoạt các Module cần thiết của Apache
Có nhiều module được đóng gói sẵn trong Apache nhưng không được kích hoạt trong lần cài đặt mới. Do đó, trước tiên, bạn cần kích hoạt các module sẽ sử dụng trong hướng dẫn này.
Các module cần thiết gồm mod_proxy và một số module mở rộng của nó, giúp hỗ trợ nhiều giao thức mạng khác nhau. Cụ thể, bạn sẽ sử dụng:
mod_proxy: module proxy chính của Apache, dùng để chuyển tiếp kết nối; cho phép Apache hoạt động như một gateway tới các máy chủ ứng dụng backend.mod_proxy_http: bổ sung hỗ trợ cho việc proxy kết nối HTTP.mod_proxy_balancervàmod_lbmethod_byrequests: bổ sung tính năng cân bằng tải cho nhiều máy chủ backend.
Để kích hoạt 4 module này, thực thi các lệnh sau theo thứ tự:
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
Để áp dụng thay đổi, khởi động lại Apache:
sudo systemctl restart apache2
Bây giờ Apache đã có thể hoạt động như một reverse proxy đáp ứng các yêu cầu HTTP. Ở bước tiếp theo (tùy chọn), bạn sẽ tạo hai máy chủ backend cơ bản để kiểm tra cấu hình, nhưng nếu bạn đã có sẵn ứng dụng backend thì bạn có thể bỏ qua bước này và chuyển sang bước 3.
Bước 2: Tạo máy chủ Backend kiểm thử (đề xuất)
Để kiểm tra xem cấu hình Apache có hoạt động đúng hay không, chúng ta sẽ chạy một số máy chủ backend đơn giản. Ở đây, bạn sẽ tạo hai máy chủ kiểm thử phản hồi các yêu cầu HTTP bằng một dòng văn bản. Một máy chủ sẽ trả về Hello world!, máy còn lại sẽ trả về Howdy world!. Phương pháp này giúp chúng ta kiểm tra cơ chế cân bằng tải giữa nhiều dịch vụ.
Lưu ý: Trong môi trường thực tế, các máy chủ backend thường trả về cùng một loại nội dung. Tuy nhiên, ở bài kiểm thử này, hai máy chủ có thể trả về hai nội dung khác nhau, chứng tỏ là cơ chế cân bằng tải đã sử dụng cho cả hai máy chủ.
Flask là một microframework Python dùng để xây dựng ứng dụng web. Chúng ta dùng Flask để tạo máy chủ kiểm thử. Vì đây là một ứng dụng cơ bản chỉ cần vài dòng mã nên bạn vẫn có thể làm được dù không biết nhiều về Python. Nếu muốn học thêm, bạn có thể tham khảo các hướng dẫn khác về Python.
Cập nhật danh sách gói:
sudo apt-get update
Cài đặt Pip, trình quản lý gói Python:
sudo apt-get install python3-pip
Dùng Pip để cài Flask:
sudo pip3 install Flask==1.1.4
Sau khi đã cài đủ thành phần, tạo một file mới trong thư mục home của người dùng hiện tại để chứa mã cho máy chủ backend đầu tiên:
nano ~/backend1.py
Sao chép đoạn mã sau vào tệp, lưu và đóng:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Hello world!'
Hai dòng đầu tiên dùng để khởi tạo Flask. Hàm home() trả về chuỗi Hello world!. Dòng @app.route('/') phía trên định nghĩa hàm cho Flask biết rằng giá trị trả về của home() sẽ được dùng làm phản hồi cho các yêu cầu HTTP đến URL gốc / của ứng dụng.
Máy chủ backend thứ hai giống hệt máy đầu tiên, chỉ khác nội dung trả về. Trước tiên, sao chép tệp đầu tiên:
cp ~/backend1.py ~/backend2.py
Mở tệp vừa sao chép:
nano ~/backend2.py
Đổi nội dung trả về từ Hello world! thành Howdy world!, rồi lưu và đóng tệp. Mã của tệp sẽ như sau:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Howdy world!'
Sử dụng lệnh sau để bắt đầu chạy máy chủ backend đầu tiên ở cổng 8080. Lệnh này cũng chuyển hướng toàn bộ đầu ra của Flask vào /dev/null để không làm lẫn kết quả hiển thị sau này:
FLASK_APP=~/backend1.py flask run --port=8080 >/dev/null 2>&1 &
Ở đây, bạn thiết lập biến môi trường FLASK_APP ngay trước lệnh flask. Biến môi trường là cách tiện lợi để truyền thông tin vào các tiến trình khởi chạy từ shell. Bạn có thể tìm hiểu thêm về biến môi trường trong bài Cách đọc và thiết lập biến môi trường và biến shell trên VPS Linux.
Trong trường hợp này, việc sử dụng biến môi trường giúp đảm bảo rằng cấu hình chỉ áp dụng cho lệnh đang được thực thi và sẽ tự động mất hiệu lực sau khi lệnh kết thúc. Lý do là vì chúng ta sẽ tiếp tục dùng chính biến này với một tên file khác để ra lệnh cho flask khởi động server thứ hai.
Tương tự, chạy máy chủ backend thứ hai ở cổng 8081. Lưu ý, đặt giá trị khác vào biến môi trường FLASK_APP:
FLASK_APP=~/backend2.py flask run --port=8081 >/dev/null 2>&1 &
Bạn có thể kiểm tra hoạt động của hai máy chủ bằng curl. Kiểm tra máy chủ thứ nhất:
curl <http://127.0.0.1:8080/>
Kết quả:
Hello world!
Kiểm tra máy chủ thứ hai:
curl <http://127.0.0.1:8081/>
Kết quả:
Howdy world!
Lưu ý: Để dừng cả hai máy chủ kiểm thử sau khi hoàn tất, chẳng hạn khi kết thúc hướng dẫn này, chỉ cần chạy lệnh killall flask.
Ở bước tiếp theo, bạn sẽ chỉnh sửa tệp cấu hình của Apache để kích hoạt tính năng reverse proxy.
Bước 3: Chỉnh sửa cấu hình mặc định để kích hoạt Reverse Proxy
Trong phần này, bạn sẽ cấu hình Virtual Host mặc định của Apache để hoạt động như một reverse proxy cho một máy chủ backend duy nhất hoặc một nhóm các máy chủ backend được cân bằng tải.
Lưu ý: Trong hướng dẫn này, cấu hình được áp dụng ở mức virtual host. Trong cài đặt mặc định của Apache, chỉ bật một virtual host mặc định. Tuy nhiên, bạn có thể áp dụng các đoạn cấu hình này cho các virtual host khác. Để tìm hiểu thêm về virtual host trong Apache, hãy tham khảo bài Cách thiết lập Apache Virtual Hosts trên Ubuntu 16.04.
Nếu Apache của bạn phục vụ cả HTTP lẫn HTTPS, cấu hình reverse proxy phải được đặt ở cả hai virtual host (HTTP và HTTPS). Để tìm hiểu thêm về SSL trên Apache, hãy tham khảo bài Cách tạo chứng chỉ SSL tự ký cho Apache trên Ubuntu 16.04.
Mở file cấu hình mặc định của Apache bằng nano hoặc trên phần mềm chỉnh sửa văn bản mà bạn thích:
sudo nano /etc/apache2/sites-available/000-default.conf
Trong file này, bạn sẽ thấy khối <VirtualHost *:80> ở dòng đầu tiên. Dưới đây là hai ví dụ: cấu hình reverse proxy cho một máy chủ backend và cấu hình cân bằng tải giữa nhiều máy chủ backend.
Ví dụ 1: Reverse Proxy cho một máy chủ backend
Thay toàn bộ nội dung trong khối <VirtualHost> để file cấu hình thành:
<VirtualHost *:80>
ProxyPreserveHost On
ProxyPass / <http://127.0.0.1:8080/>
ProxyPassReverse / <http://127.0.0.1:8080/>
</VirtualHost>
Nếu bạn đã làm theo ví dụ ở Bước 2, dùng 127.0.0.1:8080 như trên. Nếu sử dụng ứng dụng riêng, thay bằng địa chỉ của bạn.
Ở đây có ba chỉ thị chính:
ProxyPreserveHost: Yêu cầu Apache gửi nguyên vẹn tiêu đềHostgốc tới máy chủ backend. Điều này giúp backend nhận biết đúng địa chỉ được dùng để truy cập ứng dụng.ProxyPass: Là chỉ thị cấu hình proxy chính. Trong ví dụ này, nó chỉ định mọi yêu cầu tới đường dẫn gốc/sẽ được ánh xạ tới địa chỉ backend tương ứng. Ví dụ: nếu Apache nhận yêu cầu/example, nó sẽ gửi tớihttp://your_backend_server/examplevà trả phản hồi về cho client ban đầu.ProxyPassReverse: Thường được cấu hình giốngProxyPass. Nó yêu cầu Apache chỉnh sửa các tiêu đề phản hồi từ máy chủ backend. Điều này đảm bảo rằng nếu máy chủ backend trả về tiêu đề chuyển hướng vị trí (location redirect header), trình duyệt của khách sẽ được chuyển hướng đến địa chỉ của proxy thay vì địa chỉ của máy chủ backend, tránh lỗi truy cập.
Áp dụng thay đổi bằng cách khởi động lại Apache:
sudo systemctl restart apache2
Bây giờ, nếu bạn truy cập http://your_server_ip trong trình duyệt web, bạn sẽ thấy phản hồi từ máy chủ backend của mình thay vì trang chào mừng mặc định của Apache.
Tức là bạn sẽ thấy dòng Hello world! nếu đã thực hiện Bước 2.
Ví dụ 2: Cân bằng tải giữa nhiều máy chủ backend
Cách tốt để phân phối lưu lượng giữa nhiều máy chủ backend khi proxy là sử dụng tính năng cân bằng tải của mod_proxy.
Bạn hãy thay thế toàn bộ nội dung bên trong khối VirtualHost bằng nội dung sau, để tệp cấu hình sẽ là:
/etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
<Proxy balancer://mycluster>
BalancerMember <http://127.0.0.1:8080>
BalancerMember <http://127.0.0.1:8081>
</Proxy>
ProxyPreserveHost On
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
</VirtualHost>
Cấu hình này tương tự như cấu hình trước đó, nhưng thay vì chỉ định trực tiếp một máy chủ backend duy nhất, bạn sử dụng thêm một khối Proxy để định nghĩa nhiều máy chủ. Khối này được đặt tên là balancer://mycluster (tên này có thể thay đổi tùy ý) và bao gồm một hoặc nhiều BalancerMember, trong đó chỉ rõ địa chỉ các máy chủ backend tương ứng. Các chỉ thị ProxyPass và ProxyPassReverse sẽ sử dụng nhóm cân bằng tải có tên mycluster thay vì một máy chủ cụ thể.
Nếu bạn đang thực hành với các máy chủ ví dụ ở Bước 2, hãy dùng 127.0.0.1:8080 và 127.0.0.1:8081 cho các chỉ thị BalancerMember, như đã ghi trong khối trên. Bạn cũng có thể thay thế địa chỉ của các máy chủ ứng dụng riêng (nếu có).
Để áp dụng các thay đổi này, bạn hãy khởi động lại Apache:
sudo systemctl restart apache2
Khi truy cập http://your_server_ip trong trình duyệt web, bạn sẽ thấy phản hồi từ các máy chủ backend thay vì trang mặc định của Apache. Nếu bạn đã thực hiện theo Bước 2 và tải lại trang nhiều lần, phản hồi sẽ hiển thị xen kẽ “Hello world!” và “Howdy world!”, cho thấy reverse proxy đã hoạt động và đang cân bằng tải giữa cả hai máy chủ.
Kết luận
Giờ thì bạn đã biết cách thiết lập Apache làm reverse proxy cho một hoặc nhiều máy chủ ứng dụng phía sau. mod_proxy có thể là giải pháp hiệu quả để cấu hình reverse proxy cho các máy chủ ứng dụng được viết bằng nhiều ngôn ngữ và công nghệ khác nhau, chẳng hạn như Python với Django hoặc Ruby với Ruby on Rails.
Nó cũng có thể được dùng để cân bằng tải giữa nhiều máy chủ backend cho các trang web có lưu lượng truy cập lớn, đảm bảo tính sẵn sàng cao thông qua nhiều máy chủ, hoặc cung cấp hỗ trợ SSL bảo mật cho các máy chủ backend không hỗ trợ SSL một cách nguyên bản.
Mặc dù mod_proxy kết hợp với mod_proxy_http có lẽ là bộ mô-đun được sử dụng phổ biến nhất nhưng vẫn còn một số mô-đun khác hỗ trợ nhiều giao thức mạng khác nhau. Trong nội dung này, chúng ta chưa sử dụng các mô-đun đó, nhưng một số mô-đun phổ biến khác gồm:
mod_proxy_ftp: hỗ trợ giao thức FTP.mod_proxy_connect: hỗ trợ SSL tunneling.mod_proxy_ajp: hỗ trợ AJP (Apache JServ Protocol), ví dụ cho các backend dựa trên Tomcat.mod_proxy_wstunnel: hỗ trợ WebSocket.
Để tìm hiểu chi tiết hơn về mod_proxy, bạn có thể tham khảo tài liệu chính thức của Apache.