Deploy Ruby on Rails App on Ubuntu 18.04

我的收费视频里,第四期是如何布署Ruby on Rails在Ubuntu 16.04上,我觉得可能是我没讲清楚,结果很多同学照着视频也没布署成功,Linux功底越深厚的同学,布署成功的几率越高。

因为那次是我第一次录,所以经验不足,这次经验足了,希望是吧 🙂

按照这两周问的问题,我觉得主要有以下几个主要问题导致布署失败。

第一类:买了vps却无法登录的问题

这个问题几乎是无解的,一种是这个IP已经被组织上判了无期,组织在,就别想出来。另一种是你购买的IP是IP v6,国内不少运营商还没有打算布署IP v6。

第二类:分不清楚命令到底在自己电脑上执行还是在远程服务器上执行 

这个问题,我这次录的时候我会着重强调一下,有的是在自己电脑上执行,有的是在服务器上执行,我这次录的时候会标出来。

第三类:命令不执行怎么办?

这种情况基本上是你输入错了,比如你输入了$,这个符号是terminal的提示符,不用输入。

第四类:我好像布署成功了,为什么在其它电脑上看不到?我电脑开着啊!

127.0.0.1这个IP地址是本机地址,其它电脑上输入这个地址看不到。

布署开始,请小心输入命令

第一步:搞定一台服务器

这台服务器可以是你购买的网上的vps,比如如果你想相买vultr.com的话可以用我的推荐链接。如果你的地区用vultr比较慢的话,可以再试试linode.com,作为网络难民,我也有linode的推荐链接。个人觉得linode的每个月5美元的,机房在Fremont的还是可以的,这是我主要了解帝国主义的途径。

如果你不想购买,只想做做练习什么的,可以使用虚拟机。

第二步:在服务器上创建一个用户

这个用户是用来布署Ruby on Rails的,用服务商提供给你的root和密码,就可以登录上去,记住,除了这次,不要再用root登录了,root用户比较危险。然后执行如下命令,生成一个可以使用sudo命令的用户,我最喜欢的用户名,没有之一,叫treenewbee

sudo adduser treenewbee
sudo adduser treenewbee sudo
su treenewbee

注: 以上代码请一行一行执行,不想打字就复制粘贴,以后不再提示

第三步:在本机上执行,使treenewbee这个用户可以自动登录

ssh-copy-id treenewbee@IP_Address

第四步:在服务器上安装Ruby 2.5.1

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev nodejs yarn

以上安装的软件,都是一些必要的软件,有nodejs,有git,sqlite等等。

然后再执行下面的这些命令安装rbenv

cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL

rbenv install 2.5.1
rbenv global 2.5.1
ruby -v

安装完成这些以后,最后安装bundler并执行一条命令让rbenv rehash一下

gem install bundler
rbenv rehash

第五步:在服务器端安装Nginx和Passenger

在这里用的是Nginx这个软件来处理HTTP请求,Nginx这个软件是俄罗斯程序员一个叫伊戈尔·赛索耶夫的人写的,这也算是一个精彩的故事吧,以后有机会我想在电台上讲一讲这个家伙,这个加在todo列表中。

当我们的请求发送给Nginx以后,这个Nginx转手就给了应用服务器Phusion Passenger, 这个Passenger最初宣称的目标是要让部署Rails和部署PHP一样简单。这个目标暂时还没有实现。栋哥认为,没有实现的原因并非是PHP比Rails优秀,而是PHP用的多,用PHP开发的软件也多,这些用PHP开发的软件都有相应的机制来处理部署相关的问题,比如wordpress,本身就有一套机制来处理配置,因此才有了5分钟搭建一个wordpress博客,但是,如果不是用已经写好的软件,而是用PHP硬撸,PHP也同样会遇到现在Ruby和Python相对比较难布署的问题,这个要时间来解决,不是一朝一夕能完成的。

