用Mina部署Rails Application

Published about 1 year ago, last updated about 1 year ago

mina是一个用于自动化部署Rails应用的gem,配置好之后重新部署只需要在本地执行一条命令,相比于ssh登录到server之后敲一大段命令,自动化部署工具确实让人神情气爽。

不过这是我第一次部署rails app,前前后后还是折腾了一段时间,这篇文章也是为了以后遇上问题的时候可以用作参考。

目标

用Mina + puma + nginx 在digitalocean上部署一个rails app,server的系统是Ubuntu16.04

服务器端

依赖安装

Digitalocean新建的服务器是没有用户的,只有root,所以需要新建一个用户并且给予root权限。

用户建立之后,先安装Ruby的依赖

sudo apt update
sudo apt install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libmysqlclient-dev

其中sqlite3 是可选的,如果不打算用的话可以选择安装Postgresql或者MySql,我用的是Postgresql,这里附上一篇在Ubuntu16.04上安装Postgresql的教程。

Ruby版本控制

Ruby版本控制通常有两个选择

  1. rvm
  2. rbenv

两个都很好用,但是尽量保证本地和线上环境一致。这里介绍rbenv的用法

  1. 下载rbenv

    git clone https://github.com/rbenv/rbenv.git ~/.rbenv
    
  2. 添加$PATH

    echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
    

  3. 执行~/.rbenv/bin/rbenv init

  4. 执行source ~/.bash_profile 重启shell,并检查rbenv是否安装成功

    type rbenv
    #=> "rbenv is a function"
    

当rbenv安装成功之后,下载需要的Ruby版本,比如我用的是2.4.0

rbenv install 2.4.0

安装成功之后,按照自己的需求设置ruby版本

rbenv global 2.4.0 #=> set global ruby version
rbenv local 2.4.0 #=> set local ruby version
ruben shell 2.4.0 #=> set shell session ruby version

此外Rails依赖于Node.js

sudo apt install nodejs

Nginx

通过apt安装即可

sudo apt install nginx
配置Nginx

首先删除默认配置文件

sudo rm /etc/nginx/conf.d/sites-enabled/default

然后新建一个配置文件在/etc/nginx/sites-available/my_app.conf ,这里把my_app换成你的app的名字,对应的路径也全部替换掉。

upstream my_app { 
    server unix:///var/www/myapp/shared/tmp/sockets/puma.sock; 
} 
server { 
    listen 80; 
    server_name myapp.com; # change to match your URL 
    root /var/www/myapp/current/public; # I assume your app is located at this location 

    location / { 
        proxy_pass http://my_app; # match the name of upstream directive which is defined above 
        proxy_set_header Host $host; 
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    } 

    location ~* ^/assets/ { 
        # Per RFC2616 - 1 year maximum expiry 
        expires 1y; 
        add_header Cache-Control public; 

        # Some browsers still send conditional-GET requests if there's a 
        # Last-Modified header or an ETag header even if they haven't 
        # reached the expiry date sent in the Expires header. 
        add_header Last-Modified ""; 
        add_header ETag ""; 
        break; 
    } 
}

之后建立一个symlink

sudo ln -sf /etc/nginx/sites-available/my_app.conf /etc/nginx/sites-enabled/my_app.conf

重启Nginx即可

sudo service nginx restart

SSH key

mina会自动从你的remote repository pull更新的code,所以需要在添加一个公钥到github或是bitbucket。可以参考github的教程

ENV 变量

Rails在读取很多敏感信息的时候,都会用到一个ENV的hash,例如在在生产环境下读取SECRECT_KEY_BASE,会用到ENV["SECRECT_KEY_BASE"] , 设置这些变量有很多种方法,这里介绍一种非常简单的,就是把这些变量添加到.bash_profile 里。例如要存放SECRET_KEY_BASE 可以在.bash_profile 的最上方加上一句

export SECRECT=balabalabalabala

其他像DATABASE_PASSWORD 也类似。

本地

首先在你的rails项目的Gemfile里加上两个gem

