如何部署Rails应用程序使用Git Hooks的Ubuntu 14.04

在本教程中,我们将向您展示如何使用Git的挂钩自动Rails应用程序到远程Ubuntu 14.04服务器的生产环境中的部署。使用Git钩子可以让你通过简单地推你的变化到生产服务器上部署应用程序。这种特殊的设置使用一个简单的“后收到”混帐挂钩,除了Puma作为应用服务器,Nginx的作为反向代理Puma,和PostgreSQL作为数据库。

介绍

在本教程中,我们将向您展示如何使用Git钩子自动将Rails应用程序的生产环境部署到远程Ubuntu 14.04服务器。 使用Git钩子将允许您通过简单地将更改推送到生产服务器来部署应用程序,而不必手动拉并执行数据库迁移。 在继续处理应用程序时,设置某种形式的自动部署(例如Git钩子)将为您节省时间。

这个特定的设置使用一个简单的“post-receive”Git钩子,除了Puma作为应用服务器,Nginx作为Puma的反向代理,PostgreSQL作为数据库。

如果你是新来的Git钩子和想了解更多的行动之前,请阅读本教程: 如何使用Git的鱼钩来自动化开发和部署任务

先决条件

您将需要访问在Ubuntu服务器上具有超级用户权限的非root用户。 在我们的例子设置中,我们将使用一个叫做用户deploy 本教程将告诉您如何设置了: 初始服务器设置与Ubuntu 14.04 如果要在不输入密码的情况下进行部署,请确保设置SSH密钥。

您需要在服务器上安装Ruby。 如果你还没有这样做的话,你可以将它与使用Rails安装rbenvRVM

您还需要有一个在本地开发机器上的git存储库中管理的Rails应用程序。 如果你没有一个,并且想跟随,我们将提供一个简单的示例应用程序。

让我们开始吧!

安装PostgreSQL

大多数生产Rails环境使用PostgreSQL作为数据库,所以让我们现在在你的服务器上安装它。

生产服务器,更新apt-get的:

sudo apt-get update

然后使用这些命令安装PostgreSQL:

sudo apt-get install postgresql postgresql-contrib libpq-dev

注意:您也应该在开发机器上安装PostgreSQL,这样你就可以安装pgGem,PostgreSQL的适配器,本地。 这将需要运行bundle install时,我们添加了Gem到应用程序的Gemfile中。 由于安装步骤因操作系统而异,这是一个练习留给读者。

创建生产数据库用户

为了简单起见,让我们将生产数据库用户命名为应用程序名称。 例如,如果您的应用程序被称为“appname”,您应该创建一个PostgreSQL用户,如下所示:

sudo -u postgres createuser -s appname

我们要设置数据库用户的密码,因此输入PostgreSQL控制台如下:

sudo -u postgres psql

然后在示例中设置数据库用户的密码,“appname”,如下所示:

\password appname

输入所需的密码并确认。

使用以下命令退出PostgreSQL控制台:

\q

现在,我们准备好使用正确的数据库连接信息配置您的应用程序。

准备您的Rails应用程序

开发计算机上,最有可能在本地计算机中,我们将为您准备部署应用程序。

可选:创建Rails应用程序

理想情况下,您已经有一个要部署的Rails应用程序。 如果是这种情况,您可以跳过本小节,并在进行适当的替换。 如果没有,第一步是创建一个新的Rails应用程序。

这些命令将在我们的主目录中创建一个新的Rails应用程序,名为“appname”。 随意替换突出显示的“appname”用别的东西:

cd ~
rails new appname

然后切换到应用程序目录:

cd appname

对于我们的示例应用程序,我们将生成一个scaffold控制器,所以我们的应用程序将有一些显示:

rails generate scaffold Task title:string note:text

现在让我们确保我们的应用程序在一个git存储库。

初始化Git Repo

如果您的应用程序由于某种原因不在Git存储库中,请初始化它并执行初始提交。

开发计算机上,更改为您的应用程序的目录。 在我们的示例中,我们的应用程序称为“appname”,它位于我们的主目录中:

cd ~/appname
git init
git add -A
git commit -m 'initial commit'

现在让我们调整我们的应用程序,准备连接到我们的生产PostgreSQL数据库。

更新数据库配置

开发计算机上,如果你是不是已经有改变应用程序的目录。 在我们的示例中,我们的应用程序称为“appname”,它位于我们的主目录中:

cd ~/appname

现在打开您喜欢的编辑器中的数据库配置文件。 我们将使用vi

vi config/database.yml

寻找你的应用程序的数据库配置的生产工段,并与生产数据库连接信息替换它。 如果你完全遵循了示例设置,它将看起来像这样(在适当的情况下替换任何值):

