このブログは2015年2月18日に更新を停止しました。すべての記事は https://chroju.github.io へ移行されています。

chefでさくらVPSの初期構築を全部自動化してみた

Chefでサーバー構築を全部自動化して、借りているさくらVPSに当ててみた。意地でも手作業はまったく入れない完全自動構築設定。これはよい。毎回手作業やらずに済むというのは、抜け漏れをなくすという点でも、構築時間を短縮するという面でも本当に楽。

前提

今回構築するサーバーの前提は次の通り。

  • CentOS 6.4環境で試行
  • vagrantを使って試験後、さくらVPSに対して適用
  • 基本の設定として以下を実施
    • 構築用のユーザーを作成
    • 構築用ユーザーにsudo権限を付与
    • 構築用ユーザーを秘密鍵認証でsshログイン許可
    • rootによるsshログインを禁止
    • パスワード認証によるログインを禁止
    • sshのポート番号を変更
    • ssh, http以外のアクセスをiptablesでシャットアウト
  • nginxを導入
  • 将来的にunicornを入れるための設定を準備
  • 極力サードパーティクックブックは使わない(ブラックボックス化が嫌)

レシピ

まず「どんなサーバーだろうとまず実行するだろうセキュリティ上の設定」はdefault_tasksというレシピにまとめた。

default_tasks

# sshdサービスの有効化
service "sshd" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end

# sshd_configの配置
template "sshd_config" do
  path "/etc/ssh/sshd_config"
  source "sshd_config.erb"
  owner "root"
  group "root"
  mode 0600
  notifies :restart, "service[sshd]"
end

# iptablesの設定
iptables_rule "iptables"

sshdの有効化と、templateを使ったsshd_configの配置、そしてiptablesの設定。このうちiptablesについてはサードパーティ・クックブックを使っている。

opscode-cookbooks/iptables

iptablesに挿入したい内容をtemplateで用意し、iptables_rule "template name"の形でレシピ内に書き込んでおくと、templateの内容がそのままiptablesに追加されるというシンプルなレシピ。まぁ、これぐらいはサードパーティ使ってもよいかな、と。iptablesを丸ごとtemplate化してアップロードするという手もあるにはあるのだが。

sshd_configはCentOSから引っ張ってきたものをそのまま流用し、Attributesを使っていろいろと書き換えられるように仕込んだ。長いので割愛するけど、設定可能なAttributesはこんな感じ。

  "sshd" : {
    "Port" : 22,
    "MaxStartups" : 10,
    "PermitRootLogin" : "yes",
    "RSAAuthentication" : "yes",
    "PubkeyAuthentication" : "yes",
    "AuthorizedKeysFile" : ".ssh/authorized_keys",
    "PasswordAuthentication" : "yes"
  },

よく編集する箇所を中心にAttributesとしてみた。他に設定したい項目が増えたら随時足していけば良いかなと。

users

ユーザーの作成は別のレシピに切り分け。

# wheelグループの作成
group "wheel" do
  gid 10
  action :create
end

# data bagsよりユーザーを作成
data_ids = data_bag('users')

data_ids.each do |id|
  # ユーザー作成
  u = data_bag_item('users', id)
  user u['username'] do
    password u['password']
    supports :manage_home => true, :non_unique => false
    group u['group']
    action [:create]
  end

  # ssh公開鍵配置用のディレクトリ作成
  directory "/home/#{id}/.ssh" do
    owner u["id"]
    group u["id"]
    mode 0700
    action :create
  end

  # ssh公開鍵の配置
  file "/home/#{id}/.ssh/authorized_keys" do
    owner u["id"]
    mode 0600
    content u["key"]
    action :create_if_missing
  end
end

sudoを許可するユーザーをwheelグループに入れるという定番設定。wheelを実際にsudo許可するのはまた別のレシピの仕事で、ここではグループとユーザーの作成のみ。

ユーザー作成にはdata_bagを使った。こちらを参考として作っている。ポイントとしては公開鍵まで登録させていること。fileリソースを使って実にシンプルな配置の仕方だけど、非常に楽ではある。

あまり詳しくは知らないのだが、data_bagについては暗号化する方法があるみたいなので、公開鍵を生でべろっと貼っとくのが気になる場合はそのへんも仕掛けておくと良さそう。

nginx

一番苦労した気がするし、あまり美しくない。。。

# nginxインストール
package "nginx" do
  action :install
end

# nginx有効化
service "nginx" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end

# templateからnginx.confを配置
template "nginx" do
  path "/etc/nginx/nginx.conf"
  source "nginx.conf.erb"
  owner "root"
  group "root"
  mode 0644
  notifies :reload, "service[nginx]"
end

# ディレクトリ作成
directories = ["/etc/nginx/sites-available","/etc/nginx/sites-enabled","/var/www","/var/www/#{node['nginx']['root']}","/var/www/#{node['unicorn']['root']}"]
directories.each do |directory_name|
  directory "#{directory_name}" do
    owner "root"
    group "root"
    mode 0644
    action :create
  end
end

