Створення CLI-додатка за допомогою Laravel та Docker

Перекладено ШІ
Оригінал: Laravel News
Оновлено: 29 жовтня, 2024
Зазирніть у світ Laravel, де потужний CLI-фреймворк відкриває нові можливості для розробки командного інтерфейсу. Дізнайтеся, як створити просту утиліту для перевірки акцій, яка працює з Docker, та які переваги це може принести у вашому проєкті!

Laravel має потужний CLI-фреймворк, що базується на популярному компоненті Symfony Console, який приносить основні можливості Laravel у командний рядок. Хоча зазвичай Laravel використовується для створення веб-додатків, деякі проекти потребують надійних CLI-команд, які можна запускати через Docker у виробничих середовищах.

Якщо ви розробляєте проект лише з CLI, можете також розглянути використання спільного проекту Laravel Zero. Усе, що ми обговорюємо в цій статті, працює як з Laravel, так і з Laravel Zero (з деякими налаштуваннями образу Docker).

# Налаштування проекту

Ми створимо невеличкий CLI для перевірки акцій (за допомогою API Polygon.io), який ви зможете запускати через Docker. Він матиме підкоманди для перевірки акцій. Зокрема, ми реалізуємо команду stock:check, яка буде шукати акції за вказаною датою, використовуючи символ акції:

php artisan stock:check AAPL

На момент написання, Polygon.io пропонує безкоштовний базовий план API, що дозволяє виконувати 5 запитів на хвилину і має дані за останні два роки. Щоб продовжити, вам знадобиться ключ API. Він також дозволить ілюструвати, як налаштувати секрети для використання з нашим Docker-образом.

Спочатку створимо проект Laravel. Якщо ви слідуєте за нами, потрібно встановити PHP та Laravel Installer:

laravel new stock-checker --git --no-interaction

Оскільки наша програма буде лише CLI, нам не потрібні жодні стартові набори, тому використаємо параметр --no-interaction, щоб прийняти всі значення за замовчуванням. Якщо ви зможете виконати php artisan inspire після створення проекту stock-checker, ви готові до початку:

php artisan inspire
 
“Я починаю говорити лише тоді, коли впевнений, що те, що я скажу, не варто залишити невисловленим.”
  — Катон Молодший

На завершення, потрібно створити кілька файлів для роботи з Docker під час розробки, хоча кінцеві користувачі потребують лише середовища виконання для використання програми:

mkdir build/
touch \
  .dockerignore \
  compose.yaml \
  build/Dockerfile

Файл Dockerfile знаходиться в папці build. Я віддаю перевагу зберігати файли конфігурації Docker у підкаталогах для кращої організації, враховуючи такі речі, як INI-файли та інші проекти, пов'язані з Docker.

У нас є усе необхідне для старту. У наступному розділі ми створимо команду і налаштуємо додаток для роботи з Docker.

# Створення CLI-команди

Наш додаток почнеться з однієї команди check, яка шукатиме деталі акцій США за вказаним символом. Ми не будемо зосереджуватися на вмісті цього файлу, але якщо ви хочете слідувати інструкції, створіть файл команди з Artisan:

php artisan make:command CheckStockCommand

Додайте наступний код до щойно створеного файлу CheckStockCommand.php, який знаходиться в папці app/Console/Commands:

<?php
 
namespace App\Console\Commands;
 
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Illuminate\Console\Command;
 
class CheckStockCommand extends Command
{
    protected $signature = 'stock:check {symbol} {--d|date= : Дата, для якої потрібно перевірити ціну акцій}';
 
    protected $description = 'Перевірити ціну акцій за вказаним символом.';
 
