sk

開発で得たこと

5分でRailsアプリをデプロイ。

f:id:sksksksksk:20170510221830j:plain

5分でRailsアプリを本番環境へデプロイします。
ソフトウェアプログラミングを覚えたと思ったらサーバにアプリ置けないのって辛いですよね。

環境

nginx/1.12.0

centos6

Rails4.2.5

Capistranoインストール


デプロイを自動化できるプラグインです。

GitHub



/Gemfile

group :development do
gem 'web-console', '~> 2.0'

gem 'spring'
gem 'capistrano' #追加
gem 'capistrano-bundler' #追加
gem 'capistrano-rbenv' #追加
gem 'capistrano-rails' #追加
end


使用するGemはこれで十分です。


bundle

Capistranoの設定

bundle exec cap install


rootディレクトリにCapfileが作成されますので編集します。



/Capfile

require "capistrano/scm/git"
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rbenv'
require 'capistrano/bundler'
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }


install_plugin Capistrano::SCM::Git



とりあえず、これを書いて置けば通ります。

あと、2つファイルを修正します。





/config/deploy.rb

lock "3.8.1"

set :application, "アプリ名(なんでもよろし)"
set :repo_url, "GitHubのrepository名"



set :deploy_to, '/home/ユーザー名/アプリ名/'

set :keep_releases, 5 #気にしない
set :ssh_options, :port => "ポート番号" #SSH接続に使うポート名

set :rbenv_type, :system # :system or :user
set :rbenv_ruby, '2.3.1'#rubyのバージョン



#気にしない↓↓↓
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value

set :linked_dirs, %w{bin log tmp/backup tmp/pids tmp/cache tmp/sockets vendor/bundle}
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"

set :bundle_jobs, 4




after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end
# Default


上記で大事なことは上半分です。

細かいことはデプロイに慣れてから徐々にカスタマイズしてください。

set :application, "アプリ名(なんでもよろし)"

制作したアプリの名前を書けばいいと思います。

 

set :repo_url, "GitHubのrepository名"

これは大事です。

デプロイしたいアプリを収めてるリポジトリのURLを入力します。

ダウンロードやクローンの為に用意されてます。

set :deploy_to, '/home/ユーザー名/アプリ名/'

CentOS内で入った時に、lsコマンドを打つと現れると思います。

デフォルトではないかもしれないのでなければmkdirで作ってください。


Home下の階層にユーザー名の階層も作っておきましょう。

それからデプロイするアプリをどこに置くかを指定できるのが上記の処理です。


「アプリ名」の部分にアプリがデプロイされます。

set :ssh_options, :port => "ポート番号" #SSH接続に使うポート名

おそらく、SSHは済んでいると思うので、

その接続に使うポート番号を書いてくださいね。


今回の私の設定は以下です。


set :application, "deploy"
set :repo_url, "https://github.com/sagaekeiga/deploy.git"



set :deploy_to, '/home/anime/deploy_sample/'

set :keep_releases, 5
set :ssh_options, :port => "内緒"


最後にdeploy/production.rbを編集して終わりです。

ローカルにありますよ。





deploy/production.rb

set :branch, 'master'

role :app, %w{ユーザー名@IPアドレス}
role :web, %w{ユーザー名@IPアドレス}
role :db,  %w{ユーザー名@IPアドレス}

server 'IPアドレス', user: 'ユーザー名', roles: %w{web app db}

set :ssh_options, {
    forward_agent: true,
    auth_methods: %w(publickey),
    port: ポート番号
}


こちらはユーザー名・IPアドレス・ポート番号を設定するだけです。

それ以外はまたカスタマイズしてください。


デプロイ

ここは一応確認ですが、

ローカルのアプリ内に潜んでいるCapistranoがGitにあるファイルをサーバーへ送ってくれます。
ですので、最新のアプリを更新(プッシュ)します。

デプロイ時はデータの作成(migrate)などCapistranoが一通り行ってくれます。


$ git add .
$ git commit -m デプロイ
$ git push

最後にデプロイ!!

$ cap production deploy

確認しましょう。
サーバーの中はこのようになりました。

$ pwd
/home/anime/deploy_sample/current/app

次はWebサーバー(nginx)の設定をします。




まとめたソース
https://github.com/sagaekeiga/deploy

5分でActionCableの実装する!

f:id:sksksksksk:20170510221830j:plain
ActionCableを実装します。
機能に関する詳細はこちらです。

RoRでリアルタイム通信を実現することができます。
ACは、Rails5以降の機能ですので、5未満の方はアップデートをしてからこの記事を読み進めてくださいね。

Git

こちらは会員登録が必要になりますので無理なさらず。
私が個人で作ったアプリです。

Git Hub



コントローラーの作成

$ rails g controller chat show


モデルの作成

$ rails g model message body:text


チャット用チャンネルを作成

$ rails g channel chat speak
$ rails db:migrate


チャット用チャンネルを作成