gem 'mina', require: false
gem 'mina-puma', require: false,  github: 'untitledkingdom/mina-puma'

之后运行

bundle
mina init

mina 会在config文件夹下建立一个叫deploy.rb的文件,接下来我们要修改这个文件

deploy.rb

require 'mina/rails'
require 'mina/git'
require 'mina/rbenv'  # for rbenv support. (https://rbenv.org)
# require 'mina/rvm'    # for rvm support. (https://rvm.io)
require 'mina/puma'

# Basic settings:
#   domain       - The hostname to SSH to.
#   deploy_to    - Path to deploy into.
#   repository   - Git repo to clone from. (needed by mina/git)
#   branch       - Branch name to deploy. (needed by mina/git)

set :application_name, 'App' # 你的app的名称
set :domain, 'lalala.com' # 你的服务器的域名或是IP地址
set :deploy_to, '/www/var/lalala' # 部署的位置,保证自己有写的权限即可
set :repository, 'git@...' # 代码的地址,mina会自动从这个remote repo pull代码下来
set :branch, 'master'      # 
set :user, 'blog'          # Username in the server to SSH to. 之前在服务器上建立的用户

# Optional settings:
#   set :user, 'foobar'          # Username in the server to SSH to.
#   set :port, '30000'           # SSH port number.
#   set :forward_agent, true     # SSH forward_agent.

# shared dirs and files will be symlinked into the app-folder by the 'deploy:link_shared_paths' step.
# set :shared_dirs, fetch(:shared_dirs, []).push('somedir')
# set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml')
set :shared_dirs, fetch(:shared_dirs, []).push('log', 'tmp/pids', 'tmp/sockets', 'public/uploads')
set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml', 'config/puma.rb')

# This task is the environment that is loaded for all remote run commands, such as
# `mina deploy` or `mina rake`.
task :environment do
  # If you're using rbenv, use this to load the rbenv environment.
  # Be sure to commit your .ruby-version or .rbenv-version to your repository.
  invoke :'rbenv:load'

  # For those using RVM, use this to load an RVM version@gemset.
  # invoke :'rvm:use', 'ruby-1.9.3-p125@default'
end

在生产环境中,在有几个文件与本地是不同的,包括config下的database.yml, secrets.yml, puma.rb , 因此我们需要手工新建这几个文件

task :setup do
  command %[touch "#{fetch(:shared_path)}/config/database.yml"]
  command %[touch "#{fetch(:shared_path)}/config/secrets.yml"]
  command %[touch "#{fetch(:shared_path)}/config/puma.rb"]
  comment "Be sure to edit '#{fetch(:shared_path)}/config/database.yml', 'secrets.yml' and puma.rb."
end

我们需要手动把database.yml 里有关production的部分复制到服务器端的database.yml ,对于secret.yml 也一样。

接下来是deploy部分

desc "Deploys the current version to the server."
task :deploy do
  # uncomment this line to make sure you pushed your local branch to the remote origin
  # invoke :'git:ensure_pushed'
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_create'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    on :launch do
      invoke :'puma:phased_restart'
    end
  end

  # you can use `run :local` to run tasks on local machine before of after the deploy scripts
  # run(:local){ say 'done' }
end

之后我们需要配置puma.rb ,把文件中的path_to_your_app 换成你app的路径

puma.rb

environment "production"

bind  "unix://{path_to_your_app}/shared/tmp/sockets/puma.sock"
pidfile "{path_to_you_app}/shared/tmp/pids/puma.pid"
state_path "{path_to_your_app}/shared/tmp/sockets/puma.state"
directory "{path_to_your_app}/current"

stdout_redirect '{path_to_your_app}/shared/log/stdout', '{path_to_your_app}/shared/log/stderr', true

workers 2
threads 1,2

daemonize true

activate_control_app 'unix://{path_to_your_app}/shared/tmp/sockets/pumactl.sock'

prune_bundler

运行

当一切都配置好后,运行

mina deploy

如果是第一次启动,运行

mina puma:start

BOOM!


Comments

    No comment yet



Leave your comment