    public function handle()
    {
        $symbol = Str::upper($this->argument('symbol'));
 
        // Отримати найближчий торгівельний будній день.
        $date = now()->previousWeekday();
 
        if ($dateOption = $this->option('date')) {
            $date = Carbon::parse($dateOption);
            if ($date->isToday() || $date->isFuture()) {
                $this->error('Дата повинна бути в минулому.');
                return;
            }
        }
 
        if ($date->lt(now()->subYear())) {
            $this->error('Дата повинна бути в межах останнього року.');
            return;
        }
 
        // Знайти дані про акції
        $ticker = $this->getClient()
            ->withUrlParameters(['symbol' => $symbol])
            ->withQueryParameters(['date' => $date->toDateString()])
            ->throw()
            ->get("https://api.polygon.io/v3/reference/tickers/{symbol}")
            ->json('results');
 
        $openClose = $this->getClient()
            ->withUrlParameters([
                'symbol' => $symbol,
                'date' => $date->toDateString()
            ])
            ->get("https://api.polygon.io/v1/open-close/{symbol}/{date}?adjusted=true");
 
        if ($openClose->failed()) {
            $this->error("Не вдалося отримати дані акцій.\nСтатус: " . $openClose->json('status') . "\nПовідомлення: " . $openClose->json('message') . "\n");
            return;
        }
 
 
        $this->info("Акція: {$ticker['name']} ({$ticker['ticker']})");
        $this->info("Дата: {$date->toDateString()}");
        $this->info("Валюта: {$ticker['currency_name']}");
        $this->table(['Відкриття', 'Закриття', 'Максимум', 'Мінімум'], [
            [
                number_format($openClose['open'], 2),
                number_format($openClose['close'], 2),
                number_format($openClose['high'], 2),
                number_format($openClose['low'], 2),
            ],
        ]);
    }
 
    protected function getClient(): PendingRequest
    {
        return Http::withToken(config('services.polygon.api_key'));
    }
}

Ця команда терміналу знаходить символ акцій за минулу дату в межах останнього року та повертає основну інформацію про акцію на цю дату. Щоб команда працювала, потрібно налаштувати конфігурацію сервісу і визначити дійсний ключ. Додайте наступну конфігурацію у файл config/services.php, який команда використовуватиме для налаштування ключа API:

// config/services.php
return [
    // ...
    'polygon' => [
        'api_key' => env('POLYGON_API_KEY'),
    ],
];

Не забудьте додати POLYGON_API_KEY до вашого .env файлу, а також додати змінну середовища до .env.example як порожнє значення:

# .env
POLYGON_API_KEY="<ваш_секретний_ключ>"
 
# .env.example
POLYGON_API_KEY=

Якщо ви запустите команду локально з дійсним символом акції, отримаєте щось на зразок цього:

php artisan stock:check AAPL
Акція: Apple Inc. (AAPL)
Дата: 2024-10-25
Валюта: usd
+--------+--------+--------+--------+
| Відкриття | Закриття | Максимум | Мінімум |
+--------+--------+--------+--------+
| 229.74 | 231.41 | 233.22 | 229.57 |
+--------+--------+--------+--------+

Ми перевірили, що команда працює, і тепер час створити Docker-образ для нашого CLI. Docker дозволяє кожному використовувати наш CLI без необхідності знати, як налаштувати середовище виконання.

Останнє, що ми зробимо перед створенням Docker-образу — це додати файл .env до .dockerignore, щоб не копіювати чутливі дані під час створення образу:

.env

# Створення Docker-образу для CLI

Готові налаштувати CLI для роботи з Docker. Існує кілька типових випадків використання для CLI-образу Docker:

  1. Запуск CLI з Docker під час розробки
  2. Запуск CLI як частини розгортання в контейнерах
  3. Розповсюдження CLI для кінцевих користувачів

Усі ці випадки застосовні до того, як ми налаштуємо Dockerfile. У цій статті ми розглянемо кілька способів структурування образу для CLI. Ми врахуємо запуск нашого CLI як єдиної команди або можливість забезпечити користувачам виконання кількох підкоманд.

Наш Dockerfile базується на офіційному образі PHP CLI і використовуватиме інструкцію ENTRYPOINT, щоб зробити команду stock:check єдиною командою, яка може виконуватися з образу. Без переозначення точки входу всі команди, які передаються нашому образу, виконуватимуться у контексті команди stock:check:

