Vagrant で Symfony2 の環境を構築する
Symfony2 を扱う必要が出てきたので構築のメモ
Vagrant の設定
下記の Vagrantfile を利用した
GitHub - simshaun/symfony-vagrant: Deprecated. Check out Puphpet.
こいつを、適当なフォルダ作ってぶちこむ。
$ cd /path/to/work/folder $ git clone --recursive git://github.com/simshaun/symfony-vagrant.git my_symfony_folder $ cd my_symfony_folder
Vagrantfile を覗くと、デフォルトだと上記で作ったフォルダが symfony のプロジェクトルートになっているっぽい。それだとREADMEとかが上書きされちゃうし、プロジェクトファイルが散らかっちゃって管理しにくいので、別のフォルダをつくってそこに入れた。Vagrantfile の web_apps をいじればいいっぽい。バーチャルホストも切れるっぽい。
# vagrant/Vagrantfile
web_apps = { - "project1" => { - "host_project_folder" => "../", - "guest_project_folder" => "/home/vagrant/web-app", - "guest_docroot" => "/home/vagrant/web-app/web", - "server_name" => "localhost", - "server_aliases" => ["*.localhost"], - "php_timezone" => "America/New_York" - } + "site1" => { + "host_project_folder" => "../app1/", + "guest_project_folder" => "/home/vagrant/site1", + "guest_docroot" => "/home/vagrant/site1/web", + "server_name" => "site1", + "server_aliases" => ["*.site1"], + "php_timezone" => "Asia/Tokyo" + }, + "site2" => { + "host_project_folder" => "../app2/", + "guest_project_folder" => "/home/vagrant/site2", + "guest_docroot" => "/home/vagrant/site2/web", + "server_name" => "site2", + "server_aliases" => ["*.site2"], + "php_timezone" => "Asia/Tokyo" + } }
上記のように変更したら、共有先のフォルダを作る。Composer で Symfony2 を落としてくる。
Composer のインストール
色々インストール方法はあるけど、今回はComposerを使うようにした。
$ curl -sS https://getcomposer.org/installer | php -d detect_unicode=Off # エラー回避するために detect_unicode=Off に $ sudo chmod 755 composer.phar $ sudo mv composer.phar /usr/local/bin/composer
Symfony2 インストール
上記で app1、app2 とフォルダを設定したので、それぞれについて composer を実行する
$ composer create-project symfony/framework-standard-edition app1 2.4.2 $ composer create-project symfony/framework-standard-edition app2 2.4.2
最後の2.4.2はSymfonyのバージョン。今は2.4.2が最新らしい。インストール中に色々聞かれるけど、基本的に全部デフォルトでいいかも。DB の設定とかはあとで直せるし。
仕上げ
もしバーチャルホストを切っていたら、ホスト側にその名前を追加しておく。上記の設定なら
33.33.33.10 site1 33.33.33.10 site2
そしてプロビジョニング。
$ cd vagrant
$ vagrant up
どうやら上記のVagrantfileは共有フォルダだけしか反映されないようなので、apache の conf ファイルを書き足す。下記は一例
# vagrant ssh
# sudo rm /etc/apache2/site-enabled/localhost.conf
# sudo vi /etc/apache2/site-enabled/app1.conf
<VirtualHost *:80> ServerName site1 ServerAlias *.site1 DocumentRoot /home/vagrant/site1/web LogLevel debug ErrorLog /home/vagrant/site1/app/logs/error.log CustomLog /home/vagrant/site1/app/logs/access.log combined RewriteEngine On RewriteLog /home/vagrant/site1/app/logs/rewrite.log RewriteLogLevel 0 php_value date.timezone Asia/Tokyo <Directory /home/vagrant/site1/web> php_admin_value upload_tmp_dir /tmp/site1 </Directory> </VirtualHost>
設定ファイルの配置ができたら apache 再起動
$ sudo service apache2 restart
で、http://site1/ にアクセスするとサイトが見れるはず。ssh しなくてもホスト側の app1 や app2 以下のファイルをいじれば反映される。
この際、Symfony の新規インストールの場合は config.php にアクセスすることで設定をするが、ホストからアクセスする場合はIP制限がかかっていて設定できないため、下記のファイルにホスト側のIPを追記しておく
# app1/web/config.php
if (!in_array(@$_SERVER['REMOTE_ADDR'], array( '33.33.33.1', //この辺を追加 '127.0.0.1', '::1', ))) {
# app1/web/app_dev.php
if (isset($_SERVER['HTTP_CLIENT_IP']) || isset($_SERVER['HTTP_X_FORWARDED_FOR']) || !in_array(@$_SERVER['REMOTE_ADDR'], array('33.33.33.1', '127.0.0.1', 'fe80::1', '::1')) // IP を追加 ) {
そして、http://site1/config.php とかにホスト側のブラウザでアクセスし、設定していく。基本的には composer から入れていればこのへんの設定はできているはず。
あとはこの状態で git リポジトリに登録しておく
$ cd app1 $ git init $ git add . $ git rm --cached -r src/Acme* # Demoアプリは含めない $ git commit -m 'initial commit'
Sass とか Compass とか使えるようにする
このへん を参考にした
$ vagrant ssh $ sudo gem install sass --no-ri --no-rdoc $ sudo gem install compass --no-ri --no-rdoc $ exit
そして、config.yml の assetic の項目に次のように設定を追加
# vi app1/config/config.yml
assetic: debug: %kernel.debug% use_controller: false filters: sass: ~ compass: ~
そして、Twig テンプレートを使う際に、下記のように指定してあげる。
{% stylesheets filter="compass" "@DemoBundle/Resources/public/sass/main.sass" "@DemoBundle/Resources/public/sass/header.sass" "@DemoBundle/Resources/public/sass/footer.sass" %} <link rel="stylesheet" href="{{ asset_url }}" /> {% endstylesheets %}
CakePHP に Guard を導入
Guard とは何か?
Guard とは、ruby のライブラリ。ファイルの変更を監視して、変更を検知したら指定の処理を実行する。この性質を利用して、ファイルが変更されたらテストを実行するということに用いられる。
Guard のソースコード
guard のインストール
今回は Gemfile からインストールする。もし bundle をインストールしてなければ
gem install bundle --no-ri --no-rdoc
でインストールしておく。
CakePHP のプロジェクトの app 直下に Gemfile を作成しておく
$ cd {$CakePHP_Project}/app $ vim Gemfile
# Gemfile
source 'https://rubygems.org' group :development, :test do gem 'guard-shell' gem 'ruby_gntp' # 通知用のライブラリ. 監視だけならいらない end
app 直下で下記を実行する
$ bundle install
guard の設定
Gemfile と同じ階層に Guardfile を設置する。
# Guardfile guard :shell do watch(%r{Controller/(.+)\.php}) {|m| `Console/cake test app AllController --some-option`} watch(%r{Model/(.+)\.php}) {|m| `Console/cake test app AllModel`} end
watch 〜 の行が監視するファイルと、変更されたら実行するシェルスクリプトになっている。このへんは各自のプロジェクトによって変更する。Plugin をテストする場合は `Console/cake test Hoge AllPlugin` のようなシェルを実行するとよい。
監視を開始する
app 直下で、下記のコマンドを実行する
$ bundle exec guard
変更を通知する
ホストマシンで実行してるなら、このへんにあるライブラリを利用すればよい。
vagrant などで VM 上で監視している場合は、フックが必要。なぜか GNTP ではゲスト(Ubuntu13.10)からホスト(Windows7)への通知が notification: ではうまくいかなかった。適当に --log-tap オプションを追加して、テスト結果を tmp/tests フォルダ以下に出すようにして、そのファイルを監視して自力で通知した。なんかいい方法あったら教えてください。
# Guardfile
guard :shell do watch(%r{Controller/(.+)\.php}) {|m| `Console/cake test app AllController --log-tap tmp/tests/controller.tap`} watch(%r{tmp/tests/(.+)\.tap}) {|m| `ruby notify.rb`} end
そして通知用のスクリプトを Guardfile と同じ階層においた
# notify.rb
# coding: utf-8 require 'ruby_gntp' #growlを呼ぶためのgem f = open("tmp/tests/controller.tap") lastlog = f.read f.close GNTP.notify({ :app_name => "growl", :title => "Notice", :text => tap, :host => "192.168.56.1", :passwd => "pass" })
それから、Windows にも growl があるっぽいので下記を参考に growl をインストールした。
Ubuntu ServerのGuardからGrowl for Windowsにネットワーク経由で通知する | | Scimpr Blog
Error が起きる場合
下記を参考にして guard の 2.x 系をアンインストールする
【Laravel】ERROR - Could not load 'guard/phpunit' or find class Guard::Phpunitが出た時の対処法 - ikemonn's blog
$ gem list guard guard (2.5.1, 1.8.3) guard-phpunit (0.1.4) guard-shell (0.6.1) $ gem uninstall guard -v='2.5.1'
CakePHP 用のモナドプラグイン Monaca
CakePHP にモナドのライブラリ移植した。モナドがよくわかってないけど、Maybe ぐらいは使えないと今後やっていけないんじゃないかと思うぐらいに便利そうなので、勉強がてらそのへんに転がっていたライブラリを CakePHP2 に移植してみた。
ソースコードはこちら
使い方はとりあえず参考にしたライブラリの方の記事を訳す。
使い方
値をモナドでラッピングするには、コンストラクタか unit 関数を用いる。ラッピングしたモナドに対しては bind 関数を用いて関数呼び出しを行う。
App::uses('Identity', 'Monaca.Lib');
$monad = new Identity(1);
$monad->bind(function($value) { var_dump($value); });
// int(1) が表示される
bind 関数を用いた関数呼び出しは、新しくモナドインスタンスでラッピングされた値を返す。
App::uses('Identity', 'Monaca.Lib'); $monad = new Identity(1); $monad->bind(function($value) { return 2 * $value; })->bind(function($value) { var_dump($value); }); // Prints int(2)
PHP は純関数型言語ではないので、生の値を取り出す実装もしてある。
App::uses('Identity', 'Monaca.Lib'); $monad = new Identity(1); var_dump($monad->extract()); // Prints int(1)
Scala にある Option みたいな取り方も実装した。
- get($key)
- getOrElse($key, $default)
- getOrCall($key, callable $function)
- getOrThrow($key, Exception $exception)
第一引数のキーは、値が配列の場合にオフセットかキーを指定する。スカラ値の場合は null を渡せばいい。
Maybe モナド
有用なモナドのうちのひとつは Maybe モナドだ。Null である可能性がある値をラッピングすることでヌルポを防ぐ。
App::uses('Maybe', 'Monaca.Lib'); $monad = new Maybe(1); $monad->bind(function($value) { var_dump($value); }); // int(1) が表示される $monad = new Maybe(null); $monad->bind(function($value) { var_dump($value); }); // 処理されない
List モナド
リストに何か処理をしたい場合はこれを使うといい。
App::uses('ListMonad', 'Monaca.Lib'); $monad = new ListMonad(array(1, 2, 3, 4)); $doubled = $monad->bind(function($value) { return 2 * $value; }); var_dump($doubled->extract()); // array(2, 4, 6, 8) が表示される
ただし、このクラスを用いる場合は配列以外を渡すと例外がおこるので気を付けること。
また、連想配列の場合は HashMonad を使う。
Defered
バッチかなんかでは Deferred も有用だ。
App::uses('Deferred', 'Monaca.Lib'); $promise = new Deferred(); $success = function($result) { Log('success!'); }; $failure = function($result) { LogError('error!'); }; $promise->when($success, $failure); $some_long_process = function($promise) { // 処理が成功 $result = 'whatever the process result'; if (true) { $promise->succeed($result); } else { $promise->fail($result); } }
コンポジション
これらのモナドの中身はさらにモナドでラッピングでき、非常に有用である。
$monad = new ListMonad(array(1, 2, 3, null, 4)); $newMonad = $monad->bind(function($value) { return new Maybe($value); }); $doubled = $newMonad->bind(function($value) { return 2 * $value; }); var_dump($doubled->extract()); // array(2, 4, 6, null, 8);
これが多次元配列だった場合も
$monad = new ListMonad(array(array(1,2), array(3,4), array(5,6)); $newMonad = $monad->bind(function($value) { return new ListMonad($value); }); $doubled = $newMonad->bind(function($value) { return 2 * $value; }); var_dump($doubled->extract()); // array(array(2, 4), array(6, 8), array(10, 12))
また有用なunit関数のコールバック定数も用意してある。
$newMonad = $monad->bind(Maybe::UNIT); // $newMonad = $monad->bind(function($value) { return new Maybe($value); }); と同じ意味
実践
例えば、下記のような多次元配列を処理する場合
$posts = array( array("title" => "foo", "author" => array("name" => "Bob", "email" => "bob@example.com")), array("title" => "bar", "author" => array("name" => "Tom", "email" => "tom@example.com")), array("title" => "baz"), array("title" => "biz", "author" => array("name" => "Mark", "email" => "mark@example.com")), );
このような値の特定の値が欲しい場合、まず下記のような関数を実装する。
function index($key) {
return function($array) use ($key) {
return isset($array[$key]) ? $array[$key] : null;
};
}
そしてモナドを使う。
$postMonad = new ListMonad($posts); $names = $postMonad ->bind(Maybe::UNIT) ->bind(index("author")) ->bind(index("name")) ->extract();
モナドを使わないでの実装は、こんな感じになるはず。
$names = array(); foreach ($posts as $post) { if (isset($post['author'])) { if (isset($post['author']['name'])) { $names[] = $post['author']['name']; } } }
その他CakePHP用に追加した関数
CakePHP でモナドっぽく取得したらうまそうなやつを実装した。
・セッションの取得
・コンフィグの取得
・POST/GETパラメタの取得
・View の値の取得方法
ざっくり、MonadController を継承すると下記のような関数が使えるようになるので、AppController にこいつを継承させて使うことをお勧めする。ちなみに、CakePHP よろしくキーを 'foo.bar' のような形で指定しても array( 'foo' => array('bar' => 'baz') ) のようなオブジェクトのキーを指定できる。
HogeController.php
App::uses('MonadController', 'Monaca.Lib'); class HogeController extends MonadController { public function index() { // セッションをオプショナルに取得できる $aSession = $this->getSession('a'); $bSession = $this->getSessionOrElse('b', 'default value'); $cSession = $this->getSessionOrCall('c', function() { LogError('セッション C がないです'); return 'default'; }); $dSession = $this->getSessionOrThrow('d', new Exception('セッションDがないです')); // コンフィグをオプショナルに取得 // こちらも OrElse, OrCall, OrThrow を使える。用例は割愛。 $aConfig = $this->getConfig('a'); // POST/GET パラメタをオプショナルに取得 // こちらも OrElse, OrCall, OrThrow を使える。用例は割愛。 $aPost = $this->getPost('a'); $aGet = $this->getQuery('a'); // POST/GET どちらかを取得、同じキーがある場合はPOSTが優先 // こちらも OrElse, OrCall, OrThrow を使える。用例は割愛。 $aInput = $this->getInput('a'); } }
同じことが View でもできる。Controller から渡された値がない場合のデフォ値を利用できる。
こっちも getOrCall, getOrThrow は割愛。
<ul> <li><?php echo $this->getOrElse('hoge', 'fuga'); ?></li> </ul>
もしコントローラーが継承できない場合はコンポーネントにしてあるのでそれを使う。ビューはビュークラスを指定することで使える。
App::uses('MonadComponent', 'Monaca.Controller/Component'); App::uses('MonadView', 'Monaca.View'); class FooController extends Controller { $components = array('Monaca.Monad'); // MonadComponent をロードする public function __constructor($request = null, $response = null) { parent::__construct($request, $response); // viewClass を指定することで MonadView が使える $this->viewClass = 'Monaca.Monad'; } }
else のコメントの書き方
else のコメント、どこで書くのがいいかという話があったので、自分の思いを書いておく。
自分はこんな感じで書く。
// true の場合 if (hoge) { // なんかしょり } // false の場合 <- ここ! else { // あんなしょり }
一見すると見づらいかもしれないけど、最終的にこんな感じが今のところしっくりきてる。
理由はバージョン管理がしやすいしコードレビューもしやすいから。
例えば、下記のようにコメントを書いたとする。
if (hoge == 1) { foo(); } else { // 1 以外だとここに来ます! bar(); }
これだと、条件分岐が増えた場合は下記のようになる。
if (hoge == 1) { foo(); } else if (hoge == 2) { baz(); } else { // 1 か 2 以外だとここに来ます! bar(); }
この場合、diffは下記のようになる。
- } else { // 1 以外だとここに来ます!
+ } else if (hoge == 2) { + baz(); + } else { // 1 か 2 以外だとここに来ます!
シンプルだと何をやったかわかるけど、こういうのが増えると管理が大変になる。複雑になってくると、ひとつひとつ条件が変わったかを確認する必要がでてきちゃったりして効率がわるい。
なので、if 〜 else if 〜 else はそれぞれをブロックとして分離して書いといたほうがいいと思う。
if (hoge == 1) { foo(); } // hoge = 1 以外の処理! else { bar(); }
この場合なら、以下のように変更になった場合
if (hoge == 1) { foo(); } else if (hoge == 2) { baz(); } // hoge が 1 か 2 以外の処理! else { bar(); }
こんなかんじになるけど、diffとしては
- // hoge = 1 以外の処理!
+ else if (hoge == 2) { + baz(); + } + // hoge が 1 か 2 以外の処理!
こんな感じになる。
つまり、コメントが変更されたことによって差分はでるものの、この場合は else if ブロックが挿入されただけっていうのがわかりやすい。コードレビューも捗る。
ソースコードが信用できない
「他人の書いたコードは信用できない」
「昨日の自分は他人だ」
というのは、良く言われることだ。では、どこまで信用しないのか?
例えば、コードが信用できないのならば、そのシステム自体が信用できないだろう。つまり、それはリリースしてはいけないものだ。
システムが信用できないのならば、そのサービスも信用してはいけない。
さらに言えば、それを用いたサービスを展開する会社が信用できない。
その会社と取引している会社/地域/国が信用できない。人間ってなんだろう?
おちんぎんもらって生活していかねばならないので、そうそう疑ってもいられないだろう。ならば検証するしかない。そうだ、テストを書こう!・・・そのテストコード、信用できますか?テストする内容は妥当ですか?テストできてますか?機械語から検証できてますか?
う〜む・・・
そのへんのラインを自分にもつしかないんだろうか
Ubuntu 12.04 に GitLab 5.2 と Redmine 2.3 を構築
今まで CentOS でやってきたけど、Ubuntu で構築したらとても楽チンだったのでメモ
事前準備
まずは GitLab を下記のQiitaの記事とほぼ同じように構築
Ubuntu 12.04.2 LTSにGitLab5.2をインストール - Qiita
ついでにサービスに必要なものも全部いれてしまう。
# apt-get update # apt-get upgrade # apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev \ libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl git-core \ openssh-server redis-server checkinstall libxml2-dev libxslt-dev \ libcurl4-openssl-dev libicu-dev postfix python nginx \ imagemagick libmagickcore-dev libmagickwand-dev \ mysql-server mysql-client libmysqlclient-dev
デフォルトのrubyは削除しておく。共用のサーバーとか個人のマシンとかならrbenvとかrvmとかで共存させてもいいとおもう。
# apt-get remove ruby1.8
ruby のインストール
GitLab が ruby2.0 よりも1.9.3のがいいらしいので、そっちを入れる。
# mkdir /tmp/ruby && cd /tmp/ruby # curl --progress http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p429.tar.gz | tar xz # cd ruby-1.9.3-p429 # ./configure # make # make install # gem install bundler --no-ri --no-rdoc
GitLab のインストール
Gitユーザーを追加
# adduser --disabled-login --gecos 'GitLab' git
git ユーザーで作業をする
# sudo su git # cd /home/git
gitlab-shell をインストール
# git clone https://github.com/gitlabhq/gitlab-shell.git # cd gitlab-shell/ # git checkout -b v1.5.0 v1.5.0 # cp config.yml{.example,} # vim config.yml # ドメインやSSLを使うならそのへんの設定を変更する # ./bin/install # cd ../
gitlab をデプロイ
# cd /home/git # git clone https://github.com/gitlabhq/gitlabhq.git gitlab # cd /home/git/gitlab # git checkout -b v5.2.1 v5.2.1 # cp config/gitlab.yml{.example,} # vim config/gitlab.yml # いろいろ設定 # chown -R git log/ # ログや一時ディレクトリを作成して書き込み可能にしておく # chown -R git tmp/ # mkdir tmp/pids/ # mkdir tmp/sockets/ # mkdir public/uploads # chmod -R u+rwX log/ # chmod -R u+rwX tmp/ # chmod -R u+rwX public/uploads # cp config/puma.rb{.example,} # git config --global user.name "GitLab" # Gitの設定 # git config --global user.email "gitlab@example.com"
MySQLの設定
# cp config/database.yml{.mysql,} # mysql -u root -p
ここで $password は任意のものを入力し、上記のdatabase.ymlにそのパスワードを記述すること。
mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost'; mysql> exit;
接続チェック
# mysql -u gitlab -p -D gitlabhq_production
gem から必要なものをインストール
# gem install charlock_holmes --version '0.6.9.4' --no-ri --no-rdoc # bundle install --deployment --without development test postgres # bundle exec rake gitlab:setup RAILS_ENV=production
ここのあたりで失敗する場合は、git ユーザーで gitlab-shell の ./bin/install を実行するのを忘れてたりするはず。
あとは起動チェック。
# bundle exec rake gitlab:env:info RAILS_ENV=production # bundle exec rake sidekiq:start RAILS_ENV=production
起動スクリプトの設置
# su # cp lib/support/init.d/gitlab /etc/init.d/gitlab # chmod +x /etc/init.d/gitlab # update-rc.d gitlab defaults 21
こんなかんじでGitlabの設置は完了。
Redmine
redmine は /home/ 直下じゃなくて /home/project/redmine に作ります。なんとなくユーザー領域にサービスがボコボコできるのいやなんです。
ソースのダウンロード
# mkdir -m 755 /home/project # cd /home/project # git clone https://github.com/redmine/redmine.git # cd redmine
MySQL 設定
# cp config/database.yml{.example,} # mysql -u root -p
ここで $password は任意のものを入力し、上記のdatabase.ymlにそのパスワードを記述すること。
mysql> CREATE USER 'redmine'@'localhost' IDENTIFIED BY '$password'; mysql> CREATE DATABASE IF NOT EXISTS `redmine` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `redmine`.* TO 'redmine'@'localhost'; mysql> exit;
各種設定
今回はApache2じゃなくてnginxで動かす。せっかくなのでgitlabで作ったpumaファイルをコピーして流用する。
# cp /home/git/gitlab/config/puma.rb config/puma.rb # vim config/puma.rb # gitlab 用の設定を redmine 用に変更 # vim Gemfile # puma をインストールするために " gem "puma", '~> 2.0.1' " を追加 # cp -pR config/configuration.yml{.example,} # vim config/configuration.yml # 各種設定
# bundle install --without development test postgresql sqlite # rake generate_secret_token
redmine インストール
# RAILS_ENV=production rake db:migrate # RAILS_ENV=production rake redmine:load_default_data # mkdir tmp public/plugin_assets # chown -R www-data:www-data /home/project # chmod -R 755 files log tmp public/plugin_assets
起動スクリプト作成
これもせっかくなのでgitlabのを流用。ちょっと変更した点が多かったのでソースそのまま乗っけてみる。
#! /bin/bash # Redmine # Maintainer: @randx # App Version: 2.3 ### BEGIN INIT INFO # Provides: redmine # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Redmine ticket management service # Description: Redmine ticket management service ### END INIT INFO APP_ROOT="/home/project/redmine" APP_USER="www-data" DAEMON_OPTS="-C $APP_ROOT/config/puma.rb" PID_PATH="$APP_ROOT/tmp/pids" SOCKET_PATH="$APP_ROOT/tmp/sockets" WEB_SERVER_PID="$PID_PATH/puma.pid" NAME="redmine" DESC="Redmine service" check_pid(){ if [ -f $WEB_SERVER_PID ]; then PID=`cat $WEB_SERVER_PID` STATUS=`ps aux | grep $PID | grep -v grep | wc -l` else STATUS=0 PID=0 fi } execute() { sudo -u $APP_USER -H bash -l -c "$1" } start() { cd $APP_ROOT check_pid if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then # Program is running, exit with error code 1. echo "Error! $DESC $NAME is currently running!" exit 1 else if [ `whoami` = root ]; then execute "rm $SOCKET_PATH/redmine.socket" execute "RAILS_ENV=production bundle exec puma $DAEMON_OPTS" execute "mkdir -p $PID_PATH > /dev/null 2>&1 &" echo "$DESC started" fi fi } stop() { cd $APP_ROOT check_pid if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then ## Program is running, stop it. kill -QUIT `cat $WEB_SERVER_PID` execute "mkdir -p $PID_PATH > /dev/null 2>&1 &" rm "$WEB_SERVER_PID" >> /dev/null echo "$DESC stopped" else ## Program is not running, exit with error. echo "Error! $DESC not started!" exit 1 fi } restart() { cd $APP_ROOT check_pid if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then echo "Restarting $DESC..." kill -USR2 `cat $WEB_SERVER_PID` execute "mkdir -p $PID_PATH > /dev/null 2>&1 &" if [ `whoami` = root ]; then execute "mkdir -p $PID_PATH > /dev/null 2>&1 &" fi echo "$DESC restarted." else echo "Error, $NAME not running!" exit 1 fi } status() { cd $APP_ROOT check_pid if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then echo "$DESC / Puma with PID $PID is running." else echo "$DESC is not running." exit 1 fi } ## Check to see if we are running as root first. ## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" exit 1 fi case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload|force-reload) echo -n "Reloading $NAME configuration: " kill -HUP `cat $PID` echo "done." ;; status) status ;; *) echo "Usage: sudo service redmine {start|stop|restart|reload}" >&2 exit 1 ;; esac exit 0
これを /etc/init.d/redmine においておく。そして設定。
# chmod +x /etc/init.d/redmine # update-rc.d redmine defaults 21
とりあえずこんな感じでredmineも動くはず
Nginx の設定
あとは適当にNginxの設定。gitlab と redmine をバーチャルホストで動かす。SSL利用するのでkeyとかcrtとか設定したけど、必要なければそのへんはコメントアウトでいいかも。
# /etc/nginx/sites-available/gitlab
# GITLAB # Maintainer: @randx # App Version: 5.0 upstream gitlab { server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; } server { listen *:443 default_server ssl; # e.g., listen 192.168.1.1:80; In most cases *:80 is a good idea ssl on; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; server_name gitlab.hogehoge.com; # e.g., server_name source.example.com; root /home/git/gitlab/public; # individual nginx logs for this gitlab vhost access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; location / { # serve static files from defined root folder;. # @gitlab is a named location for the upstream fallback, see below try_files $uri $uri/index.html $uri.html @gitlab; } # if a file, which is not found in the root folder is requested, # then the proxy pass the request to the upsteam (gitlab unicorn) location @gitlab { proxy_read_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694 proxy_connect_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694 proxy_redirect off; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://gitlab; } }
これと redmine
/etc/nginx/sites-available/redmine
# Redmine # Maintainer: @randx # App Version: 2.3 upstream redmine { server unix:/home/project/redmine/tmp/sockets/redmine.socket; } server { listen *:443 ssl; # e.g., listen 192.168.1.1:80; In most cases *:80 is a good idea ssl on; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; server_name redmine.hogehoge.com; # e.g., server_name source.example.com; root /home/project/redmine/public; # individual nginx logs for this gitlab vhost access_log /var/log/nginx/redmine_access.log; error_log /var/log/nginx/redmine_error.log; location / { # serve static files from defined root folder;. # @gitlab is a named location for the upstream fallback, see below try_files $uri $uri/index.html $uri.html @redmine; } # if a file, which is not found in the root folder is requested, # then the proxy pass the request to the upsteam (gitlab unicorn) location @redmine { proxy_read_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694 proxy_connect_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694 proxy_redirect off; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://redmine; } }
んで、起動するときは
# service gitlab restart # service redmine restart # service nginx restart
とかやる。
メールの設定
せっかくメールと連携できるサービスなので、postfix(送信のみ)を設定しておく。
まずデフォルトでsendmailがはいっていたりするので、それを削除。
# service sendmail stop # apt-get remove sendmail
んで、postfix の設定ファイルを少しいじる
# vim /etc/postfix/main.cf
このへんはTSL使うとか、ドメイン変えるとかを変更。
そして起動。
# service postfix start
送信をテストする
# mail unko@hoge.com
メール作成を完了するには、最後の行に「.」だけをうってエンターキーを押す。うまく送信できない場合は /var/log/mail.log あたりを参考に修正していく。
こんなかんじでひととおりできるはず。
rbenv + passenger のアップデートは要注意
なんか何回やっても rake-0.10.2 なんてねーよ!って怒られてて、本番環境が稼働してなかった。
原因としては、rbenv の ruby だけバージョンあげてて、passenger で利用する ruby の環境をアップデートしてなかったから。
元々 1.9.3-p286 で passenger をインストールしてたから、利用されるバイナリの設定は下記のように apache の conf に記述してた。
LoadModule passenger_module /usr/local/rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so PassengerRoot /usr/local/rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/passenger-3.0.18 PassengerRuby /usr/local/rbenv/versions/1.9.3-p286/bin/ruby
しかし、これが 1.9.3-p327 にあがると、下記のように記述しなきゃいけない。
LoadModule passenger_module /usr/local/rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so PassengerRoot /usr/local/rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/passenger-3.0.18 PassengerRuby /usr/local/rbenv/versions/1.9.3-p327/bin/ruby
はじめは動くんだけど、上の rake みたいに、元々は 0.9.2 使っててそれがバージョンアップしたらそこのパスにインストールされていないのでエラーが起こっちゃう。
rbenv で ruby をアップデートする際は、下記のようにするといいと思う。
$ ruby-build install $ rbenv install new-ruby $ rbenv global new-ruby $ rbenv rehash $ gem install passenger --no-ri --no-rdoc $ export PATH=/usr/local/apache2/bin:$PATH # httpd-devel が必要なのでパスを通しておく $ passenger-install-apache2-module # ここで上記のような設定に必要なものが出てくるので、それを apache の conf ファイルに記述。 $ /etc/init.d/httpd restart
ハマった・・・