一次 Ruby on Rails 部署记录

1,634 阅读9分钟

记录一次 Rails 项目部署过程

rails_welcome.png

云服务器

采用的是腾讯云,系统 Ubuntu Server 16.04.1 LTS 64位

项目示例

采用 blog demo,来自 Rails 官网 文档中 Getting Started with Rails

安装服务器软件

采用 Ubuntu 内建的套件管理工具 apt-get

  1. 更新软件列表

    sudo apt-get update
    
  2. 安装 Ruby on Rails 需要的软件

    sudo apt-get install -y build-essential git-core bison openssl libreadline6-dev curl zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 autoconf libc6-dev libpcre3-dev libcurl4-nss-dev libxml2-dev libxslt-dev imagemagick nodejs libffi-dev
    
  3. 安装 Ruby,使用 Brighbox 已经编译好的 Ruby

    sudo apt-get install software-properties-common
    sudo apt-add-repository ppa:brightbox/ruby-ng
    sudo apt-get update
    sudo apt-get install ruby2.6 ruby2.6-dev
    
  4. 替换 gem source,安装 Bundler gem

    gem source
    gem source -r https://rubygems.org/
    gem source -a https://gems.ruby-china.com
    
    sudo gem install bundler
    
  5. 安装 Rails

    gem install rails -v 5.1.7
    
  6. 安装数据库,采用PostgreSQL,当然也可以选择 MySQL

    sudo apt-get install postgresql libpq-dev postgresql-contrib
    # 修改账号 postgres 的密码
    sudo -u postgres psql
    postgres=#\password # 记住密码,后续 database.yml 中需要
    # 创建数据库 blog
    sudo -u postgres createdb blog
    
  7. 安装 Nginx + Passenger Web 服务器,来自 Installing Passenger + Nginx

    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
    sudo apt-get install -y apt-transport-https ca-certificates
    sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
    sudo apt-get update
    sudo apt-get install -y nginx-extras passenger
    
  8. 直接访问 IP,确认是否为 Nginx 静态页面,Welcome to nginx on Ubuntu!

新建部署用户

  1. 新增 deploy 用户
sudo adduser --disabled-password deploy
sudo su deploy 
mkdir ~/.ssh
touch ~/.ssh/authorized_keys
# 本地电脑执行,复制公钥到剪贴板
cat ~/.ssh/id_rsa.pub
# 服务器执行,粘贴公钥
vi ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 644 ~/.ssh/authorized_keys
# 退出后,本地电脑可以 ssh deploy@<主機IP位置> 免密登录

