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

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