deployerで環境毎env反映とDB・storageのコピー

19/08/2019

個人開発でゴニョゴニョ作ったのでしばらくLaravelネタが増えそうな?気がしてます。
今回はLaravelというよりはdeployerです。

Deployer

デプロイツールって言い方が多分普通?な気がするけど
細かい事とか厳密な事はよくわかってません。
とりあえずPHP製

サーバーにデータ手動でアップ->composer install->マイグレーション->〇〇する->〇〇する

みたいな手順をコマンドで自動化するっていうツール。だと思っている。
因みに別にLaravel専門って訳では無い様子。
railsいう所のCapistranoみたいな物。だと思っている。
Capistranoでも別に出来るっぽいけど
せっかくなので使ってみました。

ちょっと設定手間なのだけれども
慣れれば確実に便利だと思います。

npmとか.envとDBとstrage等の処理いれたオレオレdeployer(Laravel)

Laravelもdeployerも利用歴浅っいので
もっといいやり方ある可能性大ですが
とりあえず個人で利用する分には困っていないので
何かの参考になりましたら幸いです。

とりあえずdeploy.phpの全体

<?php
namespace Deployer;

require 'recipe/laravel.php';

set('application', "アプリの名前");
set('repository', 'git@bitbucket.org:hoge/hoge.git');

// Shared files/dirs between deploys
add('shared_files', ['.env']);
add('shared_dirs', [
    'storage',
]);

// Writable dirs by web server

add('writable_dirs', [
    'bootstrap/cache',
    'storage',
    ' | echo',
]);
// deploy:writableがエラーで止まるって困ってたけども
// 最後に' | echo',つけると止まらなくなるらしい。

set('writable_mode', "chmod");
// デフォルトがsetfacl使うようになっているので環境によって変える

set('writable_chmod_mode', "0755");
// これで多分writable_dirsのパーミッション変えれる


// Hosts
host('sample.com')
    ->stage('production')
    ->user('user')
    ->port(22)
    ->identityFile('~/.ssh/id_rsa')
    ->set('deploy_path', '/var/www/html/hoge');

// build -------------------
task('build', function () {
    run('cd {{release_path}} && build');
});
// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');

// Migrate database before symlink new release.
before('deploy:symlink', 'artisan:migrate');

// Tasks

// npm -------------------
before('deploy:prepare', 'npm:run');
task('npm:run', function () {
    run('npm run production');
})->local();
// deploy:prepareの前にnpmでproduction用のアセットとか出力する

