Service Mesh без Istio: построение межсервисной сети на Go с Envoy и Kubernetes
Введение: когда Istio становится обузой
Istio — мощный инструмент, но его сложность часто обратно пропорциональна размеру команды. Control plane из нескольких компонентов (istiod, ingress gateway, egress gateway), overhead на ресурсы кластера, нетривиальный debugging и крутая кривая обучения делают его избыточным для многих production-систем. По данным CNCF Survey 2024, значительная часть команд, попробовавших Istio, отказывается от него в течение первого года именно из-за операционной сложности.
Если ваша команда работает с Go-микросервисами в Kubernetes и нуждается в mTLS, трейсинге, health checks и балансировке трафика — всё это можно получить без Istio, вручную настроив Envoy как sidecar-прокси. Именно этому посвящена данная статья.
Что такое service mesh и зачем он нужен в Go-архитектуре
Service mesh — это инфраструктурный слой, который управляет межсервисным взаимодействием: маршрутизацией трафика, балансировкой нагрузки, аутентификацией, шифрованием и observability. В микросервисной архитектуре на Go эти задачи традиционно решались на уровне приложения через библиотеки (например, go-kit, grpc-go с interceptors). Однако такой подход имеет недостатки:
- Логика сети размазана по коду каждого сервиса
- Обновление политик требует пересборки и передеплоя
- Разнородность реализаций между командами
- Сложность единообразного enforcement mTLS
Service mesh выносит эту логику в отдельный прокси-процесс (sidecar), работающий рядом с каждым pod. Ваш Go-сервис ничего не знает о шифровании или circuit breaking — всё это делает Envoy прозрачно.
Envoy как data plane: ключевые возможности
Envoy Proxy — высокопроизводительный L4/L7-прокси, написанный на C++. Именно он используется как data plane в Istio, Linkerd v2 (частично) и других mesh-решениях. Ключевые возможности Envoy, релевантные для нашей задачи:
- Dynamic configuration через xDS API — конфигурация обновляется без перезапуска
- HTTP/gRPC load balancing — round-robin, least-request, ring-hash
- mTLS termination и origination — шифрование между сервисами
- Distributed tracing — интеграция с Zipkin, Jaeger, OpenTelemetry
- Prometheus metrics — встроенная экспозиция метрик
- Circuit breaking и retries — отказоустойчивость на уровне прокси
- Health checking — активные проверки upstream-сервисов
В режиме «без Istio» мы управляем Envoy через статические YAML-конфигурации или собственный xDS control plane. Для большинства команд статической конфигурации вполне достаточно.
Настройка Envoy sidecar вручную в Kubernetes
Рассмотрим пошаговый пример развёртывания Go-сервиса с Envoy sidecar в Kubernetes. Предположим, у нас есть сервис order-service, который общается с payment-service.
Шаг 1: ConfigMap с конфигурацией Envoy
apiVersion: v1
kind: ConfigMap
metadata:
name: envoy-config
namespace: production
data:
envoy.yaml: |
static_resources:
listeners:
- name: ingress_listener
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: local_app
clusters:
- name: local_app
connect_timeout: 0.25s
type: STATIC
load_assignment:
cluster_name: local_app
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8080
- name: payment_service
connect_timeout: 0.5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: payment_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: payment-service.production.svc.cluster.local
port_value: 10000
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901
Шаг 2: Deployment с Envoy sidecar
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
initContainers:
- name: iptables-init
image: busybox:1.36
securityContext:
capabilities:
add: ["NET_ADMIN"]
command:
- sh
- -c
- |
iptables -t nat -A OUTPUT -p tcp --dport 8080 -j RETURN
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-port 10000
containers:
- name: order-service
image: myregistry/order-service:1.4.2
ports:
- containerPort: 8080
env:
- name: PAYMENT_SERVICE_URL
value: "http://127.0.0.1:10001"
- name: envoy
image: envoyproxy/envoy:v1.29-latest
args: ["-c", "/etc/envoy/envoy.yaml", "--log-level", "info"]
ports:
- containerPort: 10000
name: proxy
- containerPort: 9901
name: admin
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
volumes:
- name: envoy-config
configMap:
name: envoy-config
Обратите внимание на initContainer с iptables: он перехватывает исходящий трафик и перенаправляет его через Envoy. Это стандартная техника, используемая и в Istio.
Интеграция с Go-сервисами: health checks, метрики, трейсинг
Health checks
Go-сервис должен предоставлять endpoint для health check. Минимальный пример:
package main
import (
"net/http"
"encoding/json"
)
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/health", healthHandler)
mux.HandleFunc("/ready", healthHandler)
http.ListenAndServe(":8080", mux)
}
В конфигурации Envoy добавьте активный health check для upstream-кластера:
clusters:
- name: payment_service
health_checks:
- timeout: 1s
interval: 10s
unhealthy_threshold: 3
healthy_threshold: 2
http_health_check:
path: "/health"
Метрики через Prometheus
Envoy автоматически экспортирует метрики на admin-порту (по умолчанию 9901) по пути /stats/prometheus. Добавьте ServiceMonitor для Prometheus Operator:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: envoy-sidecar-metrics
namespace: production
spec:
selector:
matchLabels:
app: order-service
endpoints:
- port: admin
path: /stats/prometheus
interval: 15s
Distributed Tracing с OpenTelemetry
Добавьте tracing-конфигурацию в Envoy для отправки span'ов в Jaeger:
tracing:
http:
name: envoy.tracers.zipkin
typed_config:
"@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
collector_cluster: jaeger
collector_endpoint: "/api/v2/spans"
shared_span_context: false
collector_endpoint_version: HTTP_JSON
В Go-сервисе используйте go.opentelemetry.io/otel для создания span'ов и пробрасывания контекста через HTTP-заголовки (traceparent, b3). Envoy автоматически подхватит и продолжит trace.
mTLS между сервисами без service mesh оператора
Настройка mTLS вручную — наиболее трудоёмкая часть, но вполне выполнимая. Нам нужны: корневой CA, сертификаты для каждого сервиса и настройка Envoy на обоих концах соединения.
Генерация сертификатов (cert-manager)
Используйте cert-manager для автоматической выдачи и ротации сертификатов:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: order-service-cert
namespace: production
spec:
secretName: order-service-tls
duration: 24h
renewBefore: 1h
subject:
organizations:
- mycompany
commonName: order-service.production.svc.cluster.local
dnsNames:
- order-service.production.svc.cluster.local
issuerRef:
name: internal-ca
kind: ClusterIssuer
Конфигурация mTLS в Envoy (upstream — origination)
clusters:
- name: payment_service
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain:
filename: /etc/certs/tls.crt
private_key:
filename: /etc/certs/tls.key
validation_context:
trusted_ca:
filename: /etc/certs/ca.crt
Конфигурация mTLS (downstream — termination)
filter_chains:
- transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
tls_certificates:
- certificate_chain:
filename: /etc/certs/tls.crt
private_key:
filename: /etc/certs/tls.key
validation_context:
trusted_ca:
filename: /etc/certs/ca.crt
Секрет order-service-tls монтируется в pod через volumeMounts. При ротации cert-manager обновит секрет, а Envoy можно настроить на динамическую перезагрузку через SDS (Secret Discovery Service).
Сравнение с Linkerd и Cilium
Linkerd
Linkerd — значительно более лёгкий service mesh, чем Istio. Его data plane написан на Rust (linkerd2-proxy), потребляет меньше памяти и CPU. Установка сводится к нескольким командам CLI. Из минусов: менее гибкая конфигурация (нет такого контроля над маршрутизацией, как в Envoy), ограниченная поддержка протоколов (в основном HTTP/1, HTTP/2, gRPC). Для команд, которым нужен быстрый старт без глубокой кастомизации — отличный выбор в 2026.
Cilium
Cilium работает на уровне eBPF в ядре Linux, что даёт минимальный overhead. Он совмещает функции CNI (сетевой плагин Kubernetes) и service mesh, исключая необходимость sidecar-контейнеров вообще. Cilium Service Mesh поддерживает mTLS, L7 observability и трафик-политики. Идеален для команд, которые хотят минимальные задержки и контроль на уровне сети. Сложнее в отладке из-за работы в ядре.
Ручной Envoy sidecar (наш подход)
Максимальная гибкость и контроль. Нет зависимости от сторонних операторов. Сложнее поддерживать при росте числа сервисов — конфигурации нужно версионировать и шаблонизировать (Helm, Kustomize). Оптимален для команд от 2 до 20 сервисов или при особых требованиях к маршрутизации.
Рекомендации по мониторингу и observability
Без централизованного control plane observability особенно важна. Рекомендуемый стек:
- Prometheus + Grafana — сбор метрик с Envoy admin endpoint (
/stats/prometheus). Используйте готовые дашборды Envoy для Grafana (ID: 6693). - Jaeger или Grafana Tempo — сбор distributed traces. Envoy генерирует span'ы автоматически при настроенном tracing.
- Loki — сбор access log'ов Envoy в структурированном JSON-формате. Настройте access log format явно:
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
timestamp: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
response_code: "%RESPONSE_CODE%"
duration: "%DURATION%"
upstream_host: "%UPSTREAM_HOST%"
trace_id: "%REQ(X-B3-TRACEID)%"
- Alerting: настройте алерты на
envoy_cluster_upstream_rq_5xx,envoy_cluster_upstream_cx_connect_failи P99 latency черезenvoy_cluster_upstream_rq_time_bucket.
Итоги: когда использовать полноценный service mesh
Подход «Envoy sidecar вручную» оправдан, когда:
- У вас от 3 до 20 сервисов и небольшая DevOps-команда
- Нужен полный контроль над конфигурацией без «магии» оператора
- Istio избыточен по ресурсам и сложности
- Есть специфические требования к маршрутизации или протоколам
Переходите на полноценный service mesh (Istio, Linkerd, Cilium), когда:
- Количество сервисов превышает 30–50 и поддержка ручных конфигураций становится накладной
- Нужна автоматическая ротация сертификатов и централизованное управление политиками
- Требуется canary-деплой, traffic shifting на уровне mesh
- Команда готова инвестировать время в изучение и эксплуатацию control plane
Построение service mesh без Istio на Go с Envoy и Kubernetes — это прагматичный выбор для зрелых команд, которые ценят прозрачность и контроль над своей инфраструктурой. Главное — не забывать про версионирование конфигураций, автоматизацию через Helm/Kustomize и своевременный мониторинг состояния прокси.
Технологии
Теги
Руслан Исмаилов
Senior Web / Backend разработчик. Senior web/backend разработчик с 9-летним опытом. Стек: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, микросервисы, CI/CD. Подробнее обо мне →