Horizon
업데이트됨번역일: 2026년 9월 17일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 9월 16일
- 번역 갱신
- 2026년 9월 17일
Horizon
소개
Laravel Horizon은 Laravel 기반 Redis 큐를 위한 아름다운 대시보드이자 코드 기반 설정 도구입니다. Horizon을 사용하면 Job 처리량, 실행 시간, Job 실패 등 큐 시스템의 핵심 지표를 한눈에 파악하고 손쉽게 모니터링할 수 있습니다.
NOTE
Horizon을 처음 살펴본다면, 먼저 Laravel의 큐 서비스 기본 문서를 읽어보는 것을 권장합니다. Horizon은 이미 존재하는 큐 시스템의 기능을 확장한 도구이므로, 큐의 기본 개념(연결, 큐, Job, 워커 등)을 이해하지 못한 상태에서 Horizon만 다루면 혼란스러울 수 있습니다.
설치
WARNING
Laravel Horizon은 큐 드라이버로 Redis를 사용해야 합니다. 따라서 애플리케이션의 config/queue.php 설정 파일에서 큐 연결이 redis로 지정되어 있는지 먼저 확인하세요.
Composer 패키지 관리자를 사용해 Horizon을 프로젝트에 설치할 수 있습니다.
composer require laravel/horizonHorizon을 설치한 후에는 horizon:install Artisan 명령어를 사용해 에셋 파일을 게시합니다.
php artisan horizon:install설정
Horizon 에셋을 게시하고 나면, 애플리케이션의 config/horizon.php 파일이 주요 설정 파일이 됩니다. 이 설정 파일을 통해 애플리케이션의 큐 워커 옵션을 세밀하게 조정할 수 있으며, 각 옵션에는 목적을 설명하는 주석이 달려 있으니 파일을 꼼꼼히 읽어보시기 바랍니다.
WARNING
Horizon은 내부적으로 horizon이라는 이름의 Redis 연결을 사용합니다. 이 Redis 연결 이름은 예약되어 있으므로, database.php 설정 파일이나 horizon.php 설정 파일의 use 옵션에 다른 Redis 연결을 horizon이라는 이름으로 할당해서는 안 됩니다.
환경(Environment) 설정
설치 후 익혀야 할 가장 중요한 Horizon 설정 옵션은 environments입니다. 이 옵션은 애플리케이션이 실행되는 환경들의 배열을 정의하며, 각 환경별로 워커 프로세스의 옵션을 지정합니다. 기본적으로 production과 local 환경이 포함되어 있지만, 필요에 따라 얼마든지 환경을 추가할 수 있습니다.
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],Horizon을 실행하면 애플리케이션이 실행 중인 환경에 해당하는 워커 프로세스 설정을 사용합니다. 보통 이 환경 값은 APP_ENV 환경 변수의 값을 따릅니다. 예를 들어 기본 local Horizon 환경은 3개의 워커 프로세스를 실행하며, 각 큐에 할당된 워커 수를 자동으로 조절합니다. 기본 production 환경은 최대 10개의 워커 프로세스를 실행하며, 마찬가지로 자동으로 워커 수를 조절합니다.
WARNING
horizon 설정 파일의 environments 항목에 애플리케이션을 실제로 배포할 예정인 모든 환경이 포함되어 있는지 반드시 확인해야 합니다.
슈퍼바이저(Supervisor)
Horizon의 기본 설정 파일에서 볼 수 있듯이, 각 환경은 하나 이상의 "슈퍼바이저"를 포함할 수 있습니다. 기본적으로 설정 파일에는 supervisor-1이라는 슈퍼바이저가 정의되어 있지만, 원하는 대로 이름을 바꾸고 개수를 늘릴 수도 있습니다. 각 슈퍼바이저는 본질적으로 워커 프로세스 그룹을 "감독"하며, 큐 간 워커 프로세스 배분을 담당하는 역할을 합니다.
특정 환경 안에서 새로운 워커 프로세스 그룹을 정의하고 싶을 때 슈퍼바이저를 추가하면 됩니다. 예를 들어 해당 환경에서 사용되는 특정 큐에 대해 서로 다른 균형(balancing) 전략이나 워커 수를 지정하고 싶다면, 슈퍼바이저를 새로 만들어 설정할 수 있습니다.
기본값
Horizon의 기본 설정 파일에는 defaults라는 항목이 있습니다. 이 항목은 애플리케이션의 각 슈퍼바이저에 대한 기본값을 지정하는 데 사용됩니다. 각 환경별 슈퍼바이저 설정에 이 기본값이 병합되며, 슈퍼바이저별로 중복되는 설정을 반복하지 않아도 되므로 편리합니다.
대시보드 접근 권한(Authorization) 설정
Horizon은 /horizon 경로를 통해 대시보드를 제공합니다. 기본적으로는 local 환경에서만 이 대시보드에 접근할 수 있으므로, 프로덕션 환경에서 사용하려면 app/Providers/HorizonServiceProvider.php 파일 안의 권한(authorization) 게이트를 커스터마이징해서 접근을 제어해야 합니다. 이 게이트는 로컬이 아닌 환경에서 Horizon 접근 권한을 통제하는 역할을 합니다.
/**
* Horizon 게이트를 등록합니다.
*
* 이 게이트는 어떤 사용자가 프로덕션 환경에서 Horizon 대시보드에 접근할 수 있는지를 결정합니다.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}다른 인증 방식 사용하기
Laravel은 게이트 클로저 안에 request 인스턴스를 자동으로 주입해줍니다. 만약 애플리케이션이 IP 제한 등 다른 방식으로 Horizon 인증을 처리하고 있어서 사용자가 실제로 "로그인"할 필요가 없는 경우라면, 게이트 클로저의 타입 힌트를 Illuminate\Http\Request 타입으로 변경하면 됩니다.
use Illuminate\Http\Request;
/**
* Horizon 게이트를 등록합니다.
*
* 이 게이트는 어떤 사용자가 프로덕션 환경에서 Horizon 대시보드에 접근할 수 있는지를 결정합니다.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (Request $request) {
return $request->ip() === '192.168.1.1';
});
}최대 Job 시도 횟수
설정 파일 안에서 별도로 지정하지 않으면, Job이 실패로 처리되기 전 몇 번까지 재시도할지 궁금할 수 있습니다. 필요하다면 horizon:publish Artisan 명령어를 사용해 Horizon의 기본 Job 클래스 사본을 애플리케이션에 게시하고 원하는 대로 수정할 수 있습니다.
php artisan horizon:publishJob 타임아웃
기본적으로 Horizon 설정 파일의 retry_after 옵션 값은 60초로 설정되어 있습니다. 하지만 처리 시간이 더 긴 특정 Job이 있다면 해당 Job 클래스에서 $timeout 속성을 설정해 개별적으로 타임아웃 시간을 조정할 수 있습니다.
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Job이 타임아웃되기 전까지 실행될 수 있는 초 단위 시간
*
* @var int
*/
public $timeout = 120;
}Job 백오프(Backoff)
애플리케이션에서 예외가 발생한 Job을 재시도하기 전에 얼마나 대기해야 하는지 설정하고 싶다면, Job 클래스에 backoff 속성을 정의하면 됩니다.
/**
* Job을 재시도하기 전 대기할 시간(초)
*
* @var int
*/
public $backoff = 3;Job을 재시도하기 전 대기 시간을 더 복잡한 로직으로 결정하고 싶다면, Job 클래스에 backoff 메서드를 정의할 수 있습니다.
/**
* Job을 재시도하기 전 대기할 시간(초)을 계산합니다.
*/
public function backoff(): int
{
return 3;
}backoff 메서드에서 배열을 반환하면 "지수 백오프(exponential backoff)"도 손쉽게 구성할 수 있습니다. 아래 예시에서는 첫 번째 재시도는 1초, 두 번째 재시도는 5초, 세 번째 재시도는 10초, 이후 남은 모든 재시도는 각각 10초씩 대기하게 됩니다.
/**
* Job을 재시도하기 전 대기할 시간(초)을 계산합니다.
*
* @return array<int, int>
*/
public function backoff(): array
{
return [1, 5, 10];
}그 밖의 워커 옵션
Horizon 설정 파일에 정의된 여러 워커 옵션은 그 외에도 다양하게 존재합니다.
backoff: 예외가 발생한 Job을 재시도하기 전 대기할 초 단위 시간. Job 자체에 백오프 값이 정의되어 있다면 그 값이 우선합니다.maxTries: Job을 시도할 수 있는 최대 횟수. Job 자체에 최대 시도 횟수가 정의되어 있다면 그 값이 우선합니다.memory: 워커 프로세스가 사용할 수 있는 최대 메모리(MB).timeout: Job이 강제로 종료되기까지 허용되는 최대 초 단위 시간. 일반적으로 예상되는 최대 처리 시간보다 몇 초 짧게 설정하는 것이 좋으며, 이는retry_after설정 값과 일치시켜 좀비 Job이 발생하지 않도록 하기 위함입니다.nice: 워커 프로세스의 CPU 스케줄링 우선순위(nice value)를 지정합니다. 이 값은 운영체제의nice값이며, Laravel의 큐 우선순위와는 다른 개념입니다.
조용히 처리할(Silenced) Job
가끔 애플리케이션이나 서드파티 패키지에서 디스패치되는 특정 Job을 Horizon의 "완료된 Job" 목록에서 제외하고 싶을 수 있습니다. 이런 Job이 대시보드 완료 목록 공간을 차지하지 않도록 하려면, silenced 설정 항목에 해당 Job 클래스를 추가하면 됩니다.
'silenced' => [
App\Jobs\ProcessPodcast::class,
],또는 조용히 처리하고자 하는 Job 클래스가 Laravel\Horizon\Contracts\Silenced 인터페이스를 구현하도록 만드는 방법도 있습니다. Silenced 인터페이스를 구현한 Job은 silenced 설정 배열에 포함되어 있지 않더라도 자동으로 조용히 처리됩니다.
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
// ...
}균형(Balancing) 전략
큐 시스템 전통적인 설정 방식과 달리, Horizon은 세 가지 워커 균형 전략, 즉 simple, auto, false 중에서 선택할 수 있게 해줍니다. simple 전략은 들어오는 Job을 워커 프로세스 사이에 균등하게 나누는 방식입니다.
'balance' => 'simple',
auto 전략(설정 파일 기본값)은 각 큐의 현재 작업량을 기준으로 큐별 워커 프로세스 수를 조정합니다. 예를 들어 notifications 큐에 처리 대기 중인 Job이 1,000개 있는 반면 render 큐는 비어 있다면, Horizon은 notifications 큐에 더 많은 워커를 배정하여 해당 큐가 빨리 비워지도록 처리합니다.
auto 전략을 사용할 때는 minProcesses와 maxProcesses 설정 옵션을 통해 Horizon이 조정할 수 있는 워커 프로세스의 최소·최대 개수를 지정할 수 있습니다.
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'tries' => 3,
],
],
],autoScalingStrategy 설정 값은 Horizon이 큐를 비우는 데 걸리는 총 예상 시간(time 전략)을 기준으로 워커를 할당할지, 아니면 큐에 쌓인 전체 Job 개수(size 전략)를 기준으로 할당할지를 결정합니다.
balanceMaxShift와 balanceCooldown 설정 값은 Horizon이 워커 수요에 얼마나 빠르게 대응해서 워커 규모를 조정할지를 결정합니다. 위 예시에서는 최대 1개의 새로운 프로세스가 3초마다 생성되거나 종료될 수 있습니다. 애플리케이션 요구 사항에 맞게 이 값들을 자유롭게 조정하면 됩니다.
balance 옵션을 false로 설정하면 Laravel의 기본 동작 방식이 적용되며, 이 경우 설정 파일에 정의된 순서대로 큐가 처리됩니다.
자동 균형(Auto Balancing)
아래 다이어그램은 auto 전략이 큐의 작업량에 따라 워커를 어떻게 재배치하는지 보여줍니다. notifications 큐에 밀린 작업이 많으면 Horizon이 다른 큐에서 워커를 빼서 배정합니다.
단순 균형(Simple Balancing)
균형 미사용(No Balancing)
Horizon 업그레이드하기
Horizon의 새로운 메이저 버전으로 업그레이드할 때는 업그레이드 가이드를 반드시 꼼꼼히 확인해야 합니다.
Horizon 실행하기
애플리케이션의 config/horizon.php 설정 파일에서 슈퍼바이저와 워커를 설정했다면, horizon Artisan 명령어로 Horizon을 시작할 수 있습니다. 이 명령어 하나만 실행하면 현재 환경에 설정된 모든 워커 프로세스가 함께 시작됩니다.
php artisan horizonhorizon:pause와 horizon:continue Artisan 명령어를 사용해 Horizon 프로세스를 일시 정지하거나, 다시 재개해서 Job 처리를 계속하도록 지시할 수 있습니다.
php artisan horizon:pausephp artisan horizon:continue특정 Horizon 슈퍼바이저만 일시 정지하거나 재개하고 싶다면, horizon:pause-supervisor와 horizon:continue-supervisor 명령어를 사용하면 됩니다.
php artisan horizon:pause-supervisor supervisor-1php artisan horizon:continue-supervisor supervisor-1horizon:status Artisan 명령어를 사용하면 Horizon 프로세스의 현재 상태를 확인할 수 있습니다.
php artisan horizon:status특정 Horizon 슈퍼바이저의 현재 상태는 horizon:supervisor-status 명령어로 확인할 수 있습니다.
php artisan horizon:supervisor-status supervisor-1Horizon 프로세스를 완전히 종료하고 싶다면 horizon:terminate Artisan 명령어를 사용하면 됩니다. 이 명령어를 실행하면 현재 처리 중인 Job들은 마무리된 후 Horizon이 종료됩니다.
php artisan horizon:terminateHorizon 배포하기
실제 애플리케이션 서버에 Horizon을 배포할 준비가 되었다면, php artisan horizon 명령어를 모니터링하고 예기치 않게 종료되었을 때 자동으로 재시작해주는 프로세스 모니터를 설정해야 합니다. 애플리케이션을 새로 배포할 때는 Horizon 프로세스를 종료한 뒤 프로세스 모니터가 자동으로 재시작하도록 지시해야 합니다.
php artisan horizon:terminateSupervisor 설치하기
Supervisor는 리눅스 운영체제용 프로세스 모니터로, horizon 프로세스가 중단되었을 때 자동으로 다시 시작해줍니다. 우분투(Ubuntu) 계열에서는 다음 명령어로 Supervisor를 설치할 수 있습니다.
sudo apt-get install supervisorNOTE
Supervisor 설정이 부담스럽게 느껴진다면, Laravel Cloud에서 완전 관리형 인프라로 Laravel 애플리케이션을 손쉽게 운영해 보는 것도 고려해볼 만합니다.
Supervisor 설정하기
Supervisor 설정 파일은 보통 서버의 /etc/supervisor/conf.d 디렉터리에 저장합니다. 이 디렉터리 안에 원하는 만큼 설정 파일을 만들어 Supervisor에게 어떻게 프로세스를 모니터링할지 지시할 수 있습니다. 예를 들어 horizon.conf 파일을 만들어 horizon 프로세스를 시작하고 모니터링하도록 설정해봅시다.
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600WARNING
stopwaitsecs 값이 가장 오래 걸리는 Job의 소요 시간보다 크게 설정되어 있는지 반드시 확인하세요. 그렇지 않으면 Supervisor가 Job이 완료되기 전에 강제로 프로세스를 종료해버릴 수 있습니다.
설정 파일을 만든 후에는 다음 명령어로 Supervisor 설정을 갱신하고 모니터링 중인 프로세스를 시작할 수 있습니다.
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start horizonNOTE
Supervisor 실행에 관한 더 자세한 내용은 Supervisor 공식 문서를 참고하세요.
태그(Tags)
Horizon은 메일러블(mailable), 브로드캐스트 이벤트, 알림(notification), 큐에 등록된 이벤트 리스너 등 다양한 Job에 "태그"를 지정할 수 있습니다. 실제로 Horizon은 Job에 첨부된 Eloquent 모델을 기준으로 대부분의 Job에 지능적이고 자동으로 태그를 붙여줍니다. 예를 들어 다음 Job을 살펴보겠습니다.
<?php
namespace App\Jobs;
use App\Models\Podcast;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* 새 Job 인스턴스를 생성합니다.
*/
public function __construct(
public Podcast $podcast,
) {}
}만약 이 Job이 id 속성이 1인 App\Models\Podcast 인스턴스와 함께 큐에 등록된다면, 자동으로 App\Models\Podcast:1이라는 태그가 붙습니다. Horizon은 Job의 속성들을 검사해서 Eloquent 모델이 있는지 확인하고, 모델이 발견되면 모델의 클래스명과 기본 키(primary key)를 조합해 태그를 지능적으로 생성해주기 때문입니다.
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
$podcast = Podcast::find(1);
ProcessPodcast::dispatch($podcast);Job에 수동으로 태그 지정하기
큐에 등록 가능한 객체 중 하나에 수동으로 태그를 지정하고 싶다면, 클래스에 tags 메서드를 정의하면 됩니다.
class ProcessPodcast implements ShouldQueue
{
/**
* Job에 할당할 태그를 반환합니다.
*
* @return array<int, string>
*/
public function tags(): array
{
return ['podcast:'.$this->podcast->id];
}
}큐에 등록된 이벤트 리스너에 수동으로 태그 지정하기
큐에 등록된 이벤트 리스너의 태그를 가져올 때, Horizon은 이벤트 인스턴스를 tags 메서드에 자동으로 전달해서 이벤트 데이터를 태그에 포함시킬 수 있게 해줍니다.
class SendRenewalNotification implements ShouldQueue
{
/**
* Job에 할당할 태그를 반환합니다.
*
* @return array<int, string>
*/
public function tags(object $event): array
{
return ['podcast:'.$event->podcast->id];
}
}알림(Notifications)
WARNING
Slack 알림을 설정할 때는 Laravel notification channels에 설명된 대로 반드시 laravel/slack-notification-channel 패키지를 설치해야 합니다. 또한 Slack의 알림 채널 요구 사항이 충족되었는지도 확인이 필요합니다.
큐 중 하나의 대기 시간이 길어질 때 알림을 받고 싶다면, Horizon::routeMailNotificationsTo, Horizon::routeSlackNotificationsTo, Horizon::routeSmsNotificationsTo 메서드를 사용할 수 있습니다. 이 메서드들은 애플리케이션의 App\Providers\AppServiceProvider 서비스 프로바이더의 boot 메서드에서 호출하면 됩니다.
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('example@example.com');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');알림 발송 대기 시간 임계값 설정하기
애플리케이션의 config/horizon.php 설정 파일에서 "긴 대기 시간"으로 간주할 초 단위 시간을 설정할 수 있습니다. 이 파일의 waits 설정 항목을 통해 연결/큐 조합별로 긴 대기 시간의 기준값을 각각 지정할 수 있습니다. 정의하지 않은 연결/큐 조합의 임계값은 기본적으로 60초입니다.
'waits' => [
'redis:critical' => 30,
'redis:default' => 60,
'redis:batch' => 120,
],메트릭(Metrics)
Horizon에는 Job과 큐의 대기 시간 및 처리량 정보를 확인할 수 있는 메트릭 대시보드가 포함되어 있습니다. 이 대시보드를 채우려면 애플리케이션의 routes/console.php 파일에서 Horizon의 snapshot Artisan 명령어가 5분마다 실행되도록 스케줄을 등록해야 합니다.
use Laravel\Support\Facades\Schedule;
Schedule::command('horizon:snapshot')->everyFiveMinutes();실패한 Job 삭제하기
실패한 Job을 삭제하고 싶다면 horizon:forget 명령어를 사용하면 됩니다. horizon:forget 명령어는 실패한 Job의 ID나 UUID를 인수로 받습니다.
php artisan horizon:forget 5큐에서 모든 Job 삭제하기
애플리케이션의 기본 큐에 있는 모든 Job을 삭제하고 싶다면, horizon:clear Artisan 명령어를 사용하면 됩니다.
php artisan horizon:clear특정 큐에서만 Job을 삭제하고 싶다면 queue 옵션을 함께 지정할 수 있습니다.
php artisan horizon:clear --queue=emailsHorizon
소개
NOTE
Laravel Horizon을 본격적으로 다루기 전에, Laravel의 기본 큐 서비스에 먼저 익숙해지시는 것을 권장합니다. Horizon은 Laravel 큐에 여러 부가 기능을 더한 것이기 때문에, 기본적인 큐 개념을 모르는 상태에서는 오히려 혼란스러울 수 있습니다.
Laravel Horizon은 Laravel 기반 Redis 큐를 위한 아름다운 대시보드와 코드 기반 설정 기능을 제공합니다. Horizon을 사용하면 Job 처리량(throughput), 실행 시간, 실패한 Job 수 등 큐 시스템의 핵심 지표를 손쉽게 모니터링할 수 있습니다.
Horizon을 도입하면 큐 워커에 대한 모든 설정을 하나의 간단한 설정 파일에서 관리하게 됩니다. 애플리케이션의 워커 설정을 버전 관리되는 파일로 정의해두면, 배포할 때마다 큐 워커의 규모를 조정하거나 설정을 변경하는 작업이 훨씬 수월해집니다.
NOTE
참고로 국내 인프라 환경에서는 Redis를 관리형 서비스(예: 네이버 클라우드 Cloud DB for Redis, AWS ElastiCache 등)로 운영하는 경우가 많은데, Horizon은 이러한 관리형 Redis에도 문제없이 연결해서 사용할 수 있습니다. 다만 큐 전용 Redis 인스턴스를 별도로 분리해서 운영하는 것을 권장합니다.
설치
WARNING
Laravel Horizon을 사용하려면 큐를 구동하는 데 Redis를 사용해야 합니다. 따라서 애플리케이션의 config/queue.php 설정 파일에서 큐 연결이 redis로 설정되어 있는지 확인하세요. 참고로 Horizon은 현재 Redis Cluster와는 호환되지 않습니다.
Composer 패키지 매니저를 사용해 프로젝트에 Horizon을 설치할 수 있습니다:
composer require laravel/horizonHorizon을 설치한 후에는 horizon:install Artisan 명령어를 실행해 관련 애셋을 배포(publish)합니다:
php artisan horizon:install설정
애셋을 배포하고 나면 Horizon의 주요 설정 파일이 config/horizon.php 경로에 생성됩니다. 이 파일에서 애플리케이션의 큐 워커 옵션을 설정할 수 있습니다. 각 설정 항목에는 그 목적을 설명하는 주석이 달려 있으니, 반드시 파일 전체를 꼼꼼히 읽어보시기 바랍니다.
WARNING
Horizon은 내부적으로 horizon이라는 이름의 Redis 연결을 사용합니다. 이 이름은 Horizon 전용으로 예약되어 있으므로, database.php 설정 파일에서 다른 Redis 연결에 이 이름을 붙이거나, horizon.php 설정 파일의 use 옵션 값으로 사용해서는 안 됩니다.
Content Security Policy(CSP) Nonce
Content Security Policy를 적용하는 과정에서 Horizon 뷰에 포함된 script, style 태그에 nonce 속성을 넣고 싶다면, Horizon::cspNonce 메서드로 사용할 nonce 값을 지정할 수 있습니다. 요청마다 새로운 nonce가 할당되도록, 이 메서드는 보통 미들웨어 안에서 호출합니다:
use Closure;
use Illuminate\Http\Request;
use Laravel\Horizon\Horizon;
use Symfony\Component\HttpFoundation\Response;
public function handle(Request $request, Closure $next): Response
{
Horizon::cspNonce('csp-nonce');
return $next($request);
}이 미들웨어는 애플리케이션의 config/horizon.php 설정 파일의 middleware 옵션에 추가하면 됩니다:
'middleware' => [
'web',
App\Http\Middleware\AddHorizonCspNonce::class,
],환경(Environments)
설치 후 가장 먼저 익혀야 할 핵심 설정 항목은 environments입니다. 이 옵션은 애플리케이션이 실행되는 환경들의 배열이며, 각 환경마다 워커 프로세스 옵션을 정의합니다. 기본적으로 production과 local 두 환경이 포함되어 있지만, 필요에 따라 얼마든지 환경을 추가할 수 있습니다:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],일치하는 환경을 찾지 못했을 때 사용될 와일드카드 환경(*)도 정의할 수 있습니다:
'environments' => [
// ...
'*' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],Horizon을 시작하면 애플리케이션이 실행 중인 환경에 맞는 워커 프로세스 설정을 사용합니다. 일반적으로 이 환경은 APP_ENV 환경 변수 값으로 결정됩니다. 예를 들어 기본 local 환경은 워커 프로세스 3개를 시작하며, 각 큐에 할당되는 워커 프로세스 수를 자동으로 균형 있게 조정하도록 설정되어 있습니다. 기본 production 환경은 최대 10개의 워커 프로세스를 시작하며, 마찬가지로 큐별 워커 프로세스 수를 자동으로 균형 조정합니다.
WARNING
horizon 설정 파일의 environments 항목에는 Horizon을 실행할 계획이 있는 모든 환경에 대한 항목이 반드시 포함되어 있어야 합니다.
슈퍼바이저(Supervisors)
Horizon의 기본 설정 파일을 보면 각 환경이 하나 이상의 "슈퍼바이저"를 포함할 수 있다는 것을 알 수 있습니다. 기본 설정 파일에서는 이 슈퍼바이저를 supervisor-1이라는 이름으로 정의하고 있지만, 원하는 이름으로 자유롭게 바꿔도 됩니다. 각 슈퍼바이저는 기본적으로 워커 프로세스 그룹을 "감독"하는 역할을 하며, 큐 간 워커 프로세스 배분(balancing)을 담당합니다.
특정 환경에서 실행할 워커 프로세스 그룹을 새로 정의하고 싶다면, 해당 환경에 슈퍼바이저를 추가로 등록하면 됩니다. 애플리케이션에서 사용하는 특정 큐에 대해 서로 다른 배분 전략이나 워커 프로세스 개수를 지정하고 싶을 때 이 방법을 활용할 수 있습니다.
유지보수 모드(Maintenance Mode)
애플리케이션이 유지보수 모드 상태일 때는, Horizon 설정 파일에서 슈퍼바이저의 force 옵션을 true로 지정하지 않는 한 큐에 등록된 Job이 처리되지 않습니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'force' => true,
],
],
],기본값(Default Values)
Horizon의 기본 설정 파일을 보면 defaults라는 설정 항목이 있습니다. 이 옵션은 애플리케이션의 슈퍼바이저에 적용될 기본값을 지정합니다. 이 기본 설정 값들은 각 환경에 정의된 슈퍼바이저 설정과 병합되므로, 슈퍼바이저를 정의할 때마다 동일한 값을 반복해서 작성하지 않아도 됩니다.
대시보드 접근 권한
Horizon 대시보드는 /horizon 라우트로 접속할 수 있습니다. 기본 설정에서는 local 환경에서만 이 대시보드에 접근할 수 있습니다. 하지만 app/Providers/HorizonServiceProvider.php 파일 안에 인가 게이트(authorization gate) 정의가 있으며, 이 게이트가 local이 아닌 환경에서 Horizon 접근 권한을 제어합니다. 필요에 따라 이 게이트를 자유롭게 수정해서 Horizon 접근을 원하는 대로 제한할 수 있습니다:
/**
* Horizon 게이트를 등록합니다.
*
* 이 게이트는 local이 아닌 환경에서 누가 Horizon에 접근할 수 있는지를 결정합니다.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}다른 인증 전략 사용하기
Laravel은 게이트 클로저에 인증된 사용자를 자동으로 주입한다는 점을 기억하세요. 만약 애플리케이션이 IP 제한과 같은 다른 방식으로 Horizon 보안을 처리하고 있다면, Horizon 사용자가 별도로 "로그인"할 필요가 없을 수도 있습니다. 이 경우 위 클로저의 시그니처를 function (User $user)에서 function (User $user = null)로 변경해서, Laravel이 인증을 강제하지 않도록 해야 합니다.
최대 시도 횟수(Max Job Attempts)
NOTE
아래 옵션들을 조정하기 전에, Laravel의 기본 큐 서비스 동작 방식과 '시도(attempts)' 개념을 먼저 이해하고 있어야 합니다.
슈퍼바이저 설정에서 Job이 소비할 수 있는 최대 시도 횟수를 정의할 수 있습니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'tries' => 10,
],
],
],NOTE
이 옵션은 Artisan 명령어로 큐를 처리할 때 사용하는 --tries 옵션과 동일한 역할을 합니다.
WithoutOverlapping이나 RateLimited와 같은 미들웨어를 사용할 때는 이 미들웨어들이 시도 횟수를 소비하기 때문에 tries 옵션을 함께 조정하는 것이 중요합니다. 이를 위해 슈퍼바이저 레벨에서 tries 설정 값을 조정하거나, Job 클래스에 $tries 속성을 직접 정의하면 됩니다.
tries 옵션을 설정하지 않으면 Horizon은 기본적으로 시도 횟수를 1회로 처리합니다. 다만 Job 클래스에 $tries가 정의되어 있다면 그 값이 Horizon 설정보다 우선 적용됩니다.
tries 또는 $tries 값을 0으로 설정하면 시도 횟수에 제한이 없어지며, 이는 몇 번 시도해야 할지 예측하기 어려운 경우에 유용합니다. 다만 무한정 실패가 반복되는 것을 막으려면 Job 클래스에 $maxExceptions 속성을 설정해 허용되는 예외 발생 횟수를 제한하는 것이 좋습니다.
Job 타임아웃
이와 비슷하게, 슈퍼바이저 레벨에서 timeout 값을 설정하면 워커 프로세스가 Job을 강제로 종료당하기 전까지 실행할 수 있는 최대 시간(초)을 지정할 수 있습니다. Job이 강제 종료되면 큐 설정에 따라 재시도되거나 실패로 처리됩니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'timeout' => 60,
],
],
],WARNING
auto 배분 전략을 사용할 때, Horizon은 스케일 다운 과정에서 진행 중인 워커를 "행(hanging)" 상태로 간주하고 Horizon의 타임아웃 시간이 지나면 강제 종료합니다. 따라서 Horizon의 타임아웃 값은 Job 레벨에서 설정한 타임아웃보다 항상 더 크게 유지해야 하며, 그렇지 않으면 Job이 실행 도중에 강제 종료될 수 있습니다. 또한 timeout 값은 config/queue.php 설정 파일에 정의된 retry_after 값보다 최소 몇 초는 짧게 설정해야 합니다. 그렇지 않으면 동일한 Job이 두 번 처리될 수 있습니다.
Job 백오프(Backoff)
슈퍼바이저 레벨에서 backoff 값을 정의하면, 처리되지 않은 예외가 발생한 Job을 재시도하기 전에 Horizon이 얼마나 대기할지를 지정할 수 있습니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'backoff' => 10,
],
],
],backoff 값에 배열을 사용하면 "지수(exponential)" 백오프도 설정할 수 있습니다. 아래 예시에서는 첫 번째 재시도까지 1초, 두 번째 재시도까지 5초, 세 번째 재시도까지 10초를 대기하며, 이후 남은 시도가 있다면 매번 10초씩 대기합니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'backoff' => [1, 5, 10],
],
],
],그 외 워커 옵션
tries, timeout, backoff 외에도 각 슈퍼바이저는 워커 프로세스의 동작 방식과 자동 재시작 시점을 제어하는 여러 옵션을 지원합니다. 오래 실행되는 프로세스에서는 메모리 누수를 방지하기 위해 워커를 주기적으로 재시작하는 것이 좋은 습관입니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'memory' => 128,
'maxJobs' => 1000,
'maxTime' => 3600,
'sleep' => 3,
'rest' => 0,
'nice' => 0,
],
],
],memory는 워커 프로세스 하나가 재시작되기 전까지 사용할 수 있는 최대 메모리 용량(MB)을 정의합니다. 기본값은128입니다.maxJobs는 워커가 재시작되기 전까지 처리할 수 있는 Job 개수를 정의합니다.0으로 설정하면 처리한 Job 개수를 기준으로는 재시작하지 않습니다. 기본값은0입니다.maxTime은 워커가 재시작되기 전까지 실행될 수 있는 시간(초)을 정의합니다.0으로 설정하면 시간을 기준으로는 재시작하지 않습니다. 기본값은0입니다.sleep은 처리할 Job이 없을 때, 워커가 큐를 다시 확인하기 전까지 대기하는 시간(초)을 정의합니다. 기본값은3입니다.rest는 Job을 처리한 후 다음 Job을 처리하기 전까지 쉬는 시간(초)을 정의합니다. 기본값은0입니다.nice는 워커 프로세스의 "니스(niceness)" 값, 즉 스케줄링 우선순위를 정의합니다. 값이 높을수록 프로세스의 우선순위는 낮아집니다. 기본값은0입니다.
숨김 처리된 Job(Silenced Jobs)
애플리케이션이나 서드파티 패키지에서 실행하는 특정 Job들을 굳이 대시보드에서 확인하고 싶지 않은 경우가 있습니다. 이런 Job들이 "완료된 Job(Completed Jobs)" 목록에서 공간을 차지하지 않도록, 해당 Job을 숨김(silence) 처리할 수 있습니다. 시작하려면 애플리케이션의 horizon 설정 파일에서 silenced 옵션에 해당 Job의 클래스명을 추가하세요:
'silenced' => [
App\Jobs\ProcessPodcast::class,
],개별 Job 클래스를 숨기는 것 외에도, Horizon은 태그(tags) 기반으로 Job을 숨기는 기능도 지원합니다. 동일한 태그를 공유하는 여러 Job을 한 번에 숨기고 싶을 때 유용합니다:
'silenced_tags' => [
'notifications'
],또는, 숨기고 싶은 Job이 Laravel\Horizon\Contracts\Silenced 인터페이스를 구현하도록 만들 수도 있습니다. Job이 이 인터페이스를 구현하면, silenced 설정 배열에 포함되어 있지 않더라도 자동으로 숨김 처리됩니다:
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
use Queueable;
// ...
}워크로드 분배 전략 (Balancing Strategies)
각 슈퍼바이저는 하나 이상의 큐를 처리할 수 있습니다. 그런데 Laravel의 기본 큐 시스템과 달리, Horizon에서는 워커를 분배하는 방식을 auto, simple, false 세 가지 전략 중에서 선택할 수 있습니다.
자동 분배 (Auto Balancing)
기본 전략인 auto는 각 큐의 현재 작업 부하에 따라 워커 프로세스 수를 자동으로 조정합니다. 예를 들어 notifications 큐에 1,000개의 대기 중인 Job이 쌓여 있고 default 큐는 비어 있다면, Horizon은 notifications 큐가 비워질 때까지 해당 큐에 더 많은 워커를 할당합니다.
auto 전략을 사용할 때는 minProcesses와 maxProcesses 설정 옵션도 함께 조정할 수 있습니다.
minProcesses는 큐마다 유지할 최소 워커 프로세스 수를 정의합니다. 이 값은 1 이상이어야 합니다.maxProcesses는 슈퍼바이저가 관리하는 모든 큐를 통틀어 확장 가능한 최대 워커 프로세스 총합을 정의합니다. 일반적으로 이 값은 "큐 개수 ×minProcesses"보다 커야 합니다. 슈퍼바이저가 아예 프로세스를 생성하지 못하도록 하려면 이 값을 0으로 설정하면 됩니다.
예를 들어 아래와 같이 각 큐마다 최소 1개의 프로세스를 유지하면서, 전체적으로는 최대 10개의 워커 프로세스까지 확장하도록 설정할 수 있습니다.
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default', 'notifications'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'minProcesses' => 1,
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
],autoScalingStrategy 설정 옵션은 Horizon이 워커 프로세스를 각 큐에 어떤 기준으로 더 배정할지를 결정합니다. 선택 가능한 전략은 다음 세 가지입니다.
time전략은 큐를 모두 처리하는 데 걸릴 것으로 예상되는 총 소요 시간을 기준으로 워커를 배정합니다.size전략은 큐에 쌓인 Job의 총 개수를 기준으로 워커를 배정합니다.log전략은 큐에 쌓인 Job 개수의 로그(logarithm) 값을 기준으로 워커를 배정합니다. 이렇게 하면 유독 큰 큐 하나가 워커를 지나치게 많이 독점하는 것을 방지할 수 있습니다.
balanceMaxShift와 balanceCooldown 설정값은 Horizon이 워커 수요에 맞춰 얼마나 빠르게 스케일링할지를 결정합니다. 위 예시에서는 3초마다 최대 1개의 프로세스만 새로 생성되거나 종료됩니다. 애플리케이션의 특성에 맞춰 이 값들을 자유롭게 조정하시면 됩니다.
큐 우선순위와 자동 분배
auto 분배 전략을 사용할 때, Horizon은 큐 간에 엄격한 우선순위를 강제하지 않습니다. 슈퍼바이저 설정에서 큐를 나열한 순서는 워커 프로세스 배정 방식에 영향을 주지 않습니다. 대신 Horizon은 선택된 autoScalingStrategy를 기반으로 큐의 부하 상황에 따라 동적으로 워커 프로세스를 할당합니다.
예를 들어 아래 설정에서 high 큐가 목록의 맨 앞에 있다고 해서 default 큐보다 우선적으로 처리되는 것은 아닙니다.
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['high', 'default'],
'minProcesses' => 1,
'maxProcesses' => 10,
],
],
],큐 사이에 상대적인 우선순위를 명확히 두고 싶다면, 여러 개의 슈퍼바이저를 정의하고 처리 자원을 명시적으로 배분하는 방법을 사용할 수 있습니다.
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default'],
'minProcesses' => 1,
'maxProcesses' => 10,
],
'supervisor-2' => [
// ...
'queue' => ['images'],
'minProcesses' => 1,
'maxProcesses' => 1,
],
],
],이 예시에서 default 큐는 최대 10개까지 프로세스를 확장할 수 있는 반면, images 큐는 1개의 프로세스로 제한됩니다. 이렇게 하면 각 큐가 서로 영향을 주지 않고 독립적으로 확장될 수 있습니다.
NOTE
리소스를 많이 소모하는 Job을 디스패치할 때는, maxProcesses 값을 제한한 전용 큐에 별도로 배정하는 것이 좋습니다. 그렇지 않으면 이런 Job들이 CPU 자원을 과도하게 점유하여 시스템 전체에 부담을 줄 수 있습니다.
단순 분배 (Simple Balancing)
simple 전략은 지정된 큐들에 워커 프로세스를 균등하게 분배합니다. 이 전략에서는 Horizon이 워커 프로세스 수를 자동으로 조정하지 않고, 고정된 개수의 프로세스를 사용합니다.
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default', 'notifications'],
'balance' => 'simple',
'processes' => 10,
],
],
],위 예시에서 Horizon은 총 10개의 프로세스를 두 큐에 5개씩 균등하게 배정합니다.
큐마다 배정할 워커 프로세스 수를 각각 직접 지정하고 싶다면, 여러 개의 슈퍼바이저를 정의하면 됩니다.
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default'],
'balance' => 'simple',
'processes' => 10,
],
'supervisor-notifications' => [
// ...
'queue' => ['notifications'],
'balance' => 'simple',
'processes' => 2,
],
],
],이 설정에서는 default 큐에 10개, notifications 큐에 2개의 프로세스가 각각 배정됩니다.
분배 없음 (No Balancing)
balance 옵션을 false로 설정하면, Horizon은 Laravel의 기본 큐 시스템과 마찬가지로 큐를 나열된 순서 그대로 엄격하게 처리합니다. 다만 이 경우에도 Job이 쌓이기 시작하면 워커 프로세스 수는 여전히 확장됩니다.
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'queue' => ['default', 'notifications'],
'balance' => false,
'minProcesses' => 1,
'maxProcesses' => 10,
],
],
],위 예시에서는 default 큐의 Job이 항상 notifications 큐의 Job보다 먼저 처리됩니다. 예를 들어 default에 1,000개, notifications에 10개의 Job이 있다면, Horizon은 notifications의 Job을 처리하기 전에 default의 모든 Job을 완전히 처리합니다.
minProcesses와 maxProcesses 옵션을 사용하면 Horizon이 워커 프로세스를 확장하는 범위를 제어할 수 있습니다.
minProcesses는 전체 워커 프로세스의 최소 개수를 정의합니다. 이 값은 1 이상이어야 합니다.maxProcesses는 Horizon이 확장할 수 있는 전체 워커 프로세스의 최대 개수를 정의합니다.
Horizon 실행하기
config/horizon.php 설정 파일에서 큐 워커에 대한 설정을 마쳤다면 Artisan 명령어 horizon으로 Horizon을 실행할 수 있습니다. 이 명령어 하나로 설정된 모든 워커 프로세스가 시작됩니다.
php artisan horizonHorizon 프로세스를 일시 정지하거나, 처리 중인 Job을 마저 끝낸 뒤 다시 계속 실행하도록 지시하려면 각각 horizon:pause 명령어와 horizon:continue 명령어를 사용하면 됩니다.
php artisan horizon:pausephp artisan horizon:continue특정 Horizon 슈퍼바이저만 콕 집어 일시 정지하거나 다시 실행하고 싶다면, horizon:pause-supervisor와 horizon:continue-supervisor 명령어를 사용하세요.
php artisan horizon:pause-supervisor supervisor-1php artisan horizon:continue-supervisor supervisor-1현재 Horizon 프로세스의 상태는 horizon:status 명령어로 확인할 수 있습니다.
php artisan horizon:status특정 슈퍼바이저의 상태만 확인하려면 horizon:supervisor-status 명령어에 슈퍼바이저 이름을 넘겨주면 됩니다.
php artisan horizon:supervisor-status supervisor-1Horizon 프로세스를 완전히 종료하려면 horizon:terminate 명령어를 사용합니다. 이 명령어가 실행되면 현재 처리 중인 Job들은 모두 마무리된 뒤에 Horizon이 종료됩니다.
php artisan horizon:terminateHorizon 배포하기
애플리케이션이 실제 운영 서버에 배포될 준비가 되었다면, php artisan horizon 명령어를 계속 실행 상태로 유지해 주는 프로세스 모니터를 함께 설정해야 합니다. 코드를 배포할 때마다 Horizon 프로세스를 재시작해야 하는데, 아래 과정을 참고하세요.
Supervisor 설치하기
Supervisor는 리눅스 운영체제용 프로세스 모니터로, horizon 프로세스가 실행 중이지 않으면 자동으로 재시작해 줍니다. 우분투(Ubuntu) 계열에서는 다음 명령어로 Supervisor를 설치할 수 있습니다.
sudo apt-get install supervisorNOTE
Supervisor 설정이 부담스럽게 느껴진다면, Laravel Cloud를 이용해 완전히 관리되는 환경에서 Laravel 애플리케이션을 운영하는 것도 고려해 볼 만합니다.
Supervisor 설정하기
Supervisor 설정 파일은 보통 /etc/supervisor/conf.d 디렉터리에 저장합니다. 이 디렉터리 안에 원하는 개수만큼 설정 파일을 만들어 Supervisor가 각 프로세스를 어떻게 모니터링할지 지정할 수 있습니다. 예를 들어, horizon.conf 파일을 만들어 horizon 프로세스를 시작하고 관리하도록 설정해 보겠습니다.
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600이 설정을 작성할 때 stopwaitsecs 값이 가장 오래 걸리는 Job의 처리 시간(초 단위)보다 크게 설정되어 있는지 반드시 확인하세요. 그렇지 않으면 Supervisor가 Job이 끝나기도 전에 프로세스를 강제 종료해 버릴 수 있습니다.
WARNING
위 예시는 우분투 기반 서버에 적합한 설정이지만, 다른 서버 운영체제에서는 Supervisor 설정 파일이 위치하는 디렉터리나 파일 확장자가 다를 수 있습니다. 사용 중인 서버 문서를 참고해서 확인하시기 바랍니다.
Supervisor 실행하기
설정 파일을 작성했다면 다음 명령어로 Supervisor 설정을 갱신하고 모니터링 대상 프로세스를 시작할 수 있습니다.
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start horizonNOTE
Supervisor 운영에 대한 더 자세한 내용은 Supervisor 공식 문서를 참고하세요.
태그(Tags)
Horizon 실행하기
Horizon 실행하기
config/horizon.php 설정 파일에서 슈퍼바이저와 워커 설정을 마쳤다면, 이제 horizon Artisan 명령어로 Horizon을 실행할 수 있습니다. 이 명령어 하나만으로 현재 환경에 설정된 모든 워커 프로세스가 실행됩니다:
php artisan horizonhorizon:pause와 horizon:continue Artisan 명령어를 사용하면 Horizon 프로세스를 일시 중지했다가 다시 Job을 처리하도록 재개할 수 있습니다:
php artisan horizon:pausephp artisan horizon:continue특정 Horizon 슈퍼바이저만 일시 중지하거나 재개하고 싶다면 horizon:pause-supervisor, horizon:continue-supervisor 명령어를 사용하세요:
php artisan horizon:pause-supervisor supervisor-1php artisan horizon:continue-supervisor supervisor-1horizon:status 명령어로 현재 Horizon 프로세스의 상태를 확인할 수 있습니다:
php artisan horizon:status특정 슈퍼바이저의 상태만 확인하고 싶다면 horizon:supervisor-status 명령어를 사용하세요:
php artisan horizon:supervisor-status supervisor-1horizon:terminate 명령어를 사용하면 Horizon 프로세스를 정상적으로 종료할 수 있습니다. 현재 처리 중인 Job은 모두 완료된 후에 Horizon이 종료됩니다:
php artisan horizon:terminateHorizon 자동 재시작하기
로컬 개발 환경에서는 horizon:listen 명령어를 사용할 수 있습니다. 이 명령어를 사용하면 코드를 수정할 때마다 Horizon을 수동으로 재시작할 필요가 없습니다. 이 기능을 사용하려면 먼저 로컬 개발 환경에 Node가 설치되어 있어야 합니다. 그리고 프로젝트에 파일 변경 감지 라이브러리인 Chokidar도 설치해야 합니다:
npm install --save-dev chokidarChokidar 설치가 끝나면 horizon:listen 명령어로 Horizon을 실행할 수 있습니다:
php artisan horizon:listenDocker나 Vagrant 환경에서 실행 중이라면 --poll 옵션을 함께 사용해야 합니다:
php artisan horizon:listen --pollconfig/horizon.php 설정 파일의 watch 옵션을 통해 감시할 디렉터리와 파일을 지정할 수 있습니다:
'watch' => [
'app',
'bootstrap',
'config',
'database',
'public/**/*.php',
'resources/**/*.php',
'routes',
'composer.lock',
'.env',
],NOTE
horizon:listen은 코드 변경을 감지해서 Horizon을 자동으로 재시작해 주는 개발용 편의 기능입니다. 파일 변경 감시는 CPU 자원을 어느 정도 소모하므로, 운영 서버에서는 사용하지 말고 뒤에서 설명할 Supervisor 같은 프로세스 모니터링 도구를 사용하세요.
Horizon 배포하기
실제 운영 서버에 Horizon을 배포할 준비가 되었다면, php artisan horizon 명령어를 감시하다가 예기치 않게 종료될 경우 자동으로 재시작해 주는 프로세스 모니터를 구성해야 합니다. 프로세스 모니터를 설치하는 방법은 아래에서 설명하니 걱정하지 마세요.
애플리케이션을 배포하는 과정에서는 Horizon 프로세스를 종료하도록 지시해야 합니다. 그러면 프로세스 모니터가 이를 감지해 Horizon을 재시작하면서 변경된 코드를 반영합니다:
php artisan horizon:terminateSupervisor 설치하기
Supervisor는 리눅스 운영체제용 프로세스 모니터로, horizon 프로세스가 중단되면 자동으로 다시 실행해 줍니다. Ubuntu에서는 다음 명령어로 Supervisor를 설치할 수 있습니다. Ubuntu가 아니라면 사용 중인 운영체제의 패키지 관리자를 통해 설치하면 됩니다:
sudo apt-get install supervisorNOTE
Supervisor를 직접 설정하는 게 부담스럽다면, Laravel 애플리케이션의 백그라운드 프로세스를 대신 관리해 주는 Laravel Cloud를 고려해 보세요.
Supervisor 설정하기
Supervisor 설정 파일은 보통 서버의 /etc/supervisor/conf.d 디렉터리에 저장됩니다. 이 디렉터리 안에 원하는 만큼 설정 파일을 만들어 Supervisor에게 각 프로세스를 어떻게 감시할지 지정할 수 있습니다. 예를 들어, horizon 프로세스를 실행하고 감시하는 horizon.conf 파일을 다음과 같이 작성할 수 있습니다:
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600Supervisor 설정을 작성할 때는 stopwaitsecs 값이 가장 오래 걸리는 Job의 처리 시간보다 크게 설정되어 있는지 반드시 확인하세요. 그렇지 않으면 Job이 끝나기도 전에 Supervisor가 강제로 종료시킬 수 있습니다.
WARNING
위 예시는 Ubuntu 기반 서버 기준입니다. Supervisor 설정 파일이 위치하는 경로나 확장자는 운영체제마다 다를 수 있으니, 사용 중인 서버의 문서를 참고하시기 바랍니다.
Supervisor 실행하기
설정 파일을 작성했다면, 다음 명령어들로 Supervisor 설정을 갱신하고 감시 대상 프로세스를 실행할 수 있습니다:
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start horizonNOTE
Supervisor 실행에 대한 더 자세한 내용은 Supervisor 공식 문서를 참고하세요.
태그(Tags)
Horizon에서는 Job은 물론 메일러블(mailable), 브로드캐스트 이벤트, 알림(notification), 큐 처리되는 이벤트 리스너에도 "태그"를 지정할 수 있습니다. 심지어 Horizon은 Job에 연결된 Eloquent 모델을 분석해서 대부분의 Job에 자동으로 태그를 붙여줍니다. 예를 들어 다음과 같은 Job이 있다고 가정해 봅시다.
<?php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class RenderVideo implements ShouldQueue
{
use Queueable;
/**
* 새 Job 인스턴스를 생성합니다.
*/
public function __construct(
public Video $video,
) {}
/**
* Job을 실행합니다.
*/
public function handle(): void
{
// ...
}
}이 Job이 id 속성 값이 1인 App\Models\Video 인스턴스와 함께 큐에 등록되면, 자동으로 App\Models\Video:1이라는 태그가 부여됩니다. Horizon이 Job의 속성들을 살펴보다가 Eloquent 모델을 발견하면, 해당 모델의 클래스명과 기본 키(primary key)를 조합해 태그를 지능적으로 생성해 주기 때문입니다.
use App\Jobs\RenderVideo;
use App\Models\Video;
$video = Video::find(1);
RenderVideo::dispatch($video);NOTE
이 자동 태깅 덕분에 대시보드에서 "이 영상과 관련된 Job만 모아보기"처럼 특정 모델을 기준으로 Job을 추적할 수 있어 매우 유용합니다. 다만 클래스명과 ID 조합만으로 태그가 만들어지기 때문에, 더 의미 있는 태그가 필요하다면 아래처럼 직접 정의하는 것이 좋습니다.
Job에 수동으로 태그 지정하기
큐 처리 대상 객체에 태그를 직접 지정하고 싶다면, 클래스에 tags 메서드를 정의하면 됩니다.
class RenderVideo implements ShouldQueue
{
/**
* 이 Job에 부여할 태그 목록을 반환합니다.
*
* @return array<int, string>
*/
public function tags(): array
{
return ['render', 'video:'.$this->video->id];
}
}이벤트 리스너에 수동으로 태그 지정하기
큐 처리되는 이벤트 리스너의 태그를 조회할 때, Horizon은 이벤트 인스턴스를 tags 메서드에 자동으로 전달해 줍니다. 이를 활용하면 이벤트 데이터를 태그에 포함시킬 수 있습니다.
class SendRenderNotifications implements ShouldQueue
{
/**
* 이 리스너에 부여할 태그 목록을 반환합니다.
*
* @return array<int, string>
*/
public function tags(VideoRendered $event): array
{
return ['video:'.$event->video->id];
}
}알림
WARNING
Horizon에서 Slack이나 SMS 알림을 보내도록 설정하기 전에 해당 알림 채널의 사전 준비 사항을 먼저 확인하세요.
큐의 대기 시간이 지나치게 길어졌을 때 알림을 받고 싶다면 Horizon::routeMailNotificationsTo, Horizon::routeSlackNotificationsTo, Horizon::routeSmsNotificationsTo 메서드를 사용하면 됩니다. 이 메서드들은 애플리케이션의 App\Providers\HorizonServiceProvider에 있는 boot 메서드 안에서 호출합니다:
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
parent::boot();
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('example@example.com');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}알림 대기 시간 임계값 설정하기
무엇을 "긴 대기 시간"으로 간주할지는 애플리케이션의 config/horizon.php 설정 파일에서 조정할 수 있습니다. 이 파일의 waits 설정 옵션을 사용하면 커넥션 / 큐 조합마다 대기 시간 임계값을 원하는 대로 지정할 수 있습니다. 별도로 정의하지 않은 커넥션 / 큐 조합은 기본값으로 60초가 적용됩니다:
'waits' => [
'redis:critical' => 30,
'redis:default' => 60,
'redis:batch' => 120,
],NOTE
예를 들어 위 설정에서는 redis 커넥션의 critical 큐가 30초 이상 대기하면 알림이 발송되고, batch 큐는 2분(120초) 이상 대기해야 알림이 발송됩니다. 큐마다 우선순위나 처리 특성이 다르므로, 실제 운영 환경의 처리 속도를 참고해 임계값을 조정하는 것이 좋습니다.
특정 큐에 대해 긴 대기 알림을 아예 받고 싶지 않다면 해당 큐의 임계값을 0으로 설정하면 됩니다.
메트릭
Horizon에는 Job과 큐의 대기 시간, 처리량에 대한 정보를 제공하는 메트릭 대시보드가 포함되어 있습니다. 이 대시보드에 데이터를 채우려면 애플리케이션의 routes/console.php 파일에서 Horizon의 snapshot Artisan 명령어가 5분마다 실행되도록 스케줄을 설정해야 합니다:
use Illuminate\Support\Facades\Schedule;
Schedule::command('horizon:snapshot')->everyFiveMinutes();애플리케이션의 config/horizon.php 설정 파일에서 metrics.trim_snapshots 옵션을 사용하면 Horizon이 메트릭 그래프를 위해 보관할 스냅샷의 개수를 조정할 수 있습니다. 이 옵션은 스냅샷의 보관 기간이 아니라 개수를 제한하는 방식이므로, 실제 보관 기간은 horizon:snapshot 명령어가 얼마나 자주 실행되는지에 따라 달라집니다:
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],모든 메트릭 데이터를 삭제하고 싶다면 horizon:clear-metrics Artisan 명령어를 실행하면 됩니다:
php artisan horizon:clear-metricsHorizon
실패한 Job 삭제하기
실패한 Job을 삭제하고 싶다면 horizon:forget 명령어를 사용하면 됩니다. 이 명령어는 유일한 인자로 실패한 Job의 ID 또는 UUID를 받습니다:
php artisan horizon:forget 5실패한 Job을 모두 삭제하고 싶은 경우에는 horizon:forget 명령어에 --all 옵션을 추가하면 됩니다:
php artisan horizon:forget --allHorizon
큐에서 Job 삭제하기
애플리케이션의 기본 큐에 쌓인 모든 Job을 삭제하고 싶다면, horizon:clear Artisan 명령어를 사용하면 됩니다:
php artisan horizon:clear특정 큐의 Job만 삭제하려면 queue 옵션을 함께 지정하세요:
php artisan horizon:clear --queue=emailsNOTE
이 명령어는 개발 중 테스트 데이터를 정리하거나, 잘못 등록된 Job이 계속 실패해서 큐를 막고 있을 때 빠르게 초기화하는 용도로 유용합니다. 운영 환경에서 사용할 때는 삭제 대상 큐를 다시 한번 확인한 뒤 실행하는 것이 안전합니다.