# sites設定をtemplateごとに実行
node['nginx']['nginx_sites'].each do |site|
  # templateからsites-available配下に設定ファイルを配置
  template "nginx_sites_available" do
    path "/etc/nginx/sites-available/#{site}"
    source "nginx/#{site}.erb"
    owner "root"
    group "root"
    mode 0644
  end

  # sites-enabled配下へシンボリックリンクを配置
  link "/etc/nginx/sites-enabled/#{site}" do
    to "/etc/nginx/sites-available/#{site}"
    link_type :symbolic
    action :create
    notifies :reload, "service[nginx]"
  end

end

インストール、サービス有効化、templateからのnginx.confアップロードまでは良いとして。ディレクトリ作成がなんか。。。汚い。。。

/var/wwwを掘って、その中にウェブサイトのルートを置いたり、バーチャルホストの設定でsites-availableとかその辺を使っているもんで、ディレクトリ作成の量が多くなってしまった。いずれも設定は同じなので配列でやるのがベターだとは思うのだが、どうにも汚い。なんとかしたいとこではある。

あとはsites-availableにtemplateから設定ファイルを配置して、linkリソースでsites-enabledにシンボリックリンクを貼って終了。nginxの使い方はsites-availableを使うパターンだけではないので、もう少しスッキリさせることはできそう。

サードパーティ・クックブック

サードパーティとして導入したのはさっきのiptablesと、sudoの2つ。

opscode-cookbooks/sudo

sudoはvisudoをexecuteで走らせるってのも怖いし、その後ファイル編集をどうやったらいいのかよくわからんなってのと、sudoresファイルをtemplateで上げるってのもちょっと怖いという感覚的な問題からサードパーティ採用とした。

使い方としては簡単なもので、変数でsudoを許可するユーザーやグループを指定して、passwordlessとするかどうかを設定するぐらいで終わり。

  "authorization" : {
    "sudo" : {
      "groups" : ["wheel"],
      "passwordless" : "true"
    }
  },

冒頭でも書いた通り、なるべくサードパーティを使わない方針で行きたいんだけど、自力でレシピを書けないor書く自信がないところは已むなく実績のあるサードパーティを使ったという感じ。まぁサードパーティと言っても結局は人の手で書かれたレシピなので、自分でchef力上げれば相応なレシピは自力で書けるはず。このあたりは結局「どこまでchef力の向上に時間を割くか」という部分とトレードオフになるのかと。

今後やりたいこと

最終的にこのサーバーではRails動かしたいので、Rubypostgresqlのインストールを今後実装したい。あと「取りあえず動けばいいや」路線で一度作ってしまったので、もう少しメンテナンス性の高いもの、読みやすいレシピには育てたいところ。

汎用的に誰でもどんな用途でも使えるって感じではないと思うが、とりあえずGitHubには上げてみた。秘伝のタレのようにこれをじわじわ育てていくというのは良いなぁ。Linuxの勉強をするごとにきっと強靭なレシピとなっていくのだろう。まさに「レシピ」だな、これは。

まずは静的なページをってことで、近日中にプロフィールでも置いて公開する予定。

https://github.com/chroju/chef_web_server

参考

自分のことを「ペルソナ」から見返してみる

自分の状況を客観的に評価する(タスク、財務管理、スケジュール管理など)とき、「ペルソナ」を考え方として導入するとわりと捗ることに最近気付いた。

このご時世、人格が一面的という方はあまりいないのではないかと思う。要は家庭での顔、仕事での顔、趣味での顔とかそういうのだ。これを「ペルソナ」と呼んだのだが、自分にはどんなペルソナがあるのか把握した上で、各ペルソナごとに状況を振り返ってみると漏れが少なくなる。

使える場面としては、例えばGTDの週次レビュー。ToDoリストに漏れがないか考えるとき、各ペルソナごとに数分ずつ考えてみる。あるいは「高度」の概念を取り入れるとき、ペルソナごとの高度の視点を持ってみる。

あるいは家計簿。どのペルソナに対してどれだけお金を使っているかで出費のバランスを管理する。時間の使い方を振り返るときも、案外趣味の時間がすげー多いなとか気付けたりして有意義。

自分の場合は今のところ、生活、仕事、趣味、開発、家庭・友人、教養と6つのペルソナに分けてみている。なかなかMECEというわけにはいかないのだが、「生活」は自分自身の身の回りのこと(病気や健康、衣食住)であるのに対して「家庭・友人」は実家の事情やプライベートな友人関係の話という具合に分けていたり、仕事上必要な技術知識は「仕事」に振り分け、家で勉強している知識は「開発」に入れたりとか、そういうことをしている。

まぁ要するに、自分なりに状況整理できる手がかりがあれば良いのではないか、という話。

Chef soloはじめの一歩

Capistranoでいろいろやったので今度はChef、正確にはChef solo。サーバー立てて最初にやること(ユーザー作成、sshd_configの設定、もろもろインストールとか)はだいたい決まっていると思うので、Chefで全部コード化してしまえたらやっぱり楽だなぁという思い。なおChefとCapistranoの境界線については、システム全体をまかなうのがChefであり、Capistranoはあくまでアプリ単位のデプロイに使うイメージでいる。rbenvやRubyを入れるのはChef。/var/www/RailsApp/配下(仮)をごにょごにょするのがCapistrano

参考としたのはお馴染みのアレです。ただ、すでに出版から1年近く経っているので若干事情が変わっている箇所もあったりした。

入門Chef Solo - Infrastructure as Code
伊藤直也 (2013-03-11)
売り上げランキング: 632

