kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

Redmineのバージョンアップ0.8.0から2.5.1へ

0.8系から2.5系へバージョンアップしました。

移行元はWebサーバ、DBサーバ別にしてありました。
移行元のWebサーバにはサービスの開発環境もあり、別環境に移すことが目的で、
そのついでにバージョンアップしようというものです。
移行先は、Web、DB一緒のサーバですが、どうせならやったことのない環境に乗せたかったので、
nginx+unicornで動かしています。

移行元の環境

  • Webサーバ(AWS EC2にCentOSをインストールしたもの)
    • Apache
    • passenger
      • /usr/local/redmineにインストールされている
  • DBサーバ(AWS EC2にCentOSをインストールしたもの)

移行先の環境

移行先サーバにバックアップファイルを転送

必要なバックアップを取得します。

保存先を作る

移行先サーバでの作業

ssh ec2-user@移行先サーバ
cd /home/ec2-user
mkdir work
dbをバックアップ

ここから移行元データベースサーバでの作業

mysqldump --opt --default-character-set=binary redmine > /tmp/redmine_backup.sql
scp /tmp/redmine_backup.sql ec2-user@移行先サーバ:///home/ec2-user/work/

web側のものを転送

ここから移行元Webサーバでの作業
configはどうせ変わるので移行しません。
必要な場合は、config配下も転送します。

cd /usr/local/redmine
scp -r ./files ec2-user@移行先サーバ:///home/ec2-user/work/

移行先サーバにrubyのインストール

ここから移行先サーバでの作業

rubyが入っているかどうか確認

ruby -v
ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux]

入ってました。
しかし、redmineの2.5系をいれるためには、ruby2.0が必要なので更新
rbenvをつかってruby2.0を入れる
参考サイト
Amazon Linuxでrbenvを使ってRuby2.0を入れてみた。 - xxxcaqui.log
AmazonLinuxにrbenvでrubyを入れてみる · mechamogera/MyTips Wiki · GitHub

rbenvをインストール

必要なものを先にインストール

sudo yum install gcc git openssl-devel

共通の場所に入れたかったので、home配下ではなく、/usr/local配下に入れます。

cd /usr/local
sudo git clone git://github.com/sstephenson/rbenv.git rbenv
echo 'export RBENV_ROOT=/usr/local/rbenv' >> ~/.bash_profile
echo 'export PATH=$RBENV_ROOT/bin:$PATH' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

ここでエラー

mkdir: cannot create directory ‘/usr/local/rbenv/shims’: Permission denied
mkdir: cannot create directory ‘/usr/local/rbenv/versions’: Permission denied

それぞれを作っておきます。

sudo mkdir /usr/local/rbenv/shims /usr/local/rbenv/versions
source ~/.bash_profile

パスを通しておく

sudo visudo

以下、編集内容

#add start
Defaults    env_keep += "RBENV_ROOT"
#add end

#mod start
#Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin
Defaults    secure_path = /usr/local/rbenv/shims:/usr/local/rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin
#mod end

バージョンの確認

rbenv --version
rbenv 0.4.0-97-gfe0b243
ruby-buildをインストール

rbenvでrubyをインストールするときに必要なので、ruby-buildをインストールします。

sudo mkdir /usr/local/rbenv/plugins
cd /usr/local/rbenv/plugins
sudo git clone git://github.com/sstephenson/ruby-build.git
rubyをインストール

インストール可能なバージョン確認を確認します。

rbenv install -l

rubyバージョンの見方
参考サイト
Rubyのバージョン体系と安定版、開発版の見分け方 - 一歩前進
2.0系の最新安定版をインストールします。

sudo rbenv install 2.0.0-p481


上記コマンドを打った後、下記の表示が出てから長時間待たされました。30分くらい。
ec2のmicroインスタンスだからかな。docも入れているから余計かもしれません。

Downloading ruby-2.0.0-p481.tar.gz...
Installing ruby-2.0.0-p481...

