Capistrano 是一个使用 Ruby 构建的自动化部署工具,它除了可以部署 Ruby 应用外,也支持部署 PHP, Python 等其他语言的应用。下面就让我们开始学习如何使用 Capistrano 部署 Rails 应用吧~
搭建服务器环境
我们目标是将 Rails 应用部署到服务器 deploy 用户下。当然你也可以部署到其他地方,部署流程大同小异:
- 登录服务器添加 deploy 用户。
- 切换 deploy 用户并生成 ssh 公钥,上传到代码托管平台(如 github、gitlab),因为 Capistrano 默认通过 ssh 拉取我们的代码。
- 安装 ruby 环境并安装 bundler。
- 安装 node 环境,如果 Rails 应用使用了 Webpack 编译资源,还需要安装 yarn。
- 安装数据库并启动。
通过以上流程,我们搭建了 Rails 应用的基本部署环境。下面我们需要准备一个 Rails 应用并为其集成 Capistrano。
准备 Rails 应用
如果已经有了需要部署的 Rails 应用,可以跳过本章节。
让我们创建一个 Rails 应用,启用 Webpack 编译资源并加入 react 前端库:
1 | $ rails new capistrano-demo --webpack=react |
当前代码可看: capistrano-demo:v0.0.1
“Capify” Rails
集成 Capistrano 环境
首先我们需要为 Rails 应用集成 Capistrano 环境。
将 capistrano 和 capistrano-rails 这两个 gem 加入到 Gemfile 中。
如果前面服务器通过 rvm 或者 rbenv 安装 ruby 环境,还需要加入相应的 capistrano-rvm 或者 capistrano-rbenv gem,以让 Capistrano 在服务器部署时找到相应的 ruby 环境。
如果应用使用 Puma 或者 Passenger 等服务部署,还可以使用 capistrano3-puma 或 capistrano-passenger 等 gem 管理 Web 服务。
下面是我的 Gemfile:
1 | # Gemfile |
执行 $ bundle
安装依赖,之后我们就可以通过 $ cap -T
来查看 Capistrano 任务。
编写 Capfile
执行以下命令生成 Capfile:
1 | $ cap install |
然后 Capistrano 会生成以下文件:
1 | ├── Capfile |
其中 Capfile
作为入口文件,用于引入依赖库。 config/deploy.rb
是各部署环境的通用配置,而 config/deploy/
下的文件则是针对各环境的具体配置, lib/capistrano/tasks/
可自定义些 rake 任务便于部署。
之后我们就要编写 Capistrano 配置文件
1 | # Capfile |
1 | # config/deploy.rb |
1 | # config/deploy/production.rb |
编写好配置文件后,使用下面命令检查部署流程:
1 | $ cap production deploy --dry-run |
之后你可以在终端中看到 Capistrano 处理步骤,如果各步骤没有,那就开始我们的部署吧:
1 | $ cap production deploy |
当部署成功后,你可以看到服务器 deploy 用户多了 capistrano-demo 目录,其结构类似下面:
1 | ├── current -> /home/deploy/capistrano-demo/releases/20190927081948 |
其中 releases
保存了每次部署的代码库,current
指向了最新的代码库, repo
含有最新代码库的 git 信息, shared
是共享文件。
当前代码可看: capistrano-demo:v0.1.0
上传私密文件
如果是首次使用 Capistrano 部署,应该很快会得到一些错误。比如: master.key 不存在? database.yml 不存在?
因为上面这些文件不入版本库,Capistrano 拉下的仓库并没有这些文件,其做软连接到这些文件就会报错了。
为了解决这个问题,我们需要将本地的这些私密文件上传到服务器。
创建数据库
部署还有错误,Capistrano 执行迁移任务时没有找到数据库?
因为我们首次部署时,数据库并不存在。而 Capistrano 可能出于安全还是低频使用的考虑,并没有为此增加相关任务,我们需要连接服务器手工创建数据库。
Capistrano & Puma
Capistrano 可以通过 capistrano3-puma
扩展原本的部署任务,增加些管理 Puma 的任务。
使用下面的命令启动服务器的 Puma:
1 | $ cap production puma:start |
如果需要修改 Puma 的配置,则执行:
1 | $ cap production puma:config |
其会上传修改后的 Puma 配置文件至服务器,然后重启就可。
服务器 Puma 配置文件在 shared 文件夹,我们配置 NGINX 需要使用其中的信息。
Capistrano & Sidekiq
如果 Rails 应用中使用了 Sidekiq 作为后台处理, 我们还可以通过集成 capistrano-sidekiq 管理服务器中的 Sidekiq。
使用下面的命令就可启动服务器的 Sidekiq:
1 | $ cap production sidekiq:start |
当前代码可看: capistrano-demo:v0.1.1
添加 NGINX 配置
为我们部署的应用添加 NGINX 配置:
1 | upstream capistrano_demo { |
重启 NGINX,查看是否能正常访问我们的应用~
Note: 当我们应用使用 unix 协议时, NGINX 进程所属用户可能没有权限访问我们部署目录,这时候将会获得 403,我们需要在 NGINX 配置文件中调整 NGINX 进程用户或者将应用改成 tcp 协议。 没有权限这是由于我们部署到 deploy 用户上 home 目录下,须确保 NGINX 用户可读到 deploy 的 home 目录。
Capistrano 部署流
Capistrano 从部署到回滚都有自己的工作流,我们可以在工作流中通过钩子,添加自己的定制任务。
下面是 Capistrano 部署流:
1 | deploy:starting - start a deployment, make sure everything is ready |
而 Capistrano 回滚流如下:
1 | deploy:starting |
如果需要在每次部署完,重启 sidekiq,我们可以在 deploy:finished
钩子中添加自己的任务:
1 | task :restart_sidekiq do |
Q&A
Rails 5.2 使用 Credentials 机制加密 secrets.yml,如果在部署过程中 bundle install 出现 ActiveSupport::MessageEncryptor::InvalidMessage,该怎么办?
这是问题多半是我们修改了 config/master.key,导致解密 credentials.yml.enc 错误引起的,此时需要重新生成这两个文件,参考 How to regenerate the master key for Rails 5.2 credentials为什么 NGINX 默认 nobody 用户开启, TCP/IP domain sockets 获取资源没有权限问题, 而 UNIX domain socket 有权限问题?
因为 UNIX domain socket 需要读取 sock 文件, 须确保 NGINX 进程用户有权限读取到此文件。
参考
- linux命令之远程登录/无密码登录-ssh,ssh-keygen,ssh-copy-id
- Capistrano 3 实现 Rails 自动化部署
- 從零到有,用 Capistrano 將 Rails 專案部署自動化
- RVM 实用指南
- How to regenerate the master key for Rails 5.2 credentials
- Cannot deploy an application using Rails 5.1 Webpacker
- Rails 5.2 + Puma + Capistrano3 + Nginx + Sidekiq 自动化部署
- Sidekiq 精通 36 分钟
- How can I serve assets in /public that are not part of the asset pipeline with puma/nginx?
- What’s the difference between Unix socket and TCP/IP socket?
- TCP/IP Socket和UNIX Socket区别