FROM php:8.3-cli-alpine
 
RUN docker-php-ext-install pcntl
 
COPY . /srv/app
WORKDIR /srv/app
ENTRYPOINT ["php", "/srv/app/artisan", "stock:check"]
CMD ["--help"]

Примітка: встановлення розширення pcntl у Docker CLI проектах дозволяє м’яко завершувати контейнери Docker за допомогою процесних сигналів. Ми не демонструємо це в цій команді, але якщо у вас є довготривалий демон, вам потрібно буде обробити м’яке завершення в будь-якому серверному середовищі, включаючи контейнери.

Інструкція ENTRYPOINT вказує команду, яка виконується під час запуску контейнера. Найцікавіше в цьому контексті — що всі команди, які ми передаємо контейнеру при його запуску, «додаються» до точки входу. Спробую пояснити:

# ENTRYPOINT            # Значення за замовчуванням CMD
php artisan stock:check --help
 
# Вище еквівалентно виконанню наступного:
docker run --rm stock-checker
 
# ENTRYPOINT            # Замініть значення CMD
php artisan stock:check AAPL
 
# Вище еквівалентно виконанню:
docker run --rm stock-checker AAPL

Ми показали, що ENTRYPOINT дозволяє виконувати одну команду, але якщо ви внесете цю невелику зміну, зможете запустити будь-яку команду, доступну в консолі Artisan:

FROM php:8.3-cli-alpine
 
RUN docker-php-ext-install pcntl
 
COPY . /srv/app
WORKDIR /srv/app
ENTRYPOINT ["php", "/srv/app/artisan"]
CMD ["list"]

Тепер точка входу — це artisan, що надає можливість виконувати будь-яку команду в консольному Artisan. Якщо ви розповсюджуєте CLI для інших користувачів, не рекомендується відкривати команди, окрім тих, що ви визначили, але наразі вони можуть запускати всі команди, доступні у вашому додатку.

Виконання команди php artisan list стане значенням за замовчуванням при запуску Docker-образу без аргументів команди. Тепер ми можемо легко запускати нашу команду перевірки акцій та інші команди для нашого CLI так:

docker build -t stock-checker -f build/Dockerfile .
 
# Запустити перевірку акцій
export POLYGON_API_KEY="<ваш_ключ>"
docker run --rm --env POLYGON_API_KEY stock-checker stock:check AAPL

Частини stock:check та AAPL тепер складають CMD. Раніше stock:check була єдиною командою, яку CLI міг виконувати без переозначення точки входу (це можна зробити за допомогою параметра --entrypoint= з docker run).

Зверніть увагу, що наш CLI вимагає змінної середовища для налаштування облікових даних. Використовуючи параметр --env, ми можемо передавати локальну змінну ENV, яку ми експортували. Залежно від потреб вашого додатку ви могли б надати конфігураційний файл, з якого CLI зчитує дані з секретного обсягу. Для зручності цієї статті ми використовуємо вбудовані можливості ENV Docker для запуску CLI.

# Запуск веб-додатків Laravel як CLI

Якщо вам потрібно запустити команду CLI у вашому додатку Laravel через Docker, зазвичай ви маєте php-fpm образ Docker, що містить ваш веб-додаток/API. Замість того, щоб створювати окремий образ CLI, ви можете налаштувати точку входу та команду для запуску як консолі Artisan замість веб-додатку. Усе виглядатиме схоже на вже наявне, а перевага в тому, що ви можете повторно використовувати образ веб-додатку для запуску CLI:

docker run --rm my-app --entrypoint=/srv/app/artisan stock:check AAPL

# Досліджуйте далі

Це основи запуску Laravel CLI з Docker, використовуючи ENTRYPOINT для визначення базової команди, яку наш Docker-образ використовуватиме для виконання команд. Ви можете дізнатися більше про entrypoint і cmd у офіційній довідці Dockerfile.