APISIX как ingress в Kubernetes: глобальные правила, TLS и OpenTelemetry

Published: 2026-02-22

APISIX заменил Traefik во всех шести кластерах. Он отвечает за маршрутизацию ingress, терминацию TLS, access-логи в Elasticsearch, распределённую трассировку через OpenTelemetry и gzip-сжатие — всё через декларативные CRD.


Два ingress class

apisix-int работает в каждом окружении. Это внутренний ingress с ingressClass: apisix: через него внутренние сервисы (Grafana, Kibana, дашборды приложений) публикуются на приватных IP.

apisix-ext есть только в кластере demo. У него отдельный LoadBalancer IP, и он обслуживает внешние домены (*.test.antonnovikov.com, *.demo.test.antonnovikov.com).

Оба разворачиваются как HelmRelease через FluxCD. Базовый HelmRelease живёт в fluxcd/base/apisix-int.yaml; патчи для окружений переопределяют только LoadBalancer IP и адрес otel-collector.

Базовый HelmRelease

yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: apisix-int
  namespace: ingress-apisix
spec:
  chart:
    spec:
      chart: apisix
      version: "2.8.*"
      sourceRef:
        kind: HelmRepository
        name: apisix
        namespace: flux-system
  retries: -1
  values:
    apisix:
      enabled: true
      kind: DaemonSet
    gateway:
      type: LoadBalancer
    ingress-controller:
      enabled: true
    etcd:
      enabled: true
      replicaCount: 1

kind: DaemonSet — один под APISIX на узел, распределение нагрузки без HPA.


ApisixGlobalRule: плагины для всего кластера

Вместо того чтобы вешать плагины на каждый маршрут отдельно, ApisixGlobalRule применяет их ко всему трафику.

Логирование в Elasticsearch

yamlapiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
  name: elasticsearch-logger
  namespace: ingress-apisix
spec:
  plugins:
    - name: elasticsearch-logger
      enable: true
      config:
        endpoint_addr: "http://infra-master.elasticsearch.svc.cluster.local:9200"
        field:
          index: "apisix"
        pipeline: "apisix"
        batch_max_size: 1000
        buffer_duration: 60

Запросы со всех маршрутов попадают в индекс apisix в Elasticsearch. pipeline: "apisix" запускает ingest pipeline в ES, который разбирает сырой JSON на структурированные поля. Без ingest pipeline логи лягут неструктурированными JSON-строками — отфильтровать их в Kibana не получится.

Трассировка OpenTelemetry

yamlapiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
  name: opentelemetry
  namespace: ingress-apisix
spec:
  plugins:
    - name: opentelemetry
      enable: true
      config:
        sampler:
          name: always_on
        collector:
          address: "otel-collector.elasticsearch.svc.cluster.local:4318"
        additional_attributes:
          - apisix_service_id
          - apisix_balancer_ip
          - apisix_consumer
          - remote_addr
          - upstream_addr
          - upstream_status
          - upstream_response_time

Сэмплирование — always_on. APISIX отправляет spans по OTLP/HTTP в otel-collector на порт 4318, а коллектор проксирует их в APM Server (тот принимает только gRPC).

additional_attributes добавляет в каждый span контекст APISIX: какой upstream получил запрос, код HTTP-ответа, время ответа upstream.

Указывайте полный FQDN в адресе otel-collector: короткие DNS-имена иногда не резолвятся в Lua-рантайме APISIX.

Gzip, real-ip и request-id

yaml- name: real-ip
  enable: true
  config:
    source: http_x_forwarded_for
    recursive: true

- name: gzip
  enable: true
  config:
    min_length: 1024
    types:
      - text/html
      - application/json
      - text/plain

- name: request-id
  enable: true
  config:
    header_name: X-Request-ID
    include_in_response: true

real-ip с recursive: true корректно обрабатывает двойной NAT с несколькими балансировщиками.


TLS через ApisixTls

yamlapiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
  name: dev-antonnovikov-com
  namespace: ingress-apisix
spec:
  hosts:
    - "*.dev.test.antonnovikov.com"
  secret:
    name: dev-antonnovikov-com-tls
    namespace: ingress-apisix

TLS-секрет содержит tls.crt и tls.key, запечатанные через SealedSecrets. Ротация сертификата: обновите SealedSecret, закоммитьте, запушите — APISIX подхватит изменение без перезапуска.


Пример ApisixRoute

yamlapiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: grafana
  namespace: ingress-apisix
spec:
  http:
    - name: grafana-rule
      match:
        hosts:
          - grafana.dev.test.antonnovikov.com
        paths:
          - "/*"
      backends:
        - serviceName: kube-prom-stack-grafana
          servicePort: 80
          namespace: observability

Маршруты могут ссылаться на сервисы в других namespace — не нужно дублировать маршрут в каждом namespace.


Cilium L2 для LoadBalancer IP (on-prem)

На bare-metal-кластерах сервисы типа LoadBalancer получают IP из пула Cilium L2:

yamlvalues:
  gateway:
    type: LoadBalancer
    annotations:
      io.cilium/lb-ipam-ips: "172.16.57.200"

Патчинг APISIX для конкретного окружения

yaml# projects/dev/kustomization/hub/patches/apisix-int.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: apisix-int
  namespace: dev
spec:
  values:
    apisix:
      externalIPs:
        - 172.16.57.200
    plugins:
      - name: opentelemetry
        conf:
          collector:
            address: "otel-collector.elasticsearch.svc.cluster.local:4318"

Suspend/resume при ручном вмешательстве

bashflux suspend helmrelease apisix-int -n dev --context=infra-k8s
flux resume helmrelease apisix-int -n dev --context=infra-k8s

На кластерах Yandex Cloud APISIX иногда застревает в цикле upgrade→rollback из-за порядка обновления DaemonSet. Suspend, исправление, resume — надёжный способ восстановиться.

Проверка состояния плагинов APISIX

bashkubectl exec -n ingress-apisix deploy/apisix -- \
  curl -s http://localhost:9080/apisix/admin/plugins/list

kubectl exec -n ingress-apisix deploy/apisix -- \
  curl -s http://localhost:9080/apisix/admin/global_rules