$ rails g channel chat speak

これまで作成したものを確認しましょう。

$ rails server

/chat/showで画面が表示されます。


ソースコードの確認

ブラウザでページソースを表示してみましょう。

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Faction_cable.self-17ebe4af84895fa064a951f57476799066237d7bb5dc4dc351a8b01cca19cce9.js%3Fbody%3D1%22%20data-turbolinks-track%3D%22reload%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fcable.self-6e0514260c1aa76eaf252412ce74e63f68819fd19bf740595f592c5ba4c36537.js%3Fbody%3D1%22%20data-turbolinks-track%3D%22reload%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fchannels%2Fchat.self-660d1dfffd7c22551eb29709ea5f789dbd62d8a2e602aa8e9126bd90ab1de200.js%3Fbody%3D1%22%20data-turbolinks-track%3D%22reload%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fchat.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js%3Fbody%3D1%22%20data-turbolinks-track%3D%22reload%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fapplication.self-b89234cf2659d7fedea75bca0b8d231ad7dfc2f3f57fcbaf5f44ed9dc384137b.js%3Fbody%3D1%22%20data-turbolinks-track%3D%22reload%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

ACが読み込まれていることがわかりますね。


接続エラーの解決

ここでデベロッパーコンソールを開くとエラーが出ていることがわかります。

WebSocket connection to '~~/cable' failed: WebSocket is closed before the connection is established.

ソケットが閉じているので接続できません〜
という意味ですので、きちんと開いてあげましょう。

3つのポイントがあります。


①Pumaのインストール
ActionCableのようにリアルタイム通信を実現する為に特殊なソケットを利用する場合はWebサーバを対応しているPumaにする必要があります。


/Gemfile

gem 'puma'
bundle


サーバを再起動してみてください。
するとログが以下のように変化します。

=> Booting Puma
=> Rails 5.0.0 application starting in development on http://0.0.0.0:8080
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.8.2 (ruby 2.3.0-p0), codename: Sassy Salamander
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:8080
Use Ctrl-C to stop


① development.rbの編集
ActionCableにどのドメインを使用し、また動いていいよと命令を出してあげます。

以下を加えてください。
場所は問いません。


config/environments/development.rb

  config.action_cable.allowed_request_origins = [ 'あなたのドメイン' ]
  ActionCable.server.config.disable_request_forgery_protection = true

ドメイン例(clou9を使用)

  config.action_cable.allowed_request_origins = [ 'action-cable-sagae.c9users.io' ]

③ Redisを起動

Rails5にはredisサーバーが用意されてます。
このサーバーを動かすことでACを使うことができるようになります。


ターミナル

$ redis-server


それでは、コンソールをみてください。
どうでしょうか?
先ほどの接続エラーは出なくなると思います。


メッセージのブロードキャスト



クライアント側にメッセージを要求します。

ブラウザで入力した値を取得する処理になります。




app/assets/javascripts/channels/chat.coffee

App.room = App.cable.subscriptions.create "ChatChannel",
  connected: ->

  disconnected: ->

  received: (data) ->
    $('#messages').append data['message']

  speak: (message) ->
    @perform 'speak', message: message

次に受け取ったデータをクライアントに返します。
入力された値をブラウザに表示します。



app/channels/chat_channel.rb

# chatチャンネル接続時にコールされる
def subscribed
  stream_from "chat_channel"
  ActionCable.server.broadcast 'chat_channel', message: 'connected.'
end

# chatチャンネルのspeakメソッドは、受け取ったメッセージを全クライアントにブロードキャストする
def speak(data)
  ActionCable.server.broadcast 'chat_channel', message: data['message']
end

これでデータの受け渡しができるようになりました。
ここで、一度サーバを再起動しましょう。

Pumaとredisどちらもです。
control+cで停止、起動は先ほどのコマンドを使ってください。


フォームの作成

以下のコードでユーザーの入力値を取得できます。



app/views/chat/show.html.erb

<ul id="messages">
</ul>


<form>
    <label>say something: <input type="text" data-behavior="chat_input"></label>
</form>

app/assets/javascripts/channels/chat.coffee

↑先ほど書いたコードの下に書いてください。
$(document).on 'keypress', '[data-behavior~=chat_input]', (event) ->
  if event.keyCode is 13 # return = send
    App.room.speak event.target.value
    event.target.value = ''
    event.preventDefault()

入力値の表示

こちらは普通のJsの処理ですね。



app/assets/javascripts/channels/chat.coffee

App.chat = App.cable.subscriptions.create "ChatChannel",
  received: (data) ->
    $('#messages').append '
<li>' + data['message'] + '</li>'
↓先ほど書いたコードの上に書いてください。

ProgateやRailsチュートリアル、プログラミングスクールを通い終えたが現場のコードはかけない、

一体どうやって書くの?と思っているエンジニアのみなさんのためのチュートリアルを公開しています。

チュートリアル