GitLab CI/CD 및 Envoy로 Laravel 애플리케이션 테스트 및 배포

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

소개

GitLab은 우리의 애플리케이션에 지속적인 통합(Continuous Integration) 기능을 제공하며, 원할 때마다 프로덕션 서버에 새로운 코드 변경 사항을 쉽게 배포할 수 있습니다.

이 튜토리얼에서는 Laravel 애플리케이션을 초기화하고 Envoy 작업을 설정하는 방법을 보여줄 것이며, 그 후 GitLab CI/CD와 함께 지속적인 배포를 통해 이를 테스트하고 배포하는 방법을 살펴보겠습니다.

Laravel, 리눅스 서버에 대한 기본적인 경험이 있으며, GitLab을 사용하는 방법을 알고 있다고 가정합니다.

Laravel은 PHP로 작성된 고품질 웹 프레임워크입니다.
훌륭한 커뮤니티와 환상적인 문서를 갖추고 있습니다.
일반적인 라우팅, 컨트롤러, 요청, 응답, 뷰 및 (Blade) 템플릿 외에도, Laravel은 캐시, 이벤트, 로컬라이제이션, 인증 등과 같은 많은 추가 서비스를 기본적으로 제공합니다.

우리는 Envoy를 SSH 작업 실행기로 사용할 것입니다.
이것은 원격 서버에서 실행할 수 있는 작업을 설정하기 위해 깔끔하고 최소한의 Blade 구문을 사용합니다.
예를 들어, 저장소에서 프로젝트를 클론하고, Composer 의존성을 설치하고, Artisan 명령어를 실행하는 등의 작업을 수행할 수 있습니다.

GitLab에서 우리의 Laravel 앱 초기화

새 Laravel 프로젝트를 설치했다고 가정합니다, 그러니 단위 테스트부터 시작하여 프로젝트에 Git을 초기화합시다.

단위 테스트

Laravel의 모든 새 설치(현재 버전 8.0)에는 테스트 디렉터리에 ‘Feature’와 ‘Unit’ 두 가지 유형의 테스트가 포함되어 있습니다.
다음은 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와 함께 애플리케이션을 지속적으로 테스트하는 데 사용됩니다.

GitLab에 푸시

로컬에서 애플리케이션이 실행되고 있으므로, 코드베이스를 원격 저장소에 푸시할 차례입니다.
GitLab에서 laravel-sample이라는 새 프로젝트를 생성합시다.
그 후, 프로젝트 홈페이지에 표시된 명령줄 지침에 따라 우리 머신에서 저장소를 초기화하고 첫 번째 커밋을 푸시합니다.

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 main

프로덕션 서버 구성

Envoy 및 GitLab CI/CD 설정을 시작하기 전에, 프로덕션 서버가 배포 준비가 되었는지 빠르게 확인해 보겠습니다.
우리는 Ubuntu 16.04에 LEMP 스택(리눅스, NGINX, MySQL, PHP)을 설치했습니다.

새 사용자 만들기

이제 웹사이트를 배포하는 데 사용될 새 사용자를 만들고 Linux ACL을 사용하여 필요한 권한을 부여합시다:

# 사용자 deployer 생성
sudo adduser deployer
# /var/www 디렉토리에 대해 deployer 사용자에게 읽기-쓰기-실행 권한 부여
sudo setfacl -R -m u:deployer:rwx /var/www

Ubuntu 서버에 ACL이 설치되어 있지 않은 경우, 다음 명령어를 사용하여 설치합니다:

sudo apt install acl

SSH 키 추가

이제 GitLab의 개인 저장소에서 프로덕션 서버에 앱을 배포하고자 한다고 가정합시다. 먼저, deployer 사용자에 대해 비밀번호 없는 새로운 SSH 키 쌍 생성하기를 해야 합니다.

그 후, SSH로 deployer 사용자로 서버에 연결하는 데 사용할 개인 키를 복사하여 배포 프로세스를 자동화할 수 있도록 합니다:

# 서버에서 deployer 사용자로
#
# 공개키의 내용을 authorized_keys에 복사
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# 개인 키 텍스트 블록 복사
cat ~/.ssh/id_rsa