// push -------------------
after('npm:run', 'git:push');
task('git:push', function () {
    run('git add .');
    run("
        if ! git diff-index --quiet HEAD --; then
        git commit -m deployer
        fi
        ");
    run('git push origin master');
})->local();
// 変更があればpush

// env -------------------
before('deploy:shared', 'upload-env');
task('upload-env', function () {
    $stage = get('stage');
    $src = ".env.${stage}";
    $deployPath = get('deploy_path');
    $sharedPath = "${deployPath}/shared";
    upload(__DIR__ . "/" . $src, "${sharedPath}/.env");
});
// .env.productionとかを.envにリネームしてリモートにuploadする

// set -------------------
$date = date("Ymd_His");
set('date', $date);
$dbbkup_file = "/var/www/html/hoge/backup/data${date}.sql";
set('dbbkup_file', $dbbkup_file);

//  DBバックアップ&同期 -------------------
after('upload-env', 'db:backup');
task('db:backup', function () {
    $pass = 'password';
    $dbbkup_file = get("dbbkup_file");
    run("/usr/bin/mysqldump -u root -p${pass} hoge > ${dbbkup_file}");
});
// .バックアップというかsqlとしてエクスポートする、場所とかファイル名は$dbbkup_fileから
// 多分もっといい方法ありそう

after('db:backup', 'db:local');
task('db:local', function () {
    $dbbkup_file = get("dbbkup_file");
    $date = get("date");
    run("scp -P 22 user@sample.com:${dbbkup_file} /Users/user/path/to/sql/");
    run("/Applications/MAMP/Library/bin/mysql -uroot -proot dbname < /Users/user/path/to/sql/data${date}.sql");
})->local();
// sqlをscpでリモート > ローカルにダウンロードしてmampにコピー(インポート)

//  storageの同期 (リモート > ローカル) -------------------
after('db:local', 'storage:rsync');
task('storage:rsync', function () {
    run("rsync -avz -e 'ssh -p 22' --delete user@sample.com:/var/www/html/hoge/shared/storage/app/public/ /Users/user/path/to/app/storage/app/public/
");
})->local();
// これもstrageの画像とかrsyncでリモートからローカルに同期

// php-fpm -------------------
after('deploy', 'php-fpm:restart');
task('php-fpm:restart', function () {
    run('sudo /etc/init.d/php-fpm restart');
})->desc('Restart PHP-FPM service');
// php-fpmリスタートしないと
// 更新されない事があるのでやっておく
// visudoでパスなしsudoの設定する必要あり

です。

Task毎の補足等

あとは少し補足等入れてみます。

npm:run

before('deploy:prepare', 'npm:run');
task('npm:run', function () {
    run('npm run production');
})->local();

いちいちnpm run productionするのって個人的には本番に反映する前だけなので
deployerのtaskにいれてしまう。

task()->local();

でローカルでの処理になるっぽい。

git:push

run productionしたらpushしないと
デプロイに反映されないので
pushします。

after('npm:run', 'git:push');
task('git:push', function () {
    run('git add .');
    run("
        if ! git diff-index --quiet HEAD --; then
        git commit -m deployer
        fi
        ");
    run('git push origin master');
})->local();

if ! git diff-index –quiet HEAD –;

if ! git diff-index –quiet HEAD –;とかって書いているのは
git commitの時に変更が無いとコミット出来なくて
エラーで止まっちゃうので判定してます。

env

before('deploy:shared', 'upload-env');
task('upload-env', function () {
    $stage = get('stage');
    $src = ".env.${stage}";
    $deployPath = get('deploy_path');
    $sharedPath = "${deployPath}/shared";
    upload(__DIR__ . "/" . $src, "${sharedPath}/.env");
});

envは多分gitignoreするのが普通っぽい気がするので
ローカルからuploadしちゃってます。
staging/productionとかでアップする
envを切り替えつつリネームしてアップ。

DBバックアップ

task('db:backup', function () {
    $pass = 'password';
    $dbbkup_file = get("dbbkup_file");
    run("/usr/bin/mysqldump -u root -p${pass} hoge > ${dbbkup_file}");
});

mysqldumpでデプロイ毎にsqlにエクスポートしておく。
多分cronとかでやったほうがいいのかもしれないけど
そんな本格的にやるまでもない気がしてるのでついでバックアップ。
次のローカルDBインポートにも使う。

db:local(MAMPとかDockerにインポート)

task('db:local', function () {
    $dbbkup_file = get("dbbkup_file");
    $date = get("date");
    run("scp -P 22 user@sample.com:${dbbkup_file} /Users/user/path/to/sql/");
    run("/Applications/MAMP/Library/bin/mysql -uroot -proot dbname < /Users/user/path/to/sql/data${date}.sql");
})->local();

別に必要ないのだけれでも
何となくリモート->ローカルに同期しておこうと思って
やってみた

scpでさっきのバックアップ用に書き出したsqlをリモート->ローカルにコピー。

してからmampのmysqlにインポート。

Dockerの場合追記

ローカルでssl使いたくてなんかmampだと面倒そうな感じがしたので
docker(laradock)に移行したのだけれども
この部分やたら苦戦したので追記。

task('db:local', function () {
    $dbbkup_file = get("dbbkup_file");
    $date = get("date");

    run("scp -P 22 user@sample.com:${dbbkup_file} /Users/user/path/to/sql/");
    set('latest_file', run("cd /Users/user/path/to/sql/ && ls -lt *.sql | head -n 1 | awk '{print $9}'"));
    $latest_file = get("latest_file");

    // // dockerのコンテナ内にコピー
    run("docker cp /Users/user/path/to/sql/${latest_file} laradock_mysql_1:/tmp/${latest_file}");
    $bash = "MYSQL_PWD='password' mysql -u username table < ./tmp/${latest_file}";
    run("docker exec laradock_mysql_1 bash -c '${bash}'");
})->local();

latest_file

指定ディレクトリでの最新ファイル取得したかったのだけれども
多分なんか上手い書き方ありそうなもんだけどcd /path/ &&でやってしまった。

docker cp

dockerってコンテナ毎にcpしないとなのか?
workspace内からデータインポートしようとしたら参照できません的なエラー出て
結構困ってたけどmysqlのコンテナにcpすれば一応動いた。

bash

mysqlのバージョンによっては話だとおもいますが

Using a password on the command line interface can be insecure.

とかででパスワードとかはmy.conf書いて読めばいいって事らしいけども
ローカルに置けばいいのかコンテナ内におくのかよくわからん。
とりあえずうまく読めないのであきらめて
MYSQL_PWDでやろうと思った次第ですが、それもdocker execで結構困ってました。
bash -c “”でコンテナでのコマンド渡せるっぽい。

storage:rsync

task('storage:rsync', function () {
    run("rsync -avz -e 'ssh -p 22' --delete user@sample.com:/var/www/html/hoge/shared/storage/app/public/ /Users/user/path/to/app/storage/app/public/
");
})->local();

DBインポートするとローカルに無い画像が溢れ出てくるので
storageもrsyncでリモート->ローカル同期する。

php-fpm:restart

task('php-fpm:restart', function () {
    run('sudo /etc/init.d/php-fpm restart');
})->desc('Restart PHP-FPM service');

php-fpm使ってる場合は多分restartしないと更新が反映されない事が
あるみたいなのでデプロイしたらリスタートします。

visudo

runでsudo使うのでパスなしのsudo許可が必要になるみたい。
個人的にここが地味に面倒くさかったです。

Capistranoだと必要なかったような?
sshkit/sudoのおかげ?

多分もっとスマートな方法があると思います。

以上です。
とりあえず動いてるよってだけで
もっとスマートな方法は色々あると思うので
気になる方は方法探すかスルーしてください。

参考になりましたら幸いです。

webLaravel

Posted by admin