自动化部署

  1. 安装 Capistrano 部署工具,需要用到 blog 项目

    修改 blog 项目 Gemfile,关注处标记 #!!!,修改后 bundle install

     source 'https://gems.ruby-china.com/'
    
    git_source(:github) do |repo_name|
      repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
      "https://github.com/#{repo_name}.git"
    end
    
    
    # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
    gem 'rails', '~> 5.1.7'
    # Use sqlite3 as the database for Active Record
    # Use Puma as the app server
    gem 'puma', '~> 3.7'
    # Use SCSS for stylesheets
    gem 'sass-rails', '~> 5.0'
    # Use Uglifier as compressor for JavaScript assets
    gem 'uglifier', '>= 1.3.0'
    # See https://github.com/rails/execjs#readme for more supported runtimes
    # gem 'therubyracer', platforms: :ruby
    
    # Use CoffeeScript for .coffee assets and views
    gem 'coffee-rails', '~> 4.2'
    # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
    gem 'turbolinks', '~> 5'
    # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
    gem 'jbuilder', '~> 2.5'
    # Use Redis adapter to run Action Cable in production
    # gem 'redis', '~> 4.0'
    # Use ActiveModel has_secure_password
    # gem 'bcrypt', '~> 3.1.7'
    
    # Use Capistrano for deployment
    # gem 'capistrano-rails', group: :development
    
    #!!!
    group :production do
      gem 'pg'
    end
    
    #!!!
    group :development, :test do
      gem 'sqlite3'
    
      # Call 'byebug' anywhere in the code to stop execution and get a debugger console
      gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
      # Adds support for Capybara system testing and selenium driver
      gem 'capybara', '>= 2.15'
      gem 'selenium-webdriver'
      gem 'rspec-rails'
    	
    	#!!!
      gem 'capistrano-rails'
      gem 'capistrano-passenger'
    end
    
    group :development do
      # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
      gem 'web-console', '>= 3.3.0'
      gem 'listen', '>= 3.0.5', '< 3.2'
      # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
      gem 'spring'
      gem 'spring-watcher-listen', '~> 2.0.0'
    end
    
    # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
    gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
    
  2. 本地配置 capistrano,项目目录下执行

    cap install
    

    编辑 Capfile 文件,关注处标记 #!!!

    # Load DSL and set up stages
    require "capistrano/setup"
    
    # Include default deployment tasks
    require "capistrano/deploy"
    
    # Load the SCM plugin appropriate to your project:
    #
    # require "capistrano/scm/hg"
    # install_plugin Capistrano::SCM::Hg
    # or
    # require "capistrano/scm/svn"
    # install_plugin Capistrano::SCM::Svn
    # or
    require "capistrano/scm/git"
    install_plugin Capistrano::SCM::Git
    
    #!!!
    require 'capistrano/rails'
    require 'capistrano/passenger'
    
    # Include tasks from other gems included in your Gemfile
    #
    # For documentation on these, see for example:
    #
    #   https://github.com/capistrano/rvm
    #   https://github.com/capistrano/rbenv
    #   https://github.com/capistrano/chruby
    #   https://github.com/capistrano/bundler
    #   https://github.com/capistrano/rails
    #   https://github.com/capistrano/passenger
    #
    # require "capistrano/rvm"
    # require "capistrano/rbenv"
    # require "capistrano/chruby"
    # require "capistrano/bundler"
    # require "capistrano/rails/assets"
    # require "capistrano/rails/migrations"
    # require "capistrano/passenger"
    
    # Load custom tasks from `lib/capistrano/tasks` if you have any defined
    Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
    
  3. 修改 config/deploy.rb,关注处标记 #!!!

    # config valid for current version and patch releases of Capistrano
    #!!!
    sh "ssh-add"
    
    lock "~> 3.16.0"
    
    #!!!
    set :application, "blog"
    #!!! 将你的项目提到远程 github 仓库,包含你的之前和后续的一些修改
    set :repo_url, "git@github.com:xxx/blog.git"
    
    # Default branch is :master
    # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
    
    # Default deploy_to directory is /var/www/my_app_name
    # set :deploy_to, "/var/www/my_app_name"
    #!!!
    set :deploy_to, "/home/deploy/blog"
    
    # Default value for :format is :airbrussh.
    # set :format, :airbrussh
    
    # You can configure the Airbrussh format using :format_options.
    # These are the defaults.
    # set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
    
    # Default value for :pty is false
    # set :pty, true
    
    # Default value for :linked_files is []
    # append :linked_files, "config/database.yml"
    #!!!
    append :linked_files, "config/database.yml", "config/secrets.yml"
    
    # Default value for linked_dirs is []
    # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
    #!!!
    append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
    
    #!!!
    set :passenger_restart_with_touch, true
    
    # Default value for default_env is {}
    # set :default_env, { path: "/opt/ruby/bin:$PATH" }
    
    # Default value for local_user is ENV['USER']
    # set :local_user, -> { `git config user.name`.chomp }
    
    # Default value for keep_releases is 5
    # set :keep_releases, 5
    #!!!
    set :keep_releases, 5
    
    # Uncomment the following to require manually verifying the host key before first deploy.
    # set :ssh_options, verify_host_key: :secure
    
  4. 修改 config/deploy/production.rb,关注处标记 #!!!

    #!!!
    set :branch, "master"
    # server-based syntax
    # ======================
    # Defines a single server with a list of roles and multiple properties.
    # You can define all roles on a single server, or split them:
    
    # server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value
    # server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value
    # server "db.example.com", user: "deploy", roles: %w{db}
    
    #!!! 你的服务 IP 地址
    server "15x.13x.12x.18", user: "deploy", roles: %w{app db web}, my_property: :my_value
    
    
    
    # role-based syntax
    # ==================
    
    # Defines a role with one or multiple servers. The primary server in each
    # group is considered to be the first unless any hosts have the primary
    # property set. Specify the username and a domain or IP for the server.
    # Don't use `:all`, it's a meta role.
    
    # role :app, %w{deploy@example.com}, my_property: :my_value
    # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
    # role :db,  %w{deploy@example.com}
    
    
    
    # Configuration
    # =============
    # You can set any configuration variable like in config/deploy.rb
    # These variables are then only loaded and set in this stage.
    # For available Capistrano configuration variables see the documentation page.
    # http://capistranorb.com/documentation/getting-started/configuration/
    # Feel free to add new variables to customise your setup.
    
    
    
    # Custom SSH Options
    # ==================
    # You may pass any option but keep in mind that net/ssh understands a
    # limited set of options, consult the Net::SSH documentation.
    # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
    #
    # Global options
    # --------------
    #  set :ssh_options, {
    #    keys: %w(/home/user_name/.ssh/id_rsa),
    #    forward_agent: false,
    #    auth_methods: %w(password)
    #  }
    #
    # The server-based syntax can be used to override options:
    # ------------------------------------
    # server "example.com",
    #   user: "user_name",
    #   roles: %w{web app},
    #   ssh_options: {
    #     user: "user_name", # overrides user setting above
    #     keys: %w(/home/user_name/.ssh/id_rsa),
    #     forward_agent: false,
    #     auth_methods: %w(publickey password)
    #     # password: "please use keys"
    #   }
    
  5. 本地项目目录下执行 cap production deploy:check,这会自动在自动登录远程服务器,并建立一些 Capistrano 目录和文件

    cap production deploy:check
    
    # 会有缺少 database.yml 等文件 Error,继续看下一步
    
  6. 服务器设置 database.yml 和 secrets.yml

    # 登录或切换到 deploy 用户,添加 /home/deploy/blog/shared/config/database.yml
    production:
      adapter: postgresql
      pool: 25
      database: blog
      host: localhost
      username: postgres
      password: xxxxxx
    
    # 本地项目下执行 rake secret,生成随机数备用
    efab0aae7352a424fae818574934baf1107f96785dbdf6ee5f8e7054399a4bbbb561dfc1183e1f20f2c247d9cd437fa0908d2fcb7517b0d7c34e61b459d5969d
    
    # 登录或切换到 deploy 用户,添加 /home/deploy/blog/shared/config/secrets.yml
    production:
      secret_key_base: 把刚刚的随机数粘贴过来,这地方有个空格,不然报 parse 
    
    # 再次本地执行 cap production deploy:check
    # 应该没有第 14 步的 error 了
    
  7. 本机 blog 目录下执行 cap production deploy 进行部署

    执行登录远程服务器,从 GitHub 仓库拉取代码,安装 Gem,迁移数据库,编译 asset 等等

    ssh-add
    Identity added: /Users/gekang/.ssh/id_rsa (/Users/gekang/.ssh/id_rsa)
    00:00 git:wrapper
      01 mkdir -p /tmp
    ✔ 01 deploy@15x.x3x.xxx.13 0.351s
      Uploading /tmp/git-ssh-0633e09da1de2a9e42f4.sh 100.0%
      02 chmod 700 /tmp/git-ssh-0633e09da1de2a9e42f4.sh
    ✔ 02 deploy@15x.x3x.xxx.13 0.077s
    00:00 git:check
      01 git ls-remote git@github.com:grackanil/blog.git HEAD
      01 ac469184b28aec29e409430ab7bdc0eb6af76e5b	HEAD
    ✔ 01 deploy@15x.x3x.xxx.13 6.136s
    00:06 deploy:check:directories
      01 mkdir -p /home/deploy/blog/shared /home/deploy/blog/releases
    ✔ 01 deploy@15x.x3x.xxx.13 0.035s
    00:06 deploy:check:linked_dirs
      01 mkdir -p /home/deploy/blog/shared/log /home/deploy/blog/shared/tmp/pids /home/…
    ✔ 01 deploy@15x.x3x.xxx.13 0.464s
    00:07 deploy:check:make_linked_dirs
      01 mkdir -p /home/deploy/blog/shared/config
    ✔ 01 deploy@15x.x3x.xxx.13 0.031s
    00:08 git:clone
      The repository mirror is at /home/deploy/blog/repo
    00:08 git:update
      01 git remote set-url origin git@github.com:grackanil/blog.git
    ✔ 01 deploy@15x.x3x.xxx.13 0.077s
      02 git remote update --prune
      02 Fetching origin
    ✔ 02 deploy@15x.x3x.xxx.13 5.568s
    00:14 git:create_release
      01 mkdir -p /home/deploy/blog/releases/20210622112320
    ✔ 01 deploy@15x.x3x.xxx.13 0.075s
      02 git archive master | /usr/bin/env tar -x -f - -C /home/deploy/blog/releases/20…
    ✔ 02 deploy@15x.x3x.xxx.13 0.092s
    00:14 deploy:set_current_revision
      01 echo "ac469184b28aec29e409430ab7bdc0eb6af76e5b" > REVISION
    ✔ 01 deploy@15x.x3x.xxx.13 0.079s
    00:14 deploy:symlink:linked_files
      01 mkdir -p /home/deploy/blog/releases/20210622112320/config
    ✔ 01 deploy@15x.x3x.xxx.13 0.070s
      02 rm /home/deploy/blog/releases/20210622112320/config/database.yml
    ✔ 02 deploy@15x.x3x.xxx.13 0.074s
      03 ln -s /home/deploy/blog/shared/config/database.yml /home/deploy/blog/releases/…
    ✔ 03 deploy@15x.x3x.xxx.13 0.071s
      04 rm /home/deploy/blog/releases/20210622112320/config/secrets.yml
    ✔ 04 deploy@15x.x3x.xxx.13 0.071s
      05 ln -s /home/deploy/blog/shared/config/secrets.yml /home/deploy/blog/releases/2…
    ✔ 05 deploy@15x.x3x.xxx.13 0.071s
    00:15 deploy:symlink:linked_dirs
      01 mkdir -p /home/deploy/blog/releases/20210622112320 /home/deploy/blog/releases/…
    ✔ 01 deploy@15x.x3x.xxx.13 0.070s
      02 rm -rf /home/deploy/blog/releases/20210622112320/log
    ✔ 02 deploy@15x.x3x.xxx.13 0.070s
      03 ln -s /home/deploy/blog/shared/log /home/deploy/blog/releases/20210622112320/l…
    ✔ 03 deploy@15x.x3x.xxx.13 0.073s
      04 ln -s /home/deploy/blog/shared/tmp/pids /home/deploy/blog/releases/20210622112…
    ✔ 04 deploy@15x.x3x.xxx.13 0.071s
      05 ln -s /home/deploy/blog/shared/tmp/cache /home/deploy/blog/releases/2021062211…
    ✔ 05 deploy@15x.x3x.xxx.13 0.078s
      06 ln -s /home/deploy/blog/shared/tmp/sockets /home/deploy/blog/releases/20210622…
    ✔ 06 deploy@15x.x3x.xxx.13 0.077s
      07 ln -s /home/deploy/blog/shared/public/system /home/deploy/blog/releases/202106…
    ✔ 07 deploy@15x.x3x.xxx.13 0.069s
      08 rm -rf /home/deploy/blog/releases/20210622112320/public/assets
    ✔ 08 deploy@15x.x3x.xxx.13 0.078s
      09 ln -s /home/deploy/blog/shared/public/assets /home/deploy/blog/releases/202106…
    ✔ 09 deploy@15x.x3x.xxx.13 0.072s
    00:16 bundler:config
      01 bundle config --local deployment true
      01 You are replacing the current local value of deployment, which is currently nil
    ✔ 01 deploy@15x.x3x.xxx.13 0.241s
      02 bundle config --local path /home/deploy/blog/shared/bundle
      02 You are replacing the current local value of path, which is currently nil
    ✔ 02 deploy@15x.x3x.xxx.13 0.317s
      03 bundle config --local without development:test
      03 You are replacing the current local value of without, which is currently nil
    ✔ 03 deploy@15x.x3x.xxx.13 0.308s
    00:18 bundler:install
      The Gemfile's dependencies are satisfied, skipping installation
    00:18 deploy:assets:precompile
      01 bundle exec rake assets:precompile
      01 Yarn executable was not detected in the system.
      01 Download Yarn at https://yarnpkg.com/en/docs/install
    ✔ 01 deploy@15x.x3x.xxx.13 1.870s
    00:20 deploy:assets:backup_manifest
      01 mkdir -p /home/deploy/blog/releases/20210622112320/assets_manifest_backup
    ✔ 01 deploy@15x.x3x.xxx.13 0.073s
      02 cp /home/deploy/blog/releases/20210622112320/public/assets/.sprockets-manifest…
    ✔ 02 deploy@15x.x3x.xxx.13 0.071s
    00:20 deploy:migrate
      [deploy:migrate] Run `rake db:migrate`
    00:20 deploy:migrating
      01 bundle exec rake db:migrate
    ✔ 01 deploy@15x.x3x.xxx.13 1.584s
    00:21 deploy:symlink:release
      01 ln -s /home/deploy/blog/releases/20210622112320 /home/deploy/blog/releases/cur…
    ✔ 01 deploy@15x.x3x.xxx.13 0.075s
      02 mv /home/deploy/blog/releases/current /home/deploy/blog
    ✔ 02 deploy@15x.x3x.xxx.13 0.072s
    00:22 passenger:restart
      01 mkdir -p /home/deploy/blog/releases/20210622112320/tmp
    ✔ 01 deploy@15x.x3x.xxx.13 0.072s
      02 touch /home/deploy/blog/releases/20210622112320/tmp/restart.txt
    ✔ 02 deploy@15x.x3x.xxx.13 0.072s
    00:22 deploy:cleanup
      Keeping 5 of 6 deployed releases on 15x.x3x.xxx.13
      01 rm -rf /home/deploy/blog/releases/20210622073535
    ✔ 01 deploy@15x.x3x.xxx.13 0.080s
    00:22 deploy:log_revision
      01 echo "Branch master (at ac469184b28aec29e409430ab7bdc0eb6af76e5b) deployed as …
    ✔ 01 deploy@15x.x3x.xxx.13 0.073s
    

    后续只需要修改代码,执行部署就可以了

    git commit
    cap production deploy
    

    注意此处会生成 current 目录,后续会需要

    设置 Nginx

    使用 root 权限,设置 /etc/nginx/nginx.conf,使用 #!!!

    #!!!
    env PATH;
    
    user www-data;
    worker_processes auto;
    pid /run/nginx.pid;
    
    events {
        worker_connections 768;
        # multi_accept on;
    }
    
    http {
        #!!!
        passenger_show_version_in_header off;
        #!!!
        server_tokens       off;
        #!!!
        client_max_body_size 100m;
    
        gzip on;
        gzip_disable "msie6";
    
    #!!!
    gzip_comp_level    5;
    gzip_min_length    256;
    gzip_proxied       any;
    gzip_vary          on;
    gzip_types application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/xml text/plain text/javascript text/x-component;
    
    #!!!
    include /etc/nginx/passenger.conf;
    
    # 下略
    

    远程使用 root 权限,设置 /etc/nginx/sites-enabled/blog.conf,blog 命名随意

    server {
      listen 80;
      server_name 15x.x3x.xxx.13;   # 用你自己的服务器 IP 地址
    
      root /home/deploy/blog/current/public; # 第 16 步骤最后说的 current 目录
    
      passenger_enabled on;
    
      passenger_min_instances 1;
    
      location ~ ^/assets/ {
        expires 1y;
        add_header Cache-Control public;
        add_header ETag "";
        break;
       }
    }
    

    重启 Nginx

    sudo service nginx restart
    

    打开 IP 地址,大功告成,开心 rails.png

参考文档

欢迎关注我的小小公众号

41624457797_.pic_hd.jpg