# Test and deploy Laravel applications with GitLab CI/CD and Envoy
> 原文:[https://docs.gitlab.com/ee/ci/examples/laravel_with_gitlab_and_envoy/](https://docs.gitlab.com/ee/ci/examples/laravel_with_gitlab_and_envoy/)
* [Introduction](#introduction)
* [Initialize our Laravel app on GitLab](#initialize-our-laravel-app-on-gitlab)
* [Unit Test](#unit-test)
* [Push to GitLab](#push-to-gitlab)
* [Configure the production server](#configure-the-production-server)
* [Create a new user](#create-a-new-user)
* [Add SSH key](#add-ssh-key)
* [Configuring NGINX](#configuring-nginx)
* [Setting up Envoy](#setting-up-envoy)
* [How Envoy works](#how-envoy-works)
* [Zero downtime deployment](#zero-downtime-deployment)
* [@setup directive](#setup-directive)
* [@story directive](#story-directive)
* [Clone the repository](#clone-the-repository)
* [Installing dependencies with Composer](#installing-dependencies-with-composer)
* [Activate new release](#activate-new-release)
* [Full script](#full-script)
* [Continuous Integration with GitLab](#continuous-integration-with-gitlab)
* [Create a Container Image](#create-a-container-image)
* [Setting Up GitLab Container Registry](#setting-up-gitlab-container-registry)
* [Setting up GitLab CI/CD](#setting-up-gitlab-cicd)
* [Image and Services](#image-and-services)
* [Variables](#variables)
* [Unit Test as the first job](#unit-test-as-the-first-job)
* [Deploy to production](#deploy-to-production)
* [Turn on GitLab CI/CD](#turn-on-gitlab-cicd)
* [Conclusion](#conclusion)
# Test and deploy Laravel applications with GitLab CI/CD and Envoy[](#test-and-deploy-laravel-applications-with-gitlab-cicd-and-envoy "Permalink")
## Introduction[](#introduction "Permalink")
GitLab 通過持續集成為我們的應用程序提供功能,并且可以隨時將新的代碼更改輕松部署到生產服務器.
在本教程中,我們將向您展示如何初始化[Laravel](https://s0laravel0com.icopy.site)應用程序并設置[Envoy](https://s0laravel0com.icopy.site/docs/master/envoy)任務,然后我們將跳入如何使用[GitLab CI / CD](../README.html)通過[Continuous Delivery](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)測試和部署它的方法.
我們假設您具有 Laravel 和 Linux 服務器的基本經驗,并且知道如何使用 GitLab.
Laravel 是一個用 PHP 編寫的高質量 Web 框架. 它有一個很棒的社區,提供了[很棒的文檔](https://s0laravel0com.icopy.site/docs) . 除了常規的路由,控制器,請求,響應,視圖和(刀片)模板外,Laravel 還提供了許多其他服務,例如緩存,事件,本地化,身份驗證以及許多其他服務.
我們將使用[Envoy](https://s0laravel0com.icopy.site/docs/master/envoy)作為基于 PHP 的 SSH 任務運行程序. 它使用簡潔的[Blade 語法](https://s0laravel0com.icopy.site/docs/master/blade)來設置可以在遠程服務器上運行的任務,例如,從存儲庫克隆項目,安裝 Composer 依賴項以及運行[Artisan 命令](https://s0laravel0com.icopy.site/docs/master/artisan) .
## Initialize our Laravel app on GitLab[](#initialize-our-laravel-app-on-gitlab "Permalink")
我們假設[您已經安裝了一個新的 Laravel 項目](https://s0laravel0com.icopy.site/docs/master/installation) ,所以讓我們從單元測試開始,并為該項目初始化 Git.
### Unit Test[](#unit-test "Permalink")
Laravel 的每個新安裝(當前為 5.4)都在測試目錄中放置了兩種類型的測試:"功能"和"單元". 這是來自`test/Unit/ExampleTest.php`的單元測試:
```
<?php
namespace Tests\Unit;
...
class ExampleTest extends TestCase
{
public function testBasicTest()
{
$this->assertTrue(true);
}
}
```
該測試就像斷言給定值是 true 一樣簡單.
Laravel 默認使用`PHPUnit`進行測試. 如果運行`vendor/bin/phpunit`我們應該看到綠色輸出:
```
vendor/bin/phpunit
OK (1 test, 1 assertions)
```
該測試將在以后用于通過 GitLab CI / CD 持續測試我們的應用程序.
### Push to GitLab[](#push-to-gitlab "Permalink")
由于我們已經在本地啟動并運行了應用程序,因此現在是將代碼庫推送到我們的遠程存儲庫的時候了. 讓我們在 GitLab 中創建[一個](../../../gitlab-basics/create-project.html)名為`laravel-sample` [的新項目](../../../gitlab-basics/create-project.html) . 之后,按照項目主頁上顯示的命令行說明在我們的計算機上啟動存儲庫并推送第一個提交.
```
cd laravel-sample
git init
git remote add origin git@gitlab.example.com:<USERNAME>/laravel-sample.git
git add .
git commit -m 'Initial Commit'
git push -u origin master
```
## Configure the production server[](#configure-the-production-server "Permalink")
在開始設置 Envoy 和 GitLab CI / CD 之前,讓我們快速確保生產服務器已準備好進行部署. 我們已經在 Ubuntu 16.04 上安裝了 LEMP 堆棧,該堆棧代表 Linux,NGINX,MySQL 和 PHP.
### Create a new user[](#create-a-new-user "Permalink")
現在,讓我們創建一個新用戶,該用戶將用于部署我們的網站并使用[Linux ACL](https://serversforhackers.com/c/linux-acls)為其授予所需的權限:
```
# Create user deployer
sudo adduser deployer
# Give the read-write-execute permissions to deployer user for directory /var/www
sudo setfacl -R -m u:deployer:rwx /var/www
```
如果您的 Ubuntu 服務器上未安裝 ACL,請使用以下命令進行安裝:
```
sudo apt install acl
```
### Add SSH key[](#add-ssh-key "Permalink")
假設我們要從 GitLab 上的私有存儲庫將應用程序部署到生產服務器. 首先,我們需要為部署者用戶[生成一個**沒有密碼短語**的新 SSH 密鑰對](../../../ssh/README.html) .
之后,我們需要復制私鑰,以使用 SSH 作為部署者用戶將其用于連接到我們的服務器,以便能夠自動化部署過程:
```
# As the deployer user on server
#
# Copy the content of public key to authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# Copy the private key text block
cat ~/.ssh/id_rsa
```
現在,讓我們將其作為[變量](../../variables/README.html#gitlab-cicd-environment-variables)添加到您的 GitLab 項目中. 變量是用戶定義的變量,出于安全目的,它們存儲在`.gitlab-ci.yml` . 可以通過導航到項目的**設置** > **CI / CD**來為每個項目添加它們.
在**KEY**字段中,添加名稱`SSH_PRIVATE_KEY` ,然后在**VALUE**字段中,粘貼您先前復制的私鑰. 稍后,我們將在`.gitlab-ci.yml`使用此變量,以輕松地以部署者用戶身份連接到我們的遠程服務器,而無需輸入其密碼.
[](img/variables_page.png)
我們還需要將公共密鑰作為[部署密鑰](../../../ssh/README.html#deploy-keys)添加到" **項目"** > **"設置"** >" **存儲庫"** ,這使我們能夠通過[SSH 協議](../../../gitlab-basics/command-line-commands.html#start-working-on-your-project)從服務器訪問存儲庫.
```
# As the deployer user on the server
#
# Copy the public key
cat ~/.ssh/id_rsa.pub
```
在" **標題** "字段中,添加所需的任何名稱,然后將公共密鑰粘貼到" **密鑰"**字段中.
[](img/deploy_keys_page.png)
現在,讓我們在服務器上克隆存儲庫,以確保`deployer`用戶可以訪問該存儲庫.
```
# As the deployer user on server
#
git clone git@gitlab.example.com:<USERNAME>/laravel-sample.git
```
> **注意:**如果詢問, **請**回答**是.** `Are you sure you want to continue connecting (yes/no)?` . 它將 GitLab.com 添加到已知主機.
### Configuring NGINX[](#configuring-nginx "Permalink")
現在,讓我們確保我們的 Web 服務器配置指向`current/public`而不是`public` .
通過鍵入以下內容來打開默認的 NGINX 服務器塊配置文件:
```
sudo nano /etc/nginx/sites-available/default
```
配置應該是這樣的.
```
server {
root /var/www/app/current/public;
server_name example.com;
# Rest of the configuration
}
```
> **注意:**您可以將`/var/www/app/current/public`的應用程序名稱替換為應用程序的文件夾名稱.
## Setting up Envoy[](#setting-up-envoy "Permalink")
因此,我們已經準備好生產 Laravel 應用. 接下來是使用 Envoy 執行部署.
要使用 Envoy,我們應該首先[按照 Laravel 給出的說明](https://s0laravel0com.icopy.site/docs/master/envoy/)將其安裝在本地計算機上.
### How Envoy works[](#how-envoy-works "Permalink")
Envoy 的優點是它不需要 Blade 引擎,只使用 Blade 語法定義任務. 首先,我們在應用程序的根目錄中創建一個`Envoy.blade.php` ,其中包含一個簡單的測試 Envoy 的任務.
```
@servers(['web' => 'remote_username@remote_host'])
@task('list', ['on' => 'web'])
ls -l
@endtask
```
如您所料,我們在文件頂部的`@servers`指令中有一個數組,其中包含一個名為`web`的鍵,其值為服務器地址的值(例如, `deployer@192.168.1.1` 192.168.1.1). 然后,在我們的`@task`指令中,定義執行任務時應在服務器上運行的 bash 命令.
在本地計算機上,使用`run`命令運行 Envoy 任務.
```
envoy run list
```
它應該執行我們之前定義的`list`任務,該任務連接到服務器并列出目錄內容.
Envoy 不是 Laravel 的依賴項,因此您可以將其用于任何 PHP 應用程序.
### Zero downtime deployment[](#zero-downtime-deployment "Permalink")
每次我們部署到生產服務器時,Envoy 都會從 GitLab 存儲庫下載我們應用程序的最新版本,并將其替換為預覽版本. Envoy 做到了這一點,沒有任何[停機時間](https://en.wikipedia.org/wiki/Downtime) ,因此我們在部署過程中不必擔心有人在審查站點. 我們的部署計劃是從 GitLab 存儲庫克隆最新版本,安裝 Composer 依賴項,最后激活新版本.
#### [@setup](https://gitlab.com/setup) directive[](#setup-directive "Permalink")
我們部署過程的第一步是在內部定義一組變量 [](https://s0laravel0com.icopy.site/docs/master/envoy/)[@setup](https://gitlab.com/setup)指令. 您可以將`app`更改為您的應用名稱:
```
...
@setup
$repository = 'git@gitlab.example.com:<USERNAME>/laravel-sample.git';
$releases_dir = '/var/www/app/releases';
$app_dir = '/var/www/app';
$release = date('YmdHis');
$new_release_dir = $releases_dir .'/'. $release;
@endsetup
...
```
* `$repository`是我們存儲庫的地址
* `$releases_dir`目錄是我們部署應用程序的位置
* `$app_dir`是服務器上實時存在的應用程序的實際位置
* `$release` contains a date, so every time that we deploy a new release of our app, we get a new folder with the current date as name
* `$new_release_dir`是新版本的完整路徑,僅用于使任務更整潔
#### [@story](https://gitlab.com/story) directive[](#story-directive "Permalink")
的 [](https://s0laravel0com.icopy.site/docs/master/envoy/)[@story](https://gitlab.com/story)指令允許我們定義可以作為單個任務運行的任務列表. 在這里,我們有三個任務,稱為`clone_repository` , `run_composer`和`update_symlinks` . 這些變量可用于使我們的任務代碼更清晰:
```
...
@story('deploy')
clone_repository
run_composer
update_symlinks
@endstory
...
```
讓我們一一創建這三個任務.
#### Clone the repository[](#clone-the-repository "Permalink")
第一個任務將創建`releases`目錄(如果不存在),然后將存儲庫的`master`分支(默認情況下)克隆到由`$new_release_dir`變量指定的新 release 目錄中. `releases`目錄將包含我們所有的部署:
```
...
@task('clone_repository')
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $new_release_dir }}
git reset --hard {{ $commit }}
@endtask
...
```
隨著我們項目的發展,它的 Git 歷史將隨著時間的流逝而持續很長時間. 由于我們為每個版本創建一個目錄,因此不必為每個版本下載項目的歷史記錄. `--depth 1`選項是一個很好的解決方案, `--depth 1`可以節省系統時間和磁盤空間.
#### Installing dependencies with Composer[](#installing-dependencies-with-composer "Permalink")
您可能知道,此任務只是導航到新的發行目錄并運行 Composer 來安裝應用程序依賴項:
```
...
@task('run_composer')
echo "Starting deployment ({{ $release }})"
cd {{ $new_release_dir }}
composer install --prefer-dist --no-scripts -q -o
@endtask
...
```
#### Activate new release[](#activate-new-release "Permalink")
在準備好新版本的要求之后,接下來要做的就是從其中刪除存儲目錄,并創建兩個符號鏈接,以將應用程序的`storage`目錄和`.env`文件指向新版本. 然后,我們需要創建另一個符號鏈接到新版本用的名稱`current`放置在 app 目錄. `current`符號鏈接始終指向我們應用程序的最新版本:
```
...
@task('update_symlinks')
echo "Linking storage directory"
rm -rf {{ $new_release_dir }}/storage
ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage
echo 'Linking .env file'
ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env
echo 'Linking current release'
ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask
```
如您所見,我們使用`-nfs`作為`ln`命令的選項,它表示`storage` , `.env`和`current`不再指向預覽的發行版, `-nfs`通過強制將它們指向新發行版( `-nfs` `f`表示強制) ,這是我們進行多個部署的情況.
### Full script[](#full-script "Permalink")
腳本已準備就緒,但請確保將`deployer@192.168.1.1`更改為服務器,并使用要部署應用程序的目錄更改`/var/www/app` app.
最后,我們的`Envoy.blade.php`文件將如下所示:
```
@servers(['web' => 'deployer@192.168.1.1'])
@setup
$repository = 'git@gitlab.example.com:<USERNAME>/laravel-sample.git';
$releases_dir = '/var/www/app/releases';
$app_dir = '/var/www/app';
$release = date('YmdHis');
$new_release_dir = $releases_dir .'/'. $release;
@endsetup
@story('deploy')
clone_repository
run_composer
update_symlinks
@endstory
@task('clone_repository')
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $new_release_dir }}
git reset --hard {{ $commit }}
@endtask
@task('run_composer')
echo "Starting deployment ({{ $release }})"
cd {{ $new_release_dir }}
composer install --prefer-dist --no-scripts -q -o
@endtask
@task('update_symlinks')
echo "Linking storage directory"
rm -rf {{ $new_release_dir }}/storage
ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage
echo 'Linking .env file'
ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env
echo 'Linking current release'
ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask
```
在進行任何部署之前,我們應該做的另一件事是第一次將我們的應用程序`storage`文件夾手動復制到服務器上的`/var/www/app`目錄. 您可能想要創建另一個 Envoy 任務來為您完成此任務. 我們還在同一路徑中創建`.env`文件, `.env`設置生產環境變量. 這些是永久性數據,將與每個新版本共享.
現在,我們需要通過運行`envoy run deploy`來部署我們的應用程序,但這不是必需的,因為 GitLab 可以在 CI 的[環境下](../../environments/index.html)為我們處理此事,這將在本教程的[后面部分](#setting-up-gitlab-cicd)進行介紹.
現在是時候提交[Envoy.blade.php](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Envoy.blade.php)并將其推送到`master`分支了. 為了簡化起見,我們直接致力于`master` ,而無需使用[功能分支,](../../../topics/gitlab_flow.html#github-flow-as-a-simpler-alternative)因為協作不在本教程的討論范圍之內. 在現實世界的項目中,團隊可以使用[問題跟蹤程序](../../../user/project/issues/index.html)和[合并請求](../../../user/project/merge_requests/index.html)在分支之間移動代碼:
```
git add Envoy.blade.php
git commit -m 'Add Envoy'
git push origin master
```
## Continuous Integration with GitLab[](#continuous-integration-with-gitlab "Permalink")
我們已經在 GitLab 上準備好了我們的應用程序,我們也可以手動部署它. 但是,讓我們向前邁出一步,使用" [持續交付"](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery)方法自動完成此操作. 我們需要使用一組自動化測試來檢查每個提交,以盡早發現問題,然后,如果我們對測試結果感到滿意,則可以將其部署到目標環境.
[GitLab CI / CD](../../README.html)允許我們使用[Docker](https://www.docker.com)引擎來處理測試和部署應用程序的過程. 如果您不熟悉 Docker,請參閱[如何自動化 Docker 部署](http://paislee.io/how-to-automate-docker-deployments/) .
為了能夠使用 GitLab CI / CD 構建,測試和部署我們的應用程序,我們需要準備工作環境. 為此,我們將使用具有 Laravel 應用程序運行最低要求的 Docker 映像. [也有其他方法](../php.html#test-php-projects-using-the-docker-executor)可以執行此操作,但是它們可能會使我們的構建運行緩慢,而在使用更快的選項時,這不是我們想要的.
有了 Docker 映像,我們的構建運行得異常快!
### Create a Container Image[](#create-a-container-image "Permalink")
讓我們在應用程序的根目錄中創建一個[包含](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Dockerfile)以下內容的[Dockerfile](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Dockerfile) :
```
# Set the base image for subsequent instructions
FROM php:7.1
# Update packages
RUN apt-get update
# Install PHP and composer dependencies
RUN apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev
# Clear out the local repository of retrieved package files
RUN apt-get clean
# Install needed extensions
# Here you can install any other extension that you need during the test and deployment process
RUN docker-php-ext-install mcrypt pdo_mysql zip
# Install Composer
RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install Laravel Envoy
RUN composer global require "laravel/envoy=~1.0"
```
我們添加了[官方的 PHP 7.1 Docker 映像](https://hub.docker.com/_/php) ,該[映像](https://hub.docker.com/_/php)由 Debian Jessie 的最低安裝和預安裝的 PHP 組成,并且非常適合我們的用例.
我們使用了`docker-php-ext-install` (由官方 PHP Docker 映像提供)來安裝所需的 PHP 擴展.
#### Setting Up GitLab Container Registry[](#setting-up-gitlab-container-registry "Permalink")
現在我們有了`Dockerfile`讓我們構建并將其推送到[GitLab 容器注冊表](../../../user/packages/container_registry/index.html) .
> 注冊表是存儲和標記圖像以供以后使用的地方. 開發人員可能希望維護自己的注冊表,以用于私人,公司映像或僅用于測試的一次性映像. 使用 GitLab 容器注冊表意味著您無需設置和管理另一項服務或使用公共注冊表.
在您的 GitLab 項目存儲庫上,導航到" **注冊表"**選項卡.
[](img/container_registry_page_empty_image.png)
您可能需要對項目[啟用 Container Registry](../../../user/packages/container_registry/index.html#enable-the-container-registry-for-your-project)才能看到此選項卡. 您可以在項目的**設置>常規>可見性,項目功能,權限**下找到它.
要在我們的機器上開始使用 Container Registry,我們首先需要使用我們的 GitLab 用戶名和密碼登錄到 GitLab 注冊表:
```
docker login registry.gitlab.com
```
然后,我們可以構建圖像并將其推送到 GitLab:
```
docker build -t registry.gitlab.com/<USERNAME>/laravel-sample .
docker push registry.gitlab.com/<USERNAME>/laravel-sample
```
> **注意:**要運行上述命令,我們首先需要在計算機上安裝[Docker](https://s0docs0docker0com.icopy.site/engine/installation/) .
恭喜你! 您剛剛將第一個 Docker 映像推送到了 GitLab 注冊表,如果刷新頁面,您應該可以看到它:
[](img/container_registry_page_with_image.jpg)
> **注意:**您也可以[使用 GitLab CI / CD](https://about.gitlab.com/blog/2016/05/23/gitlab-container-registry/#use-with-gitlab-ci)來構建和推送 Docker 映像,而不是在計算機上執行.
我們將在`.gitlab-ci.yml`配置文件中進一步使用此圖像,以處理測試和部署我們的應用程序的過程.
讓我們提交`Dockerfile`文件.
```
git add Dockerfile
git commit -m 'Add Dockerfile'
git push origin master
```
### Setting up GitLab CI/CD[](#setting-up-gitlab-cicd "Permalink")
為了使用 GitLab CI / CD 構建和測試我們的應用程序,我們需要在存儲庫的根目錄中有一個名為`.gitlab-ci.yml`的文件. 它類似于 Circle CI 和 Travis CI,但內置 GitLab.
我們的`.gitlab-ci.yml`文件將如下所示:
```
image: registry.gitlab.com/<USERNAME>/laravel-sample:latest
services:
- mysql:5.7
variables:
MYSQL_DATABASE: homestead
MYSQL_ROOT_PASSWORD: secret
DB_HOST: mysql
DB_USERNAME: root
stages:
- test
- deploy
unit_test:
stage: test
script:
- cp .env.example .env
- composer install
- php artisan key:generate
- php artisan migrate
- vendor/bin/phpunit
deploy_production:
stage: deploy
script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ~/.composer/vendor/bin/envoy run deploy --commit="$CI_COMMIT_SHA"
environment:
name: production
url: http://192.168.1.1
when: manual
only:
- master
```
需要很多東西,不是嗎? 讓我們逐步進行操作.
#### Image and Services[](#image-and-services "Permalink")
[GitLab Runners](../../runners/README.html)運行`.gitlab-ci.yml`定義的腳本. `image`關鍵字告訴跑步者要使用哪個圖像. `services`關鍵字定義[鏈接到主圖像的其他圖像](../../docker/using_docker_images.html#what-is-a-service) . 在這里,我們將之前創建的容器映像用作主映像,還將 MySQL 5.7 用作服務.
```
image: registry.gitlab.com/<USERNAME>/laravel-sample:latest
services:
- mysql:5.7
...
```
> **注意:**如果要使用不同的 PHP 版本和[數據庫管理系統](../../services/README.html)測試應用程序,則可以為每個測試作業定義不同的`image`和`services`關鍵字.
#### Variables[](#variables "Permalink")
GitLab CI / CD 允許我們在工作中使用[環境變量](../../yaml/README.html#variables) . 我們將 MySQL 定義為數據庫管理系統,它帶有默認創建的超級用戶根.
因此,我們應該通過將`MYSQL_DATABASE`變量定義為數據庫名稱并將`MYSQL_ROOT_PASSWORD`變量定義為`root`的密碼來調整 MySQL 實例的配置. 在[官方的 MySQL Docker Image 中](https://hub.docker.com/_/mysql)找到有關 MySQL 變量的更多信息.
還要將變量`DB_HOST`設置為`mysql` ,將`DB_USERNAME`為`root` ,這是 Laravel 特定的變量. 我們將`DB_HOST`定義為`mysql`而不是`127.0.0.1` ,因為我們將 MySQL Docker 映像用作[與主 Docker 映像鏈接](../../docker/using_docker_images.html#how-services-are-linked-to-the-job)的服務.
```
...
variables:
MYSQL_DATABASE: homestead
MYSQL_ROOT_PASSWORD: secret
DB_HOST: mysql
DB_USERNAME: root
...
```
#### Unit Test as the first job[](#unit-test-as-the-first-job "Permalink")
我們將所需的外殼程序腳本定義為運行`unit_test`作業時要執行的[腳本](../../yaml/README.html#script)變量的數組.
These scripts are some Artisan commands to prepare the Laravel, and, at the end of the script, we’ll run the tests by `PHPUnit`.
```
...
unit_test:
script:
# Install app dependencies
- composer install
# Set up .env
- cp .env.example .env
# Generate an environment key
- php artisan key:generate
# Run migrations
- php artisan migrate
# Run tests
- vendor/bin/phpunit
...
```
#### Deploy to production[](#deploy-to-production "Permalink")
`deploy_production`作業會將應用程序部署到生產服務器. 要使用 Envoy 部署我們的應用程序,我們必須將`$SSH_PRIVATE_KEY`變量設置為[SSH 私鑰](../../ssh_keys/README.html#ssh-keys-when-using-the-docker-executor) . 如果 SSH 密鑰已成功添加,我們可以運行 Envoy.
如前所述,GitLab 也支持[持續交付](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery)方法. [環境](../../yaml/README.html#environment)關鍵字告訴 GitLab 該作業已部署到`production`環境. `url`關鍵字用于在 GitLab 環境頁面上生成指向我們應用程序的鏈接. `only`關鍵字告訴 GitLab CI / CD 只有在管道正在構建`master`分支時才應執行作業. 最后, `when: manual`用于將作業從自動運行轉變為手動操作.
```
...
deploy_production:
script:
# Add the private SSH key to the build environment
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
# Run Envoy
- ~/.composer/vendor/bin/envoy run deploy
environment:
name: production
url: http://192.168.1.1
when: manual
only:
- master
```
您可能還需要為[登臺環境](https://about.gitlab.com/blog/2016/08/26/ci-deployment-and-environments/)添加另一個作業,以便在部署到生產[環境](https://about.gitlab.com/blog/2016/08/26/ci-deployment-and-environments/)之前對應用程序進行最終測試.
### Turn on GitLab CI/CD[](#turn-on-gitlab-cicd "Permalink")
我們已經準備好使用 GitLab CI / CD 測試和部署應用程序所需的一切. 為此,請提交`.gitlab-ci.yml`并將其推入`master`分支. 它將觸發管道,您可以在項目的**Pipelines**下實時觀看.
[](img/pipelines_page.png)
Here we see our **Test** and **Deploy** stages. The **Test** stage has the `unit_test` build running. click on it to see the Runner’s output.
[](img/pipeline_page.png)
代碼成功通過管道后,我們可以通過單擊右側的**播放**按鈕將其部署到生產服務器.
[](img/pipelines_page_deploy_button.png)
部署管道成功通過后,導航至" **管道">"環境"** .
[](img/environments_page.png)
如果某些操作無法正常工作,則可以回滾到應用程序的最新工作版本.
[](img/environment_page.png)
通過單擊右側指定的外部鏈接圖標,GitLab 將打開生產網站. 我們的部署成功完成,我們可以看到該應用程序正在運行.
[](img/laravel_welcome_page.png)
如果您想了解部署后生產服務器上的應用程序目錄結構如何,這里有三個目錄,分別名為`current` , `releases`和`storage` . 如您所知, `current`目錄是指向最新版本的符號鏈接. `.env`文件包含我們的 Laravel 環境變量.
[](img/production_server_app_directory.png)
如果導航到`current`目錄,則應該看到應用程序的內容. 如您所見, `.env`指向`/var/www/app/.env`文件, `storage`也指向`/var/www/app/storage/`目錄.
[](img/production_server_current_directory.png)
## Conclusion[](#conclusion "Permalink")
我們將 GitLab CI / CD 配置為執行自動化測試,并使用" [持續交付](https://continuousdelivery.com/) "方法直接從代碼庫部署到帶有 Envoy 的 Laravel 應用程序.
Envoy 也非常適合幫助我們在不編寫自定義 bash 腳本和進行 Linux 魔術的情況下部署應用程序.
- GitLab Docs
- Installation
- Requirements
- GitLab cloud native Helm Chart
- Install GitLab with Docker
- Installation from source
- Install GitLab on Microsoft Azure
- Installing GitLab on Google Cloud Platform
- Installing GitLab on Amazon Web Services (AWS)
- Analytics
- Code Review Analytics
- Productivity Analytics
- Value Stream Analytics
- Kubernetes clusters
- Adding and removing Kubernetes clusters
- Adding EKS clusters
- Adding GKE clusters
- Group-level Kubernetes clusters
- Instance-level Kubernetes clusters
- Canary Deployments
- Cluster Environments
- Deploy Boards
- GitLab Managed Apps
- Crossplane configuration
- Cluster management project (alpha)
- Kubernetes Logs
- Runbooks
- Serverless
- Deploying AWS Lambda function using GitLab CI/CD
- Securing your deployed applications
- Groups
- Contribution Analytics
- Custom group-level project templates
- Epics
- Manage epics
- Group Import/Export
- Insights
- Issues Analytics
- Iterations
- Public access
- SAML SSO for GitLab.com groups
- SCIM provisioning using SAML SSO for GitLab.com groups
- Subgroups
- Roadmap
- Projects
- GitLab Secure
- Security Configuration
- Container Scanning
- Dependency Scanning
- Dependency List
- Static Application Security Testing (SAST)
- Secret Detection
- Dynamic Application Security Testing (DAST)
- GitLab Security Dashboard
- Offline environments
- Standalone Vulnerability pages
- Security scanner integration
- Badges
- Bulk editing issues and merge requests at the project level
- Code Owners
- Compliance
- License Compliance
- Compliance Dashboard
- Create a project
- Description templates
- Deploy Keys
- Deploy Tokens
- File finder
- Project integrations
- Integrations
- Atlassian Bamboo CI Service
- Bugzilla Service
- Custom Issue Tracker service
- Discord Notifications service
- Enabling emails on push
- GitHub project integration
- Hangouts Chat service
- Atlassian HipChat
- Irker IRC Gateway
- GitLab Jira integration
- Mattermost Notifications Service
- Mattermost slash commands
- Microsoft Teams service
- Mock CI Service
- Prometheus integration
- Redmine Service
- Slack Notifications Service
- Slack slash commands
- GitLab Slack application
- Webhooks
- YouTrack Service
- Insights
- Issues
- Crosslinking Issues
- Design Management
- Confidential issues
- Due dates
- Issue Boards
- Issue Data and Actions
- Labels
- Managing issues
- Milestones
- Multiple Assignees for Issues
- Related issues
- Service Desk
- Sorting and ordering issue lists
- Issue weight
- Associate a Zoom meeting with an issue
- Merge requests
- Allow collaboration on merge requests across forks
- Merge Request Approvals
- Browser Performance Testing
- How to create a merge request
- Cherry-pick changes
- Code Quality
- Load Performance Testing
- Merge Request dependencies
- Fast-forward merge requests
- Merge when pipeline succeeds
- Merge request conflict resolution
- Reverting changes
- Reviewing and managing merge requests
- Squash and merge
- Merge requests versions
- Draft merge requests
- Members of a project
- Migrating projects to a GitLab instance
- Import your project from Bitbucket Cloud to GitLab
- Import your project from Bitbucket Server to GitLab
- Migrating from ClearCase
- Migrating from CVS
- Import your project from FogBugz to GitLab
- Gemnasium
- Import your project from GitHub to GitLab
- Project importing from GitLab.com to your private GitLab instance
- Import your project from Gitea to GitLab
- Import your Jira project issues to GitLab
- Migrating from Perforce Helix
- Import Phabricator tasks into a GitLab project
- Import multiple repositories by uploading a manifest file
- Import project from repo by URL
- Migrating from SVN to GitLab
- Migrating from TFVC to Git
- Push Options
- Releases
- Repository
- Branches
- Git Attributes
- File Locking
- Git file blame
- Git file history
- Repository mirroring
- Protected branches
- Protected tags
- Push Rules
- Reduce repository size
- Signing commits with GPG
- Syntax Highlighting
- GitLab Web Editor
- Web IDE
- Requirements Management
- Project settings
- Project import/export
- Project access tokens (Alpha)
- Share Projects with other Groups
- Snippets
- Static Site Editor
- Wiki
- Project operations
- Monitor metrics for your CI/CD environment
- Set up alerts for Prometheus metrics
- Embedding metric charts within GitLab-flavored Markdown
- Embedding Grafana charts
- Using the Metrics Dashboard
- Dashboard YAML properties
- Metrics dashboard settings
- Panel types for dashboards
- Using Variables
- Templating variables for metrics dashboards
- Prometheus Metrics library
- Monitoring AWS Resources
- Monitoring HAProxy
- Monitoring Kubernetes
- Monitoring NGINX
- Monitoring NGINX Ingress Controller
- Monitoring NGINX Ingress Controller with VTS metrics
- Alert Management
- Error Tracking
- Tracing
- Incident Management
- GitLab Status Page
- Feature Flags
- GitLab CI/CD
- GitLab CI/CD pipeline configuration reference
- GitLab CI/CD include examples
- Introduction to CI/CD with GitLab
- Getting started with GitLab CI/CD
- How to enable or disable GitLab CI/CD
- Using SSH keys with GitLab CI/CD
- Migrating from CircleCI
- Migrating from Jenkins
- Auto DevOps
- Getting started with Auto DevOps
- Requirements for Auto DevOps
- Customizing Auto DevOps
- Stages of Auto DevOps
- Upgrading PostgreSQL for Auto DevOps
- Cache dependencies in GitLab CI/CD
- GitLab ChatOps
- Cloud deployment
- Docker integration
- Building Docker images with GitLab CI/CD
- Using Docker images
- Building images with kaniko and GitLab CI/CD
- GitLab CI/CD environment variables
- Predefined environment variables reference
- Where variables can be used
- Deprecated GitLab CI/CD variables
- Environments and deployments
- Protected Environments
- GitLab CI/CD Examples
- Test a Clojure application with GitLab CI/CD
- Using Dpl as deployment tool
- Testing a Phoenix application with GitLab CI/CD
- End-to-end testing with GitLab CI/CD and WebdriverIO
- DevOps and Game Dev with GitLab CI/CD
- Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
- How to deploy Maven projects to Artifactory with GitLab CI/CD
- Testing PHP projects
- Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD
- Test and deploy Laravel applications with GitLab CI/CD and Envoy
- Test and deploy a Python application with GitLab CI/CD
- Test and deploy a Ruby application with GitLab CI/CD
- Test and deploy a Scala application to Heroku
- GitLab CI/CD for external repositories
- Using GitLab CI/CD with a Bitbucket Cloud repository
- Using GitLab CI/CD with a GitHub repository
- GitLab Pages
- GitLab Pages
- GitLab Pages domain names, URLs, and baseurls
- Create a GitLab Pages website from scratch
- Custom domains and SSL/TLS Certificates
- GitLab Pages integration with Let's Encrypt
- GitLab Pages Access Control
- Exploring GitLab Pages
- Incremental Rollouts with GitLab CI/CD
- Interactive Web Terminals
- Optimizing GitLab for large repositories
- Metrics Reports
- CI/CD pipelines
- Pipeline Architecture
- Directed Acyclic Graph
- Multi-project pipelines
- Parent-child pipelines
- Pipelines for Merge Requests
- Pipelines for Merged Results
- Merge Trains
- Job artifacts
- Pipeline schedules
- Pipeline settings
- Triggering pipelines through the API
- Review Apps
- Configuring GitLab Runners
- GitLab CI services examples
- Using MySQL
- Using PostgreSQL
- Using Redis
- Troubleshooting CI/CD
- GitLab Package Registry
- GitLab Container Registry
- Dependency Proxy
- GitLab Composer Repository
- GitLab Conan Repository
- GitLab Maven Repository
- GitLab NPM Registry
- GitLab NuGet Repository
- GitLab PyPi Repository
- API Docs
- API resources
- .gitignore API
- GitLab CI YMLs API
- Group and project access requests API
- Appearance API
- Applications API
- Audit Events API
- Avatar API
- Award Emoji API
- Project badges API
- Group badges API
- Branches API
- Broadcast Messages API
- Project clusters API
- Group clusters API
- Instance clusters API
- Commits API
- Container Registry API
- Custom Attributes API
- Dashboard annotations API
- Dependencies API
- Deploy Keys API
- Deployments API
- Discussions API
- Dockerfiles API
- Environments API
- Epics API
- Events
- Feature Flags API
- Feature flag user lists API
- Freeze Periods API
- Geo Nodes API
- Group Activity Analytics API
- Groups API
- Import API
- Issue Boards API
- Group Issue Boards API
- Issues API
- Epic Issues API
- Issues Statistics API
- Jobs API
- Keys API
- Labels API
- Group Labels API
- License
- Licenses API
- Issue links API
- Epic Links API
- Managed Licenses API
- Markdown API
- Group and project members API
- Merge request approvals API
- Merge requests API
- Project milestones API
- Group milestones API
- Namespaces API
- Notes API
- Notification settings API
- Packages API
- Pages domains API
- Pipeline schedules API
- Pipeline triggers API
- Pipelines API
- Project Aliases API
- Project import/export API
- Project repository storage moves API
- Project statistics API
- Project templates API
- Projects API
- Protected branches API
- Protected tags API
- Releases API
- Release links API
- Repositories API
- Repository files API
- Repository submodules API
- Resource label events API
- Resource milestone events API
- Resource weight events API
- Runners API
- SCIM API
- Search API
- Services API
- Application settings API
- Sidekiq Metrics API
- Snippets API
- Project snippets
- Application statistics API
- Suggest Changes API
- System hooks API
- Tags API
- Todos API
- Users API
- Project-level Variables API
- Group-level Variables API
- Version API
- Vulnerabilities API
- Vulnerability Findings API
- Wikis API
- GraphQL API
- Getting started with GitLab GraphQL API
- GraphQL API Resources
- API V3 to API V4
- Validate the .gitlab-ci.yml (API)
- User Docs
- Abuse reports
- User account
- Active sessions
- Deleting a User account
- Permissions
- Personal access tokens
- Profile preferences
- Threads
- GitLab and SSH keys
- GitLab integrations
- Git
- GitLab.com settings
- Infrastructure as code with Terraform and GitLab
- GitLab keyboard shortcuts
- GitLab Markdown
- AsciiDoc
- GitLab Notification Emails
- GitLab Quick Actions
- Autocomplete characters
- Reserved project and group names
- Search through GitLab
- Advanced Global Search
- Advanced Syntax Search
- Time Tracking
- GitLab To-Do List
- Administrator Docs
- Reference architectures
- Reference architecture: up to 1,000 users
- Reference architecture: up to 2,000 users
- Reference architecture: up to 3,000 users
- Reference architecture: up to 5,000 users
- Reference architecture: up to 10,000 users
- Reference architecture: up to 25,000 users
- Reference architecture: up to 50,000 users
- Troubleshooting a reference architecture set up
- Working with the bundled Consul service
- Configuring PostgreSQL for scaling
- Configuring GitLab application (Rails)
- Load Balancer for multi-node GitLab
- Configuring a Monitoring node for Scaling and High Availability
- NFS
- Working with the bundled PgBouncer service
- Configuring Redis for scaling
- Configuring Sidekiq
- Admin Area settings
- Continuous Integration and Deployment Admin settings
- Custom instance-level project templates
- Diff limits administration
- Enable and disable GitLab features deployed behind feature flags
- Geo nodes Admin Area
- GitLab Pages administration
- Health Check
- Job logs
- Labels administration
- Log system
- PlantUML & GitLab
- Repository checks
- Repository storage paths
- Repository storage types
- Account and limit settings
- Service templates
- System hooks
- Changing your time zone
- Uploads administration
- Abuse reports
- Activating and deactivating users
- Audit Events
- Blocking and unblocking users
- Broadcast Messages
- Elasticsearch integration
- Gitaly
- Gitaly Cluster
- Gitaly reference
- Monitoring GitLab
- Monitoring GitLab with Prometheus
- Performance Bar
- Usage statistics
- Object Storage
- Performing Operations in GitLab
- Cleaning up stale Redis sessions
- Fast lookup of authorized SSH keys in the database
- Filesystem Performance Benchmarking
- Moving repositories managed by GitLab
- Run multiple Sidekiq processes
- Sidekiq MemoryKiller
- Switching to Puma
- Understanding Unicorn and unicorn-worker-killer
- User lookup via OpenSSH's AuthorizedPrincipalsCommand
- GitLab Package Registry administration
- GitLab Container Registry administration
- Replication (Geo)
- Geo database replication
- Geo with external PostgreSQL instances
- Geo configuration
- Using a Geo Server
- Updating the Geo nodes
- Geo with Object storage
- Docker Registry for a secondary node
- Geo for multiple nodes
- Geo security review (Q&A)
- Location-aware Git remote URL with AWS Route53
- Tuning Geo
- Removing secondary Geo nodes
- Geo data types support
- Geo Frequently Asked Questions
- Geo Troubleshooting
- Geo validation tests
- Disaster Recovery (Geo)
- Disaster recovery for planned failover
- Bring a demoted primary node back online
- Automatic background verification
- Rake tasks
- Back up and restore GitLab
- Clean up
- Namespaces
- Maintenance Rake tasks
- Geo Rake Tasks
- GitHub import
- Import bare repositories
- Integrity check Rake task
- LDAP Rake tasks
- Listing repository directories
- Praefect Rake tasks
- Project import/export administration
- Repository storage Rake tasks
- Generate sample Prometheus data
- Uploads migrate Rake tasks
- Uploads sanitize Rake tasks
- User management
- Webhooks administration
- X.509 signatures
- Server hooks
- Static objects external storage
- Updating GitLab
- GitLab release and maintenance policy
- Security
- Password Storage
- Custom password length limits
- Restrict allowed SSH key technologies and minimum length
- Rate limits
- Webhooks and insecure internal web services
- Information exclusivity
- How to reset your root password
- How to unlock a locked user from the command line
- User File Uploads
- How we manage the TLS protocol CRIME vulnerability
- User email confirmation at sign-up
- Security of running jobs
- Proxying assets
- CI/CD Environment Variables
- Contributor and Development Docs
- Contribute to GitLab
- Community members & roles
- Implement design & UI elements
- Issues workflow
- Merge requests workflow
- Code Review Guidelines
- Style guides
- GitLab Architecture Overview
- CI/CD development documentation
- Database guides
- Database Review Guidelines
- Database Review Guidelines
- Migration Style Guide
- What requires downtime?
- Understanding EXPLAIN plans
- Rake tasks for developers
- Mass inserting Rails models
- GitLab Documentation guidelines
- Documentation Style Guide
- Documentation structure and template
- Documentation process
- Documentation site architecture
- Global navigation
- GitLab Docs monthly release process
- Telemetry Guide
- Usage Ping Guide
- Snowplow Guide
- Experiment Guide
- Feature flags in development of GitLab
- Feature flags process
- Developing with feature flags
- Feature flag controls
- Document features deployed behind feature flags
- Frontend Development Guidelines
- Accessibility & Readability
- Ajax
- Architecture
- Axios
- Design Patterns
- Frontend Development Process
- DropLab
- Emojis
- Filter
- Frontend FAQ
- GraphQL
- Icons and SVG Illustrations
- InputSetter
- Performance
- Principles
- Security
- Tooling
- Vuex
- Vue
- Geo (development)
- Geo self-service framework (alpha)
- Gitaly developers guide
- GitLab development style guides
- API style guide
- Go standards and style guidelines
- GraphQL API style guide
- Guidelines for shell commands in the GitLab codebase
- HTML style guide
- JavaScript style guide
- Migration Style Guide
- Newlines style guide
- Python Development Guidelines
- SCSS style guide
- Shell scripting standards and style guidelines
- Sidekiq debugging
- Sidekiq Style Guide
- SQL Query Guidelines
- Vue.js style guide
- Instrumenting Ruby code
- Testing standards and style guidelines
- Flaky tests
- Frontend testing standards and style guidelines
- GitLab tests in the Continuous Integration (CI) context
- Review Apps
- Smoke Tests
- Testing best practices
- Testing levels
- Testing Rails migrations at GitLab
- Testing Rake tasks
- End-to-end Testing
- Beginner's guide to writing end-to-end tests
- End-to-end testing Best Practices
- Dynamic Element Validation
- Flows in GitLab QA
- Page objects in GitLab QA
- Resource class in GitLab QA
- Style guide for writing end-to-end tests
- Testing with feature flags
- Translate GitLab to your language
- Internationalization for GitLab
- Translating GitLab
- Proofread Translations
- Merging translations from CrowdIn
- Value Stream Analytics development guide
- GitLab subscription
- Activate GitLab EE with a license