Service Mesh и PHP: как микросервисы становятся умнее

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

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

Начать курс

Ваши PHP-микросервисы общаются шепотом? Пора включить громкую связь

Представьте: у вас 15 PHP-микросервисов. Один отвечает за авторизацию, другой — за платежи, третий — за уведомления. Они общаются по HTTP, но каждую неделю что-то ломается: таймауты, падения, потерянные запросы. Вы добавляете ретраи, circuit breaker'ы, балансировку — и код превращается в спагетти из логики сетевого взаимодействия.

Здесь и приходит Service Mesh — инфраструктурный слой, который выносит всю сетевую магию из вашего PHP-кода. Не бойтесь: это не магия, а продуманная архитектура. И да, PHP-микросервисы могут (и должны) использовать Service Mesh.

Что такое Service Mesh и зачем он PHP-разработчику?

Service Mesh — это выделенный уровень инфраструктуры для управления межсервисным взаимодействием. Вместо того чтобы писать в каждом микросервисе логику балансировки, шифрования, трейсинга и таймаутов, вы делегируете это sidecar-прокси (обычно Envoy или Linkerd).

Для PHP это особенно актуально, потому что:

  • PHP-приложения часто работают через FastCGI или FPM, и добавление сетевой логики в каждый сервис — это overhead
  • Service Mesh даёт mTLS (взаимную аутентификацию) без изменения кода
  • Вы получаете единый дашборд для мониторинга всех микросервисов
  • Можно внедрять circuit breaker и retry прямо на уровне прокси

Самый популярный стек — Istio + Envoy. Но мы рассмотрим его с точки зрения PHP-разработчика.

Как это работает: PHP-сервис + sidecar

Допустим, у вас есть микросервис user-service на PHP. Он слушает порт 9000 (FPM). Без Service Mesh вы обращаетесь к нему напрямую:

GET http://user-service:9000/api/users/1

С Service Mesh в ваш pod (контейнер) добавляется Envoy-прокси. Теперь любой входящий и исходящий трафик проходит через него:

┌─────────────────┐       ┌──────────────┐
│ PHP-приложение   │ ──►   │ Envoy Proxy  │
│ (user-service)   │       │ (sidecar)    │
└─────────────────┘       └──────┬───────┘
                                  │
                                  ▼
                          ┌──────────────┐
                          │ Envoy Proxy  │
                          │ (другой сервис)│
                          └──────────────┘

Ваш PHP-код не знает о прокси. Он просто шлёт запросы на localhost:80 или localhost:9000 — Envoy перехватывает их и маршрутизирует по правилам Istio.

Практический пример: PHP-микросервис с Service Mesh на Istio

Шаг 1. Настройка PHP-сервиса (без изменений в коде)

Предположим, у вас есть простой PHP-скрипт, который возвращает данные о пользователе:

<?php
// user-service/index.php
$userId = $_GET['id'] ?? 0;
$users = [
    1 => ['name' => 'Alice', 'email' => 'alice@example.com'],
    2 => ['name' => 'Bob', 'email' => 'bob@example.com'],
];

header('Content-Type: application/json');
echo json_encode($users[$userId] ?? ['error' => 'User not found']);

Заверните в Docker:

# Dockerfile
FROM php:8.2-fpm
COPY index.php /var/www/html/index.php
CMD ["php-fpm"]

Всё стандартно. Никаких изменений для Service Mesh.

Шаг 2. Деплой с sidecar в Kubernetes

Добавьте аннотацию sidecar.istio.io/inject: "true" — Istio сам вставит Envoy:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
      annotations:
        sidecar.istio.io/inject: "true"  # <-- ключевая строка
    spec:
      containers:
      - name: php
        image: user-service:latest
        ports:
        - containerPort: 9000
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
  - port: 80
    targetPort: 9000

Теперь ваш PHP-сервис работает внутри Service Mesh.

Шаг 3. Настройка маршрутизации и таймаутов через Istio

Создайте VirtualService, который добавит таймаут и ретраи — без единой строчки PHP:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: user-service
spec:
  hosts:
  - user-service
  http:
  - timeout: 3s
    retries:
      attempts: 3
      perTryTimeout: 2s
    route:
    - destination:
        host: user-service
        port:
          number: 80

Если ваш PHP-сервис зависнет на 10 секунд, Istio оборвёт соединение через 3 секунды и попробует ещё 3 раза. Код не трогали!

Продвинутый кейс: PHP-клиент с gRPC через Service Mesh

PHP не очень дружит с gRPC из коробки, но через Service Mesh вы можете использовать HTTP/1.1 → gRPC transcoding. Envoy может преобразовывать REST-запросы в gRPC-вызовы.

Допустим, у вас есть gRPC-сервис на Go, а PHP-клиент хочет с ним общаться. Вместо установки protobuf в PHP вы:

  1. Делаете обычный POST-запрос на Envoy
  2. Envoy транскодирует его в gRPC
  3. Отправляет сервису

Пример конфигурации Envoy для транскодирования:

http_filters:
- name: envoy.filters.http.grpc_json_transcoder
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
    proto_descriptor: "/etc/envoy/proto.pb"
    services: ["user.UserService"]
    print_options:
      add_whitespace: true
      always_print_primitive_fields: true

PHP-код остаётся простым:

<?php
$client = new GuzzleHttp\Client(['base_uri' => 'http://user-service-envoy']);
$response = $client->post('/api/users', [
    'json' => ['id' => 1]
]);
echo $response->getBody();

Envoy сам преобразует тело запроса в protobuf и вызовет gRPC-метод

Блоги

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