基本構成

chefの基本構成を取りあえず押さえる。

インストール

上記のものを全部インストール。Gemfile書いてる場合はそっちに追加で。また最後にrbenv rehashを忘れずに。

gem install chef -v 11.10 --no-ri --no-rdoc
gem install knife-solo --no-ri --no-rdoc
gem install berkshelf
rbenv rehash

ちなみにChefの最新はv11.12.xなんだけど、どうもバグがあるっぽく上手くいかなかったのでバージョン指定で古いの入れてる。

セットアップ

初期設定。

knife configure # 対話は全部デフォでOK
knife solo init chef-repo # chef-repoフォルダがレポジトリとして作られる
cd chef-repo

chef soloを使うリモート側へもchefをインストール。

$ knife solo prepare vagrant@192.168.1.1

Berkshelf

続いてBerkshelfの初期設定。

$ berks init

しかし、ここでまさかのエラー。。。

cannot load such file -- hitimes/hitimes (LoadError)

ここによればgem updateしてからhitimesを再インストールすれば大丈夫だよ的な話。やってみる。

$ gem update
$ gem -v
2.2.2
$ gem install hitimes
$ rbenv rehash

気を取り直して。

$ berks init
The resource at '/Users/chroju/Dropbox/lab/chef/vagrant-repo/metadata.rb' does not appear to be a valid cookbook. Does it have a metadata.rb?

