Deploy Rails App to Staging with Unicorn & Nginx & switch HTTP + HTTPS

Theo mình thấy (ý kiến chủ quan), công việc deploy lên staging hay production của 1 project thường chỉ dành cho team-leader là chủ yếu, tuy nhiên điều đó không có nghĩa là member (dev) không cần tìm hiểu về công việc này, có thể thấy việc config để sử dụng app trên môi trường staging hay production sẽ cho chúng ta thấy những điểm khác nhau rất lớn so với việc chỉ đơn thuần làm việc trên môi trường develop.

HTTPS trên Rails

  • Là Web Developer (RoR Developer nói riêng), khi làm việc với các thông tin quan trọng như: thông tin cá nhân người dùng, thông tin về tín dụng, thương mại ... thì thường chúng ta phải dùng các kết nối có bảo mật (secure connection), mà điển hình là sử dụng HTTPS. HTTPS là gì? có rất nhiều bài định nghĩa, giải thích chi tiết trên viblo, nên mình sẽ không tập trung đi vào giới thiệu ở trong bài viết này. Để setup HTTPS trên rails, thì trước hết mình thấy là các web server phổ biến trên rails như webrick, unicorn hay thin đều không hỗ trợ HTTPS từ đầu.

  • Ở bài viết này mình sẽ nói về thiết lập môi trường ssl ở trên môi trường staging sử dụng Unicorn và Nginx.

Setup & Config Unicorn

  • Thêm dòng sau vào Gemfile
    group :staging, :production do
      gem "unicorn"
    end
  • Chạy bundle install => Unicorn đã cài đặt xong, giờ chúng ta cần config lại 1 chút

  • Config Unicorn: trong thư mục unicorn/ có 2 file production.rb, staging.rb, tùy xem chúng ta muốn deploy trên môi trường nào mà lựa chọn file config

    unicor\staging.rb

    app_path = "/home/framgia/Projects/COLANTOTTE"

    worker_processes 2
    listen "#{app_path}/tmp/sockets/unicorn.sock", :backlog => 64

    rails_env = 'staging'

    working_directory app_path
    pid "#{app_path}/tmp/pids/unicorn.pid"

  • Note: những thiết lập này liên quan tới đường dẫn của app, file env, log và PID... Ngoài ra bạn có thể chỉnh sửa và thêm các tùy chọn khác nếu cần thiết.

Setup & Config Nginx

  • Cài đặt Nginx sử dụng apt-get
    sudo apt-get install nginx
  • Config SSL trong Nginx

  • Đầu tiền, get key với certificate của ssl

    • Tạo đường dẫn chứa key ssl sudo mkdir /etc/nginx/ssl

    • Chạy lệnh để get key

      sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

  • Tiếp theo mở thiết lập của nginx để có thể sử dụng được https bằng câu lệnh

    sudo vi /etc/nginx/sites-available/default

    server {
            listen 80 default_server;

            root /usr/share/nginx/html;
            index index.html index.htm;

            # Make site accessible from http://localhost/
            server_name localhost;

            location / {
                    try_files $uri $uri/ =404;
            }
    }

    upstream app {
        # Path to Unicorn SOCK file, as defined previously
        server unix:/home/framgia/Projects/COLANTOTTE/tmp/sockets/unicorn.sock fail_timeout=0;
    }

    server {
        listen      80;
        listen      443;
        server_name colantotte.local;

        ssl     on;
        ssl_certificate /etc/nginx/ssl/server.crt;
        ssl_certificate_key /etc/nginx/ssl/server.key;
       # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
       # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
       # ssl_prefer_server_ciphers on;

        # Application root, as defined previously
        root /home/framgia/Projects/COLANTOTTE/public;
        location ^~ /assets/ {
                gzip_static on;
                expires max;
                add_header Cache-Control public;
        }

        location ~ ^/(robots.txt|sitemap.xml.gz)/ {
                root /home/framgia/Projects/COLANTOTTE/public;
        }

        try_files $uri/index.html $uri @app;

        location @app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app;
        }

        error_page 500 502 503 504 /500.html;
        client_max_body_size 4G;
        keepalive_timeout 10;
    }

Ở đây, có thể thấy mình sử dụng ssl bằng lệnh `ssl: on;` và mở thêm 1 cổng 443 với tên server là `colantotte.local`, cổng này để lắng nge kết nối https bên cạnh cổng 80 với kết nối http truyền thống.
  • Note:
     # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
     # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
     # ssl_prefer_server_ciphers on;
Những thiết lập này giúp tăng tính bảo mật của hệ thống, bạn có thể comment vào nếu không muốn sử dung, có thể  xem thêm ý nghĩa các dòng lệnh này tại trang chủ của nginx
  • Kiểm tra xem các lệnh trong config đúng chưa

    sudo nginx -t

  • Chạy Nginx bằng câu lệnh

    sudo service nginx restart/start/stop

Run staging

    rake db:create RAILS_ENV=staging
    rake db:seed RAILS_ENV=staging
    rake i18n:js:export
    bundle exec rake assets:precompile RAILS_ENV=staging
    bundle exec unicorn -E staging -c config/unicorn/staging.rb
    Xem log: tail -f log/staging.log

Khác với ENV Develop 1 chút, thì trên Staging/Production mỗi khi chạy lại hệ thống chúng ta cần build laị CSS, JS hoặc db thì app mới nhận được những thay đổi đó

Switch http và https

  • Như đã nói từ đầu bài, chúng ta chỉ sử dụng kết nối https cho khi làm việc với các thông tin quan trọng. Còn với những trang không chứa thông tin cần bảo mật (như các page Static chẳng hạn) chúng ta chỉ cần kết nối http thường

  • Nếu trong trường hợp add thêm plugin để sử dụng https thì việc switch này sẽ tương đối khó khăn, chẳng hạn khi sử dụng server webrick + plugin "webrick/https" thì để switch được giữa ssl và non-ssl chúng ta sẽ phải chạy song song 2 bản webrick, 1 bản có ssl và 1 bản non-ssl, điều này thực sự bất tiện.

  • Với việc sử dụng unicorn + nginx chúng ta có thể dễ dàng switch giữa http & https như sau:

    • using gem https://github.com/tobmatth/rack-ssl-enforcer

    • config lại 1 chút như sau trong config/environments/staging.rb

      config.middleware.use Rack::SslEnforcer, strict: true,
        only: ["/login", "/password", "/admin", "/users", "/payments", "card"]
  • Ví dụ ở đây mình sẽ chỉ sử dụng kết nối https cho các kết nối đến /login, /password, /admin, /payments ... khi đó app sẽ tự redirect đến https với các URL như trên

Run App

  • Xong rồi, giờ bạn có thể truy cập vào hệ thống với đường dẫn http://colantotte.local khi đến các kết nối có yêu cầu https như trong config, hệ thống sẽ tự redirect, ví dụ: https://colantotte.local/payments

Bài viết tham khảo

http://nginx.org/en/docs/http/configuring_https_servers.html https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-unicorn-and-nginx-on-ubuntu-14-04