Laravel + Kubernetes: деплой масштабируемых PHP-приложений с нуля в 2026 году
Введение: почему Kubernetes стал стандартом деплоя для PHP-приложений в 2026 году
Если в 2020 году Kubernetes воспринимался как инструмент для крупных компаний, то в 2026-м это де-факто стандарт для любого PHP-приложения, которое должно выдерживать нагрузку и легко масштабироваться. Managed-кластеры от GKE, EKS и AKS стали дешевле и проще в настройке. Экосистема вокруг Kubernetes — Helm, Argo CD, Kustomize — достигла зрелости. А главное: Laravel как фреймворк стал значительно лучше адаптирован к cloud-native среде благодаря Octane, нативной поддержке Redis-очередей и горизонтальному масштабированию воркеров.
В этой статье мы пройдём весь путь: от подготовки Laravel-приложения к stateless-архитектуре до написания production-ready Kubernetes-манифестов, настройки HPA и организации миграций. Целевая аудитория — middle и senior PHP-разработчики и DevOps-инженеры, которые уже работают с Docker, но хотят перейти на Kubernetes.
1. Подготовка Laravel-приложения к работе в Kubernetes
Stateless-архитектура
Kubernetes предполагает, что поды могут быть пересозданы в любой момент. Это значит, что Laravel-приложение не должно хранить состояние локально. Проверьте следующие моменты:
- Сессии — переведите с драйвера
fileнаredisилиdatabase. - Кеш — используйте Redis или Memcached вместо файлового кеша.
- Очереди — Redis или Amazon SQS, не синхронный драйвер.
- Хранение файлов — S3-совместимое хранилище (AWS S3, MinIO), не локальный диск.
Переменные окружения
В Kubernetes конфигурация передаётся через ConfigMap и Secret, а не через файл .env. Убедитесь, что приложение читает все настройки через env() и config(), а не из жёстко прописанных значений. Файл .env не должен попадать в Docker-образ.
Health Checks
Kubernetes требует эндпоинты для проверки состояния контейнера. Добавьте в маршруты простой health check:
// routes/web.php
Route::get('/healthz', function () {
return response()->json(['status' => 'ok']);
});Для более глубокой проверки (соединение с БД, Redis) используйте пакет spatie/laravel-health или напишите собственный контроллер, который проверяет все зависимости и возвращает HTTP 200 или 503.
2. Создание Docker-образа для Laravel с многоэтапной сборкой
Многоэтапная сборка (multi-stage build) позволяет получить компактный production-образ без dev-зависимостей и build-инструментов. Ниже — рабочий Dockerfile для Laravel в 2026 году:
# ---- Stage 1: Node build (для компиляции ассетов) ----
FROM node:20-alpine AS node-builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Stage 2: Composer dependencies ----
FROM composer:2.7 AS composer-builder
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--prefer-dist \
--optimize-autoloader
# ---- Stage 3: Production image ----
FROM php:8.3-fpm-alpine
# Системные зависимости
RUN apk add --no-cache \
nginx \
supervisor \
libpq-dev \
libzip-dev \
oniguruma-dev \
&& docker-php-ext-install \
pdo_pgsql \
pdo_mysql \
zip \
opcache \
pcntl
# Копируем приложение
WORKDIR /var/www/html
COPY --from=composer-builder /app/vendor ./vendor
COPY --from=node-builder /app/public/build ./public/build
COPY . .
# Настройки прав
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
# Оптимизация Laravel
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
EXPOSE 9000
CMD ["php-fpm"]Обратите внимание: config:cache на этапе сборки имеет смысл только если переменные окружения не меняются между средами. В большинстве случаев лучше вызывать кеширование в entrypoint-скрипте после инъекции переменных из Kubernetes.
3. Написание Kubernetes-манифестов
ConfigMap
Не-секретные переменные окружения выносим в ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: laravel-config
namespace: production
data:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
CACHE_DRIVER: redis
SESSION_DRIVER: redis
QUEUE_CONNECTION: redis
REDIS_HOST: redis-service
DB_CONNECTION: pgsql
DB_HOST: postgres-service
DB_PORT: "5432"
DB_DATABASE: laravel_dbSecret
Секреты (пароли, ключи) — в Kubernetes Secret (в production используйте External Secrets Operator или Vault):
apiVersion: v1
kind: Secret
metadata:
name: laravel-secrets
namespace: production
type: Opaque
stringData:
APP_KEY: base64:ВАШ_КЛЮЧ
DB_PASSWORD: ВАШ_ПАРОЛЬ
REDIS_PASSWORD: ВАШ_REDIS_ПАРОЛЬDeployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: laravel-app
template:
metadata:
labels:
app: laravel-app
spec:
containers:
- name: php-fpm
image: your-registry/laravel-app:1.2.3
ports:
- containerPort: 9000
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secrets
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10Service и Ingress
apiVersion: v1
kind: Service
metadata:
name: laravel-service
namespace: production
spec:
selector:
app: laravel-app
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: laravel-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- yourdomain.com
secretName: laravel-tls
rules:
- host: yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: laravel-service
port:
number: 804. Горизонтальное масштабирование (HPA) для PHP-воркеров и очередей
HPA (Horizontal Pod Autoscaler) автоматически изменяет количество реплик в зависимости от нагрузки. Для Laravel-приложения настраивают HPA по CPU, а для Queue Workers — по длине очереди через KEDA (Kubernetes Event-Driven Autoscaling).
HPA по CPU для web-приложения
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: laravel-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: laravel-app
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60Queue Workers с KEDA
Для масштабирования воркеров Laravel Queue используйте KEDA со ScaledObject, который реагирует на длину очереди в Redis:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: laravel-queue-scaler
namespace: production
spec:
scaleTargetRef:
name: laravel-queue-worker
minReplicaCount: 1
maxReplicaCount: 10
triggers:
- type: redis
metadata:
address: redis-service:6379
listName: queues:default
listLength: "50"Deployment для Queue Worker должен использовать команду php artisan queue:work с флагом --max-time или --max-jobs, чтобы воркер корректно перезапускался при обновлениях.
5. Управление миграциями базы данных в Kubernetes
Запуск php artisan migrate в Kubernetes — нетривиальная задача. Есть два подхода:
Kubernetes Job
Job запускается один раз и завершается. Подходит для CD-пайплайнов, где миграция выполняется перед обновлением Deployment:
apiVersion: batch/v1
kind: Job
metadata:
name: laravel-migrate
namespace: production
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: your-registry/laravel-app:1.2.3
command: ["php", "artisan", "migrate", "--force"]
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secretsInit Container
Init Container запускается до основного контейнера пода. Удобно для автоматического применения миграций при деплое:
initContainers:
- name: migrate
image: your-registry/laravel-app:1.2.3
command: ["php", "artisan", "migrate", "--force"]
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secretsВажно: при использовании Init Container миграция запустится для каждого пода. Чтобы избежать конфликтов при параллельном деплое нескольких реплик, убедитесь, что ваши миграции идемпотентны, или используйте Job + PreSync Hook в Argo CD.
6. Мониторинг Laravel-приложения в кластере
Для полноценного наблюдения за PHP-приложением в Kubernetes используйте следующий стек:
- Prometheus + Grafana — сбор метрик кластера и приложения. Используйте пакет
spatie/laravel-prometheusили экспортируйте метрики через/metricsэндпоинт. - Loki — агрегация логов. Настройте Laravel на вывод логов в
stderr(LOG_CHANNEL=stderr), и Loki автоматически соберёт их через Promtail. - OpenTelemetry — распределённая трассировка запросов. Пакет
open-telemetry/opentelemetry-phpинтегрируется с Laravel и отправляет трейсы в Jaeger или Tempo. - Kubernetes Events + Alertmanager — алерты при падении подов, OOMKill, ошибках liveness probe.
Настройте дашборд Grafana с ключевыми метриками: RPS, p95/p99 latency, количество ошибок 5xx, использование памяти PHP-FPM, длина очереди Redis.
7. Типичные ошибки и как их избежать
- Хранение сессий в файлах — при перезапуске пода пользователи теряют сессии. Решение: Redis-драйвер.
- APP_KEY не задан или разный в разных репликах — данные, зашифрованные в одном поде, не расшифруются в другом. Решение: единый Secret с APP_KEY.
- config:cache при сборке образа — кешированный конфиг содержит значения из среды сборки, не из production. Решение: кешировать конфиг в entrypoint или не кешировать вовсе.
- Отсутствие resource limits — один под может потребить все ресурсы ноды. Всегда задавайте
requestsиlimits. - Queue workers без graceful shutdown — при обновлении деплоя воркер убивается на середине задачи. Решение:
terminationGracePeriodSeconds: 60и флаг--stop-when-empty. - Нет readiness probe — трафик идёт на под, который ещё не готов. Всегда настраивайте readinessProbe отдельно от livenessProbe.
- Образ без pinned-тегов — использование тега
latestделает деплой непредсказуемым. Всегда тегируйте образы конкретным хешем или версией.
Заключение и итоговые рекомендации
Деплой Laravel в Kubernetes в 2026 году — это не сложно, если подходить системно. Вот краткий чеклист для production-готового деплоя:
- Переведите приложение на stateless-архитектуру: Redis для сессий/кеша/очередей, S3 для файлов.
- Соберите компактный Docker-образ с multi-stage build на базе PHP 8.3 FPM Alpine.
- Разделите конфигурацию на ConfigMap (не-секретное) и Secret (пароли, ключи).
- Настройте livenessProbe и readinessProbe для всех контейнеров.
- Задайте resource requests и limits для предсказуемого планирования подов.
- Используйте HPA для web-реплик и KEDA для queue workers.
- Запускайте миграции через Job (в CI/CD) или Init Container с идемпотентными миграциями.
- Настройте сбор логов в stderr, метрики через Prometheus и трассировку через OpenTelemetry.
- Используйте Argo CD или Flux для GitOps-деплоя — никаких ручных
kubectl applyв production.
Kubernetes даёт Laravel-приложению горизонтальное масштабирование, самовосстановление и предсказуемые деплои. Инвестиция в правильную начальную настройку окупается при первом же всплеске нагрузки. Начните с локального кластера через kind или k3d, перенесите один микросервис или Laravel-монолит, и постепенно выстройте полноценный production-пайплайн.
Технологии
Теги
Руслан Исмаилов
Senior Web / Backend разработчик. Senior web/backend разработчик с 9-летним опытом. Стек: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, микросервисы, CI/CD. Подробнее обо мне →