部署 Capistrano + Rails 5.2 自动化部署

canonpd · 2018年06月05日 · 最后由 Rei 回复于 2018年06月13日 · 2065 次阅读
本帖已被设为精华帖!

原文地址 https://itfun.tv/news/47

相关文章 「Mina + Rails 5.2 自动化部署」

前言

1. 为什么要用它?

这是一个一劳永逸的工作,有了它。你不用每次版本更新都去服务器上git pull,不用bundle install,不用编译静态文件,不用跑migartion,也不用跑 touch tmp/restart.txt之类的命令,更不需要还在用FTP上传代码了。每次新版本更新,只需要在你本机,运行一条cap production deploy命令。服务器就会自动git pull远程仓库的最新代码,并自动完成一系列部署相关的操作。

2. 环境

网上诸多Capistrano都已经过时了,部署中因为版本原因会出现一大堆错误,于是我重新整理了完整的流程。只要你不是完全的Linux小白,稍微对这方面有一点了解,相信你参照我写的流程,都能自己部署出来了。

流程中,所采用的服务器环境如下:

  1. 服务器系统:我选用的服务器系统是 Ubuntu Server 16.04,如果你是其他系统,流程可能会有部分不一样。如果你用Rails,那么我只推荐这一个服务器操作系统,坑少、易解决。
  2. Rails版本:我所使用的Rails版本是5.2。如果你用的老版本Rails,会有一些不一样的地方,哪里需要注意的,文里也有说明。
  3. 数据库: MySQL 5.7
  4. 其他:再就是用了NginxPassenger了。还介绍了SSH秘钥Git使用的基础知识。

Tips: 需要注意的是,有的命令要在服务器上运行,有的是在你电脑本地运行的。注释里都有说明,请务必不要搞错了~! 如果文章中,有哪里写的有错误,或者不够清晰。请在下面留言给我,我看到后会第一时间处理。

一、新建用户

# 服务器上
# `--ingroup sudo`是说,新建的用户直接就有执行`sudo`命令的权限。
adduser deploy --ingroup sudo
# 输入新密码。
# 问其他的Full Name之类的,你可以填写,也可以不填,直接回车。

# 切换到deploy用户
sudo su deploy

# 进入`家`目录
cd ~

二、使用秘钥登录服务器

很多云服务器,大部分默认都是使用账号密码登录的。这样做非常不安全,所以你需要改为SSH秘钥登录。如果你想改为秘钥登录,一种是直接在云主机管理界面,上传自己的秘钥。另一种通用方法,就是我下面的操作了。

相信玩Ruby On Rails的基本都是Mac OS用户了。以下演示都以Mac机为例子操作。

# Mac 本地
ssh-keygen -t rsa
# 如果不需要加密,就直接全部回车。需要加密,就自己填写密码

cat ~/.ssh/id_rsa.pub
# 会出现一段文字,复制下来
# 服务器上
ssh-keygen -t rsa
# 依然全部直接回车

vi /home/deploy/.ssh/authorized_keys
# 将刚才Mac本地命令行中,复制的那一段文字,粘贴进去,然后按:wq保存离开

chmod 644 /home/deploy/.ssh/authorized_keys
sudo service ssh restart

现在你再用SSH连服务器,直接ssh deploy@你的ip,不再需要输入密码了。

三、禁用密码登录(可选)

用密码登录服务器, 并不是一个安全的选择。最好方法是直接禁用密码登录,改为必须使用SSH秘钥登录。当然这一步是可选的,和我们学习Capistrano并没有什么关系。

# 服务器上
vi /etc/ssh/sshd_config

# 将`PasswordAuthentication yes` 修改成 `PasswordAuthentication no`
# :wq退出后
sudo service ssh restart

四、服务器基础准备工作

# 服务器上
# 确认一下,当前用户依然是deploy。如果不是,先sudo su deploy

# 更新
sudo apt-get update
sudo apt-get upgrade -y
sudo dpkg-reconfigure tzdata
# 选择时区 Time zone=>Asia=>Shanghai

# 安装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 curl libcurl4-nss-dev libxml2-dev libxslt-dev imagemagick nodejs libffi-dev

Ubuntu 16.04apt-get install 默认只支持 Ruby 2.3。对新版本Rails 5.2来说,已经无法运行了。所以我们选择使用 rbenv 来安装 Ruby

五、使用rbenv安装Ruby

# 服务器上
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

rbenv需要ruby-build,才能安装ruby。所以现在来安装它。

mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

