開發完項目,免不了要部署上線。純手動操做,登陸、拉代碼、改配置、清緩存、各類服務重啓等等一條龍下來,人生寶貴的幾分鐘就過去了。並且手動操做十分容易出錯,遺漏部分步驟都有可能產生一些邪門問題。因此我很早就開始尋求一種能輕鬆部署 Laravel 項目的辦法。php
laravel 的官方文檔裏介紹了 Envoy,以前用過,能知足大部分場景,但仍然有一些限制。直到後來看到了 deployer,大有相見恨晚之感!前端
首先說明下我的實際使用場景。nginx
本人使用 win10 系統,使用 Homestead 做爲 PHP 項目的開發環境(vagrant v2.1.1, homestead v7.4.1, virtualbox v5.2.8, homestead 的 virtual box 版本爲 v5.2)。laravel
本地開發能完成絕大部分開發和測試任務,但在部署到生產機以前仍然須要先部署到開發機上進行測試。線上測試與生產使用的是青雲的雲主機,Ubuntu16 系統。git
如下的操做都是在 homestead 虛擬機裏進行操做!
cd /path/to/your/project composer require deployer/deployer --dev
我的習慣於將其做爲項目依賴安裝,固然也能夠根據須要或我的喜愛全局安裝。
vendor/bin/dep init
由於我用的是 laravel 輸入項目類型 1 後回車,而後會出現一個讓設置 git 倉庫的,默認是對應項目的 git 遠端倉庫,不須要修改的話確認就能夠了。github
完成上面的初始化後,項目要目錄下會出現一個 deploy.php 文件,deployer 的配置就靠它了。初始的配置以下,裏面顯示了一些基本的配置。web
<?php namespace Deployer; require 'recipe/laravel.php'; // Project name // 項目名 set('application', 'my_project'); // Project repository // 項目倉庫地址不解釋 set('repository', 'git@github.com:tianyong90/xxx.git'); // [Optional] Allocate tty for git clone. Default value is false. set('git_tty', true); // Shared files/dirs between deploys // 分享文件即目錄,一般也不用改,默認包含了 storage 目錄 add('shared_files', []); add('shared_dirs', []); // Writable dirs by web server // 可寫目錄,通常不用改 add('writable_dirs', []); // Hosts // 目標主機配置,這是最基本的 host('project.com') ->set('deploy_path', '~/{{application}}'); // Tasks // 這算是個自定義任務示例 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', 'database:migrate');
默認的配置確定是不行的,目標主機啥的還不知道呢。下面直接貼上本身用到的配置,並加入了少許說明。redis
<?php namespace Deployer; require 'recipe/laravel.php'; // Project name set('application', 'xxx'); // Project repository set('repository', 'git@github.com:tianyong90/xxx.git'); // [Optional] Allocate tty for git clone. Default value is false. set('git_tty', true); // Shared files/dirs between deploys add('shared_files', []); add('shared_dirs', []); // Writable dirs by web server add('writable_dirs', []); // 保存最近五次部署,這樣的話回滾最多也只能回滾到前 5 個版本 set('keep_releases', 5); // 實踐證實,這樣能減小一些沒必要要的麻煩,如出現權限相關的問題,也可將此項設置爲 true 後嘗試 set('writable_use_sudo', false); // 生產用的主機 host('172.16.1.1') ->stage('production') ->user('root') ->port(22) ->set('branch', 'master') // 最新的主分支部署到生產機 ->set('deploy_path', '/data/wwwroot/xxx') ->identityFile('/home/vagrant/.ssh/id_rsa') ->forwardAgent(true) ->multiplexing(true) ->set('http_user', 'www') // 這個與 nginx 裏的配置一致 ->addSshOption('UserKnownHostsFile', '/dev/null') ->addSshOption('StrictHostKeyChecking', 'no'); // 測試用的主機 host('172.16.3.2') ->stage('debug') ->user('root') ->port(22) ->set('branch', 'develop') // 通常是把 develop 分支弄到測試機測試,沒問題再合併 ->set('deploy_path', '/data/wwwroot/xxx') ->identityFile('/home/vagrant/.ssh/id_rsa') ->forwardAgent(true) ->multiplexing(true) ->set('http_user', 'www') ->addSshOption('UserKnownHostsFile', '/dev/null') ->addSshOption('StrictHostKeyChecking', 'no'); // 自定義任務:重置 opcache 緩存 task('opcache_reset', function () { run('{{bin/php}} -r \'opcache_reset();\''); }); // 自定義任務:重啓 php-fpm 服務 task('php-fpm:restart', function () { run('systemctl restart php-fpm.service'); }); // 自定義任務:supervisor reload task('supervisor:reload', function () { run('sudo supervisorctl reload'); }); // 自定義任務:部署成功了用 bearychat 發消息給大佬和本身 task('send_message', function () { run('{{bin/php}} {{release_path}}/artisan deployed'); }); // 自定義任務:緩存路由,recipe/laravel.php 默認的流程裏沒有這個,因此加上,息看須要 after('artisan:config:cache', 'artisan:route:cache'); // 執行自定義任務,注意時間點是 current 已經成功鏈向新部署的目錄以後 after('deploy:symlink', 'php-fpm:restart'); after('deploy:symlink', 'supervisor:reload'); // 部署成功後重置 opcache 緩存 after('deploy:symlink', 'opcache_reset'); // 部署成功後調用 laravel 命令行發送通知 after('success', 'send_message'); // [Optional] if deploy fails automatically unlock. after('deploy:failed', 'deploy:unlock');
修改完成後記得先提交併將代碼推送到遠端倉庫。而後執行以下命令進行部署:數據庫
vendor/bin/dep deploy debug // 部署到測試機 vendor/bin/dep deploy production // 部署到生產機
過程當中若是提示要輸入密碼,則輸入登陸目標主機的密碼。或者想辦法設置 SSH key 實現免密碼登陸。緩存
默認狀況下,首次部署後,.env 文件是不會自動建立的,須要本身建立並修改,同時 nginx 站點配置也須要本身動手。對於 .env 文件,存放於目標主機的 /path/to/project/shared/
目錄下。
修改 .env 後記得從新緩存配置 php artisan config:cache
另外須要注意的是配置 nginx 站點時,網站根目錄應該爲 /path/to/project/current/public
。若是使用 supervisor 之類的,相關的目錄在配置時也要注意了。
在部署的目標目錄下執行 ls -la
,能夠看到以下結果:
說明:
| projectname |--- @current -> releases/<num> |--- .dep |--- releases 一個文本文件,裏面存着各次部署的時間、次數序號(或者說版本號)信息 |--- releases // 目錄下根據配置保存近幾回部署,更早的則會被自動清理 |--- 1 |--- 2 |--- . |--- . |--- <num> |--- 目錄中是項目的實際代碼 |--- 包括 .git, vendor, .env, storage ... |--- .env, storage 實際經過 symlink 連接到 shared 目錄下對應的文件上 |--- shared |--- storage // 即 laravel 項目的 storage 文件夾 |--- .env // 即 laravel 項目的 .env
每次部署更新,會在 releases 下新建文件夾如 num,拉取對應的最新代碼,安裝 composer 依賴完成一些其它自定義任務,並將 storage, .env 連接到 shared 文件夾下的那兩個上去,而後項目根目錄下的 current 經過 syslink 連接到這個新文件夾 num 上,這算是其動做的基本原理,網站在部署過程當中能繼續訪問也得益於此。
.env 和 storage 下的一些未加入代碼庫中的內部,部署時不會自動更新,所以有些狀況下須要手動處理。
正常狀況下,部署過程當中 deployer 會自動完成緩存配置、清理已編譯的緩存等任務。理論上咱們不須要本身再動手,但須要時也能夠手動執行
// 緩存路由 vendor\bin\dep artisan:route:cache production // 緩存配置 vendor\bin\dep artisan:config:cache production // 清視圖緩存 vendor\bin\dep artisan:view:clear production // 執行自定義任務,如前面提到的從新載入 supervisor vendor\bin\dep supervisor:reload production // ssh 鏈接到主機,hostname 也能夠不輸入,而後從選項裏選 vendor\bin\dep ssh <hostname> // 列出其它一些可用的命令 vendor\bin\dep list
在 deploy 命令後加上 -vvv 選項能夠輸出詳細錯誤信息,方便調試。
目標主機 php.ini
裏的 disabled_functions
項裏配置了一些被禁用的函數,若是 deployer 用到了這些函數就可能報錯,修改 php.ini
解除相關函數的禁用狀態就能夠了。
目標主要經過 apt-get 命令或 oneinstack 一類的一鍵包安裝的 PHP,可執行文件一般在 /usr/local/php/bin/php
,而 deployer 內使用 /usr/bin/env: php 形式調用,至關於 /usr/local/bin/php
。這就可能出錯,通常是報 command -v 'php' failed
。解決辦法很簡單,只要加個軟連接就能夠了。
ln -s /usr/local/php/bin/php /usr/local/bin/php
解決辦法固然是打開目錄主機並檢查網絡狀況
deployer 的 laravel 默認部署流程中,會執行 php artisan cache:clear 命令,若是你的項目裏使用了 redis 驅動的隊列或者一些強依賴於緩存的業務邏輯(如緩存文章閱讀數按期再入庫),則須要進行一些騷操做了。
好比,你能夠在 config/database.php
的 redis
項中爲隊列連接指定其它的 database。
或者修改 deploy.php
配置默認的緩存清理任務,跳過緩存清理動做。(一般並不建議這麼作,由於項目的緩存,應該是可清理的,若是部分業務確實十分依賴於緩存,則應該考慮一些緩存持久化的實現了)
// 覆蓋 recipe/laravel 裏默認的 artisan:cache:clear 任務,部署時不清緩存 task('artisan:cache:clear', function () { return true; });