Деплой PHP на Kubernetes: пошаговое руководство на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем деплоить PHP в Kubernetes?

Многие разработчики до сих пор воспринимают PHP как язык для «классического» хостинга с FTP и phpMyAdmin. Однако в современной DevOps-культуре PHP-приложения всё чаще запускаются в контейнерах, а управление ими ложится на плечи Kubernetes (K8s). Это даёт не только автоматическое масштабирование и отказоустойчивость, но и единообразие окружения на всех этапах — от разработки до продакшена.

В этом руководстве мы на практике разберём, как подготовить PHP-проект к деплою в Kubernetes: от создания Docker-образа до написания манифестов и запуска в кластере. Вы узнаете, как сделать контейнер лёгким, как правильно настроить php-fpm и как связать всё с Nginx. Поехали!

Шаг 1: Подготовка PHP-приложения и Docker-образа

Перед тем как загружать код в K8s, нужно упаковать его в Docker-образ. Рассмотрим типовой PHP-проект на чистом PHP (без фреймворка) — простой скрипт, который выводит приветствие и phpinfo.

1.1. Структура проекта

Создадим минимальную структуру:

my-php-app/
├── src/
│   └── index.php
├── docker/
│   ├── php.Dockerfile
│   └── nginx.conf
└── k8s/
    ├── deployment.yaml
    └── service.yaml

1.2. Код PHP-приложения

Файл src/index.php:

<?php
echo "<h1>Привет, Kubernetes!</h1>";
echo "<p>Версия PHP: " . phpversion() . "</p>";
echo "<p>Сервер: " . gethostname() . "</p>";
?>

1.3. Dockerfile для PHP-FPM

Создаём docker/php.Dockerfile:

FROM php:8.2-fpm-alpine

# Устанавливаем зависимости (если нужны)
RUN docker-php-ext-install pdo pdo_mysql

# Копируем код приложения
COPY ./src /var/www/html

# Права доступа (важно для FPM)
RUN chown -R www-data:www-data /var/www/html

WORKDIR /var/www/html

EXPOSE 9000
CMD ["php-fpm"]

Почему alpine? Он даёт образ размером ~30 МБ вместо 200+ МБ на Debian. Это критично для быстрой загрузки в кластере.

1.4. Конфигурация Nginx

PHP-FPM не умеет отдавать статику напрямую — нужен веб-сервер. Создадим docker/nginx.conf:

server {
    listen 80;
    server_name _;
    root /var/www/html;

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

В Kubernetes мы запустим Nginx и PHP-FPM в одном Pod'е (как sidecar-контейнеры) или как отдельные сервисы. Для простоты используем один Pod с двумя контейнерами.

Шаг 2: Сборка и публикация образов

Соберём образ PHP-FPM и образ Nginx с нашей конфигурацией. В реальном проекте можно использовать один образ, объединив Nginx и PHP в supervisord, но мы сделаем «правильно» — разделим.

Сборка PHP-образа:

docker build -t my-php-app:latest -f docker/php.Dockerfile .

Сборка Nginx-образа:

Создадим отдельный Dockerfile для Nginx:

FROM nginx:alpine
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
COPY ./src /var/www/html
docker build -t my-nginx:latest -f docker/nginx.Dockerfile .

Затем загрузите образы в registry (Docker Hub, GitLab Container Registry и т.д.):

docker tag my-php-app:latest your-registry/my-php-app:latest
docker push your-registry/my-php-app:latest

Шаг 3: Манифесты Kubernetes

Теперь самое интересное — опишем, как Kubernetes будет запускать наше приложение.

3.1. Deployment (основной манифест)

Создаём k8s/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-app-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: php-app
  template:
    metadata:
      labels:
        app: php-app
    spec:
      containers:
      - name: php-fpm
        image: your-registry/my-php-app:latest
        ports:
        - containerPort: 9000
      - name: nginx
        image: your-registry/my-nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: shared-files
          mountPath: /var/www/html
      volumes:
      - name: shared-files
        emptyDir: {}

Обратите внимание: emptyDir — это временный том, который позволяет контейнерам обмениваться файлами. Мы копируем код в оба образа, но через emptyDir синхронизируем их.

3.2. Service (доступ к приложению)

Создаём k8s/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: php-app-service
spec:
  selector:
    app: php-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: LoadBalancer

type: LoadBalancer даст внешний IP (если ваш кластер поддерживает это, например, в minikube — minikube service).

Шаг 4: Деплой в кластер

Убедитесь, что у вас есть доступ к кластеру (кубеконфиг). Примените манифесты:

kubectl apply -f k8s/deployment.yaml
kub                

Блоги

Рекомендаций по книгам