# 看一下,现在可安装的Ruby版本
rbenv install -l
# 我写文章这个时间,最新的是2.5.1

rbenv install 2.5.1

# 设置成全局默认使用
rbenv global 2.5.1

# 看一下是否装成功
ruby -v

# 使用Ruby China的RubyGems(境外服务器请略过)
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
gem sources -l
# 确保只有 gems.ruby-china.org

# 接着安装 bundler gem
gem install bundler

六、安装MySQL

# 服务器上
sudo apt-get install mysql-common mysql-client libmysqlclient-dev mysql-server
# 安装过程中,会让你输入密码。自己记好了哦!

# 新建一个数据库,其中deployment是你数据库的名字,可根据需求自行修改
mysql -u root -p
CREATE DATABASE deployment_production CHARACTER SET utf8mb4;

# 退出 mysql console
exit

七、安装 Nginx + Passenger

# 服务器上
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7

sudo apt-get install -y apt-transport-https ca-certificates

# 添加 APT 仓库地址
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

# 安装 Passenger + Nginx
sudo apt-get install -y nginx-extras passenger

八、注册Coding账号

你这里使用GitHubCoding码云哪个都行。我这里以Coding为例子演示,之所以用Coding,是考虑到国内企业付费用GitHub的比较少。没用码云,是因为页面没有Coding的好看,这个很重要

Coding的注册地址https://coding.net

九、Git设置SSH公钥

Git设置SSH公钥

# Mac 本地
cat ~/.ssh/id_rsa.pub
# 会出现一段文字,复制下来,粘贴到下面`公钥内容`中,选择`永久有效`。

Git设置SSH公钥 这样你在使用Git的时候,就不需要每次输入账号密码了,直接用SSH秘钥。

十、新建一个Git项目

新建一个Git项目

新建一个Git项目

点击代码浏览后,选择SSH方式,点击复制按钮。

十一、新建一个Rails项目

# Mac 本地
# 如果你已经有开发好的项目,这一步请直接略过。

# 新建一个目录,存放rails项目
mkdir -p ~/Developer/Rails

# 新建一个Rails项目,默认使用MySQL数据库
rails new deployment -d mysql

十二、修改gemfile,安装Capistrano以及插件

gemfilegroup :development中,添加如下代码,这些都是capistrano的插件

group :development do
  # ...

  # 其中`capistrano-rails`包含了以下三个插件。
  # gem 'capistrano/bundler'
  # gem 'capistrano/rails/assets'
  # gem 'capistrano/rails/migrations'
  # 你也可以分别一个个加进去,但是何必呢?这些基本都是`rails`部署必须的。
  # 直接用`gem 'capistrano-rails'`这一个就好了。
  gem 'capistrano-rails'

  # 对`passenger`与`rbenv`的支持
  gem 'capistrano-passenger'
  gem 'capistrano-rbenv'
end
# 改完后,Mac 本地,命令行进入自己项目中
cd ~/Developer/Rails/deployment
bundle install

十三、将代码推送到Git远程仓库

Mac上的Git客户端,常用的有两个。一个是需要收费的Tower,另个一是免费的SourceTree。其实用起来差别不大,只是我个人Tower用的比较顺手,于是直接购买了。当然你也可以不装任何客户端,直接用命令。

我们现在为了演示方便,直接用就用命令行操作

# 确认命令行当前的路径,在刚才新建的项目中
git add .
git commit -m "初次提交"
# 将下面的地址换成自己刚复制的
git remote add origin git@git.coding.net:aaronryuu/deployment.git
git push -u origin master

coding刷新页面,代码都已经提交上去了 Coding Push

十四、配置Capistrano

1. 生成capistrano的相关配置文件。

# Mac本地运行
cap install

2. 编辑 Capfile(项目的根目录下)

# 加上这行
require "capistrano/rails"

# 去掉这两行前面的`#`号 
require "capistrano/rbenv"
require "capistrano/passenger"

其他配置可保持默认。

配置Capistrano

3. 编辑 config/deploy.rb

# 最顶上加这行,注意是「`」号而不是单引号「'」
# 如果你对ssh-add有兴趣,你可以去读这一篇。https://ihower.tw/blog/archives/7837
`ssh-add`

# 项目名称
set :application, "deployment"

# git仓库地址
set :repo_url, "git@git.coding.net:aaronryuu/deployment.git"

# 需要部署到服务器的位置
set :deploy_to, "/home/deploy/deployment"

# 去掉注释,并加上 "config/master.key"
append :linked_files, "config/database.yml", "config/master.key"

# 去掉注释
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system'

