Bạn đã sẵn sàng cho ứng dụng production đầu tiên, và đã đến lúc kết nối nó với các dịch vụ bên ngoài. Vẫn còn nhiều việc cần thiết lập. Cách tốt nhất để cấu hình các dịch vụ trong production mà không làm phức tạp máy phát triển là gì?
Thiết lập môi trường
Để cấu hình ứng dụng production, phương pháp tốt nhất hiện nay là sử dụng biến môi trường (những thứ dạng ENV["REDIS_HOST"]).
Tại sao lại như vậy?
-
Khó hơn để vô tình commit các khóa production.
Nếu không chú ý, bạn có thể
git pushmột file chứa khóa bí mật quan trọng. Đó có thể là một sai lầm tốn kém. -
Cấu hình chính là mục đích của biến môi trường.
Biến môi trường là cách phổ biến để cấu hình ứng dụng trên hầu hết các hệ thống. Nhiều chương trình khác (như Ruby) dùng biến môi trường để cấu hình, nên việc áp dụng cho ứng dụng của bạn là hợp lý.
-
Biến môi trường dễ thiết lập trong production.
Heroku cung cấp giao diện web và công cụ dòng lệnh để dễ dàng thiết lập biến môi trường. Nếu tự xây dựng server, các công cụ quản lý như Chef và Docker cũng làm cho việc này trở nên đơn giản.
Cách thực hiện ở phía Rails
Đây là cách một ứng dụng phụ thuộc vào biến môi trường có thể tự cấu hình:
production:
host: <%= ENV["MY_SERVICE_HOST"] %>
port: <%= ENV["MY_SERVICE_PORT"] %>
my_service_config = Rails.application.config_for(:my_service)
my_service = MyService.new(my_service_config["host"], my_service_config["port"])
Initializer sử dụng phương thức config_for của Rails 4.2 để tìm đúng file .yml và chọn môi trường phù hợp.
Sau đó, config_for chạy mã ERB bên trong my_service.yml, lấy MY_SERVICE_HOST và MY_SERVICE_PORT từ môi trường, rồi truyền giá trị đó cho MyService.
Bạn cũng có thể để initializer đọc trực tiếp từ ENV["MY_SERVICE_HOST"]. Tuy nhiên, tôi thích giữ chúng trong file .yml vì những lý do bạn sẽ thấy ngay sau đây.
Cấu hình ứng dụng trong môi trường phát triển
Biến môi trường phù hợp cho production. Nhưng sau khi cấu hình production, làm sao xử lý cho development và test?
Có vài lựa chọn. Thông thường tôi tuân theo quy ước của config/secrets.yml trong Rails: dùng biến môi trường trong production, còn development và test thì hardcode các giá trị không bí mật.
Với development và test, config/my_service.yml có thể trông như sau:
production:
host: <%= ENV["MY_SERVICE_HOST"] %>
port: <%= ENV["MY_SERVICE_PORT"] %>
development:
host: localhost
port: 8081
test:
host: localhost
port: 8081
Tuyệt vời là initializer vẫn giữ nguyên. Các giá trị trong file này sẽ dùng cho development và test, còn production lấy giá trị từ biến môi trường.
Tại sao lại hardcode các giá trị này?
-
Giá trị cấu hình dễ nhìn và dễ thay đổi.
Bạn có thể điều chỉnh cấu hình khi thử nghiệm tính năng mới, điều mong muốn trong development nhưng không nên trong production.
-
Dễ dàng cho người mới bắt đầu.
Nếu toàn bộ cấu hình mẫu đã được commit vào git, lập trình viên mới chỉ cần clone và chạy ứng dụng. Không cần loay hoay thiết lập giá trị phù hợp.
-
Không lo xung đột biến môi trường.
Bạn sẽ làm việc trên nhiều ứng dụng trên máy dev hơn là bao giờ deploy lên một production duy nhất. Nếu dùng biến môi trường system-wide cho tất cả ứng dụng, rất dễ xảy ra xung đột.
Vậy hãy thử dùng biến môi trường trong production, và cấu hình .yml hardcode trong development. Cách này dễ dàng, dễ đọc, và Rails có hỗ trợ sẵn cho loại file cấu hình này.
Một lựa chọn khác cho development
Còn một cách khác để xử lý cấu hình trong development: dotenv. Trông khá hay nhưng tôi chưa thử trong dự án thực tế.
Với dotenv, bạn có thể đặt biến môi trường trong file .env ở thư mục gốc của ứng dụng Rails, và các giá trị đó sẽ được ứng dụng nạp. Điều này tốt vì môi trường development hoạt động giống production hơn, giúp tránh những bug chỉ xuất hiện ở production.
Đây là điều tôi sẽ thử sau. Nhưng hiện tại, chưa thấy cách nào tiện hơn .yml và config_for.
Hầu hết ứng dụng production đều cần một dạng cấu hình nào đó. Vì vậy, khi deploy ứng dụng tiếp theo, hãy thử dùng file .yml, được điền bởi biến môi trường trong production, để cấu hình. Bạn sẽ có sự linh hoạt, đơn giản và tin cậy như mong đợi.
Bạn có cách cấu hình production khác? Hãy để lại bình luận, tôi rất muốn nghe!