心配だったら、ログが出ているかどうか確認します。最長でも5分くらいで次のログが出るようになります。

tail -f /tmp/ruby-build.latest.log

でログを確認して、進んでいるようなら見守ります。
最後に下記が出れば問題ないです。

Installed ruby-2.0.0-p481 to /usr/local/rbenv/versions/2.0.0-p481

インストールが終わったら、各種設定をします。

sudo rbenv rehash
sudo rbenv global 2.0.0-p481

各種確認。

ruby -v
ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-linux]
which gem
/usr/local/rbenv/shims/gem

redmineの準備

databaseを作っておく
mysql -u root
create database redmine  default character set utf8;
grant all on redmine.* to redmine@localhost identified by '************';
flush privileges;
取得して、展開して、オーナー、権限変更して、シンボリックリンクを貼る
cd /home/ec2-user/
sudo wget http://www.redmine.org/releases/redmine-2.5.1.tar.gz
sudo tar zxf redmine-2.5.1.tar.gz
sudo chown -R nginx.nginx redmine-2.5.1
chmod -R 755 files log tmp plugins
sudo ln -s redmine-2.5.1 redmine

ここからnginxユーザーで作業

sudo su nginx
cd /home/ec2-user/redmine
unicornの準備
echo "gem 'unicorn'" >> Gemfile
bundle install

redmineを動かすのに必要なものをインストール。本番だけあればいいので、development、testは除きました。

bundle install --without development test


エラーが発生しました。

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

/usr/local/rbenv/versions/2.0.0-p481/bin/ruby extconf.rb
checking for ruby/thread.h... yes
checking for rb_thread_call_without_gvl() in ruby/thread.h... yes
checking for rb_thread_blocking_region()... yes
checking for rb_wait_for_single_fd()... yes
checking for rb_hash_dup()... yes
checking for rb_intern3()... yes
-----
Using mysql_config at /usr/bin/mysql_config
-----
checking for mysql.h... no
checking for mysql/mysql.h... no
-----
mysql.h is missing.  please check your installation of mysql and try again.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/local/rbenv/versions/2.0.0-p481/bin/ruby
        --with-mysql-dir
        --without-mysql-dir
        --with-mysql-include
        --without-mysql-include=${mysql-dir}/include
        --with-mysql-lib
        --without-mysql-lib=${mysql-dir}/
        --with-mysql-config
        --without-mysql-config


Gem files will remain installed in /usr/local/rbenv/versions/2.0.0-p481/lib/ruby/gems/2.0.0/gems/mysql2-0.3.15 for inspection.
Results logged to /usr/local/rbenv/versions/2.0.0-p481/lib/ruby/gems/2.0.0/gems/mysql2-0.3.15/ext/mysql2/gem_make.out
An error occurred while installing mysql2 (0.3.15), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.3.15'` succeeds before bundling.


mysql2がインストールできないって言われているので、インストールに必要なモジュールをyumで入れます。

sudo yum install mysql-devel


もう一度実行

bundle install --without development test

またエラーが発生しました。

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

    /usr/local/rbenv/versions/2.0.0-p481/bin/ruby extconf.rb
checking for Ruby version >= 1.8.5... yes
checking for gcc... yes
checking for Magick-config... no
Can't install RMagick 2.13.2. Can't find Magick-config in /usr/local/rbenv/versions/2.0.0-p481/bin:/usr/local/rbenv/libexec:/usr/local/rbenv/plugins/ruby-build/bin:/usr/local/rbenv/shims:/usr/local/rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin

*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/local/rbenv/versions/2.0.0-p481/bin/ruby


