Transient APIがW3TCでAPCを有効にすると動作しない件

Transient APIを使ってキャッシュをしようしても正常に動作しない。

環境と事象

環境:
PHP 5.4.45
APC 3.1.15dev
WordPress 4.3.1
W3 Total Cache 0.9.4.1

具体的な事象としては、

  • set_transient()をするとtrue
  • get_transient()で保存したキャッシュを取得しようとするとfalse
  • W3 Total CacheのObject Cache Method:をDiskにすると正常に動作

といった感じです。

TransientAPIが動作しない原因

WordPressのバージョンを更新した時に発生したので、おそらくW3 Total CacheのオブジェクトキャッシュとWordPress本体のTransient APIが何らかのコンフリクトを起こしているのかと思いました。

これを詳しく調べると泥沼にはまりそう&原因がわかってもWordPressプラグイン側の問題だとしたらあまり手を出したくないので、今回は以下の解決策で対処しました。

TransientAPIのエラー解決策

1. オブジェクトキャッシュにAPCを使わない

Diskにするとパファーマンスは落ちますが、正常に動作します。

eAcceleratorやmemcachedは試していないので不明です。

2. APCを直接操作する

オブジェクトキャッシュをAPCのユーザーキャッシュにストアするには、なにもTransient APIを使う必要はありません。

APCが有効されているPHPでは、apc_store()やapc_fetch()といった関数でAPCのキャッシュを操作できます。

ぞれぞれ、下記のようにset_transient()など同じように引数をとります。APCを利用する場合は、ほぼ同様に使うことができるのではないでしょうか。

apc_store ( string $key , mixed $var [, int $ttl = 0 ] )
apc_fetch ( mixed $key [, bool &$success ] )

$key:一意なキー
$var:保存する値
$ttl:保存期間

なので、set_transient()をapc_store()に、get_transient()をapc_fetch()にそれぞれ変更すれば動作すると思います。

注意点:APCPHP CLIからは実行できない

PHP CLIからはAPCは動かないので、テスト用スクリプトを書いてターミナルで実行してもfalseになるので注意が必要です。

(これで数時間無駄にしました。。。)

Crondの(CRON) bad commandエラーの対処法

cronを設定したはずなのに、動かない時の対処方法です。ログを確認する前にcronの状態とファイルパーミッションを確認します。

$ service crond status

crondがrunning状態であることを確認。

$ ls -la

また実行ファイル(シェルスクリプト)のパーミッションを確認して、ファイルの実行権限が正しく設定されているか確認します。

ここまで問題なければ、cronのログファイル(/var/log/cron)を確認します。

$ tail /var/log/cron

確認すると以下のようなbad commandエラーが出ていました。

crond[1668]: (CRON) bad command (/etc/cron.d/sample)

cronの実行エラーの原因・対処法

原因は、/etc/cron.d/sampleのファイル末尾に空行がなかったせいでした。

以下のようにファイル末尾には空の行を1つ入れないとbad commandとなってしまうようです。

0 3 * * * /backup/bin/mysql_dump.sh

これはcrontab -e コマンドを使わずに、以下のように直接ファイルを作成したことによるものでした。

$ echo "0 3 * * * /backup/bin/sample.sh" > /etc/cron.d/sample

対処法は簡単で作成したファイルの末尾に行を追加すれば良いだけです。

Webhook+PHPを利用したGitデプロイ自動化

BitbucketのWebhook+PHPスクリプトによる外部サービスを使わない簡単なデプロイ自動化をしてみます。

今回の環境は以下のような感じ。nginxですが、ApacheでもSSHキーの部分を変更すれば対応できるかと思います。

▼環境
Bitbucket
CentOS 6
nginx
php-fpm

PHPでシェルコマンドを実行するexec()関数が使える前提

デプロイ自動化のイメージ

  1. ローカルからBitbucketにpush
  2. BitbucketからWebhookでリモートサーバーのdeploy.phpにリクエス
  3. deploy.phpがexec()でgitコマンドを実行
  4. git fetch && git checkoutで更新を反映
ローカル → Bitbucket → リモートサーバー

サーバーにGitレポジトリをクローン

$ mkdir /git
$ cd /git
$ git clone --mirror git@bitbucket.org:accountname/myproject.git

git clone –mirror とするとリポジトリのミラーが作成されます。

$ cd /git/myproject.git
$ GIT_WORK_TREE=/var/www/html git checkout -f

WordPressのテーマだけホストする場合は、GIT_WORK_TREEを/var/www/html/wp-content/themes/twentyfourteenというようにします。

デプロイ用のPHPスクリプトを作成