('A`)ヴァー

わけわからんし、berks initしなくてもBerkfile自前で作りゃイケるらしいので諦めることにする。Berksfileをtouchしてからvimで編集。

site :opscode

cookbook 'sudo'
cookbook 'iptables`

Gemfileと似たような書き方で、cookbook 'hoge'と書くとそのクックブックがOpscodeからインストールされる。Opscode以外のGitHubで公開されているクックブックを使う場合は、これもGemfileのようにcookbook 'fuga' git:"..."の形でURL指定が可能。

そしていざインストール……だがまたしてもエラー。

$ berks install --path cookbooks
DEPRECATED: `berks install --path [PATH}` has been replaced by `berks vendor`.
DEPRECATED: Re-run your command as `berks vendor [PATH]` or see `berks help vendor`.

そろそろこのあたりから笑えてきた。調べたところ、Berkshelf v3.xではberks install --path hogeは使えないらしいス。berks vendor hogeが今どきらしい。

$ berks vendor cookbooks
destination already exists /Users/chroju/Dropbox/lab/chef/vagrant-repo/cookbooks. Delete it and try again or use a different filepath.

えー。

$ rm -rf cookbooks
$ berks vendor cookbooks

DEPRECATED: Your Berksfile contains a site location pointing to the Opscode Community Site (site :opscode). Site locations have been replaced by the source location. Change this to: 'source "http://api.berkshelf.com"' to remove this warning. For more information visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations
Resolving cookbook dependencies...
Using iptables (0.13.2)
Using sudo (2.5.2)
# 以下略

文句言いながらもやってくれました。冒頭の1行目の書き方はsite :opscodeというのが通例だったけど、v2.0からsource "https://api.berkshelf.com"に変わった模様。ただ、コマンドは通るには通るんだけど。

最初にberks vendor cookbooksしたときに出たエラー、要は「すでにあるフォルダにはインストールできねーよ」って話なのだが、だとしたらBerksfileを書き換えて追加インストールしたいときはどうしたらいいんだろう?というのは素朴な疑問。サードパーティのクックブックは直接編集しない(変数などは自分のレシピの方で代入する)のが基本らしいので、一度フォルダを消してから再度berks vendro cookbooksでも不都合はないと言えばないのだが、なんだか馬鹿らしい感じもする。

あとberks installコマンドもあるんだけど使い方わかってない。

レシピ作成

その前におさえる基本。

  • nodes/hostname.json : 実行するレシピをrun_listとして記述
  • solo.rb : chefが利用する各種パスの設定ファイル(knife soloの場合は生成されない)
  • cookbooks/hoge/recipes/default.rb : レシピ本体
  • cookbooks/hoge/templates/default/* : テンプレートファイル
  • cookbooks/hoge/files/* : 静的ファイル
  • cookbooks/hoge/attributes/* : 変数の格納

あとcookbooksフォルダはサードパーティ製のものを入れて、自家製のクックブックはsite-cookbooksフォルダに入れるっていうルールもある。名前がわかりにくいが。

このへん押さえつつ、次回はレシピ作成編です。

エンジニアの転職において「技術」とは目的か手段か

渋谷ヒカリエー2

ヒカリエ綺麗すぎる。。。

えー。こちらのイベントに参加してきました。こういう社外での勉強会チックなものに参加するって初めてですげービビってたけどわりと楽しめた。

登壇者の方はSIerを経てWeb系に行かれたということでしたけど、その理由が「コードを書けないことへの危機感」だとか「3.11を目の当たりにして、やりたいことやっとかないといつ死ぬかわからん」みたいな話だったのはだいぶ自分とも似ている点でした。というかSIerを脱出して別のIT系に転職する人って2パターンあって、1つがこのように「より技術的なスキルアップをしたい」という人、もう1つが「ユーザー企業の側に行ってみたい」という人。少なくとも自分の周囲を見回しているとそういう感じで、比較的安定的でよく狙われてるのは後者なのかなという印象を持っています。ただ、最近のトレンドとして手に職をつけるじゃないですけど、自分で出来ることの幅を増やしたいという人は多い気もしている。イベント後の懇親会で話していてもそんな感じでした。

自分としても技術的な危機感というのはあって、だからこそのこのブログなのだけど、最近より強く感じているのは「長期的な目的なしに転職したくない」ということ。

突然の話になりますが、自分は今のインターネットを巡るこの国の状況って実にダサいなと思ってます。光回線はだいぶ普及したし、ポケットに入るコンピュータを誰もが持ち歩いているし、Wi-Fiサービスだってそこここにある。確かにインフラ面では多大なる普及を終えているんだけど、じゃあそれを使ってみんな何をしてるの?というと、今ってソシャゲで金銭巻き上げられたりSNS疲れが社会問題化したり、あるいは歩きスマホしてて階段から落ちて死ぬみたいな感じじゃないかと。なんてーか、まだ十分に社会が、ソフトウェアの方がインフラの状況に追いついていないように思うのですよ。ネットを通じて社会を良くしよう!みたいな話がよくあるけど、その前にネット自体を何とかしないとヤバくね?ってのが自分の実感です。

だからキャリアプランとしては、そういう方面に進んでいきたい。ITと社会がうまく調和していくような、そういう動きを促せるような働きかけをしていきたいという思いが強くある。となると、自分の手で技術的なこと出来るか?ってのは二の次になるんですよね。手段と目的の違いです。もちろん、今挙げた「目的」を達するために技術力という「手段」を使えれば自分にとって最も望む結果だと思うし、その方向ってのは目指したいと思ってる。ただ、じゃあ実際どういう職種がその方向の先にあるんだろうってのがあまり具体化できてなくて。考えたのはUIデザインとかは少し近いなと思うんですけど、あまりしっくりはきていない。むしろ法整備したりだとか、あるいはMIAUのやっているようなことが自分の目的には近いのだけど、そうなると技術者じゃなくて良くなっちゃうなぁってのもあって悩ましい。

世の「技術やりたい!」というエンジニアのみなさんは、もうそれ自体が目的化しているのか、それとも技術を通して実現したい目的が別にあるのか、というのは大変に気になるところです。今日聞けばよかったかな。まあ、もうちょっといろいろ考えてみます。

Capistrano3を最後にもう一度だけ懇切丁寧にまとめてみる

2017-08-15 追記

Googleの「Capistrano」検索順位で上位にあるためか、いまだにこの記事がたびたびブクマされるんですが、3年前の情報ですし、執筆者はRubyを専門としたプログラマーではないのでその点ご注意ください。(追記ここまで)


いろいろエントリーを上げながら苦しんでいたCapistranoだが、ようやっとそこそこ落ち着いてきた気がするのでそろそろ完結編といく。Capistranoの基本とかはすでにこちらのエントリーで書いたので、今回は各設定ファイルの書き方とか、その他ハマったポイントを中心に。

今回作成したファイル

以下4ファイルを作成した。

基本的にCapistranoを使う場合「必須」なのは上2つのファイル。deploy/hoge.rbも確実に必要にはなるが、デプロイ先の環境が本番なのかステージングなのかでproduction.rbとstaging.rbを使い分けることになる。もちろん、ステージング環境を用意していない場合はstaging.rbは不要。また4つ目のファイルだが、自分の場合はunicornをデプロイ先で使っているので、デプロイ後にunicornを再起動する目的で独自タスクを作成している。説明が遅れたが、今回デプロイ先となる環境は大雑把に以下の通り。

Capfile

ここはだいたい環境を問わず同じになってくる箇所。まずcapistrano/setupcapistrano/deployrequireが必須。RailsCapistranoで扱う場合は、capistrano/bundlerからcapistrano/rails/assetsまでも必須となる。rbenvを使っている場合はcapistrano/rbenvが必要。rbenvをどこにインストールしたのかにより、set :rbenv_typeを指定する。あとはデプロイするアプリケーションで使うRubyのバージョンも指定してやる。rvmを使う場合も似たようなcapistrano/rvmを使うみたいだが、そちらはよくわからんので割愛。

で、ここまでがrequireということなので、デプロイ元サーバーにGemfile書いてインストールしておくことを忘れずに。

group :development do
  gem 'capistrano', '~> 3.1.0', :require => false
  gem 'capistrano-rails', '~> 1.0.0', :require => false
    gem 'capistrano-rbenv', '~> 2.0', :require => false
  gem 'capistrano-bundler', '~> 1.1.2', :require => false
end

最終行は独自カスタムタスクを読み込むための行。デフォルトで入っているのでそのままにしておけばよい。先のunicorn.capのように、何か独自タスクを作った場合はこのパスに入れれば読み込むよーということでもある。

なお、以前書いたエントリーではcapistrano3/unicornrequireしていたのだが、これは撤回した。詳細はunicorn.capについて説明するときに後述。

staging.rb

ステージング環境、というかデプロイ先環境の設定を書く。vagrantを使う場合はだいたいこれと同じように書いておけば通るんじゃないかと思う。Vagrantはデフォルトではvagrantユーザーによるsshが可能になっているので、それをそのまま使わせてもらっている。何か他のユーザーで入りたい場合などは頑張るしかない。

一点だけ注意すべきは、RAILS_ENVがstagingに設定されるということ。Capistrano3ではcap installを叩くとデフォルトでstaging.rbとproduction.rbが作られ、RAILS_ENVもこのファイル名に倣うことになるのだが、Railsの動作環境は通常test, development, productionの3種類であり、stagingは存在していない。従ってこのままデプロイを始めてもうまくはいかない。面倒であればstaging.rbは使わず、production.rbだけを使っていてもいいかもしれない。

staging.rbを使う場合は、Rails側で環境の準備が必要になる。やることは大きく3つ。

1. database.ymlにstagingを作成する

rake db:migrateなどのDB系のコマンドはiRAILS_ENVを指定して実行される。ここで指定されたRAILS_ENVをdatabase.ymlに見に行くので、設定を入れておく必要がある。ステージング環境の本来の意味を考えると、production用の記述をそのままコピーするだけで良いはず。

2. config/environments/staging.rbを作成する

動作環境設定ファイルとして、デフォルトではproduction.rbとdevelopment.rbだけが用意されているので、これもproduction.rbをコピーしてstaging.rbを作る。

3. Gemfileにgroup :stagingを作成する

これはRAILS_ENVと直結するわけではないが、GemfileのグループもRails環境ごとに作られているのでstagingを設けてやった方が良さそう。これもここまでと同様の話で、group :staging, :production doとしてやるだけで良い。

deploy.rb

いよいよデプロイ用の設定。面倒なので説明は省くが、だいたいはコメント読めば設定できるはず。

linked_fileslinked_dirs前回のエントリーで書いた通り、shared配下に置いているファイルやフォルダのうち、currentに必要となるものを指定することでシンボリックリンクを張ってくれるというものなのだが、ではshared配下にこれらファイルやフォルダはいつ作られるのか?というのがかねてから疑問だった。

結論を言えば、linked_filesは勝手には作成されない。というより、ここに指定したファイルがshared下に存在しない場合、Capistranoはデプロイ時にエラーを吐いてくる。正確に言えばdeploy:check:linked_filesというタスクがあり、ここで存在確認を行っている(参照)。したがってこのタスクが投げられる前にファイルは別でアップロードしなくてはならない。ここではdetabase.ymlを指定しているので、アップロードタスクを独自で組み込んでいる。詳細は後述。

一方のlinked_dirsは、shared配下に存在しない場合はdeploy:check:make_linked_dirsで空っぽのフォルダとして作成してくれる。逆に言えば、元々ファイルが入っているようなフォルダをここで指定することはあまりないということ。例えばbundleなり、logやtmpなり、アプリが動作する中で必要とされるフォルダ(従ってGitHubで静的に管理はしていないフォルダ)がここに来ている。なお、shared/bundleはcapistrano/bundlebundle install --pathで指定するデフォルトのフォルダなんだそうだが、古いバージョンのcapistrano/bundleではshared/vendor/bundleにインストールするらしいので要確認。

後半では独自タスクを3つ追加している。

1つ目はdatabase.ymlをアップロードするタスク。デプロイ先にshared/configフォルダを作成した上で、ローカルのconfig/database.ymlをアップロードしている。2つ目はbundle exec rake db:createを発動するタスク。Capistranocapistrano/railsにはDB作成用のタスクは一切含まれていないので、自力であらかじめ作っておく必要がある。手動で作成したり、Chefなんかで仕込んでおく手もあるのだろうが、ここではCapistranoのタスクとして書いている。必要になるのはもちろん初回1回限りなので、デプロイのルーチンの中には組み込んでいない。

3つ目はunicornを再起動するタスク。これはunicorn.capの中で作成したunicorn:restartというタスクを呼んでいる。

最後の2行は独自タスクのルーチンへの組み込み。database.ymlのアップロードは真っ先にやらなくてはならないので、deploy:startingというデフォルトで言えば最初のタスクのさらに前に組み入れている。一方のunicorn再起動はdeploy:publishingの後、ほぼすべてのタスクが終わったタイミング。

unicorn.cap

ほぼほぼ以下エントリーの中身をそのまま使わせてもらいました。

異なる箇所はstart_unicornの内部処理。元エントリーではbundle exec unicornを叩いているのだが、自分としてはbundle exec unicorn_railsを叩く認識だったので書き換えている。ここの書き換えが必要なのかどうかはちょっと自信がない。またコマンドのオプションとして--path hogeを指定しているのだが、これはサブディレクトリでRailsアプリを動作させたいため。予定としてhttp://www.chroju.net/は静的なサイト、www.chroju.net/hogeでアプリごとにフォルダ作って管理としたかったもので。まー、こういう要らんチューニングを最初からやろうとするから迷走するわけなのだが。はじめはマニュアル通りにまず写経すべきだと思います。はい。サブディレクトリ云々の件はまた別途エントリー書きます。

ちなみにパスの指定やら何やらやらないのであれば、capistrano3-unicornを使うのも手だと思う。

その他トラブルなど

以上、ここまでの設定にはそれなりに自信がある。とりあえずbundle exec cap staging deployを叩いてエラーが出ないことも確認はした。が、現状アプリは動いてない。。。いや、正確に言うと一度動いたのだが、その後Vagrant落として、セキュリティアップデートのためにiMacも再起動してもう一度やってみたところ、ダメダメになってしまったのだ。何が原因なのやらさっぱりだ。。。トラブルは3つほど遭遇している。

1. Gitが通らない

git ls-remotePermission denid(publickey)のエラーが出るという事象が発生、その先に進めなくなった。これについてはドンピシャなエントリーを探し当てたので、そのまま解決策を適用したらなんとかなった。なぜこのエラーが起きたのかまではまだ深堀りできてない。

2. 404が返される

Nginxは動いている。unicornもプロセスは上がっているのだが、アプリを配置したフォルダを見に行くとThe page you were looking for doesn’t exist.というメッセージが返される状態。他の有りもしないフォルダを見に行こうとするとNginxがBad Gatewayを返してくるし、unicorn.stderr.rbにログが残っているのでアクセスはできているようなのだが、なぜ表示に至らないのかわからない。

ちなみにstaging.logにはActionController::RoutingErrorが出ている。???

3. Assets周りが不具合起こしている模様

Vagrantを落とす前はアプリ自体つながりはしたのだが、jsとcssが反映されていない状態だった。ソースから探ってjsとcssをブラウザ上で開いてみたのだが、真っ白になっていた。アセットコンパイルのエラーなのかと思い、サーバー上でもコンパイル後のファイルを開いたりしてみたが、こっちは中身がきちんと書かれていた。なんなんだ一体。

以上。だいたい整ったはずなのだが、まだまだ先が長そうでいい加減ゲンナリしている。自分、この手のこと向いてないのかもしれんと思い始めた。。。

Capistrano3がわからんので今一度イチから考えなおしてみる

前回Capistranoが上手くいかないというエントリーを上げてから1か月。いまだにハマってしまっている……。何が悪いの皆目検討もつかない、というほどではないのだが、なんというか、雲を掴んでいるような状態ではある。一旦Capistranoについて整理してみるべきなんだろう。

Capistranoは何をしてくれるのか

そもそもCapistranoとは何をしてくれるツールなのか?

Capistrano3のデプロイフレームワークの使い方 - Qiita

Capistranoは2まではRailsのデプロイツールだったけど、3は汎用的なデプロイツールに変わっている。したがってデフォルトの状態ではRails用のデプロイタスクは特に含まれていない。このあたりが上の方のリンク先で語られている内容になる。

ではデフォルトでのデプロイタスクは何をするのかと言えば、およそサーバーへのデプロイとして一般的に行われるようなものが組み込まれている。capistrano/lib/capistrano/tasksあたりを探るとデフォルトタスクがよくわかる。

ざっくり見てしまうと、単純にサーバーへファイルを上げて自動的に世代管理をさせるだけであればこれだけでもなんとかなりそうなところではある。逆にこれらが煩わしいのであれば、デフォルトタスクを無効化して使う手もある。デフォルトタスクの内容がよくわからないのであれば、一旦無効化してすべて自分でタスクを書くというのも手だと思う。

Railsをデプロイする場合はこれだけでは足りないので、capistrano/railscapistrano/bundleを追加で読み込むことになる。

Capistranoのディレクトリ構成

Capistranoでデフォルトのデプロイを行った時、デプロイ先サーバーに作成されるディレクトリについてもよくわかってないのでまとめておく。deploy.rbのdeploy_toで指定したディレクトリ内に、次の3つのディレクトリが作られる。

  • releases : デプロイした内容を世代管理する
  • shared : bundleとかdatabase.ymlとかGitで管理してないファイル置き場(多分)
  • current : releasesの最新世代とsharedのシンボリックリンクが置かれる

基本的に「デプロイ」される先はreleasesだ。releases配下にはデプロイ時のタイムスタンプから生成された名前のフォルダが作られ、その中にごっそりデプロイしたファイルが入っている。タイムスタンプが付くということは当然世代管理されているわけだが、保持される世代数はdeploy.rbのset :keep_releasesで好きに指定できる。で、最新世代についてはcurrent配下にシンボリックリンクが自動的に貼られる。

sharedは正直よくわからない。デプロイのたびに更新するわけではない、すなわちGitで管理をしていないファイルやディレクトリを置いておくみたいなのだが、ではどうやって配置すれば良いのか?がわからない。配置したファイルのうち、必要なものについてはlinked_dirsとlinked_filesで指定すればcurrent配下にこれもまたシンボリックリンクが貼られる。しかし、どうやって配置すれば良いのかわからない。

関連:[自分用メモ]Capistrano3のlinked_filesって自分でuploadしなきゃダメですか? - Qiita

で、これを書いていて気付いたのだが、アプリの中身がcurrent配下に展開されるということは、ウェブサーバーの設定もdeploy_toではなくてcurrentに飛ばすようにしておかないといけないわけだ。俺だけかも知れんが、案外罠ではないかという気がする。

capistrano/rails

取りあえずsharedの話はほっといて、Railsをデプロイする話に戻る。冒頭でデフォルトタスクにはRailsに対するものは何もないと書いたが、それではRails用のデプロイタスクはどこで生成されているのか? 答えから書けば、capistrano-railsを読み込むことによって初めて生成される。

Capistrano3におけるRailsのデプロイタスクの内部実装 - Qiita いわく、追加されるタスクは主に3つ。

  • assets compile(deploy:compile_assets)
  • assetsのタイムスタンプ更新(deploy:normalize_assets)
  • db:migrate(deploy:migrate)

逆に言えば、ここで追加される以外のタスクはデフォルトでは行われないということになる。その点を配慮せずにただググって適当なdeploy.rbをコピーしたりしただけでは、思ったとおりのデプロイは出来ない。

一例として自分がハマったのが、db:createは行われないということ。schema.rbを使って初回デプロイのときに上手いこと云々なんてことは一切してくれないので、このあたりは自分で書くか、サーバーを作ったときに予めDBをこしらえておく必要がある。

capistrano/bundle

もうひとつ、RailsCapistranoでデプロイする場合に必要なのがcapistrano/bundler。Rails使っててGemfileがないなんてことはないだろうし、というかcapistrano/railsの中で明示的にrequireされている。何をやっているのかと言えば、簡単な話しである。

  before 'deploy:updated', 'bundler:install'

デフォルトではshared配下にbundleディレクトリを作ってbundle installをかけてくれる。パスについてはset :bundle_pathオプションで自由に指定ができる。

capistrano/rbenv

あとよく使われるものとして、capistrano/rbenvがある。デプロイ先のサーバーで、Rubyをrbenvを使って入れてる場合には必須になるもの。中身はよく知らないが、そこまで気にしなくても良いのではないかという感覚がある。入れたらdeploy.rbにset :rbenv_ruby_versionで使うRubyのバージョンを指定すればOK。

同じ類のものとして、capistrano/rvmももちろんあります。

ソースにあたることの必要性

以上がCapistrano3でRailsをデプロイする場合の主なデフォルトタスクの内容になる。繰り返しになるが、これ以外に何をやりたいのならタスクは書く必要がある。よく言われるのがデプロイ後のウェブサーバーの再起動で、unicornを再起動するタスクを追加している例はよく見かける。ただ、これもcapistrano3-unicornという便利なものがすでに作られていたりはする。

個人的な思いなのだが、Capistrano公式の「Getting Started」は説明が不十分な気がしてならない。デプロイに至るまでの設定ファイルの書き方やら何やらは確かにだいたい載っているのだが、ではCapistrano3の内部実装がどうなっているのかとか、タスクを自分で書くにはどうすればよいのかといったあたりの情報が足りない。自分はまだこのDSLを読み下せないのだが、rakeあたりを知っていると結構書けるものなんだろうか? ひょっとしたら、自分のようなRubyRails初心者が安易に手を出すものではないのかなぁとか思う。

ただ、GitHubにいずれもソースが上がっているので、根気よく読んでいけばCapistranoが何をしているのか、また何を求めているのかは理解できる。誰かの書いたノウハウに頼るより、ソースにあたるのが大切なのは基本だと思う。常に。

Rails環境構築(5)Capistranoによるデプロイ ※未完

だいぶ間が空いてしまった……。Rails環境構築シリーズ、あとサーバーにはDB(postgresql)、unicornRailsを入れれば終わりなのだが、いずれもアプリ側のGemfileを使ってローカルにインストールするつもりなので、まずはアプリのデプロイが必要となる。そしてRailsのデプロイといえばCapistranoだ!と、いきたいところだったのだが。

Capistrano 3.0を試しているのだが、どうにもハマっている。ハマっているというのはのめり込んで楽しくなっちゃった的な意味ではなく、上手くいかずにっちもさっちもいかない的な意味である。とりあえず頭からやり方おさらいしつつ、ハマった箇所をまとめてみたい。

3.0についてはまだ登場してから時間も経ってないためか、日本語で有益な記事は少ない。が、まずは本家をよく読むこと推奨。一応これに則れば出来るようになっているはずではある。他に個人の方がまとめたハウツーで参考になったのは以下の記事。すでに2.0を使っている人ならよりわかりやすいのだろうが、これがCapistrano初体験となる自分にはちょっとわかりにくかったりも、した。特に3番目の記事は完全に2.0からの移行組に向けて書かれた内容。

Capistranoとは?

そもそも論。開発完了したアプリをサーバーにデプロイするときの手順をRubyで書いて自動化しておくためのもの。デプロイ先はステージング環境とか本番環境とか何種類か存在する場合もあるが、環境ごとにデプロイ手順は別々に設定したりできる。なお、Rails用のツールだと思われがちだが、3.0からRails以外でも使える汎用的なツールになったらしい。自分の場合はRailsで使っているのであしからず。あと、SCMはGitを使っている必要がある。

インストール

インストールはGemで行う。デプロイ予定のRailsアプリで、Gemfileに以下追記してbundle install。

group :development do
  gem 'capistrano', '~> 3.1.0'
  gem 'capistrano-rails', '~> 1.0.0'
  gem 'capistrano-bundler', '~> 1.1.2'
end

capistranoが本体。先の本家によれば、Railsで使う場合はcapistrano-railsも必要になるとのこと。他にもいろいろ便利なプラギンがあったりするっぽいけど、わかんないので今はここまで。

初期作業

$ bundle exec cap install

初期ファイルの生成。手元の本にはcapify .コマンドだと載っていたのだが、capistrano 3.0から変わったらしい。ほあ。これによりCapfileと、config配下にdeploy.rb、さらにconfig/deploy配下にproduction.rbとstaging.rbが作成される。それぞれの役割は次の通り。

  • Capfile : 他のGem(capistrano-railsとか)の読み込みなどを記述
  • config/deploy.rb : デプロイタスクを記述
  • config/deploy/hoge.rb : 各サーバー情報を記述

Capfile編集

capistrano-railsと同bundlerを読み込むため、Capfileを開いて以下追記。

require 'capistrano/rails'
require 'capistrano/bundler'

config/deploy.rb編集

いろいろコメントアウトしてあってどれを使えばいいかよくわかんなかったりもするのだが、最低限以下は必要っぽい。

set :application, 'hoge'
set :repo_url, 'git@github.com:chroju/hoge'
set :deploy_to, '/var/www/hoge'
set :scm, :git

上から。1行目はアプリケーション名。これはどこかで使われる設定ではないようなので、適当に入れておけばよさそう。2行目はGithubリポジトリURL。3行目はデプロイ先となるサーバー上のディレクトリパス指定。ここではApacheの設定にならってみた。4行目は使用しているSCMの指定。とはいえ、現状Gitしか対応していないのでこれ以外の書き方は不可。

ファイル後半にはデプロイタスクのデフォルト設定が書かれている。一応、ここはそのままでも動くみたい。自分で何か特別なことをやらせたい場合は当然ながら編集する必要があるが、まだその域に至ってないです。

config/deploy/hoge.rb編集

ここではステージング環境としてvagrantを使うものと仮定。WebサーバーやDBサーバーを分けたりはせず、1台のサーバーですべてをまかなうこととする。よってstaging.rbを編集する。このファイルもデフォルトでいろいろ書かれてはいるのだが、とりあえず次の4行があればOK。

server '127.0.0.1', port: 2222, user: 'vagrant', roles: %w{web, app, db}, ssh_options: {
  user: 'vagrant',
  keys: %w(~/.vagrant.d/insecure_private_key),
  auth_methods: %w(publickey)
}

server〜で1台のサーバーの設定を一括で書ける。言わずもがな、まず対象IP。portがポート番号、userがSSHログインするユーザー名。roleはこのサーバーの役割を書く。web、app、dbの中から当てはまるものを書けばいいようだが、allという指定もできるとかできないとか。ssh_options:でさらに詳しい内容を記述。userはさっきのと被ってるので不要な気がするのだが、他のサイトを見てたら書いている設定があったので取りあえず入れてる。keysは認証に使うキーのパス。auth_methods:で認証方法を指定しているが、通例publickey認証とする。 ここに書いた設定の大半はvagrant ssh-configコマンドで拾えるので、自分の環境に合った設定に書き換えて下さい。

デプロイ実行

で、あとはデプロイでいけちゃうみたいです。

bundle exec staging deploy

stagingをproductionに換えればproduction.rbの設定に則ります。あと、末尾に:checkを付けるとコマンドが通るかコールドでチェックしてくれるので、まずは:checkをした方がいい。あと、cap -Tで実行可能なコマンドがずらずら出てくる。デプロイのロールバックしたり、rake db:migrateを走らせたり、結構いろんなことができるので一度見ておくと吉。

ハマった点

自分の場合は以上の手順で上手くいきませんでした。何点かハマったポイントをば。

デプロイ先ディレクトリのパーミッション

大変アホみたいな話だが最初これでmkdirが上手くいかなかった。公式ドキュメントのココに書いてあるけど、SSHユーザーはsudoなしでデプロイ先ディレクトリを操作できる権限がなきゃダメです。vagrantユーザーは~/var配下の操作権限なかったので弾かれた次第。。。

対応としてはさっきのリンク先に書いてある手順通りsgidの設定とかしてあげたらなんとかなりました。あるいはパスワードなしでsudo実行可能にしてやっても動くみたいだけど、当然ながらサーバーセキュリティ的にはNGなので。まぁvagrantであればどうでもいいかもしんないけど。

SQLiteのエラー

deploy中にsqlite3.hが存在しないよというエラーが発生。このページに書いてあるのとまったく同じ症例だったので、同様の対策をしてあげて解決。これは俺のvagrant環境の準備に不備があったってことでいいんだろうか。根本原因がイマイチ謎。

rake db:migrateのエラー

そして現在進行形で未解決なのがこのエラー。デフォルトのデプロイタスクでは途中でデプロイ先サーバー上でRAILS_ENV=staging bundle exec rake db:migrateを叩くんだけど、これが次のエラーで停まってしまう。

DEBUG [4be84d91]        rake aborted!
DEBUG [4be84d91]        database configuration does not specify adapter

いくつか思い当たった原因に取り組んではみたのだが、なおエラーが起きている。

  • RAILS_ENV=hogeを追加した場合、Gemfileのgroup :hogeを見に行くのだが、stagingグループを用意していなかった(参考)。
  • database.ymlにstagingの設定を用意していなかった。

たぶんRAILS_ENV周りの設定が何かおかしいと思うのだが、わからないので取りあえず保留にしている。というか、これに行き当たるまでRAILS_ENVのことを全然知らなかった自分にも問題大有りなので、一度Railsの勉強に立ち返ってみている次第。知識がないと、連鎖的に他のこともできなくなってくるなぁというのを痛感している。