グローバルIPアドレスが一つしかないサーバで本番用、開発用二つの WordPress を動かすために怠惰な私がやっている運用方法

#概要
タイトルのまんまです。
レンタルサーバを借りて WordPress を動かしていますが、開発環境も欲しくて、なんとか楽に開発したり、開発したものを本番に持ってったりできないか、グローバルIPアドレスを追加するのもお金かかってやだな、と考える、とにかく楽をしたい怠惰な私が、とりあえず運用している方法を晒してみます。

何か思い出したら加筆するかもしれません。

#概略
これだけ読めばいいのかも。[^1]

[^1]: 概要と概略は何が違うんだろう。

* 同じサーバに WordPress を二つ入れるよ。
* バーチャルホストを設定するよ。
* 開発環境にそれっぽいデータを準備するのは面倒なので、cron で daily に本番環境の DB から開発環境の DB にコピーするよ。
* DB のコピーは本番環境から Dropbox ディレクトリに dump、それを perl で書き換えつつ開発環境に restore、なんで自動的に DB のバックアップにもなっているよ。
* どっちもドキュメントルート以下を git で管理するよ。開発環境での開発結果は開発環境で git push して本番環境で git pull するよ。
* git のリモートリポジトリとして Drobox ディレクトリを設定してあるから、たまにそっちにも push しているから、ファイルのバックアップもできているよ。
* 画像データは基本的に Picasa ウェブアルバムに上げちゃうんで、実質ローカルにアップロードデータはない方針だよ。
* バックアップは直近 5 日分をローテーションで取っているみたい。今、スクリプトを見て、過去の私がやっていたことを知りました。

||本番環境|開発環境|
|:-:|:-:|:-:|
|URL|`http://mato.me/`|`http://wp2.mato.me`|
|WEB認証|公開だからもちろんなし|Digest認証でブロック|
|ドキュメントルート|/var/web/matome/wp/|/var/web/maome/wp2/|
|mysql DB 名|wp_matome|wp2_matome|
|mysql ユーザ名|wp_user|wp_user|
|mysql パスワード|開発環境と同じ|本番環境と同じ|

`/var/web/maome/wp2` というディレクトリは `/var/web/maome/wp2.curr` というディレクトリへのシンボリックリンクですが、理由は後述。 

## バックアップスクリプト

こんな感じです。

```bash:xbackup.sh
#!/bin/bash

script_dir=/home/takeyuki/Dropbox/matome
backup_dir=/home/takeyuki/Dropbox/matome
repo_dir=/home/takeyuki/Dropbox/matome
db_name=wp_matome
db_user=wp_user
db_password=__ないしょ__
dev_db_name=wp2_matome
log_file=${backup_dir}/backup_${db_name}.log
backup_file=${backup_dir}/${db_name}_`date "+%Y-%m-%d"`.dump
mysqldump=/usr/bin/mysqldump

log() {
  echo -e `date "+%Y-%m-%d %H:%M:%S"` "\t" $* >> $log_file
}

log start
$mysqldump -u $db_user -p$db_password $db_name > $backup_file
if [ $? -gt 0 ] ; then
  log failed to dump : "$db_name -> $backup_file"
   exit
fi
log dumped : "$db_name -> $backup_file"

git_repo=${repo_dir}/${db_name}.git

db_size=$(ls -l $backup_file | awk '{print $5}')
repo_size=$(du -sb ${git_repo} | awk '{print $1}')

human_readable() {
  byte=$1
  echo $byte | awk -f ${script_dir}/human_readable.awk
}

log db_size: $db_size "($(human_readable $db_size))" "\t" repo_size: $repo_size\
 "($(human_readable $repo_size))"

count=0
ls -t $backup_dir | grep -E "^${db_name}_[0-9]{4}-[0-9]{2}-[0-9]{2}\.dump\$" | \
while read file ; do
  if [ $count -eq 0 ] ; then
    # sed -e 's/http:\/\/mato\.me/http:\/\/wp2\.mato\.me/g' $backup_dir/$file |\
 mysql  -u $db_user -p$db_password $dev_db_name
    perl -e '$s="wp2";' -e '$f="http://mato.me";$t="http://$s.mato.me";$d=lengt\
h($t)-length($f);$f="(s:(\\\d+):\\\\\")?".quotemeta($f);while(<>){$_=~s/$f/$2?"\
s:".($2+$d).":\\\"$t":$t/eg;print}' $backup_dir/$file | mysql  -u $db_user -p$d\
b_password $dev_db_name
    log copy $db_name to $dev_db_name
  fi
  count=$(($count + 1))
  if [ $count -gt 5 ]; then
    rm $backup_dir/$file
    if [ $? -gt 0 ] ; then
      log failed to remove : $file
    else
      log removed : $file
    fi
  fi
done

log end

# end of file
```

ログに残すファイル容量を読みやすくするのに awk 使っているみたい（なにぶん、1 年以上前の私がやったことなんで、今の私はよく知らない）。