まずデプロイ用のPHPスクリプトを作ります。PHPでシェルコマンドを実行するので、exec()関数使います。

実際にexec()で実行するシェルコマンドは以下のような感じ。

$ cd /git/myproject.git/
$ git fetch
$ GIT_WORK_TREE=/var/www/html/ git checkout -f

実際のPHPスクリプトは以下の通りです。今回は、ログ出力もなしのでミニマムのスクリプトを紹介します。

$git_path, $www_path, $secret_keyを環境に合わせて変更してください。

//GitとWebのディレクトリへのフルパス
$git_path = '/git/myproject.git/';
$www_path = '/var/www/html';

//example.com/deploy.php?key=以下の任意のキー
$secret_key = 'YOUR_SECRET_KEY';

//keyパラメータと$secret_keyが合っていればexec()
if ($_GET[key] === $secret_key)  {
    $command = "cd {$git_path} && git fetch && GIT_WORK_TREE={$www_path} git checkout -f";
    exec($command);
}

/var/www/html以下にdeploy.phpとして保存します。

$ vi /var/www/html/deploy.php

nginxのSSHキーをssh-keygenで作成

リモートサーバーのSSHキーをBitbucketに登録します。

外部(Bitbucket)からのHTTPリクエストを受けて、deploy.phpが実行されるのでSSHキーはPHPを実行するnginxのものを作ります。

もしApacheを使っている場合は、Apacheに置き換えればOKかと。

sshキーの置き場所は、Apacheのデフォルトと同じ/var/www/.sshにします。

nginxのデフォルトだと/var/cache/nginx/.sshになっていて、キャッシュ用ディレクトリのせいなのか2度ほど消えてしまったことがあるためです。

$ mkdir /var/www/.ssh
$ chmod 777 .ssh
$ sudo -u nginx ssh-keygen
Enter file in which to save the key (/var/cache/nginx/.ssh/id_rsa):

とでるので、/var/www/.ssh/id_rsaと入力してEnter。パスワードは空のままなのでEnterします。