config / database.yml摘录
production:
  <<: *default
  host: localhost
  adapter: postgresql
  encoding: utf8
  database: appname_production
  pool: 5
  username: <%= ENV['APPNAME_DATABASE_USER'] %>
  password: <%= ENV['APPNAME_DATABASE_PASSWORD'] %>

保存并退出。 这指定应用程序的生产环境应在本地主机(生产服务器)上使用名为“appname_production”的PostgreSQL数据库。 请注意,数据库用户名和密码设置为环境变量。 我们将在以后在服务器上指定。

更新Gemfile

如果您的Gemfile还没有PostgreSQL的适配器的Gem, pg ,并指定了Puma的Gem,你现在应该添加它们。

在您喜欢的编辑器中打开应用程序的Gemfile。 我们将使用vi在这里:

vi Gemfile

将以下行添加到Gemfile中:

Gemfile摘录
group :production do
  gem 'pg'
  gem 'puma'
end

保存并退出。 这指定production环境中应该使用pgpuma的Gem。

配置Puma

在配置Puma之前,您应该查找服务器具有的CPU核心数。 你可以很容易地在服务器上使用这个命令:

grep -c processor /proc/cpuinfo

现在,你的开发机器上,添加Puma配置config/puma.rb 在文本编辑器中打开文件:

vi config/puma.rb

将此配置复制并粘贴到文件中:

config / puma.rb
# Change to match your CPU core count
workers 2

# Min and Max threads per worker
threads 1, 6

app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"

# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env

# Set up socket location
bind "unix://#{shared_dir}/sockets/puma.sock"

# Logging
stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true

# Set master PID and state locations
pidfile "#{shared_dir}/pids/puma.pid"
state_path "#{shared_dir}/pids/puma.state"
activate_control_app

on_worker_boot do
  require "active_record"
  ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
  ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env])
end

的数字更改workers到你的服务器的CPU内核的数量。 该示例假设您有2个内核。

保存并退出。 这将配置Puma与应用程序的位置,其套接字,日志和PID的位置。 随意修改文件,或添加您需要的任何其他选项。

提交您最近的变更:

git add -A
git commit -m 'added pg and puma'

继续之前,请生成将用于应用程序生产环境的密钥:

rake secret
29cc5419f6b0ee6b03b717392c28f5869eff0d136d8ae388c68424c6e5dbe52c1afea8fbec305b057f4b071db1646473c1f9a62f803ab8386456ad3b29b14b89

您将复制输出,并用它来设置你的应用程序的SECRET_KEY_BASE下一步。

创建Puma Upstart脚本

让我们创建一个Upstart init脚本,以便我们可以轻松地启动和停止Puma,并确保它将在启动时启动。

生产服务器 ,请从PumaGitHub的库主目录的丛林Upstart的工具:

cd ~
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma-manager.conf
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma.conf

现在打开提供puma.conf文件,这样我们就可以配置Puma部署用户:

vi puma.conf

寻找指定的两条线setuidsetgid ,并与您的部署用户和组的名称替换“应用”。 例如,如果您的部署用户名为“deploy”,则行应如下所示:

puma.conf摘录1 of 2
setuid deploy
setgid deploy

现在看与此行: exec /bin/bash <<'EOT' 在它下面添加以下行,确保替换PostgreSQL用户名和密码,以及您之前创建的rake密钥:

puma.conf摘录2之2
  export APPNAME_DATABASE_USER='appname'
  export APPNAME_DATABASE_PASSWORD='appname_password'
  export SECRET_KEY_BASE='rake_secret_generated_above'

保存并退出。

现在将脚本复制到Upstart服务目录:

sudo cp puma.conf puma-manager.conf /etc/init

puma-manager.conf脚本引用/etc/puma.conf的,它应该管理的应用程序。 让我们现在创建和编辑该库存文件:

sudo vi /etc/puma.conf