```awk:human_readable.awk
{
    byte = $1;
}
byte < 1024 {
    print byte "B";
    next;
}
{
    kb = byte / 1024;
}
kb < 1024 {
    printf "%4.2fKB\n", kb;
    next;
}
{
    mb = kb / 1024;
}
mb < 1024 {
    printf "%4.2fMB\n", mb;
    next;
}
{
    gb = mb / 1024;
}
{
    printf "%4.2fGB\n", gb;
}
```

##設定ファイルの変更
本番環境と開発環境とで同一のファイルを使いつつ、設定を切り替えるのには、`wp-config.php` において `$_SERVER['SERVER_NAME']` を見て DB 名を切り替えるだけにしています。

```php:wp-config.php
...
if ($_SERVER['SERVER_NAME'] == 'wp2.mato.me') {
     $db_name = 'wp2_matome';
} else {
     $db_name = 'wp_matome';
}
define('DB_NAME', $db_name);"
...
```

##普段の開発

プラグインやらテーマなんかは開発環境のディレクトリで直接ファイルをいじって、適宜 git commit や git push。本番環境にも適用していいと判断したら本番環境側で git pull。

外部のプラグインを導入する場合などで場合によっては本番環境に直接入れちゃうこともあります。そういう場合はプラグインのインストール＆設定後、本番環境で git commit & git push して、開発環境で git pull します。



## WordPress のアップデート
これがわりと難関でした。
が、step by step で半自動的にアップデートできるスクリプト `upgwp` を導入してからだいぶ楽になりました。
今、久々に `upgwp` の中身を見てみたら、もう少しこうした方がいいかな、とか、現在の WordPress のバージョンにあっていないな、という箇所もいくつかありました。そのうち対応したいです。 
何か問題があったらすぐに元の環境に戻せるよう、`wp.next` という環境に新しいバージョンを構築、完了後 `wp.curr` を `wp.back` に、`wp.next` を `wp.curr` にリネームし、シンボリックリンクを張り直します[^2]。

すなわち、何か問題があったら `wp2` のシンボリックリンクを `wp.back` に張り直せば、以前のバージョンで動作する、ということです[^3]。

[^2]: ハードリンクじゃないんだから張り直さなくてもいい気がしてきた。
[^3]: 切り戻しが目的なら本番環境の方も開発環境と同じように、前のバージョンを残しておく方がいいような。

 