Generating public/private rsa key pair.
Enter file in which to save the key (/var/cache/nginx/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/cache/nginx/.ssh/id_rsa.
Your public key has been saved in /var/cache/nginx/.ssh/id_rsa.pub.
The key fingerprint is:
aa:jd:87:40:58:cn:as:jd:73:8f:a0 nginx@example.com
The key's randomart image is:
+--[ RSA 2048]----+
|          .      |
|         .       |
|     . + *       |
|    .   S o      |
|    = . = .  oE  |
|       .   .=.+o |
|        S...o= oo|
+-----------------+

SSHキーは/var/www/.ssh/に作成されました。

パーミッションを変更しておきます。

$ chown -R nginx:nginx .ssh
$ chmod 700 -R .ssh

あとはSSHを登録。

$ ssh-agent /bin/bash
$ ssh-add /var/www/.ssh/id_rsa

登録されていることを確認します。

$ ssh-add -l
  2048 aa:jd:87:40:58:cn:as:jd:73:8f:a0/var/www/.ssh/id_rsa (RSA)

Bitbucketのデプロイキー登録&Webhookの設定

$ cat /var/www/.ssh/id_rsa.pub

Settings > デプロイ鍵 > 鍵を追加をクリック。catした文字列をコピペ。

bitbucket-auto-deploy4

さらに、Webhookを設定します。Settings > Webhooks > Add webhookをクリック。

タイトルは、「Deploy to exmaple.com」など好きなタイトルにして、URLは「http://example.com/deploy.php?key=SECRET_KEY」を入力します。

bitbucket-auto-deploy3

フックとなるアクションはpush以外にもmergeなどもあるので、開発サーバーのフックはdevelopブランチへのpush、本番サーバーのフックはmasterブランチへのmergeなど分けることもできます。

パーミッションを変更

/gitのパーミッションと/var/www/.sshを変更します。

$ chown -R nginx:nginx /git/
$ chown nginx:nginx /var/www/html/deploy.php

ssh_configの編集

nginxのsshキーの場所をデフォルトから変更したので、ssh_configを編集します。

~/.ssh/configはユーザー用、ssh_configはシステム共通のsshの設定ファイルです。

$ vi /etc/ssh/ssh_config

以下の内容を追記します。

Host bitbucket.org
  User git
  IdentityFile /var/www/.ssh/id_rsa

ローカル開発環境からpush

最後にローカル開発環境からpushします。README.mdなど好きなファイルを編集して、pushしてください。

$ git add README.md
$ git commit -m 'change README.md
$ git push -u origin master

あとは、サーバー側も更新されていればOK!ファイルサイズによっては、デプロイに時間がかかる場合もあります。

BitbucketのWebhookが動かない場合のトラブルシューティング

以下の順で確認していきます。

1. Webhookが200を返しているか
2. PHPスクリプトは間違っていないか
3. ファイルパーミッションの確認
4. デプロイキーが正常に登録されているか
5. exec()でgitコマンドが実行されない

1. Webhookが200を返しているか

Bitbucketのレポジトリ > Settings > Webhook からView requestsをクリック。

bitbucket-auto-deploy1

Webhookのリクエスト履歴が見れるので、右端のステータスコードが200になっていることを確認。

bitbucket-auto-deploy2

2. PHPスクリプトは間違っていないか

サーバーにssh接続して、ターミナルから直接PHPを実行してみます。これで更新が反映されているようなら、PHPには問題ないはずです。

$ cd /var/www/html/
$ php deploy.php

3. ファイルパーミッションの確認

ファイルパーミッションが正しく設定されているか確認します。ls -laで確認するか、chown、chmodコマンドを再実行してみてください。

4. デプロイキーが正常に登録されているか

そのユーザーのssh鍵の設定が正しく行われているか確認します。実際にnginxユーザーになってgit fetchして確認するのが簡単なのでやってみます。

$ su -s /bin/bash nginx
$ cd /git/myproject.git
$ git fetch

ついでに、nginxユーザーでphp deploy.phpをして正常に動くことも確認しておくと良いと思います。

5. exec()でgitコマンドが実行されない

意外と落とし穴なのが、HTTPリクエストで実行されるPHPスクリプトがnginxじゃなくてApacheだったといった単純な罠。

これを確認するために、シェルコマンドのwhoamiを利用します。/var/www/htmlにtest.phpなどを作って以下をコピペ。

echo exec("whoami");

http://example.com/test.phpにブラウザからアクセスするとPHPスクリプトがどのユーザーで実行されたのか表示されます。

もし実行結果がapacheとなっていれば、service nginx statusとservice httpd statusでnginxが起動していて、apacheが止まっていることを確認。

さらに、/etc/php-fpm.d/www.confを開いてuserとgroupがnginxになっていることを確認します。

user = nginx
group = nginx

ここまで確認できればある程度のエラーには対応できるかなと思います。

MySQLが「Fatal error: cannot allocate memory for the buffer pool」となり起動しない問題

開発環境のWordPressがいきなり「データベース接続確立エラー」となり、mysqlが起動しなくなってしまった時の対処法です。

原因:buffer poolのメモリ割当が少なかったせい

mysqlを起動すると以下のようにエラーに。

$ service mysqld start
MySQL Daemon failed to start.
Starting mysqld:                                           [FAILED]

まずは、/var/log/mysqld.logを確認。すると以下のようなエラーがでていました。

151117  9:55:58 InnoDB: Initializing buffer pool, size = 128.0M
InnoDB: mmap(137363456 bytes) failed; errno 12
151117  9:55:58 InnoDB: Completed initialization of buffer pool
151117  9:55:58 InnoDB: Fatal error: cannot allocate memory for the buffer pool
151117  9:55:58 [ERROR] Plugin 'InnoDB' init function returned error.
151117  9:55:58 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
151117  9:55:58 [ERROR] Unknown/unsupported storage engine: InnoDB
151117  9:55:58 [ERROR] Aborting

原因となりそうなのはこの部分。

151117  9:55:58 InnoDB: Fatal error: cannot allocate memory for the buffer pool

対処法:my.cnfのinnodb_buffer_pool_sizeを増やす

メモリの割当ができなかったようなので、/etc/my.cnfを編集してメモリの割当を増やします。とりあえず、512Mにすることに。

innodb_buffer_pool_size = 512M

そこで、MySQLを再起動。

$ service mysqld start
Starting mysqld:   [  OK  ]

これでMySQLが再起動しました。

*注意* innodb_log_file_sizeは変更しない

ちなみにinnodb_log_file_sizeを合わせて変更しようとすると、MySQL 5.6未満だとエラーとなるようです。

特に理由がなければ、innodb_buffer_pool_sizeのメモリを増やすだけで良いかなと思います。

[MySQL] innodb_log_file_sizeを変更した時のエラー対処

innodb_log_file_sizeを変更した時のエラー対処

/tmp/my.cnfのinnodb_log_file_sizeを変更すると、エラーになりmysqlが起動できないという現象に。

mysqld.logを見てみると以下のようなエラーに。

InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytes
InnoDB: than specified in the .cnf file 0 134217728 bytes!

my.cnfで指定したメモリのサイズとib_logfile0のサイズが違うとのこと。ib_logfile0はInnoDB ログファイルです。

どうやら、MySQL 5.6 未満でmy.cnfのinnodb_log_file_sizeを変更すると発生するようです。

いきなり消すのは怖いので、/tmpに一旦退避させます。

$ mv /var/lib/mysql/ib_logfile0 /tmp
$ mv /var/lib/mysql/ib_logfile1 /tmp

そして、またmysqlを再起動すると以下のようなエラーに。

151118  7:04:09  InnoDB: Log file ./ib_logfile0 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile0 size to 128 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100
151118  7:04:09  InnoDB: Log file ./ib_logfile1 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile1 size to 128 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100
151118  7:04:09 InnoDB: Cannot initialize created log files because
151118  7:04:09 InnoDB: data files are corrupt, or new data files were
151118  7:04:09 InnoDB: created when the database was started previous
151118  7:04:09 InnoDB: time but the database was not shut down
151118  7:04:09 InnoDB: normally after that.

調べてみると、ib_logfile0などを消すのはあまり良くない対処のよう。

公式の、nnoDB ログファイルの数またはサイズの変更、および InnoDB テーブルスペースのサイズの変更を参考に対処しました。

外部サーバーの画像参照のために画像のベースURLを変更する方法

例えば、ローカル開発環境をつくっていてデータベースは同期しているけど、画像などは重いからローカル環境に置きたくないという時の対処法です。

今回の簡単な要件

環境は以下のような状況とします。

ローカル開発環境
URL → http:/localhost

本番環境
URL → http://production.com

であると、ローカル開発環境に本番環境のデータをインポートしても、localhost/wp-content/uploadsには画像がないため、当たり前ですが画像は表示されません。

そこで、画像はhttp://production.com/wp-content/uploads以下のものを参照したいとします。

画像のパスをoptions.phpから変更

まずは、http://localhost/wp-admin/options.phpにアクセス。

それぞれ、以下のように入力します。

upload_path:画像ディレクトリの名前(この場合、uploads) 
upload_url_path:画像パスのURL(この場合、http://production.com/wp-content/uploads

wordpress_change_image_path

あとは、「変更を保存」とするだけでOKです。

Poeditを使った多言語化の翻訳ファイル作り方

WordPressを多言語化する必要があり、翻訳ファイルを作成しました。

WordPressではテーマの翻訳は基本的にlanguagesディレクトリに入れることとなっています。

そして、翻訳情報を入れたpoファイルとそれを元にコンパイルしたmoファイルの2つで1セットを使います。

Poeditをインストー

公式サイトからインストールします。

有料版もありますが、普通に使う分なら無料版でも十分対応できます。

Poeditを起動

起動すると以下のような画面が出てきます。すでに翻訳ファイルがある場合は、「翻訳を編集」または「翻訳プロジェクトを新規作成」を選択。

WordPressのテーマ・プラグインを編集」は無料版の場合は使用できないので、今回はスルーします。

poedit1

翻訳ファイル(新規カタログ)を1から作成する

1から翻訳ファイルを作るには、メニューのファイル>新規カタログから新規の翻訳ファイル(.poファイル)を作成します。

次に翻訳先言語を選択。

poedit2

Command + S で一旦カタログを保存します。すると「プロジェクト名-ja.po」「プロジェクト名-ja.mo」の2つが生成されます。

翻訳部分を「ソースから抽出」

次に翻訳部分を抽出するために、「ソースから抽出」を選択します。

poedit3

「ソースの検索パス」にWordpressのフォルダを追加します。「+」のマークでできます。

poedit5

次に「ソース中のキーワード」を選択。WordPressなら翻訳の関数「e()」と「__()」があるので、ぞれぞれ()をとった「e」「__」を登録して起きます。

poedit5

翻訳の開始

読み込みが正常にできれば、以下のような画面になります。エラーの場合は、下記のエラーの場合の対処法を確認してください。

poedit8

「ソーステキスト」が翻訳元なので、「翻訳」のテキストエリアに翻訳を記入していきます。

全部記入したら保存してから、ファイル> MOにコンパイルをすることで完了します。

エラー:「ソースから抽出」で「カタログの更新に失敗しました」となる

「ソースから抽出」をすると、以下のようなエラーとなることがあります。

poedit6

ちゃんと検証してないですが、登録した翻訳用関数(WordPressだと_()やe()が該当)の近くに日本語があるとエラーとなる様子。

関数の直前または直後のコメントアウトの中に日本語がある場合に起こるっぽいです。対策として、前後にコメントアウトがある場合は消すか英語にしてみてください。