Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện

Muốn quản trị hiệu quả web app ở development và production, bạn bắt buộc hiểu biến môi trường (environment variables).

Chưa phải lúc nào cũng vậy. Vài năm trước, hầu như không ai config Rails app bằng env var. Nhưng rồi Heroku xuất hiện.

Heroku giới thiệu 12-factor app approach. Trong manifesto họ nêu best practice tạo app dễ deploy. Phần về env var đặc biệt có ảnh hưởng.

The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

Nhiều Rubyist dùng env var hơn bao giờ hết. Nhưng thường theo phong trào "cargo cult" — dùng mà không hiểu bản chất.

Bài này chỉ cách env var thực sự hoạt động — và quan trọng hơn, cách chúng KHÔNG hoạt động. Cũng khám phá cách phổ biến quản lý env var trong Rails app. Bắt đầu!

LƯU Ý: Đọc về bảo mật env var tại đây.

Mỗi process có tập env var riêng

Mọi chương trình chạy trên server có ít nhất một process. Process đó có tập env var riêng. Một khi có, không gì bên ngoài process đó thay đổi được.

Lỗi hiểu lẽ thường gặp của newbie: nghĩ env var mang tính server-wide. Heroku làm cho thấy set env var = edit config file trên disk. Nhưng env var khác hẳn config file.

Mọi chương trình chạy trên server nhận tập env var riêng tại thời điểm khởi chạy.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Mỗi process có môi trường riêng.

Env var chết cùng process

Từng set env var, reboot thấy nó mất? Vì env var thuộc process, nên khi process thoát, env var biến mất.

Thử set env var trong IRB session, đóng, truy cập trong IRB session 2.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Khi process tắt, env var của nó mất.

Cùng nguyên lý khiến mất env var khi reboot server, hoặc thoát shell. Muốn persist qua session, phải lưu trong config file như .bashrc.

Process lấy env var từ parent

Mọi process có parent. Vì mọi chương trình phải được khởi động bởi chương trình khác.

Dùng bash shell launch vim → parent của vim là shell. Rails app dùng imagemagick identify image → parent của `identify` là Rails app.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Child process kế thừa env var từ parent.

Ví dụ dưới: set `$MARCO` trong IRB process. Dùng back-ticks shell out echo giá trị biến.

Vì IRB là parent của shell vừa tạo, nó nhận copy `$MARCO`.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Env var set trong Ruby được kế thừa bởi child process.

Parent có thể tùy chỉnh env var gửi cho con

Mặc định child nhận copy mọi env var của parent. Nhưng parent kiểm soát được.

Từ command line, dùng chương trình `env`. Trong bash có cú pháp đặc biệt set env var cho child mà không set trên parent.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Dùng lệnh env set env var cho child mà không set trên parent.

Shell out từ Ruby cũng cung cấp env var tùy chỉnh cho child mà không làm bẩn ENV hash. Dùng cú pháp sau với method `system`:

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Cách truyền env var tùy chỉnh vào method system của Ruby.

Con không thể set env var cho parent

Vì child chỉ nhận copy env var của parent, thay đổi của child không ảnh hưởng parent.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Env var được "passed by value" không phải "by reference".

Dùng back-tick shell out thử set env var. Biến được set cho child, giá trị mới không nổi lên parent.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Child process không thể thay đổi env var của parent.

Thay đổi env không sync giữa process đang chạy

Ví dụ dưới chạy 2 IRB song song. Thêm biến vào env của IRB 1 không ảnh hưởng IRB 2.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Thêm env var vào process này không thay đổi cho process kia.

Shell chỉ là UI cho hệ thống env var

Hệ thống là một phần của OS kernel. Shell không có quyền phép màu trên env var. Nó tuân quy tắc như mọi chương trình khác.

Env var KHÔNG giống shell var

Hiểu lầm lớn vì shell cung cấp hệ thống "local" shell variable riêng. Cú pháp dùng local var thường giống env var. Newbie hay nhầm lẫn.

Nhưng local var không copy sang child.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Env var không giống shell var.

Xem ví dụ. Set local shell var `MARCO`. Vì là local, không copy sang child. Khi in qua Ruby, không work.

Tiếp theo, dùng `export` chuyển local var thành env var. Bây giờ copy sang mọi process mới shell tạo. Env var available cho Ruby.

Làm chủ Biến Môi Trường cho Ruby on Rails: Hướng Dẫn Toàn Diện Local var không available cho child process. Export chuyển local var thành env var.

Quản lý Env Var thực tế

Thực tế hoạt động ra sao? Làm ví dụ:

Hai Rails app chạy trên một máy. Dùng Honeybadger monitor exception. Vấn đề: muốn lưu API key trong `$HONEYBADGER_API_KEY`. Nhưng hai app có hai API key riêng.

Làm sao một env var có hai giá trị khác nhau?

Hy vọng bạn đã biết đáp án. Vì env var per-process, hai rails app chạy process khác nhau → mỗi app có giá trị `$HONEYBADGER_API_KEY` riêng.

Câu hỏi còn lại: setup như thế nào. May mắn có vài gem làm rất dễ.

Figaro

Cài Figaro gem, mọi giá trị trong `config/application.yml` load vào Ruby ENV hash khi startup.

Cài gem:

# Gemfile
gem "figaro"

Thêm item vào `application.yml`. Rất quan trọng: add file này vào `.gitignore` để không commit secret nhầm.

# config/application.yml
HONEYBADGER_API_KEY: 12345

Dotenv

Gem `dotenv` tương tự Figaro, nhưng load từ `.env`, không dùng YAML.

Cài gem:

# Gemfile
gem 'dotenv-rails'

Thêm config vào `.env` — và git ignore file để không publish lên GitHub nhầm.

HONEYBADGER_API_KEY=12345

Truy cập giá trị trong Ruby ENV hash:

ENV["HONEYBADGER_API_KEY"]

Chạy command trong shell với env var pre-defined:

dotenv ./my_script.sh

Secrets.yml?

Xin lỗi. `Secrets.yml` — dù cool — không set env var. Vì vậy không thực sự thay thế Figaro, dotenv.

Linux cơ bản

Cũng có thể maintain tập env var riêng per app dùng command Linux cơ bản. Cách: mỗi app trên server owned by user khác. Dùng `.bashrc` của user lưu giá trị app-specific.