```bash:upgwp
#!/bin/bash

self=upgwp
matome_dir=/var/web/matome
work_dir=$matome_dir/$self
next_cmd_file=$work_dir/next.cmd
ope_file=$work_dir/next.ope
first_cmd=pleaseCheckWorkTrees

set_next_cmd() {
  echo $1
  echo $1 > $next_cmd_file
}

get_next_cmd() {
  if [ -f "$next_cmd_file" ]; then
    cat $next_cmd_file
  else
    echo $first_cmd
  fi
}

exe() {
  cmd=${1}
  ope=`cat $ope_file`
  echo -e "\t=== $cmd ===" 1>&2
  case "$cmd" in
    pleaseCheckWorkTrees)
      cd $matome_dir
      curr_diff=$work_dir/curr.gitdiff
      prod_diff=$work_dir/prod.gitdiff
      cd $matome_dir
      (cd wp2.curr; git diff > $curr_diff)
      (cd wp; git diff > $prod_diff)
      if [ -s $curr_diff -o -s $prod_diff ]; then
        echo "未コミットの変更を処理してください。終了したら再び upgwp してください" 1>&2
      else
        set_next_cmd clean
      fi
      ;;
   clean)
     cd $work_dir
     rm -fr wordpress/ "*.zip"
     cd $matome_dir
     rm -fr wp2.next/ wp2.back/
     set_next_cmd downloadAndUnzip
     ;;
   downloadAndUnzip)
     echo "zip ファイルの URL を入力してください" 1>&2
     read url
     cd $work_dir
     wget $url
     zipfile=${url##*/}
     unzip $zipfile 1>&2
     set_next_cmd copyToNext
     ;;
   copyToNext)
     cd $matome_dir
     rsync -a upgwp/wordpress/ wp2.next
     mv wp2.curr/.git wp2.curr/.gitignore wp2.curr/tmp/ wp2.next/
     find wp2.curr/ -name ".gitignore" | while read file; do
       mv -v wp2.curr/$file wp2.next/$file
     done
     rm -fr wp2.next/wp-content/plugins/akismet/
     rm -fr wp2.next/wp-content/plugins/hello.php
     ls -1 wp2.curr/wp-content/plugins/ | while read p; do
       cp -r wp2.curr/wp-content/plugins/$p wp2.next/wp-content/plugins/
     done
     cp wp2.curr/favicon.ico wp2.next/
     rm wp2.next/{license.txt,readme.html,readme-ja.html}
     mv wp2.next/wp-config-sample.php wp2.next/wp-config.php
     rm -fr wp2.next/wp-content/themes/{twentyeleven/,twentyten/,twentyfourteen/,twelve/twnty/}
     cp -r wp2.curr/wp-content/themes/twentytwelve.matome/ wp2.next/wp-content/themes/
     set_next_cmd pleaseConfigureOnNexttree
     ;;
   pleaseConfigureOnNexttree)
    cd $matome_dir
     if [ "$ope" == 'next' ]; then
       set_next_cmd pleaseGitStatusOnNexttree
     else
       tmp=$work_dir/tmp.$$
       mv wp2.next/wp-config.php $tmp
       tr -d \\r < $tmp | awk -f bin/upgwp_config.awk | sed -e 's/$/\r/' > wp2.next/wp-config.php
       rm $tmp
       echo "wp2.next の wp-config.php を設定してください。終了したら upgwp next を実行してください。" 1>&2
       diff wp2.curr/wp-config.php wp2.next/wp-config.php 1>&2
     fi
     ;;
   pleaseGitStatusOnNexttree)
     if [ "$ope" == 'next' ]; then
       set_next_cmd mksymlinkForNextTree
     else
       echo "wp2.next で git status して変更を確認してください。終了したら upgwp next を実行してください。" 1>&2
     fi
     ;;
    mksymlinkForNextTree)
      cd $matome_dir
      rm wp2; ln -s wp2.next wp2
      set_next_cmd pleaseAccessToNexttreeByBrowser
      ;;
    pleaseAccessToNexttreeByBrowser)
      if [ "$ope" == 'next' ]; then
        set_next_cmd activateNexttree
      else
        echo "ブラウザから wp2.mato.me にアクセスして表示を確認してください。" 1>&2
        echo "管理画面のダッシュボードから必要に応じて DB の更新をしてください。" 1>&2
        echo "終了したら upgwp next を実行してください。" 1>&2
      fi
      ;;
    activateNexttree)
     cd $matome_dir
      mv wp2.curr wp2.back
      mv wp2.next wp2.curr
      rm wp2
      ln -s wp2.curr wp2
      set_next_cmd pleaseGitCommitOnNewtree
      ;;
    pleaseGitCommitOnNewtree)
      if [ "$ope" == 'next' ]; then
        set_next_cmd finish
      else
        echo "wp2.curr で git commit, git push を実行してください。" 1>&2
        echo "終了したら upgwp next を実行してください。" 1>&2
      fi
      ;;
    finish)
      ;;
    *)
      echo "unknown cmd 「$cmd」" 1>&2
      exit -1
      ;;
  esac
  echo > $ope_file
}

ope=${1}
echo $ope > $ope_file
cmd=`get_next_cmd`
while [ -n "$cmd" ]; do
  cmd=`exe $cmd`
done
# end of file
```

設定ファイルはかなりの力技で書き換えているようです。

```awk:upgwp_config.awk
BEGIN {
    r["define('DB_NAME', 'database_name_here');"] =         \
        "if ($_SERVER['SERVER_NAME'] == 'wp2.mato.me') {\n" \
        "    $db_name = 'wp2_matome';\n"                    \
        "} else {\n"                                        \
        "    $db_name = 'wp_matome';\n"                     \
        "}\n"                                               \
        "define('DB_NAME', $db_name);";
    r["define('DB_USER', 'username_here');"] = "define('DB_USER', 'wp_user');";
    r["define('DB_PASSWORD', 'password_here');"] = "define('DB_PASSWORD', '__ないしょ__');";
    r["define('AUTH_KEY',    'put your unique phrase here');"] = "define('AUTH_KEY',         '__ないしょ__');";
    r["define('SECURE_AUTH_KEY',  'put your unique phrase here');"] = "define('SECURE_AUTH_KEY',  '__ないしょ__');";
    r["define('LOGGED_IN_KEY',    'put your unique phrase here');"] = "define('LOGGED_IN_KEY',    '__ないしょ__');";
    r["define('NONCE_KEY',        'put your unique phrase here');"] = "define('NONCE_KEY',        '__ないしょ__');";
    r["define('AUTH_SALT',        'put your unique phrase here');"] = "define('AUTH_SALT',        '__ないしょ__');";
    r["define('SECURE_AUTH_SALT', 'put your unique phrase here');"] = "define('SECURE_AUTH_SALT', '__ないしょ__');";
    r["define('LOGGED_IN_SALT',   'put your unique phrase here');"] = "define('LOGGED_IN_SALT',   '__ないしょ__');";
    r["define('NONCE_SALT',       'put your unique phrase here');"] = "define('NONCE_SALT',       '__ないしょ__');";
}

{
    if (r[$0] != "") {
        print r[$0];
    } else {
        print;
    }
}

END {
    print "\ndefine('FS_METHOD', 'direct');";
}
# end of file
```