이제 이를 GitLab 프로젝트에 CI/CD 변수로 추가합시다.

프로젝트 CI/CD 변수는 사용자 정의 변수이며 보안상의 이유로 .gitlab-ci.yml 외부에 저장됩니다.

프로젝트의 Settings > CI/CD로 이동하여 추가할 수 있습니다.

KEY 필드에는 SSH_PRIVATE_KEY라는 이름을 추가하고, VALUE 필드에는 아까 복사한 개인 키를 붙여넣습니다.

이 변수는 이후 .gitlab-ci.yml에서 사용하여 비밀번호를 입력하지 않고 deployer 사용자로 원격 서버에 쉽게 연결할 수 있게 합니다.

variables page

또한 Project > Settings > Repository에 공개 키를 배포 키로 추가해야 합니다. 이렇게 하면 SSH 프로토콜을 통해 서버에서 저장소에 접근할 수 있습니다.

# 서버에서 deployer 사용자로
#
# 공개 키 복사
cat ~/.ssh/id_rsa.pub

Title 필드에는 원하는 이름을 추가하고, Key 필드에 공개 키를 붙여넣습니다.

deploy keys page

이제 서버에서 deployer 사용자가 저장소에 접근할 수 있는지 확인하기 위해 저장소를 클론합니다.

# 서버에서 deployer 사용자로
#
git clone git@gitlab.example.com:<USERNAME>/laravel-sample.git

Are you sure you want to continue connecting (yes/no)?라는 질문이 나오면 yes라고 답하십시오. 이렇게 하면 GitLab.com이 알려진 호스트에 추가됩니다.

NGINX 구성

이제 웹 서버 구성이 public이 아닌 current/public을 가리키는지 확인합시다.

기본 NGINX 서버 블록 구성 파일을 열려면 다음을 입력합니다:

sudo nano /etc/nginx/sites-available/default

구성은 다음과 같아야 합니다.

server {
    root /var/www/app/current/public;
    server_name example.com;
    # 나머지 구성
}

/var/www/app/current/public에서 앱의 이름을 애플리케이션의 폴더 이름으로 교체할 수 있습니다.

Envoy 설정하기

우리는 우리의 Laravel 앱을 프로덕션 환경에 배포할 준비가 되었습니다.

다음 단계는 Envoy를 사용하여 배포를 수행하는 것입니다.

Envoy를 사용하기 위해, 먼저 Laravel에서 제공하는 지침에 따라 로컬 머신에 설치해야 합니다.

Envoy 작동 방식

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)이 포함된 배열이 있습니다.

그 다음, @task 지시문 내에서 작업이 실행될 때 서버에서 실행되어야 할 bash 명령을 정의합니다.

로컬 머신에서 run 명령을 사용하여 Envoy 작업을 실행합니다.

envoy run list

이 명령은 우리가 이전에 정의한 list 작업을 실행하며, 서버에 연결하고 디렉토리 내용을 나열합니다.

Envoy는 Laravel의 의존성이 아니므로, 모든 PHP 애플리케이션에 대해 사용할 수 있습니다.

제로 다운타임 배포

우리가 프로덕션 서버에 배포할 때마다, Envoy는 GitLab 리포지토리에서 우리의 최신 앱 릴리즈를 다운로드하고 이전 릴리즈로 교체합니다.

Envoy는 다운타임 없이 이를 수행하므로, 누군가 사이트를 검토하는 동안 배포 중에 걱정할 필요가 없습니다.

우리의 배포 계획은 GitLab 리포지토리에서 최신 릴리즈를 클론하고, Composer 의존성을 설치한 후, 마지막으로 새로운 릴리즈를 활성화하는 것입니다.

@setup 지시문

우리의 배포 프로세스의 첫 번째 단계는 @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는 날짜를 포함하고 있으므로, 우리의 앱의 새로운 릴리즈를 배포할 때마다 현재 날짜를 이름으로 가지는 새로운 폴더를 생성합니다.
  • $new_release_dir는 새로운 릴리즈의 전체 경로로, 작업을 더 깔끔하게 만들기 위해 사용됩니다.