注意了:我这里是Rails 5.2的部署,这个版本开始,config/secrets.yml变成了config/master.key。所以,如果你的项目Rails版本低于5.2,那这里应该是

append :linked_files, 'config/database.yml', 'config/secrets.yml'

如果这里不正确处理,后面部署可能会碰到这个错误!

ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`

配置Capistrano

4. 编辑config/deploy/production.rb

# 改成你自己的ip
server "114.67.72.94", user: "deploy", roles: %w{app db web}, my_property: :my_value

set :ssh_options, {
  keys: %w(~/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}

5. 尝试报错

# Mac 本地执行
cap production deploy:check

出现错误

ERROR linked file /home/deploy/deployment/shared/config/database.yml does not exist on 114.67.72.94

解决方法,是需要手动到服务器上,建config/database.ymlconfig/secrets.yml这两个文件,并做好配置。

服务器上,会出现releasesshared两个目录。releases是每次部署的文件,shared目录则是一些公用的配置文件。那么我们现在就去shared目录中,添加这两个公用的配置文件。

6. 新建database.yml文件

# 服务器上
cd ~/deployment/shared/config
vim database.yml

里面填写以下内容,当然,将password换成刚才装MySQL时自己填写的密码

production:
  adapter: mysql2
  pool: 25
  encoding: utf8mb4
  database: deployment_production
  host: localhost
  username: root
  password: itfun

7. 新建master.key文件

vim master.key

然后将自己本地项目config/master.key中的内容,复制进去。

8. 再试一次

# Mac 本地执行
cap production deploy:check

这次就没有报错了。

十五、正式部署

# Mac 本地
cap production deploy

第一次会比较慢,请耐心等待。这条命令,还会在服务器上建立一个叫current的目录,用symbolic link指向releases目录下最新的版本。

十六、配置nginx

1. 配置nginx支持passenger

# 服务器上
sudo vim /etc/nginx/nginx.conf
# 在此文件最顶部加上
env PATH;

# ...

# 去掉这一行的注释
include /etc/nginx/passenger.conf;

2. 新增一个项目配置

sudo vim /etc/nginx/sites-enabled/deployment.conf
server {
  listen 80;

  # 如果你有域名,并做好了域名解析,直接填域名。
  server_name 114.67.72.94; 

  root /home/deploy/deployment/current/public;

  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

十七、尝试访问项目

Rails部署报错

这是其实是有由两个原因造成的。 第一、因为我们项目,目前还连首页都没有。 第二、Passanger需要指定使用的Ruby路径。查资料了解到,如果是apt-get安装的Ruby则没有这个问题。如果和我一样,用rbenv安装的,则需要手动指定Ruby路径。

Tips: 如果部署中,你还碰到了其他问题。你可以看一下Nginx 错误日志中,是否有相关提示。路径在/var/log/nginx/error.log

1. Rails的首页

config/routes.rb中,添加

root 'home#index'
# Mac本地
rails g controller home

编辑app/controllers/home_controller.rb

class HomeController < ApplicationController
  def index
     render plain: "Capistrano 自动化部署"
  end
end

每次修改完代码后,需要部署上线,都是以下这么个流程

# Mac本地,先确认现在命令行,是在项目目录中。
# 提交到git
git add .
git commit -m "新增首页"
git push

# 重新部署
cap production deploy

2. 设置passanger,使用指定的 Ruby 版本

# 服务器上
# 先看一下ruby所在的路径
which ruby
sudo vim /etc/nginx/passenger.conf
# 屏蔽掉默认配置,添加自己的ruby路径
#passenger_ruby /usr/bin/passenger_free_ruby;
passenger_ruby /home/deploy/.rbenv/shims/ruby;
# 重启nginx
sudo service nginx restart

十八、成功

成功

共收到 44 条回复

@huacnlee 请帮忙掌掌眼。如果流程都还行,看是否能加到ruby china的wiki中? wiki中的部署相关的内容,都已经很有年代了。相信很多新手朋友们会需要这个。

嗯,网上很多资源写的不够详细,看了这篇文章,步骤写的很清楚,有空试试。。学习了,谢谢大神。

huacnlee 将本帖设为了精华贴 06月05日 12:05

写得很详细,学习一波

完全没有看到自动化在哪里

nouse 回复

最顶部,已经加了 “为什么要用它?”

18282906067 回复

感谢支持

holyzq 回复

谢谢支持

nouse 回复

这是一个一劳永逸的工作,有了它。你不用每次版本更新都去服务器上git pull,不用bundle install,不用编译静态文件,不用跑migartion,也不用跑 touch tmp/restart.txt之类的命令,更不需要还在用FTP上传代码了。每次新版本更新,只需要在你本机,运行一条cap production deploy命令。服务器就会自动git pull远程仓库的最新代码,并自动完成一系列部署相关的操作。

足够自动了。

相信玩Ruby On Rails的基本都是Mac OS用户......................还有 archlinux 用户。。。

Capistrano 我一直没明白优势在那?。。。目前用自己写的shell 脚本和jenkins 通过git 不同分支来自动化发布

不错,可是 cap 实在太慢了,比 mina 慢了太多

hfpp2012 回复

没有选Mina,只是考虑到了一个方面的问题。 Mina在Github的Star和Capistrano差距太大了,于是优先考虑了Capistrano。 Mina我也会试试,我自己服务器还没正式使用这种东西部署。 都试试了,我到时候选一个合适的。

canonpd 回复

mina挺好的😁

希望可以加上CircleCI或者jenkins之类的CI工具来自动化

canonpd 回复

如果虚拟机挂了呢,你的一到十四又要手工来一遍吗?

nouse 回复

本地已经配置过的,根本就不需要再重新配置了。哪里还有这么多步骤? 服务器只简单的装下nginx、mysql之类的基础环境就好了。

内容很详细,先赞一个

BUT 说不出来为啥,感觉有点乱,牵扯了略多和 cap 不是强关联的内容,如果打算做 wiki 的话建议再精简一下

另:一图胜千言,如果能把 cap 的部署流程画一画,也许不需要码这么多字,贴这么多截图(个人感觉)

IChou 回复

主要定位是,想给只是有一点点部署基础的新手同学们看。 所以完整的流程就写的非常细了。这些步骤倒是都没有多余的,基本每一行都是必须的。 东西确实有点太多了,容易搞晕。

IChou 回复

我有空,在录制一整套视频算了。分多个小节讲解。

另外 @nouse 问你 『完全没有看到自动化在哪里』你还没有 get 到他的点,哈哈哈~~~

我来帮你拓宽一下思路吧

  1. 你是使用的本机到服务器直连,不过在一个多人的团队里,一般会有一台专门用来发起部署命令的 虚拟机/跳板机/iop 以确保线上环境的安全性和部署配置的一致性,所以他会提到『虚拟机挂了』
  2. 线上有多个实例,配置如何管理就变得很重要了,比如这里有 14 台机器,要修改 database.yml 需要手动来操作么?
  3. 再复杂一点,由于这 14 台机器承担的是不同的角色,有些是 api server,有些是 sidekiq,配置可能还有点不一样
  4. 新加一个实例的时候,你需要做哪些操作,需要多久才能完成这些操作

所以 nouse 问你的,其实应该是想问你在真实『战场』环境下的『自动化』,对应你想表达的主题,其实有点超纲了 哈哈~

canonpd 回复

我来给点建议哈, 你可以把全文分成四个大的章节:

  1. 准备一个线上环境
  2. 准备一个 Rails 应用
  3. 安装配置 Capistrano
  4. 部署上线

然后画个时序图,哪些是本机操作,哪些是线上操作

完美~ (突然感觉当键盘指挥官有这么爽啊 可把我牛B坏了 叉会儿腰)

IChou 回复

这个好,就这样安排了。太复杂的,我还不敢继续写。毕竟暂时定位的新手学习,新手朋友们,先能把这些操作都搞明白就不错了。然后可以再进一步深入。 这篇只能算是带入门,抛砖引玉的内容。

canonpd Mina + Rails 5.2 自动化部署 中提及了此贴 06月06日 10:53
lengcb 回复

@lengcb @hfpp2012 Mina我已经试过了,感觉很满意,用起来也容易。 部署流程也写了,在这里 https://ruby-china.org/topics/36910 哪里有问题的,还请帮忙指正。

不是我自己卖瓜,参见 这里 如果还要到服务器上执行一大堆命令,那坑就多。。。

【ERROR linked file /home/deploy/deployment/shared/config/database.yml does not exist on 114.67.72.94 解决方法,是需要手动到服务器上,建config/database.yml,config/secrets.yml这两个文件,并做好配置。】

我们的做法是尽量不上服务器,把本地的文件 cap production deploy:upload_config 代码如下:

另外还尝试了通过另外一个 git 项目来管理配置文件, config/deploy.rb 的配置要稍微修改一下。

namespace :deploy do
 desc 'upload config files for application'
 task :upload_config do
    on roles(:web), in: :sequence, wait: 5 do
      fetch(:linked_files).each do |file_path|
        unless test "[ -f #{shared_path}/#{file_path} ]"
          upload!("#{file_path}", "#{shared_path}/#{file_path}", via: :scp)
        end
      end
    end
  end
end
sandy_xu 回复

我们的做法是有一个config.yml.default的文件保存线上环境配置,本地会有config.yml,这类的文件会在.gitignore中,发布的时候直接cp xxx.yml.default xxx.yml 这样不需要重新upload一个文件了

clarkyi 回复

问一下你们 线上正式服的 config.yml.default 存放在哪里呢?

我们所有线上正式服的配置不在代码项目里,项目里只有 .example 的测试账号数据,本地开发直接 cp 开箱使用测试账号数据。

Capistrano在执行bundle install时服务器CPU会爆满,请问这个问题怎么解决?

docker 不好吗

sandy_xu 回复

哦,那我们的正式服的配置都在同一个项目里面😂

不知道为什么,我点收藏没反应,哈哈。谢谢你的文章,学习了!

很好啊,还有错误提示的解释,比digital ocean的教程要详细。往后再弄个chef的服务器部署教程就更好了

不建议直接更改 /etc/nginx/passenger.conf 中的配置 在nginx中指定

passenger_ruby /home/deploy/.rvm/gems/ruby-2.5.1/wrappers/ruby;

太复杂了。建议用docker

alex_l_zhang 回复

docker 并不是银弹,我在开发环境用 docker 很久了,部署环境用 k8s 还是很痛苦。或者你发个帖子分享一下 docker 部署的经验?

我不喜欢这种教程.

  • 揉了太多没用的东西进去, 部署 rails 项目/ 使用 capistrano 部署 对新手来说本就没必要揉在一起做.
  • 只教怎么做而不进行说明依然不明白 cap 是做什么的, 稍微有一点变动这个就不通了..
  • 对 cap 和 rails 5.2 这俩关键字说明不够. 比如我如果在 rails 5.2 用了 webpacker 怎么处理. cap 的 role 是干嘛的. task 是怎么 hook 的.
OrderSun 回复

通常这个是因为 assets:precompile 导致的,应该不是每次都这样吧,只有在你修改了 js 或 css 的时候才会

如果是,可能是你没有把 /public 设置成 link_dir

flypiggys 回复

这个教程只是扫盲而已,高阶操作还是要靠使用者自己修行打怪

毕竟所有的姿势,都已经写在 Capistrano 的官方文档里了

PS: Capistrano 的文档真心不错,大多数场景都不需要去看源码了

IChou 回复

没错assets:precompile导致的,public设置了link_dir,每次编译大约需要5分钟,不知有什么好的解决方法?

目前解决方法是不使用capistrano编译,写脚本ssh进行编译,大约20s左右。

OrderSun 回复

不用 capistrano 就 20s, 用了就 5min? 明显是设置没对导致 cap 每次都在重新编译静态资源

打开 cap 的详细日志,看看是不是重新编译所有的静态资源

你还可以在 assets:precompile 开始的时候手动中断部署,再登录到服务器上最新一个 release 目录下检查原因

IChou 回复

有道理,感觉像是在重新编译所有静态资源。

项目的静态资源很大,可以用“庞然巨物”来形容~

OrderSun 回复

兔羊吐神波

可能你对『庞然巨物』还有点误解,之前有个项目,如果有人更新了前端基础资源,本地 rails s 之后出去喝杯咖啡回来大概就能正常访问了

IChou 回复

Really young.

Rei 回复

k8s还有ubuntu痛苦?自动维护高可用性,自动资源管理。k8s就是免费的vsphere,当然现在也就是vsphere的水平。

docker不是银弹,Go也不是。但是做web应用比Rails就是顺手多了。以前部署要会打包,会ssh,会chef。现在只要会做成多个不同进程,有人叫微服务,放入容器就好了。剩下的部署工作都是k8s或类似平台自带的,不需要操心

nouse 回复

这是一个部署在 gcloud 的配置例子 https://github.com/getcampo/campo-gcloud 看着不长,很多内容隐藏在链接和配置文件里面,需要很多前置知识,花了我几天时间才总结出来,然后发现没法给不了解的 k8s 的人介绍这要怎么用。k8s 管理容器,人要管理 k8s,可靠性和易用性不一定比现有云平台的服务好。

所以我说希望已经在生产环境用上 K8s 的人分享下经验,写一下教程就知道有多难。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册