V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rina
V2EX  ›  Ruby on Rails

Rails 布署最佳实践

  •  
  •   rina · 2017-12-28 11:13:31 +08:00 · 2726 次点击
    这是一个创建于 1433 天前的主题,其中的信息可能已经有所发展或是发生改变。

    准备工作

    • 购买域名:http://name.com/
    • 购买一台云服务器, 提供商: 阿里云,AWS
    • 域名绑定 IP 为服务器 IP 地址

    本文介绍的内容为:服务器 ubuntu16.04, Rails 项目,mina 发布

    安装 rails 环境

    • 使用 root 账号登录服务器,创建一个没有 root 权限的账号。
    adduser newuser
    
    

    设置新用户密码

    配置 ssh

    在本地生成 SSH KEY (如果已经有了,可以忽略这步。)

    • 打开终端

    • 用你自己的邮箱地址替换下面的命令,并执行

    $ ssh-keygen -t rsa -b 4096 -C "[email protected]"
    
    • 执行这条命令时,系统会提示你输入要保存密钥的文件位置及安全 passphrase,请按 Enter 键就行了。

    生成完之后,在 ~/.ssh/ 目录下会添加两个文件,默认情况下一个私钥 id_rsa, 一个公钥 id_rsa.pub.

    配置无密码登录服务器

    这一步是为了给 mina 发布提供方便,因为 mina 发布需要无密登录服务器,执行发布命令。

    • 将公钥 id_rsa.pub 文件上传到服务器上, 并添加到 authorized_keys
    $ scp ~/.ssh/id_rsa.pub [email protected]:~/.ssh/
    
    $ ssh [email protected]
    
    $ cd ~/.ssh/
    
    $ cat id_rsa.pub >> authorized_keys
    
    $ rm id_rsa.pub
    
    
    • 重新开一个终端, 登录服务器,如果无需输入密码登录成功,那配置上面的配置就成功了。
    ssh [email protected]
    

    以上是配置 root 用户无密码登录, 同样给 newuser 用户配置成无密码登录。

    • 登录 root 账号,将服务器上的 ssh 配置关掉密码登录
    vi /etc/ssh/sshd_config
    
    更新:
    
    PasswordAuthentication no
    
    

    为了确保能顺利安装 Rails,先要安装 Node.js 和 Yarn 的系统依赖

    以下操作使用 root 账号执行

    curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
    
    sudo apt-get update
    sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev nodejs yarn
    
    

    安装 rbenv

    以下操作使用 newuser 账号执行

    cd
    git clone https://github.com/rbenv/rbenv.git ~/.rbenv
    echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
    echo 'eval "$(rbenv init -)"' >> ~/.bashrc
    exec $SHELL
    
    git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
    echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
    exec $SHELL
    
    rbenv install 2.4.3
    rbenv global 2.4.3
    ruby -v
    
    gem install bundler
    rbenv rehash
    

    配置 git 账号

    以下操作使用 newuser 账号执行

    • 用你的 github 账号信息,替换下面的用户名和邮箱,并执行
    git config --global color.ui true
    git config --global user.name "YOUR NAME"
    git config --global user.email "[email protected]"
    ssh-keygen -t rsa -b 4096 -C "[email protected]"
    

    在服务器上生成了 ssh key 后, 将下面命令输入的内容,复制后粘贴到你 github 账号SSH and GPG keys下:https://github.com/settings/keys

    cat ~/.ssh/id_rsa.pub
    
    • 测试 github 配置是否成功
    ssh -T [email protected]
    

    如果成功会输出以下信息:

    Hi kfzyutc! You've successfully authenticated, but GitHub does not provide shell access.
    

    安装 Rails

    • 使用 newuser 账号执行以下命令
    $ gem install rails -v 5.1.4
    
    $ rbenv rehash
    
    $ rails -v
    # Rails 5.1.4
    

    安装 MySQL

    • 使用 root 账号执行以下命令
    sudo apt-get install mysql-server mysql-client libmysqlclient-dev
    

    安装 PostgreSQL

    • 使用 root 账号执行以下命令
    sudo sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
    wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add -
    sudo apt-get update
    sudo apt-get install postgresql-common
    sudo apt-get install postgresql-9.5 libpq-dev
    

    postgres 安装不会为你设置一个用户,所以你需要按照以下步骤创建一个有权创建数据库的用户。 随意用你的用户名替换 chris

    sudo -u postgres createuser chris -s
    
    # If you would like to set a password for the user, you can do the following
    sudo -u postgres psql
    postgres=# \password chris
    
    

    配置 nginx

    • 使用 root 账号登录服务器

    • home/newuser/xxx, xxx 的目录改成你发布项目的目录,将 example.com 改成你的域名。

    upstream RBlog {
      server unix:///home/newuser/xxx/shared/tmp/sockets/puma.sock fail_timeout=0; 
    }
    
    server {
      listen 80;
      server_name example.com;
      root /home/newuser/xxx/current/public;
    
      location ^~ /assets/ {
        gzip_static on;
        expires max;
        add_header Cache-Control public;
      }
    
      location /cable {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://xxx;
      }
    
      location ~ ^/(uploads)/  {
        expires max;
        break;
      }
    
    
      try_files $uri/index.html $uri @RBlog;
      location @xxx {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_pass http://xxx;
      }
    
      error_page 500 502 503 504 /500.html;
      client_max_body_size 20M;
      keepalive_timeout 10;
    }
    
    
    • 将以上更换后的配置信息,保存到 /etc/nginx/conf.d/ 目录下,命令为 xxx.conf xxx 用你想要的命名替换。

    • 测试配置信息是否 OK

    nginx -t
    # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    # nginx: configuration file /etc/nginx/nginx.conf test is successful
    
    • 重启 nginx
    nginx -s restart
    

    发布 Rails 项目

    如果你是用这个 模板 创建的 Rails 项目, 以下配置信息都已经添加了,只需要更新目录信息就行。

    如果没有需要在 Gemfile 里添加这些 gem 包。

    gem 'mina', '~>0.3.8', require: false
    gem 'mina-puma', '~>0.3.2', require: false
    gem 'mina-multistage', '~> 1.0', '>= 1.0.2', require: false
    gem 'mina-sidekiq', '~> 0.3.1', require: false
    gem 'mina-logs', '>= 0.1.0', require: false
    gem 'whenever', :require => false
    

    在你的 Rails 项目下配置 puma,config/puma.rb, 替换 /home/newuser/xxx 目录.

    if ENV['RAILS_ENV'] == 'production'
      app_root = "/home/newuser/xxx/shared"
      pidfile "#{app_root}/tmp/pids/puma.pid"
      state_path "#{app_root}/tmp/pids/puma.state"
      bind "unix://#{app_root}/tmp/sockets/puma.sock"
      activate_control_app "unix://#{app_root}/tmp/sockets/pumactl.sock"
      daemonize true
      workers 4
      threads 8, 16
      preload_app!
    
      on_worker_boot do
        ActiveSupport.on_load(:active_record) do
          ActiveRecord::Base.establish_connection
        end
      end
    
      before_fork do
        ActiveRecord::Base.connection_pool.disconnect!
      end
    else
      plugin :tmp_restart
    end
    
    

    配置 deploy, config/deploy.rb

    set :stages, %w(production staging utcprod)
    set :default_stage, 'staging'
    
    require 'mina/multistage'
    require 'mina/bundler'
    require 'mina/rails'
    require 'mina/git'
    require 'mina/rbenv'
    require 'mina/puma'
    require "mina_sidekiq/tasks"
    require 'mina/logs'
    require 'mina/whenever'
    
    set :shared_paths, ['config/database.yml', 'config/newrelic.yml', 'config/application.yml', 'log', 'public/uploads']
    
    task :environment do
      invoke :'rbenv:load'
    end
    
    task :setup => :environment do
      queue! %[mkdir -p "#{deploy_to}/shared/tmp/sockets"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/tmp/sockets"]
    
      queue! %[mkdir -p "#{deploy_to}/shared/pids"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/pids"]
    
      queue! %[mkdir -p "#{deploy_to}/shared/tmp/pids"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/tmp/pids"]
    
      queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"]
    
      queue! %[mkdir -p "#{deploy_to}/#{shared_path}/public/uploads"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/public/uploads"]
    
      queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]
      queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/config"]
    
      queue! %[touch "#{deploy_to}/#{shared_path}/config/application.yml"]
      queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/application.yml'"]
    
      queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"]
      queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml'"]
    end
    
    desc "Deploys the current version to the server."
    task :deploy => :environment do
      queue  %[echo "-----> Server: #{domain}"]
      queue  %[echo "-----> Path: #{deploy_to}"]
      queue  %[echo "-----> Branch: #{branch}"]
    
      deploy do
        invoke :'sidekiq:quiet'
        invoke :'git:clone'
        invoke :'deploy:link_shared_paths'
        invoke :'bundle:install'
        invoke :'rails:db_migrate'
        invoke :'rails:assets_precompile'
        invoke :'deploy:cleanup'
    
        to :launch do
          invoke :'puma:hard_restart'
          invoke :'sidekiq:restart'
          invoke :'whenever:update'
        end
      end
    end
    
    desc "Deploys the current version to the server."
    task :first_deploy => :environment do
      queue  %[echo "-----> Server: #{domain}"]
      queue  %[echo "-----> Path: #{deploy_to}"]
      queue  %[echo "-----> Branch: #{branch}"]
    
      deploy do
        invoke :'git:clone'
        invoke :'deploy:link_shared_paths'
        invoke :'bundle:install'
        invoke :'deploy:cleanup'
    
        to :launch do
          invoke :'rails:db_create'
        end
      end
    end
    

    config/deploy/production.rb,将 domain, deploy_to, repository, branch, user 替换成你自己的信息。

    set :domain, 'your.domain.com'
    set :deploy_to, '/home/newuser/xxx'
    set :repository,  '[email protected]:xxx/xxx.git'
    set :branch, 'master'
    set :user, 'newuser'
    set :puma_config, ->{ "#{deploy_to}/#{current_path}/config/puma/production.rb" }
    

    这些配置信息更新好之后,提交到 github

    首次发布

    mina setup
    mina first_deploy
    

    后续发布

    mina deploy
    

    本地查看生产环境日志

    mina log
    

    本地登录生产环境数据库控制台

    mina console
    

    本地登录服务器

    mina ssh
    

    参考文档

    Ubuntu 安装 Rails 环境: https://gorails.com/setup/ubuntu/16.04

    Mina 配置: https://github.com/80percent/rails-template

    原文: http://liuzhen.me/articles/17

    9 条回复    2017-12-31 18:35:46 +08:00
    linuxchild
        1
    linuxchild   2017-12-28 14:17:00 +08:00
    lz 是把这个当博客了么。。
    rina
        2
    rina   2017-12-28 14:26:19 +08:00
    @linuxchild

    首先我并不认为我这篇文章没有价值,在发之前我也考虑了最近已经发过几篇会不会发太多了,但是从我博客的这篇文章的访问量看远远超过其他文章,所以我觉得应该有很多人需要,而且目前为止也有人收藏,另一篇文章收藏数也有 10 几个人,从中也证明确实对一些人是有帮助的,如果你不喜欢可以不看。
    msg7086
        3
    msg7086   2017-12-28 19:38:08 +08:00   ❤️ 1
    这是哪门子最佳实践……
    要我说最佳实践的话,一个,服务器用 puppet 或者 chef 管理组件,一个,用 CI 保证程序能在几个主要 ruby 版本上运行,然后让操作系统去管理 ruby 的版本升级。
    再或者索性上 docker,让 docker 的 up 主来管理 ruby 的升级。
    sparkle2015
        4
    sparkle2015   2017-12-28 22:49:58 +08:00
    对我来说还挺有价值的,前段时间刚在 EC2 上折腾过一次 Rails 的布署,把这些流程都走过一遍。不过用的是 capistrano,好奇 lz 为什么没用 capistrano (这个应该是主流了吧)。
    msg7086
        5
    msg7086   2017-12-29 00:18:54 +08:00
    @sparkle2015 mina 比 capistrano 不知道快到哪里去了。
    bydmm
        6
    bydmm   2017-12-29 10:07:00 +08:00 via iPhone
    docker push
    docker compose up
    zealinux
        7
    zealinux   2017-12-29 12:53:31 +08:00
    lz 用 mina 的新版了吗?
    问题一大堆,mina-puma
    r00tt
        8
    r00tt   2017-12-29 13:14:26 +08:00
    还在用着 capistrano,感觉还行
    yuanfnadi
        9
    yuanfnadi   2017-12-31 18:35:46 +08:00 via iPhone
    docker run

    一句话就够了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4252 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 06:14 · PVG 14:14 · LAX 22:14 · JFK 01:14
    ♥ Do have faith in what you're doing.