记录一次 Rails 项目部署过程
云服务器
采用的是腾讯云,系统 Ubuntu Server 16.04.1 LTS 64位
项目示例
采用 blog demo,来自 Rails 官网 文档中 Getting Started with Rails
安装服务器软件
采用 Ubuntu 内建的套件管理工具 apt-get
-
更新软件列表
sudo apt-get update 复制代码
-
安装 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 复制代码
-
安装 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 复制代码
-
替换 gem source,安装 Bundler gem
gem source gem source -r https://rubygems.org/ gem source -a https://gems.ruby-china.com sudo gem install bundler 复制代码
-
安装 Rails
gem install rails -v 5.1.7 复制代码
-
安装数据库,采用
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 复制代码
-
安装 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 复制代码
-
直接访问 IP,确认是否为 Nginx 静态页面,
Welcome to nginx on Ubuntu!
新建部署用户
- 新增 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位置> 免密登录
复制代码
自动化部署
-
安装 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] 复制代码
-
本地配置 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 } 复制代码
-
修改
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 复制代码
-
修改
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" # } 复制代码
-
本地项目目录下执行
cap production deploy:check
,这会自动在自动登录远程服务器,并建立一些 Capistrano 目录和文件cap production deploy:check # 会有缺少 database.yml 等文件 Error,继续看下一步 复制代码
-
服务器设置 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 了 复制代码
-
本机 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 地址,大功告成,开心
参考文档
- Ruby China:ruby-china.org/wiki
- Getting Started with Rails:guides.rubyonrails.org/getting_sta…
- Brighbox:www.brightbox.com/docs/ruby/u…
- Capistrano:capistranorb.com/
- Ruby on Rails:guides.rubyonrails.org/
- Rails 实战圣经:ihower.tw/rails/fulls…
- Installing Passenger + Nginx www.phusionpassenger.com/library/ins…
- gorails:gorails.com/deploy/ubun…