在这个文件中的每一行应该是路径到您想要的应用程序puma-manager来管理。 我们将把我们的应用程序部署到用户主目录中名为“appname”的目录。 在我们的示例中,它将是以下(确保更新路径到您的应用程序将生活:

/etc/puma.conf
/home/deploy/appname

保存并退出。

现在您的应用程序配置为在引导时通过Upstart启动。 这意味着即使您的服务器重新启动后,您的应用程序也将启动。 请记住,我们尚未部署应用程序,因此我们不想开始它。

安装和配置Nginx

为了使应用程序可以访问互联网,我们应该使用Nginx作为Web服务器。

使用apt-get安装Nginx:

sudo apt-get install nginx

现在使用文本编辑器打开默认服务器块:

sudo vi /etc/nginx/sites-available/default

使用以下代码块替换文件的内容。 请务必使用适当的用户名和应用程序名称(两个位置)替换突出显示的部分:

/ etc / nginx / sites-available / default
upstream app {
    # Path to Puma SOCK file, as defined previously
    server unix:/home/deploy/appname/shared/sockets/puma.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/deploy/appname/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

保存并退出。 这将Nginx配置为反向代理,因此HTTP请求通过Unix套接字转发到Puma应用程序服务器。 随意地做任何更改,你认为合适。

现在,我们将阻止重新启动Nginx,因为应用程序在服务器上不存在。 接下来我们将准备应用程序。

准备生产Git远程

生产服务器上安装的git用apt-get的:

sudo apt-get install git

然后为远程存储库创建一个目录。 我们将在主目录中创建一个名为“appname_production”的裸git存储库。 随意命名任何你想要的(除了不把它放在你的远程仓库~/appname ,因为那是我们将应用程序部署到):

mkdir ~/appname_production
cd ~/appname_production
git init --bare

由于这是一个裸存储库,没有工作目录,常规设置中位于.git中的所有文件都位于主目录本身。

我们需要创建后接收git钩子,这是生产服务器收到git push时运行的脚本。 打开hooks/post-receive在编辑器中的文件:

vi hooks/post-receive

复制以下脚本粘贴到post-receive的文件:

钩/后接收
#!/bin/bash

GIT_DIR=/home/deploy/appname_production
WORK_TREE=/home/deploy/appname
export APPNAME_DATABASE_USER='appname'
export APPNAME_DATABASE_PASSWORD='appname_password'

export RAILS_ENV=production
. ~/.bash_profile

while read oldrev newrev ref
do
    if [[ $ref =~ .*/master$ ]];
    then
        echo "Master ref received.  Deploying master branch to production..."
        mkdir -p $WORK_TREE
        git --work-tree=$WORK_TREE --git-dir=$GIT_DIR checkout -f
        mkdir -p $WORK_TREE/shared/pids $WORK_TREE/shared/sockets $WORK_TREE/shared/log

        # start deploy tasks
        cd $WORK_TREE
        bundle install
        rake db:create
        rake db:migrate
        rake assets:precompile
        sudo restart puma-manager
        sudo service nginx restart
        # end deploy tasks
        echo "Git hooks deploy complete"
    else
        echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."
    fi
done

请务必更新以下突出显示的值:

  • GIT_DIR :您之前创建裸Git仓库的目录
  • WORK_TREE :该目录要部署应用程序(这应该匹配在Puma配置中指定的位置)
  • APPNAME_DATABASE_USER :PostgreSQL的用户名(要求rake任务)
  • APPNAME_DATABASE_PASSWORD :PostgreSQL的密码(需要rake任务)

接下来,您应该检查之间的命令# start deploy tasks# end deploy tasks的意见。 这些是将运行的每个主分支推到生产远程Git(时间命令appname_production )。 如果您保留原样,服务器将尝试对您的应用程序的生产环境执行以下操作:

  • 运行bundler
  • 创建数据库
  • 迁移数据库
  • 预编译资产
  • 重新启动Puma
  • 重新启动Nginx

如果你想进行任何更改,或添加错误检查,请随时在这里。

完成审阅后接收脚本后,保存并退出。

接下来,使脚本可执行:

chmod +x hooks/post-receive

密码:

因为,收到后钩需要执行sudo命令,我们将允许部署用户使用无密码sudo (此处替代你的用户名部署,如果是不同的):

sudo sh -c 'echo "deploy ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-deploy'

这将允许deploy用户运行sudo不提供密码的命令。 请注意,您可能希望限制部署用户可以使用超级用户权限运行的命令。 至少,您将要使用SSH密钥身份验证并禁用密码身份验证。

添加生产Git远程

现在我们已经在生产服务器上设置了所有内容,让我们将生产git远程添加到应用程序的存储库。

开发计算机上,请确保你在你的应用程序的目录:

cd ~/appname

然后添加一个新的远程Git命名为“生产”指向裸Git仓库, appname_production ,您在生产服务器上创建的。 替换用户名(deploy),服务器IP地址和远程存储库名称(appname_production):

git remote add production deploy@production_server_public_IP:appname_production

现在你的应用程序已准备好与git push部署。

部署到生产

通过所做的所有准备工作,现在可以通过运行以下git命令将应用程序部署到生产服务器:

git push production master

这只是将您的本地主分支推送到您之前创建的生产远程。 当生产遥控接收推,它将执行post-receive我们先前设置钩子脚本。 如果正确设置一切,您的应用程序现在应该可以在您的生产服务器的公共IP地址。

如果您使用我们的示例应用程序,你应该能够访问http:// production_server_IP /tasks在Web浏览器,看到这样的事情:

示例Rails App

结论

每次对应用程序进行更改时,都可以运行相同的git push命令部署到生产服务器。 这单独应该可以为你的项目的生命节省大量的时间。

本教程仅讨论“后接收”钩子,但是还有其他几种类型的钩子可以帮助提高部署过程的自动化。 阅读本教程以了解更多有关Git的挂钩: 如何使用Git的鱼钩来自动化开发和部署的任务