Passenger同样有众多的竞争者,有Puma,Thin,Unicorn。我这里选择Passenger的原因是,这家公司看起来比较进取,现在不止支持Rails的布署,还支持Python和Nodejs的布署,像是个做实事的公司。希望我没有看走眼。这几个软件也是相互借鉴吧。

Phusion这家公司已经出了一个官方的源,分别为Ubuntu等主要发行版本提供软件安装,这样的话,可以相对容易的进行安装,要执行以下的命令,如果要安装其它Linux版本,可以去官方网站上找一下,这里只管Ubuntu的:

sudo apt-get install -y dirmngr gnupg
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 bionic main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

sudo apt-get install -y nginx-extras libnginx-mod-http-passenger

if [ ! -f /etc/nginx/modules-enabled/50-mod-http-passenger.conf ]; then sudo ln -s /usr/share/nginx/modules-available/mod-http-passenger.load /etc/nginx/modules-enabled/50-mod-http-passenger.conf ; fi
sudo ls /etc/nginx/conf.d/mod-http-passenger.conf

执行完上面的这些命令以后,就已经安装了nginx和passenger,这时候还要启动nginx完成安装。

sudo service nginx start
# 如果要重启或者停止nginx,可以用下面的命令
sudo service nginx restart
sudo service nginx stop

如果nginx能够成功的没有报错就启动了,说明没什么问题。那么如何才知道上面我们安装的passenger也没什么问题呢?执行下面这条命令:

sudo /usr/bin/passenger-config validate-install

如果这两个都没有什么问题,可以打开自己的浏览器,输入IP地址试试。如果这一步也通过了,可以再进行下一步了。