Gem files will remain installed in /usr/local/rbenv/versions/2.0.0-p481/lib/ruby/gems/2.0.0/gems/rmagick-2.13.2 for inspection.
Results logged to /usr/local/rbenv/versions/2.0.0-p481/lib/ruby/gems/2.0.0/gems/rmagick-2.13.2/ext/RMagick/gem_make.out
An error occurred while installing rmagick (2.13.2), and Bundler cannot continue.
Make sure that `gem install rmagick -v '2.13.2'` succeeds before bundling.

今度はrmagickが入らないって怒られたので、それも必要なものをyumで入れておきます。

yum install ImageMagick-devel

もう一度実行

bundle install --without development test

今度は通りました。

各種設定変更

databaseの設定

cp config/database.yml.example config/database.yml
vim config/database.yml

編集内容

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "************"
  encoding: utf8

メールの設定
Gmailで飛ばすように変えました。

cp config/configuration.yml.example config/configuration.yml
vi config/configuration.yml

編集内容

# default configuration options for all environments
default:
  # Outgoing emails configuration (see examples above)
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      enable_starttls_auto: true
      address: "smtp.gmail.com"
      port: 587
      domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
      authentication: :plain
      user_name: "your_gmail_account@gmail.com"
      password: "*******"
データ以降の下準備

移行する前に、2.5系のdatabaseのスキーマがほしいので、データが空の状態でmigrateしておく
後でこのスキーマを参照します。

bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate

空のdatabaseのdumpをとります。

mysqldump --opt redmine > /tmp/redmine_empty.sql
データの移行

添付ファイルを戻します。
移行先サーバに持ってきておいたファイルをコピーします。