@story 지시문

@story 지시문은 단일 작업으로 실행될 수 있는 작업 목록을 정의할 수 있게 해줍니다.

여기에서는 clone_repository, run_composer, update_symlinks라는 세 가지 작업이 있습니다.

이 변수들은 작업 코드의 가독성을 높이는 데 사용될 수 있습니다:

...

@story('deploy')
    clone_repository
    run_composer
    update_symlinks
@endstory

...

이제 이 세 가지 작업을 하나씩 만들어 봅시다.

저장소 복제

첫 번째 작업은 releases 디렉터리를 생성하는 것입니다(존재하지 않는 경우) 그리고 $new_release_dir 변수에 의해 지정된 새로운 릴리즈 디렉터리로 리포지토리의 main 브랜치를(기본값으로) 복제합니다.
releases 디렉터리는 모든 배포를 보관합니다:

...

@task('clone_repository')
    echo '저장소 복제 중'
    [ -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 옵션은 시스템 시간과 디스크 공간을 절약할 수 있는 훌륭한 해결책입니다.

Composer로 종속성 설치

이 작업은 새로운 릴리즈 디렉터리로 가서 애플리케이션의 종속성을 설치하기 위해 Composer를 실행합니다:

...

@task('run_composer')
    echo "배포 시작 ({{ $release }})"
    cd {{ $new_release_dir }}
    composer install --prefer-dist --no-scripts -q -o
@endtask

...

새로운 릴리즈 활성화

새로운 릴리즈의 요구 사항을 준비한 후 다음으로 해야 할 일은 저장소 디렉터리를 제거하고 애플리케이션의 storage 디렉터리와 .env 파일을 새 릴리즈로 가리키는 두 개의 심볼릭 링크를 만드는 것입니다.
그런 다음, 앱 디렉터리에 current라는 이름으로 새 릴리즈에 대한 또 다른 심볼릭 링크를 만들어야 합니다.
current 심볼릭 링크는 항상 앱의 최신 릴리즈를 가리킵니다:

...

@task('update_symlinks')
    echo "저장소 디렉터리 링크"
    rm -rf {{ $new_release_dir }}/storage
    ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage

    echo '.env 파일 링크'
    ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env

    echo '현재 릴리즈 링크'
    ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask

보시다시피, 우리는 ln 명령에 대해 -nfs를 옵션으로 사용하여 storage, .envcurrent가 더 이상 이전 릴리즈를 가리키지 않고 새 릴리즈를 강제로 가리키도록 했습니다(f는 강제를 의미함), 이는 여러 배포를 진행할 때 해당됩니다.

전체 스크립트

스크립트는 준비되었지만, deployer@192.168.1.1를 귀하의 서버로 변경하고 /var/www/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 '저장소 복제 중'
    [ -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 "배포 시작 ({{ $release }})"
    cd {{ $new_release_dir }}
    composer install --prefer-dist --no-scripts -q -o
@endtask

@task('update_symlinks')
    echo "저장소 디렉터리 링크"
    rm -rf {{ $new_release_dir }}/storage
    ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage

    echo '.env 파일 링크'
    ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env

    echo '현재 릴리즈 링크'
    ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask

배포를 시작하기 전에 우리가 해야 할 또 하나의 작업은 최초로 애플리케이션 storage 폴더를 서버의 /var/www/app 디렉터리에 수동으로 복사하는 것입니다.
이를 위해 별도의 Envoy 작업을 생성할 수 있습니다.
우리는 또한 동일한 경로에 .env 파일을 생성하여 Laravel의 프로덕션 환경 변수를 설정합니다.
이들은 지속적인 데이터로, 모든 새로운 릴리즈와 공유됩니다.

이제 envoy run deploy를 실행하여 앱을 배포할 준비가 되었지만, GitLab이 CI의 environments로 이를 처리할 수 있으므로 그럴 필요는 없습니다. 이는 나중에 이 튜토리얼에서 설명할 것입니다.

이제 Envoy.blade.php를 커밋하고 main 브랜치에 푸시할 시간입니다.
간단한 작업을 위해 기능 브랜치를 사용하지 않고 직접 main에 커밋합니다. 협업은 이 튜토리얼의 범위를 넘어섭니다.
실제 프로젝트에서는 팀이 Issue Trackermerge requests를 사용하여 코드를 브랜치 간에 이동할 수 있습니다:

git add Envoy.blade.php
git commit -m 'Envoy 추가'
git push origin main

GitLab을 활용한 지속적 통합

우리는 GitLab에서 앱을 준비했으며, 이를 수동으로 배포할 수도 있습니다.

하지만, 지속적 배포 방법을 통해 자동으로 진행해 보겠습니다.

모든 커밋을 자동화된 테스트 세트로 확인하여 문제를 조기에 인식해야 하며, 테스트 결과에 만족한다면 대상 환경에 배포할 수 있습니다.

GitLab CI/CD는 우리의 앱을 테스트하고 배포하는 프로세스를 처리하기 위해 Docker 엔진을 사용할 수 있게 해줍니다.

Docker에 익숙하지 않다면, 자동화된 빌드 설정을 참조하세요.

GitLab CI/CD를 사용하여 우리의 앱을 빌드, 테스트 및 배포할 수 있도록 작업 환경을 준비해야 합니다.

이를 위해 Laravel 앱이 실행되는 데 필요한 최소 요구 사항을 갖춘 Docker 이미지를 사용하겠습니다.

다른 방법도 있습니다, 하지만 이들은 빌드를 느리게 실행하게 만들 수 있으며, 이는 더 빠른 옵션을 사용할 때 원하지 않는 결과입니다.

컨테이너 이미지 생성하기

다음 내용을 포함한 Dockerfile을 앱의 루트 디렉토리에 생성합시다:

# 이후 지시를 위한 기본 이미지를 설정
FROM php:7.4

# 패키지 업데이트
RUN apt-get update

# PHP 및 composer 의존성 설치
RUN apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev

# 로컬 저장소의 패키지 파일 제거
RUN apt-get clean

# 필요한 확장 설치
# 테스트 및 배포 과정 중 필요한 다른 확장도 설치할 수 있습니다
RUN docker-php-ext-install mcrypt pdo_mysql zip

# Composer 설치
RUN curl --silent --show-error "https://getcomposer.org/installer" | php -- --install-dir=/usr/local/bin --filename=composer

# Laravel Envoy 설치
RUN composer global require "laravel/envoy=~1.0"

우리는 PHP가 미리 설치된 Debian buster의 최소 설치로 구성된 공식 PHP 7.4 Docker 이미지를 추가했으며, 이는 우리의 용도에 완벽하게 작동합니다.

필요한 PHP 확장을 설치하기 위해 docker-php-ext-install(공식 PHP Docker 이미지에서 제공됨)을 사용했습니다.

GitLab 컨테이너 레지스트리 설정

이제 Dockerfile을 준비했으므로, 이를 빌드하고 GitLab 컨테이너 레지스트리에 푸시합시다.

레지스트리는 나중에 사용하기 위해 이미지를 저장하고 태그를 지정하는 위치입니다. 개발자는 비공식 및 회사 이미지를 위해 자신의 레지스트리를 유지 관리하거나, 테스트를 위해서만 사용되는 임시 이미지를 위해 레지스트리를 원할 수 있습니다. GitLab 컨테이너 레지스트리를 사용하면 또 다른 서비스를 설정하고 관리할 필요가 없으며, 공개 레지스트리를 사용할 필요가 없습니다.

GitLab 프로젝트 리포지토리에서 Registry 탭으로 이동합니다.

빈 컨테이너 레지스트리 페이지 이미지

이 탭을 보려면 프로젝트에 대해 컨테이너 레지스트리를 활성화해야 할 수도 있습니다. 이는 프로젝트의 Settings > General > Visibility, project features, permissions 아래에서 찾을 수 있습니다.

우리의 머신에서 컨테이너 레지스트리를 사용하기 시작하려면, 먼저 GitLab 사용자 이름과 비밀번호를 사용하여 GitLab 레지스트리에 로그인해야 합니다.

Docker가 우리 머신에 설치되어 있는지 확인한 후, 다음 명령어를 실행합니다:

docker login registry.gitlab.com

그런 다음 GitLab에 우리의 이미지를 빌드하고 푸시할 수 있습니다:

docker build -t registry.gitlab.com/<USERNAME>/laravel-sample .

docker push registry.gitlab.com/<USERNAME>/laravel-sample

축하합니다! 처음으로 Docker 이미지를 GitLab 레지스트리에 푸시했습니다. 페이지를 새로 고침하면 이를 볼 수 있어야 합니다:

이미지가 있는 컨테이너 레지스트리 페이지

또한 GitLab CI/CD를 사용할 수 있습니다 Docker 이미지를 빌드하고 푸시할 때, 머신에서 직접 하지 않고 자동화할 수 있습니다.

우리는 이 이미지를 .gitlab-ci.yml 구성 파일에서 앱의 테스트 및 배포 프로세스를 처리하는 데 사용할 것입니다.

이제 Dockerfile 파일을 커밋합시다.

git add Dockerfile
git commit -m 'Add Dockerfile'
git push origin main

GitLab CI/CD 설정하기

GitLab CI/CD를 사용하여 앱을 빌드하고 테스트하려면, 우리 저장소의 루트에 .gitlab-ci.yml이라는 파일이 필요합니다. 이는 Circle CI 및 Travis CI와 비슷하지만 GitLab에 내장되어 있습니다.

우리의 .gitlab-ci.yml 파일은 다음과 같이 보일 것입니다:

default:
  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
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

많은 내용을 한 번에 이해하기 어려운가요? 하나씩 살펴보겠습니다.

이미지 및 서비스

Runners.gitlab-ci.yml에 정의된 스크립트를 실행합니다.

image 키워드는 러너가 사용할 이미지를 알려줍니다.

services 키워드는 메인 이미지에 연결된 추가 이미지를 정의합니다.

여기서는 앞서 만든 컨테이너 이미지를 메인 이미지로 사용하고, MySQL 5.7을 서비스로 사용합니다.

default:
  image: registry.gitlab.com/<USERNAME>/laravel-sample:latest
  services:
    - mysql:5.7

...

다양한 PHP 버전 및 데이터베이스 관리 시스템으로 앱을 테스트하려면, 각 테스트 작업에 대해 다른 imageservices 키워드를 정의할 수 있습니다.

CI/CD 변수

GitLab CI/CD는 작업에서 CI/CD 변수를 사용할 수 있게 해줍니다.

우리는 MySQL을 데이터베이스 관리 시스템으로 정의했으며, 기본적으로 superuser인 root가 생성됩니다.

따라서 MySQL 인스턴스의 구성을 조정하려면 MYSQL_DATABASE 변수를 데이터베이스 이름으로, MYSQL_ROOT_PASSWORD 변수를 root의 비밀번호로 정의해야 합니다.

MySQL 변수에 대한 자세한 내용은 공식 MySQL Docker 이미지에서 확인하세요.

또한 DB_HOST 변수를 mysql로, DB_USERNAME 변수를 root로 설정합니다. 이는 Laravel 특유의 변수입니다.

우리는 MySQL Docker 이미지를 서비스로 사용하므로 DB_HOST127.0.0.1 대신 mysql로 정의합니다. 이는 메인 Docker 이미지에 연결된 서비스입니다.

variables:
  MYSQL_DATABASE: homestead
  MYSQL_ROOT_PASSWORD: secret
  DB_HOST: mysql
  DB_USERNAME: root

단위 테스트를 첫 번째 작업으로

우리는 unit_test 작업을 실행할 때 실행될 script 키워드의 배열로 필요한 셸 스크립트를 정의했습니다.

이 스크립트들은 Laravel을 준비하기 위한 Artisan 명령어이며, 스크립트의 끝에서 PHPUnit으로 테스트를 실행할 것입니다.

unit_test:
  script:
    # 앱 종속성 설치
    - composer install
    # .env 설정
    - cp .env.example .env
    # 환경 키 생성
    - php artisan key:generate
    # 마이그레이션 실행
    - php artisan migrate
    # 테스트 실행
    - vendor/bin/phpunit

프로덕션에 배포

deploy_production 작업은 앱을 프로덕션 서버에 배포합니다.
Envoy로 앱을 배포하기 위해서는 $SSH_PRIVATE_KEY 변수를 SSH 개인 키로 설정해야 했습니다.
SSH 키가 성공적으로 추가되었다면, Envoy를 실행할 수 있습니다.

앞서 언급했듯이, GitLab은 지속적 배포 방법도 지원합니다.
환경 키워드는 이 작업이 production 환경에 배포된다는 것을 GitLab에 알립니다.
url 키워드는 GitLab 환경 페이지에 있는 애플리케이션의 링크를 생성하는 데 사용됩니다.
rules:if 키워드는 GitLab CI/CD에 파이프라인이 main 브랜치를 빌드할 때만 작업이 실행되어야 한다고 알립니다.
마지막으로, when: manual은 작업이 자동으로 실행되는 것을 수동 작업으로 변경합니다.

deploy_production:
  script:
    # 빌드 환경에 개인 SSH 키 추가
    - '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'
    # Envoy 실행
    - ~/.composer/vendor/bin/envoy run deploy
  environment:
    name: production
    url: http://192.168.1.1
  when: manual
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

또한 프로덕션에 배포하기 전에 애플리케이션을 최종 테스트하기 위해 스테이징 환경을 위한 또 다른 작업을 추가할 수 있습니다.

GitLab CI/CD 활성화

우리는 GitLab CI/CD로 앱을 테스트하고 배포하는 데 필요한 모든 것을 준비했습니다.
이를 위해 .gitlab-ci.yml 파일을 main 브랜치에 커밋하고 푸시합니다. 그러면 파이프라인이 트리거되고, 프로젝트의 파이프라인 항목에서 라이브로 확인할 수 있습니다.

pipelines page

여기서 우리의 테스트배포 단계가 보입니다.
테스트 단계에서는 unit_test 빌드가 실행 중입니다.
선택하여 러너의 출력을 확인하세요.

pipeline page

코드가 파이프라인을 성공적으로 통과한 후, 오른쪽에서 실행 ( )을 선택하여 프로덕션 서버에 배포할 수 있습니다.

pipelines page deploy button

배포 파이프라인이 성공적으로 통과한 후, 파이프라인 > 환경으로 이동합니다.

environments page

예상대로 작동하지 않으면 최신의 작동하는 버전으로 롤백할 수 있습니다.

environment page

오른쪽에 지정된 외부 링크 아이콘을 선택하면 GitLab이 프로덕션 웹사이트를 엽니다.
우리의 배포는 성공적으로 완료되었으며, 애플리케이션이 실시간으로 작동하는 것을 볼 수 있습니다.

Laravel welcome page

배포 후 프로덕션 서버의 애플리케이션 디렉토리 구조가 어떻게 되는지 알고 싶다면, current, releases, storage라는 세 가지 디렉토리가 있습니다.
알고 계시다시피, current 디렉토리는 최신 릴리스를 가리키는 심볼릭 링크입니다.
.env 파일은 우리의 Laravel 환경 변수를 포함하고 있습니다.

production server app directory

current 디렉토리로 이동하면 애플리케이션의 콘텐츠를 볼 수 있어야 합니다.
보시다시피, .env/var/www/app/.env 파일을 가리키고 있으며, storage/var/www/app/storage/ 디렉토리를 가리키고 있습니다.

production server current directory

결론

우리는 GitLab CI/CD를 구성하여 자동화된 테스트를 수행하고 지속적인 배포(Continuous Delivery) 방법을 사용하여 코드베이스에서 직접 Laravel 애플리케이션을 프로덕션에 배포했습니다.

Envoy는 또한 우리의 맞춤형 bash 스크립트를 작성하거나 리눅스 마법을 수행하지 않고 애플리케이션을 배포하는 데 큰 도움이 되었습니다.