如前文所说,passenger是和Nginx集成的软件,不但可以和Nginx集成,还可以与Apache集成(这闭着眼也能猜出来吧 🙂 那么,肯定要配置一下Nginx的。使用vim编辑器对文件进行修改,主要是告诉Nginx用的是哪个版本的ruby

sudo vim /etc/nginx/conf.d/mod-http-passenger.conf

如果是使用的rbenv,修改这样一行就行了:

passenger_ruby /home/treenewbee/.rbenv/shims/ruby;

然后,按照惯例,还要让Nginx重启一下:

sudo service nginx restart

至此为止,如果运气好,没搞错什么的话,Nginx和Passenger已经可以正常运行了。只是,现在还没有什么东西让它去运行,所以不要着急。在配置Capistrano的部分会再进行进一步的设置。

第六步: 在服务器端安装MySQL

数据库这事,我最会用的就是MySQL,如果大家有MySQL的问题,我还能回答一些,有些人用PG的话,我也用过一些,但是那些问Oracle的,NoSQL的,我也不会,没用过。我只负责介绍MySQL。

MySQL是一个开源的数据库,关于数据库,是一门高深的学问,幸亏在这里只要懂一点皮毛也能做下去,主要是建立数据库的用户和建个数据库给项目使用。

sudo apt-get install mysql-server mysql-client libmysqlclient-dev

这样安装完成以后,并不安全,所以还要执行:

sudo mysql_secure_installation

安装的过程会有root弹出,可以使用这个用户名和设置的密码再进行进一步的设置。比如建一个单独的用户和一个单独的数据库。

在使用如下命令登录MySQL以后

sudo mysql -u root -p

因为Ubuntu 18.04上的MySQL的版本已经是5.7了,5.7版本的MySQL用的是auth_socket而不是mysql_native_password,这个增加了安全性,但是增加了一些麻烦,我想换回去用。

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'RussianW0rdcUp&';

新建一个数据库:

 CREATE DATABASE lmzdx;

最后再执行:

FLUSH PRIVILEGES;

这样,数据库部分就安装好了。当然也可以再建一个mysql用户,再分配相关的权限,都是可以的。最重要的是,不要把用户名和密码给忘记了,这个还要用呢。

有一些人会问我,为什么用root来登录mysql呢,为什么不新建一个用户呢?其实这个,都是可以的,只要你觉得好,没什么不可以尝试的,我的方法又不是最好的。

第七步:在开发机上配置Capistrano

Capistrano是个什么软件?简单来说,是一个用Ruby写的可以自动布署的软件。

和我当年一样,我觉得自动布署这种东西,有必要这么复杂么?让我自己写shell脚本,用不了5行代码就搞定了,一行取代码,一行重启代码,再加一行失败后回滚的代码。你看,总共三行,多么简洁啊!实际上,这也对,尤其是项目很小的时候,用个FTP软件就完成部署了 🙂 但是,将来的麻烦在这里:

要是将来多台服务器同时部署怎么办?那些bundle install,bundle update总得加到shell代码里吧?那些有密码存在的文件总不能就直白的写到shell里吧?符号链接还是需要的吧?网络共享文件的链接能不要吗?migrate数据库也要吧?重启异步任务和定时任务也要加到写的shell代码里吧?缓存管理和清理也得考虑吧,打发布标记也要自己写shell脚本吧?……

最后,当初认为5行代码就能搞定的shell脚本,就扩充到几百数千行了,万一出错,自己写的那些脚本,按下葫芦浮起瓢,每次布署,得花大量的精力去维稳,还不如早点用自动布署工具,并不是说布署工具都是好的,Capistrano和Mina这些自动布署工具的确能减少一些麻烦。相比于手工写shell,手工用FTP布署,用自动布署工具可以说是最不坏的一个方法。

要理解Capistrano做了一件什么事情:在服务器上保存一份和开发机上的git代码仓库,并且会自动维护一些咱们设定好的文件夹,这些文件夹包括发布目录在哪,哪些目录有敏感信息(比如密码)不要上传,哪些文件夹的东西不要动,比如有图片这些静态的资源,不用每次重新发布一次。还有发布失败以后,能够回退到以前的版本。

首先要安装Capistrano,在Gemfile中加入这些包:

gem 'capistrano', '~> 3.11'
gem 'capistrano-rails', '~> 1.4'
gem 'capistrano-passenger', '~> 0.2.0'
gem 'capistrano-rbenv', '~> 2.1', '>= 2.1.3'

布署之前,要先生成一个rails项目

rails的版本进化相对来说比较快,我录第一个布署视频的时候是5.2,结果现在已经是5.2.1了,按照惯例,升级小数点最后一位的版本一般问题不大,如果是升级为5.3可能有比较大的变化,如果升级成6.0,变化就更大。

与时俱进吧,我用5.2.1版本来生成一个小项目吧。(详细见视频)

视频的内容主要是生成了一个rails项目,并把该项目推送到github上。

.       .......................................................       .
.   o   \ o /  _ o        __|    \ /     |__         o _  \ o /   o   .
.  /|\    |     /\   __\o   \o    |    o/     o/__   /\     |    /|\  .
.  / \   / \   | \  /) |    ( \  /o\  / )    |   (\  / |   / \   / \  .
休息一下................................................................

修改Gemfile文件,这些文件只用在development环境下安装就行

group :development do
gem 'capistrano', '~> 3.11' gem 'capistrano-rails', '~> 1.4' gem 'capistrano-passenger', '~> 0.2.0' gem 'capistrano-rbenv', '~> 2.1', '>= 2.1.3'
end

然后再运行bundle命令进行安装上面的gem

bundle
bundle --binstubs

然后再生成capistrano的配置文件

cap install STAGES=production

生成了这些配置文件以后,再对这些配置文件进行配置,首先修改Capfile文件:

require 'capistrano/rails'
require 'capistrano/passenger'
require 'capistrano/rbenv'
set :rbenv_type, :user
set :rbenv_ruby, '2.5.1'

再对生成的config/deploy.rb文件进行配置

set :application, "lmzdx2"
set :repo_url, "git@example.com:liuyandong/lmzdx2.git"
set :deploy_to, '/home/treenewbee/lmzdx2'
append :linked_files, "config/database.yml", "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/system", "public/uploads"

还有一个文件要配置config/deploy/production.rb

server '45.79.84.153', user: 'treenewbee', roles: %w{app db web}

第八步: 在本地开发机上隐藏一些敏感信息

在部署的时候,肯定不想让一些敏感信息上传到github上,在这个项目里,主要有两个,一个是config/database.yml, 另一个是config/master.key,后者在生成rails项目的时候已经加入了.gitignore文件,但是前者没有,因此,手动撸一发就O了。

echo "config/database.yml" >> .gitignore
git add .gitignore
git mv config/database.yml config/database.yml.sample
git commit -m "Only store sample database config"
cp config/database.yml.sample config/database.yml

修改完以后,记得再提交到github上。

git add .
git commit -m "configure Capistrano"
git push

第九步:在本地机器上执行命令布署

终于到了最后部署的时候了,要知道,因为还有很多的错误还存在,不要让部署给搞的束手无策,慢慢来,至少要遇到5-6次错误,慢慢一个一个解决掉就是了。部署的命令是:

cap production deploy

遇到的第一个错误:我把github.com的地址写错了

解决方法:在config/deploy.rb文件里的地址改成github就可以了。

遇到第二个错误:服务器没有权限使用github

解决方法:将服务器的ssh key加入到github上就可以了。

遇到第三个错误:服务器上找不到database.yml

解决方法:建一个database.yml就可以了,因为使用的是mysql,用户名和密码用你设置的那个就好。现在capistrano已经上传了一部分文件,这时候要在服务器上建立这个文件。目录在服务器的/home/treenewbee/lmzdx2/shared/config这个目录下,在建立的database.yml中输入如下内容:

production:
  adapter: mysql2
  database: lmzdx
  username: root
  password: RussianW0rdcUp&

遇到第四个错误:服务器上找不到master.key文件

解决方法:将该文件通过scp类似的方法copy过去就行。这个文件非常重要,最好不要示人。

scp config/master.key treenewbee@45.79.84.153:/home/treenewbee/lmzdx2/shared/config

遇到第五个错误:找不到JavaScript库

解决方法:因为Ruby on Rails要将CoffeeScript转换为JavaScript,所以要用到nodejs这个库,只要在服务器端安装就可以了。

sudo apt install nodejs

至此为止,已经没有任何错误了,只需要再改变几个小地方就可以了。

第十步: 配置Nginx

现在Rails的App已经部署好了,但是,Nginx现在还不知道App布署在什么地方,所以,还要告诉它才可以。还是用配置文件的方式。用编辑器打开/etc/nginx/sites-enabled/default文件,做如下的修改:

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        server_name lmzdx.net;
        passenger_enabled on;
        rails_env    production;
        root         /home/treenewbee/lmzdx2/current/public;

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
}