cp -pr /home/ec2-user/work/files/* ./files/


databaseのmigrateでつまずくのは2パターンでした。

パターン1 新規追加のカラム?

多分、特定のテーブルに新規のカラムが存在しており、その関係でエラーがでるもの。
このタイプは、一旦、そのカラムをmysqlから直接追加しておきます。
その後、後述するテーブル削除のエラーがなくなったあと、追加したカラムを削除することで対応できるようです。
インデックスを削除するというような情報があったりしたのですが、削除しろというインデックス自体存在しなかったんで、
バージョン違いの問題なのかもしれません。

下記のようなエラーが出ます。

最初下記のようなエラーが出ます。

undefined method `inherit_members_changed?'
Unknown column 'is_default' in 

カラムを追加して、上記を解決してもdb:migrateが進んで行くと
後で、下記のようなエラーが出ます。

Duplicate column name 'is_default'

パターン2 テーブルがすでに存在する

すでに存在するテーブルを再度追加しようとするみたいです。
単純にそのテーブルを削除することで対応出来ました。

下記のようなエラーが出ます。

Table 'member_roles' already exists


大まかな手順としては、rake db:migrateを実行して、
パターン1のエラーが出たら、さっきとっておいた、空のdatabaseのスキーマを参照して、対象のテーブルにカラムを追加する。
パターン2のエラーが出たら、単純にテーブルをdropする。
パターン1で追加したカラムが原因でカラムの重複が起こるので、追加したカラムをdropする。

という手順で、最終的にはうまくいきました。

以下実作業

移行元でとったバックアップを復元します。

mysql -u root redmine < /home/ec2-user/work/redmine_backup.sql
RAILS_ENV=production bundle exec rake db:migrate


最初のエラー

undefined method `inherit_members_changed?' for #/home/ec2-user/redmine-2.5.1/app/models/project.rb:83:in `block in '
/home/ec2-user/redmine-2.5.1/app/models/project.rb:423:in `block in rebuild_tree!'
/home/ec2-user/redmine-2.5.1/app/models/project.rb:421:in `rebuild_tree!'
/home/ec2-user/redmine-2.5.1/db/migrate/105_build_projects_tree.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

カラムを追加します。後でdropします。

alter table projects add column inherit_members boolean default false;

再度実行します。
次のエラー

Mysql2::Error: Unknown column 'is_default' in 'where clause': SELECT  `repositories`.* FROM `repositories`  WHERE `repositories`.`project_id` = 3 AND (is_default = 1) LIMIT 1/home/ec2-user/redmine-2.5.1/app/models/project.rb:1043:in `set_or_update_position_under'
/home/ec2-user/redmine-2.5.1/app/models/project.rb:424:in `block (2 levels) in rebuild_tree!'
/home/ec2-user/redmine-2.5.1/app/models/project.rb:424:in `each'
/home/ec2-user/redmine-2.5.1/app/models/project.rb:424:in `block in rebuild_tree!'
/home/ec2-user/redmine-2.5.1/app/models/project.rb:421:in `rebuild_tree!'
/home/ec2-user/redmine-2.5.1/db/migrate/105_build_projects_tree.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

カラムを追加します。後でdropします。

alter table repositories add column`is_default` tinyint(1) DEFAULT '0';

再度実行します。
次のエラー

Mysql2::Error: Table 'member_roles' already exists: CREATE TABLE `member_roles` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `member_id` int(11) NOT NULL, `role_id` int(11) NOT NULL) ENGINE=InnoDB/home/ec2-user/redmine-2.5.1/db/migrate/20090503121501_create_member_roles.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

dropします。

drop table member_roles;

再度実行します。
次のエラー

Mysql2::Error: Table 'groups_users' already exists: CREATE TABLE `groups_users` (`group_id` int(11) NOT NULL, `user_id` int(11) NOT NULL) ENGINE=InnoDB/home/ec2-user/redmine-2.5.1/db/migrate/20090704172355_create_groups_users.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

dropします。

drop table groups_users;

再度実行します。
次のエラー

Mysql2::Error: Table 'changeset_parents' already exists: CREATE TABLE `changeset_parents` (`changeset_id` int(11) NOT NULL, `parent_id` int(11) NOT NULL) ENGINE=InnoDB/home/ec2-user/redmine-2.5.1/db/migrate/20110902000000_create_changeset_parents.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

dropします。

drop table changeset_parents;

再度実行します。
次のエラー

Mysql2::Error: Duplicate column name 'is_default': ALTER TABLE `repositories` ADD `is_default` tinyint(1) DEFAULT 0/home/ec2-user/redmine-2.5.1/db/migrate/20120115143100_add_repositories_is_default.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

先ほど追加したカラムをdropします。

alter table repositories drop column is_default;

再度実行します。
次のエラー

Mysql2::Error: Duplicate column name 'inherit_members': ALTER TABLE `projects` ADD `inherit_members` tinyint(1) DEFAULT 0 NOT NULL/home/ec2-user/redmine-2.5.1/db/migrate/20130202090625_add_projects_inherit_members.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

先ほど追加したカラムをdropします。

alter table projects drop column inherit_members;

再度実行します。
次のエラー

Mysql2::Error: Table 'queries_roles' already exists: CREATE TABLE `queries_roles` (`query_id` int(11) NOT NULL, `role_id` int(11) NOT NULL) ENGINE=InnoDB/home/ec2-user/redmine-2.5.1/db/migrate/20130602092539_create_queries_roles.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

dropします。

drop table queries_roles;

再度実行します。
次のエラー

Mysql2::Error: Table 'custom_fields_roles' already exists: CREATE TABLE `custom_fields_roles` (`custom_field_id` int(11) NOT NULL, `role_id` int(11) NOT NULL) ENGINE=InnoDB/home/ec2-user/redmine-2.5.1/db/migrate/20130713104233_create_custom_fields_roles.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

dropします。

drop table custom_fields_roles;

ここで正常終了しました。

nginxとunicornの設定

nginxにバーチャルホストの設定を追加します。

vi /etc/nginx/conf.d/virtual.conf
#redmine
upstream redmine {
    ip_hash;
    server 127.0.0.1:8081;
}

server {
    listen      80;
    server_name redmine.example.com;
    charset     utf-8;
    location / {
        root /home/ec2-user/redmine/public;
        if (-f $request_filename) { break; }
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://redmine/;
    }
}

redmine配下にunicorn.rbを新しく作ります。
microインスタンスなので、プロセス数は1にしてあります。

vi /home/ec2-user/redmine/config/unicorn.rb

内容

#マニュアル http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen

worker_processes 1
user "nginx", "nginx"

#nginxからはポート8081で受け付ける
listen 8081, :tcp_nopush => true
pid File.expand_path("tmp/pids/unicorn.pid", ENV['RAILS_ROOT'])

timeout 60

#ダウンタイムをなくす
preload_app true

stdout_path File.expand_path("log/unicorn.stdout.log", ENV['RAILS_ROOT'])
stderr_path File.expand_path("log/unicorn.stderr.log", ENV['RAILS_ROOT'])

GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

before_fork do |server, worker|
  #この設定はpreload_app trueの場合に必須
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{server.config[:pid]}.oldbin"
    if old_pid != server.pid
      begin
        sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
        Process.kill(sig, File.read(old_pid).to_i)
      rescue Errno::ENOENT, Errno::ESRCH
      end
    end

    sleep 1
  end

after_fork do |server, worker|
  #この設定はpreload_app trueの場合に必須
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

参考
Class: Unicorn::Configurator
Redmine on unicorn on nginx with mysql, my configuration files.
Ubuntu 12.04 + ruby 1.9.3 + nginx + unicorn + Redmine - くじらにっき++
Redmine を Nginx + Unicorn で動かす - 明日から本気出すブログ

Unicornの起動スクリプト

sudo vi /etc/init.d/unicorn_redmine

中身
参考
猫ぐらし: 自分でインストールしたサービスを chkconfig --add するためには

#!/bin/sh

# chkconfig: - 85 15
# description: redmine on unicorn start/stop script.
# processname: unicorn_redmine

export PATH=/usr/local/rbenv/shims:/usr/local/rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin

# move to project root directory
NAME=redmine
ENVIROMENT=production

ROOT_DIR="/home/ec2-user/redmine"
PID="${ROOT_DIR}/tmp/pids/unicorn.pid"
CONF="${ROOT_DIR}/config/unicorn.rb"
 
start()
{
  if [ -e $PID ]; then
    echo "$NAME already started";
    exit 1;
  fi
  echo "start $NAME";
  cd $ROOT_DIR
  bundle exec unicorn_rails -c ${CONF} -E ${ENVIROMENT} -D
}
 
stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    exit 1;
  fi
  echo "stop $NAME";
  kill -QUIT `cat ${PID}`
  rm -f $PID
}
 
force_stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    exit 1;
  fi
  echo "stop $NAME";
  kill -TERM `cat ${PID}`
  rm -f $PID
}
 
reload()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    start
    exit 0;
  fi
  echo "reload $NAME";
  kill -HUP `cat ${PID}`
}
 
restart()
{
    stop
    start
}
 
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  force-stop)
    force_stop
    ;;
  reload)
    reload
    ;;
  restart)
    restart
    ;;
  *)
    echo "Syntax Error: release [start|stop|force-stop|reload|restart]"
    ;;
esac

実行権限付与

sudo chmod+x /etc/init.d/unicorn_redmine

unicorn起動

sudo service unicorn_redmine start

unicronをchkconfigに登録して自動起動設定

sudo chkconfig unicorn_redmine on


nginxの設定再読み込み

sudo service nginx reload


http://redmine.example.com

にアクセスして確認します。


その他参考サイト
Ubuntu12.04 LTS + Nginx + MySQLでRedmineを動かすメモ | ブログ :: Web notes.log
UbuntuでRedmineとGitlabをNGINXでホスティングする - slowbirds.d.hatena
Redmine を Nginx + Unicorn で動かす - 明日から本気出すブログ
ruby2.1 - Redmine のサーバー(nginx + unicorn)起動方法 - Qiita