部署 无缝部署 (0 down time deploy) 的正确姿势是什么?大家是怎么部署新 Rails 代码上线的?

1c7 · July 03, 2018 · Last by 1c7 replied at February 22, 2019 · 9180 hits

0. 环境

Puma + Mina + Rails 5.2 + Ubuntu 16.04

1. 期望

部署新代码期间网站依然可用,而不是卡 10-30 秒
发帖子前我谷歌查了半天依然搞不定,想看看大家的做法。
目前服务器我用的是 Puma。phased_restart 已经配置了
用了 mina-puma gem:https://github.com/untitledkingdom/mina-puma

mina puma:phased_restart

跑如上命令也毫无问题。结果如下图:

2. 实际结果

phased_restart 期间,网站有 10 秒 -30 秒不响应

3. 问题

大家是怎么做无缝部署的?(0 downtime deploy)
如果有人用 puma 实现过无缝部署,能分享下经验吗?

4. 贴 2 个文件

  1. config/deploy.rb(Mina)
  2. config/puma.rb(Puma 配置文件)

config/deploy.rb

require 'mina/rails'
require 'mina/git'
require 'mina/rvm'    # for rvm support. (https://rvm.io)
require 'mina/puma'  # https://github.com/untitledkingdom/mina-puma

set :application_name, '因隐私原因删除掉原有值'
set :domain, '因隐私原因删除掉原有值'
set :deploy_to, '因隐私原因删除掉原有值'
set :repository, '因隐私原因删除掉原有值'
set :branch, 'master'

set :user, 'ubuntu'          # Username in the server to SSH to.
set :port, '22'           # SSH port number.
set :forward_agent, true     # SSH forward_agent.

# For mina-puma
set :pumactl_socket, '/tmp/witt.sock'
set :puma_state, "/var/www/witticism.com/shared/tmp/sockets/puma.state"
set :puma_pid, "/var/www/witticism.com/shared/tmp/pids/puma.pid"

task :remote_environment do
  invoke :'rvm:use', 'ruby-2.4.1@default'
end

desc "Deploys the current version to the server."
task :deploy do
  deploy do
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'deploy:cleanup'

    on :launch do
      in_path(fetch(:current_path)) do
        command %{mkdir -p tmp/}
        command %{touch tmp/restart.txt}
      end
    end
  end
end

config/puma.rb

bind "unix:///tmp/witt.sock"

threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
environment ENV.fetch("RAILS_ENV") { "production" }

app_name = "witticism.com"
app_dir = File.expand_path("../..", __FILE__)
app_dir_parent = File.expand_path("../../", __FILE__)
application_path = "#{app_dir}"

pidfile "/var/www/witticism.com/shared/tmp/pids/puma.pid"
state_path "/var/www/witticism.com/shared/tmp/sockets/puma.state"

workers ENV.fetch("WEB_CONCURRENCY") { 2 }

before_fork do
  ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
end

on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end

与此同时我也会继续查谷歌,如果有结果了会回来说

Reply to ny

感谢回复,现在去看

swarm mode rolling update

如果你有多台机子,分批部署😂

有一个大胆的想法:

假设原来 puma 在 3000 端口运行

nginx 负载均衡:3000 和 3001 端口

然后 deploy 到 3001 端口,成功后,然后 kill 3000 端口的 puma,kill -9 $(lsof -i:#{3000} -t)

同理,下次检测到 3001 端口被占用,则先部署到 3000 端口,然后kill -9 $(lsof -i:#{3001} -t)

从部署日志上看,你发送的是 TERM 信号,让进程先全部挂了,然后才重新启动。在进程挂掉到新的进程启动完成的过程中,你的服务肯定会有中断的。给进程发送 USR2 信号,puma 本身是支持平滑重启的,https://github.com/puma/puma/blob/master/docs/signals.md

多谢各位~~(我有别的事情就把这茬忘了,隔了一天才回来看到各位的回帖,非常感谢) 这些我都看看细节去,最后有结果了回来说~

Reply to tinyfeng

ruby china 的 docker 部署好像就是这么做的

dokku 可以 0 down check,成功后再切换 proxy

建议你先好好读一下官方(比较权威)的文档说明和源代码里关键配置,不容易理解的地方,再 Google 进行补充。

puma 配置示例
mina-puma README
mina-puma tasks

你上面贴的配置,有的非常不合适,配置了还不如不配置(使用默认);
还有一些是错误配置。

Reply to leekelby

好~ 谢谢

2018-8-3 更新进展

我碰到了什么问题

当初之所以问这个问题,是因为在 mina deploy --verbose 部署成功后用 mina puma:phased_restart 重启服务器 (我用了 mina gem + mina-puma gem)

然后(重点来了)服务器上 puma 会占用 100% CPU 持续 2-8 分钟才更新成功。
(我用 htop 命令看的)

我有两台机子,都在 UCloud,都是 CPU 2 核,内存 4G。
在机器 A(测试环境)上完全没问题。phased_restart 只需 3 秒。一切工作完美。
在机器 B(生产环境)上,不论我怎么查谷歌来试图跟踪问题,就是搞不定(具体细节可参考末尾给出的链接)
最后决定不折腾这个问题了

我是怎么解决的

最后用 nginx load balancer 来指向 2 个 puma 里其中一个,每次要更新了就更新另一个,然后指向那个新的。
这样来回交替。
我写了篇博客:http://1c7.me/2018-8-3-solve-rubyonrails-puma-server-phased-restart-100percent-cpu-problem/
和 Github Issue:https://github.com/puma/puma/issues/1627

希望这篇回复能帮助以后遇到同样问题的人。

Reply to leekelby

宽哥 其实配置两个进程 依次 Restart 就好了

Reply to 1c7

puma 启动时 100% cpu 的问题,检查一下 bootsnap , 这个会有影响。

Reply to lyfi2003

感谢新思路。但不知道具体怎么"检查"。Gemfile 里的确用到了 bootsnap。官方 README 也大概看了下。没有特别提到什么要注意的。

Reply to 1c7

先移掉,再试试是否加快了。

2019 年进展更新:使用了 Docker。一劳永逸的解决了部署问题。 不再需要折腾 puma 的 phased_restart 或者 nginx 的 load balance 来变相达到这种效果。 目前生产环境已使用 Docker Swarm。

You need to Sign in before reply, if you don't have an account, please Sign up first.