然后再执行命令让Nginx重启

sudo service nginx restart

第十一步:部署小测试

经过了上面的十个步骤,如果不出问题的话,其实已经完成任务了,接下来我布署一个小任务测试一下,(见视频)

先在开发机上完成一个小功能,只是测试,我先把这个lmzdx2项目的数据库设计好,然后使用scaffold生成一个和手机品牌的模型(只能有自己的名字),用户可以对手机品牌名称进行增删查改。

1. 设置数据库
2. rails g scaffold Phone name
3. 修改routes文件, 添加一条路由root to:"phones#index"

在本地完成测试功能以后,就把代码提交到github上

git add .
git commit -m "test deploy"
git push

然后执行一条命令

cap production deploy

当执行完上面这条命令以后,会显示成功,这时候我们可以在浏览器上输入IP地址或者域名 lmzdx.net 

在视频里,我会再演示一下更新一个小功能,然后用一条命令就能布署到服务器上。自动布署是一个第一次麻烦,以后会一劳永逸的操作。

我的公众号: 软件那些事儿

如果你觉得有用,可以关注我的公众号,因为我写文章在网上收不到钱,如果你能帮忙点击一下公众号里的广告就再好不过了。

《Deploy Ruby on Rails App on Ubuntu 18.04》有8个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注