GitLab CI/CD Laravel 自動化資料庫更新配置
您好!很高興能為您提供關於如何在 GitLab CI/CD 中為 Laravel 專案配置自動化資料庫更新的詳細指南,其中包含了測試、構建和部署階段,並整合了資料庫備份、錯誤通知和環境變數管理。作為一名資深 PHP 工程師,您將會發現這些步驟清晰易懂。
專案設定概述
我們將會利用您的 Laravel 專案,結合 MySQL 資料庫和 PHPUnit 進行測試。部署目標是遠端伺服器。
程式碼範例
以下是您專案中需要準備的相關程式碼和設定檔案:
1. GitLab CI/CD 設定檔:.gitlab-ci.yml
這個檔案定義了 CI/CD 流程的每個階段和任務。請將其儲存於您的專案根目錄。
stages:
- test
- build
- deploy
variables:
# Laravel 相關環境變數 (可在 GitLab CI/CD 變數中覆寫)
APP_ENV: production
APP_DEBUG: false
# MySQL 服務設定
MYSQL_DATABASE: $DB_DATABASE
MYSQL_ROOT_PASSWORD: $DB_PASSWORD # 使用 DB_PASSWORD 作為 root 密碼以便於 CI 環境設置
MYSQL_USER: $DB_USERNAME
MYSQL_PASSWORD: $DB_PASSWORD
cache:
paths:
- vendor/
.php_template: &php_template
image: php:8.2-fpm-alpine # 使用較輕量的 Alpine 映像
before_script:
- apk add --no-cache git openssh-client curl # 安裝必要的工具
- docker-php-ext-install pdo_mysql # 安裝 MySQL 擴展
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # 安裝 Composer
- composer install --no-interaction --prefer-dist --optimize-autoloader
test:
<<: *php_template
stage: test
services:
- mysql:8.0 # 使用 MySQL 8.0 服務
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes" # 允許 MySQL 服務使用空密碼,方便測試環境
script:
- cp .env.example .env # 複製 .env.example 到 .env
- php artisan key:generate # 生成應用程式金鑰
- php artisan migrate:fresh --seed # 執行 Migration 並填充測試資料
- vendor/bin/phpunit --coverage-text --colors=never # 執行 PHPUnit 測試並生成覆蓋率報告
only:
- main
- merge_requests
build:
<<: *php_template
stage: build
script:
- composer install --no-dev --optimize-autoloader # 安裝生產環境依賴
- php artisan config:cache # 快取設定
- php artisan route:cache # 快取路由
- php artisan view:cache # 快取視圖
- zip -r laravel-app.zip . -x ".git/*" "vendor/*" # 壓縮應用程式程式碼
artifacts:
paths:
- laravel-app.zip
only:
- main
deploy:
stage: deploy
image: alpine/git # 使用輕量級映像,包含 SSH 客戶端
before_script:
- apk add --no-cache openssh-client rsync # 安裝 SSH 客戶端和 rsync
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa # 寫入 SSH 私鑰
- chmod 600 ~/.ssh/id_rsa # 設定私鑰權限
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts # 寫入已知主機
- chmod 644 ~/.ssh/known_hosts
script:
# 資料庫備份
- ssh "$SERVER_USER"@"$SERVER_HOST" "mysqldump -u '$DB_USERNAME' -p'$DB_PASSWORD' '$DB_DATABASE' > /backups/db_backup_$(date +%Y-%m-%d_%H-%M-%S).sql" || (echo "資料庫備份失敗!" && exit 1)
# 部署應用程式
- rsync -avz --delete --exclude 'vendor/' --exclude '.git/' --exclude '.env' laravel-app.zip "$SERVER_USER"@"$SERVER_HOST":/tmp/laravel-app.zip
- ssh "$SERVER_USER"@"$SERVER_HOST" "unzip -o /tmp/laravel-app.zip -d /var/www/laravel-app && rm /tmp/laravel-app.zip"
# 動態生成 .env 文件
- |
ssh "$SERVER_USER"@"$SERVER_HOST" << EOF
cd /var/www/laravel-app
echo "APP_NAME=Laravel" > .env
echo "APP_ENV=$APP_ENV" >> .env
echo "APP_KEY=$(php artisan key:generate --show)" >> .env
echo "APP_DEBUG=$APP_DEBUG" >> .env
echo "APP_URL=https://your-domain.com" >> .env
echo "DB_CONNECTION=mysql" >> .env
echo "DB_HOST=$DB_HOST" >> .env
echo "DB_PORT=3306" >> .env
echo "DB_DATABASE=$DB_DATABASE" >> .env
echo "DB_USERNAME=$DB_USERNAME" >> .env
echo "DB_PASSWORD=$DB_PASSWORD" >> .env
chmod 664 .env # 設定 .env 檔案權限
chown www-data:www-data .env # 設定 .env 檔案所有者
php artisan optimize:clear # 清除應用程式快取
php artisan config:cache # 重新快取設定
php artisan route:cache # 重新快取路由
php artisan view:cache # 重新快取視圖
EOF
# 執行資料庫 Migration
- ssh "$SERVER_USER"@"$SERVER_HOST" "cd /var/www/laravel-app && php artisan migrate --force" || (echo "資料庫 Migration 失敗!" && exit 1)
# 重啟 Nginx
- ssh "$SERVER_USER"@"$SERVER_HOST" "sudo service nginx restart" || (echo "Nginx 重啟失敗!" && exit 1)
# 發送 Slack 通知 (成功)
- |
if [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -X POST -H 'Content-type: application/json' --data '{"text":"✅ Laravel 應用程式已成功部署到 '$SERVER_HOST'!"}' $SLACK_WEBHOOK_URL
fi
after_script:
# 發送 Slack 通知 (失敗)
- |
if [ "$CI_JOB_STATUS" == "failed" ] && [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -X POST -H 'Content-type: application/json' --data '{"text":"❌ Laravel 部署到 '$SERVER_HOST' 失敗!請檢查 GitLab CI/CD 管道日誌。"}' $SLACK_WEBHOOK_URL
fi
only:
- main
when: manual # 建議部署階段設為手動觸發,以進行更多控制
2. 範例 Migration 文件
這是一個新增 phone_number
欄位的 Migration 檔案。
// database/migrations/YYYY_MM_DD_HHMMSS_add_phone_to_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddPhoneToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('phone_number')->nullable()->after('email');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('phone_number');
});
}
}
3. 範例 .env.example
文件
請確保您的專案根目錄有此檔案,其中包含資料庫連線設定。
APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel_user
DB_PASSWORD=secret
4. PHPUnit 測試範例
此測試用於驗證 users
表是否包含 phone_number
欄位。
// tests/Feature/UserTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\User;
class UserTest extends TestCase
{
use RefreshDatabase;
/**
* Test if the user table has the phone_number column.
*
* @return void
*/
public function test_user_table_has_phone_column()
{
$this->artisan('migrate'); // 確保在測試前執行 Migration
$user = User::create([
'name' => 'Test User',
'email' => 'test@example.com',
'password' => bcrypt('password'),
'phone_number' => '1234567890',
]);
$this->assertDatabaseHas('users', [
'email' => 'test@example.com',
'phone_number' => '1234567890',
]);
}
}
5. PHPUnit 配置範例
確保 phpunit.xml
配置正確,支援 MySQL 或 SQLite 測試環境。
<phpunit>
<php>
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_HOST" value="mysql"/> # 因為 CI 服務名稱為 mysql
<env name="DB_DATABASE" value="laravel"/> # 與 CI/CD 中設定的資料庫名稱一致
<env name="DB_USERNAME" value="laravel_user"/>
<env name="DB_PASSWORD" value="secret"/>
</php>
<testsuites>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">./app</directory>
</include>
</source>
</phpunit>
配置步驟
1. GitLab CI/CD 變數設定
前往您的 GitLab 專案:Settings > CI/CD > Variables,然後添加以下變數。請確保敏感資訊的變數(如密碼)勾選 "保護變數"。
SSH_PRIVATE_KEY
: 遠端伺服器的 SSH 私鑰。SSH_KNOWN_HOSTS
: 遠端伺服器的公鑰。您可以透過在終端機執行ssh-keyscan your-server.com
來獲取。SERVER_USER
: 伺服器用戶名 (例如:deployer
)。SERVER_HOST
: 伺服器 IP 或域名 (例如:your-domain.com
)。DB_HOST
: 生產環境資料庫主機 (例如:mysql
或db.your-domain.com
)。DB_DATABASE
: 資料庫名稱 (例如:laravel
)。DB_USERNAME
: 資料庫用戶名 (例如:laravel_user
)。DB_PASSWORD
: 資料庫密碼 (敏感資訊,請設為保護變數)。SLACK_WEBHOOK_URL
: Slack 通知的 Webhook URL (可選)。
2. 伺服器環境準備
確保您的遠端伺服器已安裝以下軟體和配置:
- PHP 8.2+
- MySQL 8.0+
- Nginx (配置 Nginx 指向
/var/www/laravel-app/public
作為根目錄) - Composer
請確保伺服器上存在資料庫用戶和資料庫,並授予適當的權限。您可以執行以下指令來完成:
mysql -u root -p
CREATE DATABASE laravel;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'secret';
GRANT ALL PRIVILEGES ON laravel.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
exit;
此外,請確保 /var/www/laravel-app
目錄存在且具有正確的權限,以便您的部署用戶可以寫入。
3. 專案準備
- 將您的 Migration 文件、測試檔案和
.env.example
提交到 GitLab 儲存庫。 - 確認
phpunit.xml
已根據上述範例正確配置。
程式碼功能與說明
這個 CI/CD 管道包含三個主要階段:
1. 測試階段 (test
)
- 環境模擬:使用
php:8.2-fpm-alpine
映像和mysql:8.0
服務,模擬生產環境。 - 依賴安裝:安裝專案所需的 Composer 依賴。
- 資料庫操作:執行
php artisan migrate:fresh --seed
來重置資料庫並運行所有 Migration,同時填充測試資料。 - 單元測試:運行 PHPUnit 測試,並生成程式碼覆蓋率報告。
- 觸發條件:僅在推送到
main
分支或建立 Merge Requests 時觸發。
2. 構建階段 (build
)
- 生產環境優化:安裝 Composer 依賴時排除開發環境的依賴 (
--no-dev
),並優化自動載入器。 - 應用程式快取:快取 Laravel 的設定、路由和視圖,以提升效能。
- 打包應用程式:將 Laravel 應用程式程式碼壓縮成
laravel-app.zip
檔案,以方便部署。 - 觸發條件:僅在推送到
main
分支時觸發。
3. 部署階段 (deploy
)
- 資料庫備份:在部署前自動備份生產環境的資料庫到遠端伺服器的
/backups
目錄。備份檔案會包含時間戳記,例如:db_backup_YYYY-MM-DD_HH-MM-SS.sql
。 - 應用程式部署:使用
rsync
將壓縮好的laravel-app.zip
傳輸到遠端伺服器,然後解壓縮到/var/www/laravel-app
。 - 動態生成
.env
:在遠端伺服器上根據 GitLab CI/CD 變數動態生成.env
檔案,確保生產環境的配置正確。 - 執行 Migration:在遠端伺服器上執行
php artisan migrate --force
來更新資料庫結構。--force
選項在生產環境中是必要的,否則會出現提示。 - 重啟 Nginx:重啟 Nginx 服務,以確保應用程式的新程式碼被載入。
- Slack 通知:部署成功或失敗時,會自動發送通知到您配置的 Slack 頻道。
- 觸發條件:僅在推送到
main
分支時觸發。 - 建議手動觸發:為了更好地控制部署過程,建議將部署階段設定為
when: manual
,以便您可以手動啟動部署。
錯誤處理
- 如果任何一個關鍵步驟(例如:資料庫備份、Migration)失敗,管道將會中止,並且會發送 Slack 通知報告錯誤,幫助您及時發現問題。
- 資料庫備份確保了在 Migration 失敗時,您可以回滾到先前的狀態。
- Laravel Migration 的
down
方法也提供了回滾機制,以防需要撤銷 Migration。
執行與驗證
-
觸發 CI/CD 管道:將您的程式碼(包含
.gitlab-ci.yml
、新的 Migration 文件、更新後的測試檔案和.env.example
)推送到 GitLab 的main
分支。 -
檢查管道狀態:前往 GitLab 專案的 CI/CD > Pipelines,確認測試、構建和部署階段是否成功。
-
驗證資料庫更新:登錄到遠端伺服器,執行以下指令,檢查
users
表是否包含了phone_number
欄位:Bashmysql -u laravel_user -p laravel -e "DESCRIBE users;"
您應該會看到
phone_number
欄位在輸出中。 -
驗證應用程式運行:訪問您的應用程式 URL (例如:
https://your-domain.com
),確保應用程式正常運行,並且新的功能(如果有的話)可以正常使用。
進階建議
1. 多環境支持
您可以為不同的環境(例如 staging
和 production
)配置不同的資料庫和部署路徑,並在 .gitlab-ci.yml
中添加條件分支。
deploy_staging:
stage: deploy
environment:
name: staging
url: https://staging.your-domain.com
script:
- # 針對 staging 環境的部署邏輯
- ssh $STAGING_USER@$STAGING_HOST "cd /var/www/staging && php artisan migrate --force"
only:
- staging # 假設有一個 staging 分支
deploy_production:
stage: deploy
environment:
name: production
url: https://your-domain.com
script:
- # 針對 production 環境的部署邏輯
- ssh $PRODUCTION_USER@$PRODUCTION_HOST "cd /var/www/production && php artisan migrate --force"
only:
- main # 假設 main 分支代表 production
when: manual # 通常生產環境部署建議手動觸發
2. 回滾機制
在部署失敗時,您可以考慮執行 php artisan migrate:rollback --step=1
來回滾最後一次的 Migration。這需要更精確的錯誤處理和觸發條件。
# 部署階段的 after_script 中可以添加
# ... (前略)
after_script:
- |
if [ "$CI_JOB_STATUS" == "failed" ]; then
echo "部署失敗,嘗試回滾 Migration..."
ssh "$SERVER_USER"@"$SERVER_HOST" "cd /var/www/laravel-app && php artisan migrate:rollback --step=1" || echo "Migration 回滾失敗!"
# 發送 Slack 失敗通知
if [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -X POST -H 'Content-type: application/json' --data '{"text":"❌ Laravel 部署到 '$SERVER_HOST' 失敗,且已嘗試回滾!"}' $SLACK_WEBHOOK_URL
fi
elif [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -X POST -H 'Content-type: application/json' --data '{"text":"✅ Laravel 應用程式已成功部署到 '$SERVER_HOST'!"}' $SLACK_WEBHOOK_URL
fi
3. 測試覆蓋率整合
您可以整合 Codecov 或其他工具,在 CI/CD 中上傳測試覆蓋率報告,並在您的 README 中添加覆蓋率徽章。
test:
# ... (前略)
after_script:
- bash <(curl -s https://codecov.io/bash) # 將覆蓋率報告上傳到 Codecov
4. 零停機部署
對於對服務可用性要求極高的專案,您可以考慮使用更進階的零停機部署策略,例如:
- Laravel Envoyer:一個專門為 Laravel 應用程式設計的零停機部署服務。
- Blue-Green 部署:透過維護兩個相同的生產環境(Blue 和 Green),並在部署時切換流量來實現零停機。
- 符號連結 (Symlink) 部署:這在我們目前的部署腳本中已有初步體現,通過先將新版本部署到一個新目錄,然後更新 Nginx 的符號連結來實現。
驗證與除錯
- 檢查管道失敗原因:當 CI/CD 管道失敗時,請務必查看 GitLab CI/CD 的日誌。日誌會提供詳細的錯誤訊息,幫助您判斷是依賴安裝問題、資料庫連線錯誤,還是 Migration 語法錯誤。
- 本地模擬:在推送程式碼之前,您可以在本地環境使用 Docker Compose 模擬 MySQL 和 PHP 環境,並嘗試運行
.gitlab-ci.yml
中的指令,以提前發現問題。 - 資料庫確認:部署後,務必手動登錄遠端伺服器,檢查資料庫結構和資料完整性,確保 Migration 已正確應用且沒有資料遺失。
希望這份詳盡的指南能幫助您在 GitLab CI/CD 中成功配置 Laravel 專案的自動化資料庫更新!
您是否還有其他特定場景(例如處理大表 Migration、多租戶資料庫或 FastAPI/Django 整合)的程式碼範例需要我提供呢?
沒有留言:
張貼留言