Lanzamiento Canary Suave Usando APISIX Ingress Controller con Flagger

Hengliang Tan

January 19, 2023

Ecosystem

Autor: Hengliang Tan, Gerente de Ingeniería en XPENG

En el proceso de desarrollo de proyectos, las actualizaciones de servicios suelen ser un desafío. Para ofrecer la mejor experiencia de usuario, necesitamos evitar en la medida de lo posible el riesgo de indisponibilidad del servicio. Así nació la entrega continua, aceptada como una práctica empresarial de software y una evolución natural de los principios bien establecidos de integración continua. Sin embargo, el despliegue continuo sigue siendo muy raro debido a la complejidad de la gestión y al temor de que los fallos en el despliegue afecten la disponibilidad del sistema. La liberación Canary es probablemente el escenario más clásico en el sistema de entrega continua. Basándonos en esto, podemos descubrir rápidamente servicios no saludables y problemáticos y revertir a la versión anterior sin esfuerzo.

Liberación Canary

La liberación Canary también se conoce como liberación en escala de grises. En términos generales, la nueva versión de la aplicación se lanza y despliega como un "canario" para probar su rendimiento. La versión anterior permanece para operaciones normales en la misma etapa. Durante la actualización, algunos usuarios serán dirigidos a usar la nueva versión, mientras que otros continuarán usando la versión anterior. Bajo la premisa de garantizar la estabilidad general del sistema, permite la detección temprana de errores y ajustes oportunos.

La liberación Canary no lanza directamente la actualización. Dirige lentamente un cierto porcentaje de tráfico a un pequeño número de usuarios. Si no se detectan errores, se promocionará a todos los usuarios y la versión anterior se eliminará gradualmente. Este método reduce el riesgo de introducir nuevas funciones en el entorno de producción.

Este artículo presentará cómo lograr una liberación Canary fluida a través de Apache APISIX Ingress y Flagger, mejorar la eficiencia de la liberación y reducir los riesgos de la misma.

Sobre Apache APISIX Ingress

Apache APISIX Ingress se realiza mediante el Kubernetes Ingress Controller que utiliza Apache APISIX como el proxy del plano de datos. Ofrece cientos de funciones, como balanceo de carga, upstream dinámico, liberación Canary, enrutamiento granular, limitación de tasa, degradación de servicio, corte de servicio, autenticación y observabilidad. Ha sido adoptado por empresas y organizaciones nacionales e internacionales, incluyendo Zoom, Tencent Cloud, Jiakaobaodian, Horizon Robotics, European Copernicus Reference System, entre otros.

Sobre Flagger

Flagger es un proyecto de CNCF (Cloud Native Computing Foundation) y parte de la familia de herramientas GitOps de Flux. Recientemente, la CNCF también anunció la graduación oficial de Flux, lo cual es un buen indicador del éxito y el futuro prometedor de la tecnología cloud-native. Como herramienta de entrega progresiva, Flagger automatiza el proceso de liberación para aplicaciones que se ejecutan en Kubernetes. Reduce el riesgo de introducir una nueva versión de software en producción al trasladar gradualmente el tráfico a la nueva versión mientras mide métricas analíticas y ejecuta pruebas de conformidad.

Después de los esfuerzos continuos de las comunidades de Apache APISIX y Flux, Flagger lanzó recientemente la versión v1.27.0, que soporta liberaciones Canary automatizadas utilizando Apache APISIX Ingress y Flagger.

featured-<Flagger y Apache APISIX Ingress>.jpg

Experimentemos juntos este proceso fluido de liberación Canary.

Entorno

Se requiere un clúster de Kubernetes v1.19 o más reciente, que puedes instalar a través de kind.

Instalación de Componentes

Usa Helm V3 para instalar Apache APISIX y Apache APISIX Ingress Controller

helm repo add apisix https://charts.apiseven.com
kubectl create ns apisix


helm upgrade -i apisix apisix/apisix --version=0.11.3 \
--namespace apisix \
--set apisix.podAnnotations."prometheus\.io/scrape"=true \
--set apisix.podAnnotations."prometheus\.io/port"=9091 \
--set apisix.podAnnotations."prometheus\.io/path"=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.export_addr.ip=0.0.0.0 \
--set pluginAttrs.prometheus.export_addr.port=9091 \
--set pluginAttrs.prometheus.export_uri=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.metric_prefix=apisix_ \
--set ingress-controller.enabled=true \
--set ingress-controller.config.apisix.serviceNamespace=apisix

Instala los componentes de Flagger y Prometheus en el namespace apisix.

helm repo add flagger https://flagger.app


helm upgrade -i flagger flagger/flagger \
--namespace apisix \
--set prometheus.install=true \
--set meshProvider=apisix

Nota: si necesitas personalizar Prometheus o Prometheus Operator, puedes buscar artículos relacionados para modificarlo.

Inicialización de la Aplicación

Flagger puede aplicarse a despliegues de Kubernetes y otras cargas de trabajo, y también puede combinarse con HPA. Creará una serie de objetos: despliegues de Kubernetes, servicios ClusterIP y ApisixRoute. Estos objetos pueden exponer aplicaciones a clústeres externos para proporcionar servicios y se utilizan para el análisis del proceso de liberación Canary.

Crea un nuevo namespace de prueba:

kubectl create ns test

Crea un nuevo despliegue y HPA. Aquí extraemos el código de muestra oficial de Flagger.

kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main

Despliega el servicio de prueba de carga de Flagger para generar tráfico durante la liberación Canary para su análisis.

helm upgrade -i flagger-loadtester flagger/loadtester \ --namespace=test

Crea el ApisixRoute de Apache APISIX, luego Flagger hará referencia al recurso creado y generará el ApisixRoute de Apache APISIX Ingress en la versión Canary. (Reemplaza app.example.com en el siguiente ejemplo con tu nombre de dominio real)

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: podinfo
  namespace: test
spec:
  http:
    - backends:
        - serviceName: podinfo
          servicePort: 80
      match:
        hosts:
          - app.example.com
        methods:
          - GET
        paths:
          - /*
      name: method
      plugins:
        - name: prometheus
          enable: true
          config:
            disable: false
            prefer_name: true

Guárdalo como podinfo-apisixroute.yaml y envíalo al clúster:

kubectl apply -f ./podinfo-apisixroute.yaml

Crea un recurso personalizado Canary de Flagger. (Reemplaza app.example.com en el ejemplo con tu nombre de dominio real)

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: apisix
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # Referencia a la ruta de apisix
  routeRef:
    apiVersion: apisix.apache.org/v2
    kind: ApisixRoute
    name: podinfo
  progressDeadlineSeconds: 60
  service:
    port: 80
    targetPort: 9898
  analysis:
    interval: 10s
    # número máximo de fallos para revertir
    threshold: 10
    # porcentaje máximo de tráfico a la versión Canary
    # (0-100)
    maxWeight: 50
    # tamaño del paso del análisis Canary
    # (0-100)
    stepWeight: 10
    # usa Prometheus para verificar la información de tráfico de APISIX
    metrics:
      - name: request-success-rate
        # tasa mínima de éxito (respuestas no 5xx)
        # (0-100)
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        # P99 es el mayor retraso de solicitud(ms)
        thresholdRange:
          max: 500
        interval: 30s
    webhooks:
        # tráfico automatizado para el análisis Canary, modificado según el escenario real
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        type: rollout
        metadata:
          cmd: |-
            hey -z 1m -q 10 -c 2 -h2 -host app.example.com http://apisix-gateway.apisix/api/info

Guárdalo como podinfo-canary.yaml y envíalo al clúster:

kubectl apply -f ./podinfo-canary.yaml

Flagger generará automáticamente los recursos relacionados:

# Enviado deployment.apps/podinfo horizontalpodautoscaler.autoscaling/podinfo apisixroute/podinfo canary.flagger.app/podinfo

# Generado automáticamente deployment.apps/podinfo-primary horizontalpodautoscaler.autoscaling/podinfo-primary service/podinfo service/podinfo-canary service/podinfo-primary apisixroute/podinfo-podinfo-canary

featured-<versión 1>.jpg

En este punto, puedes acceder a la aplicación a través del nombre de dominio app.example.com (Reemplaza app.example.com en el ejemplo con tu nombre de dominio real), y verás la versión actual de la aplicación.

Automatización de la Liberación Canary

Flagger implementa un bucle de control que traslada gradualmente el tráfico a los nodos Canary mientras mide métricas clave de rendimiento como la tasa de éxito de solicitudes HTTP, la duración promedio de las solicitudes y la salud de los pods. Según el análisis de las métricas relevantes, libera o detiene el despliegue Canary y publica los resultados del análisis en plataformas relevantes como Slack, MS Teams o Prometheus Alert Manager, entre otros.

Bucle de Control de Flagger

Desencadena una liberación Canary actualizando la versión de la imagen del contenedor

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.1

Flagger detecta que hay una nueva versión del despliegue y comenzará una ejecución de prueba del análisis de liberación Canary.

kubectl -n test describe canary/podinfo

Estado:
  Peso Canary:  0
  Condiciones:
    Mensaje:               Análisis Canary completado con éxito, promoción finalizada.
    Razón:                 Exitoso
    Estado:                Verdadero
    Tipo:                  Promovido
  Chequeos Fallidos:       1
  Iteraciones:             0
  Fase:                   Exitoso

Eventos:
  Tipo     Razón  Edad                    Desde     Mensaje
  ----     ------  ----                   ----     -------
  Advertencia  Sincronizado  2m59s                  flagger  podinfo-primary.test no está listo: esperando que finalice el despliegue: la generación observada del despliegue es menor que la generación deseada
  Advertencia  Sincronizado  2m50s                  flagger  podinfo-primary.test no está listo: esperando que finalice el despliegue: 0 de 1 (umbral de listo 100%) réplicas actualizadas están disponibles
  Normal   Sincronizado  2m40s (x3 sobre 2m59s)  flagger  ¡todos los proveedores de métricas están disponibles!
  Normal   Sincronizado  2m39s                  flagger  ¡Inicialización completada! podinfo.test
  Normal   Sincronizado  2m20s                  flagger  ¡Nueva revisión detectada! Escalando podinfo.test
  Advertencia  Sincronizado  2m (x2 sobre 2m10s)     flagger  despliegue Canary podinfo.test no está listo: esperando que finalice el despliegue: 0 de 1 (umbral de listo 100%) réplicas actualizadas están disponibles
  Normal   Sincronizado  110s                   flagger  Iniciando análisis Canary para podinfo.test
  Normal   Sincronizado  109s                   flagger  Avanzando peso Canary de podinfo.test 10
  Advertencia  Sincronizado  100s                   flagger  Deteniendo avance, no se encontraron valores para la métrica de apisix request-success-rate probablemente podinfo.test no está recibiendo tráfico: ejecución de consulta fallida: no se encontraron valores
  Normal   Sincronizado  90s                    flagger  Avanzando peso Canary de podinfo.test 20
  Normal   Sincronizado  80s                    flagger  Avanzando peso Canary de podinfo.test 30
  Normal   Sincronizado  69s                    flagger  Avanzando peso Canary de podinfo.test 40
  Normal   Sincronizado  59s                    flagger  Avanzando peso Canary de podinfo.test 50
  Advertencia  Sincronizado  30s (x2 sobre 40s)      flagger  podinfo-primary.test no está listo: esperando que finalice el despliegue: 1 réplica antigua está pendiente de terminación
  Normal   Sincronizado  9s (x3 sobre 50s)       flagger  (combinado de eventos similares): ¡Promoción completada! Escalando hacia abajo podinfo.test

Durante el proceso de liberación Canary, recibirás diferentes respuestas cuando accedas a la aplicación a través del nombre de dominio app.example.com (Reemplaza app.example.com con tu nombre de dominio real).

featured-<versión 2>.jpg

Al ver el recurso ApisixRoute podinfo-podinfo-canary de Apache APISIX creado automáticamente por Flagger, encontrarás que los pesos del servicio podinfo-primary y el servicio podinfo-canary cambian junto con el proceso de publicación.

spec:
  http:
    - backends:
        - serviceName: podinfo-primary
          servicePort: 80
          # Ajustado automáticamente por Flagger
          weight: 80
        - serviceName: podinfo-canary
          servicePort: 80
          # Ajustado automáticamente por Flagger
          weight: 20

Verás la última versión estable cuando la liberación final esté completa.

featured-<versión 3>.jpg

Nota: Flagger volverá a ejecutar el análisis Canary si cambias el despliegue nuevamente durante la liberación Canary.

Puedes observar todas las liberaciones Canary con este comando:

watch kubectl get canaries --all-namespaces

NAMESPACE   NOMBRE      ESTADO      PESO   ÚLTIMATRANSICIÓNTIEMPO
test        podinfo-2   En Progreso   10       2022-11-23T05:00:54Z
test        podinfo     Exitoso     0        2022-11-23T06:00:54Z

Reversión

Durante el análisis de la liberación Canary, puedes probar Flagger para suspender la liberación Canary y revertir a la versión anterior generando un HTTP 500 Bad Request.

Desencadena otra liberación Canary:

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.2

Ingresa al contenedor de prueba de carga

kubectl -n test exec -it deploy/flagger-loadtester bash

Genera un error HTTP 500:

hey -z 1m -c 5 -q 5 -host app.example.com http://apisix-gateway.apisix/status/500

Simula un retraso del servidor:

watch -n 1 curl -H \"host: app.example.com\" http://apisix-gateway.apisix/delay/1

Cuando el número de fallos detectados alcanza el umbral del análisis Canary, el tráfico se redirige automáticamente al nodo maestro, el nodo Canary se reduce a cero y el proceso de liberación Canary se marca como fallido.

kubectl -n apisix logs deploy/flagger -f | jq .msg

"¡Nueva revisión detectada! Escalando podinfo.test"
"despliegue Canary podinfo.test no está listo: esperando que finalice el despliegue: 0 de 1 (umbral de listo 100%) réplicas actualizadas están disponibles"
"Iniciando análisis Canary para podinfo.test"
"Avanzando peso Canary de podinfo.test 10"
"Deteniendo avance de podinfo.test tasa de éxito 0.00% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 26.76% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 34.19% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 37.32% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 39.04% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 40.13% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 48.28% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 50.35% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 56.92% < 99%"
"Deteniendo avance de podinfo.test tasa de éxito 67.70% < 99%"
"¡Reversión de podinfo.test umbral de chequeos fallidos alcanzado 10"
"¡Canary fallido! Escalando hacia abajo podinfo.test"

Personalización de Métricas para el Análisis Canary

El análisis Canary puede extenderse consultando métricas de Prometheus. Personalizamos según los escenarios comerciales reales. Crea una plantilla de métrica y envíala al clúster.

apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: test
spec:
  provider:
    type: prometheus
    address: http://flagger-prometheus.apisix:9090
  query: |
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
          code!~"4.."
        }[{{ interval }}]
      )
    )
    /
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
        }[{{ interval }}]
      )
    ) * 100
# Modifica el análisis en la liberación Canary y agrega la plantilla de indicador creada anteriormente.
  analysis:
    metrics:
      - name: "404s percentage"
        templateRef:
          name: not-found-percentage
        thresholdRange:
          max: 5
        interval: 1m

La configuración anterior valida el Canary verificando si el QPS (Consultas por segundo) de solicitudes HTTP 404 es mayor al 5% del tráfico total. La liberación Canary falla si las solicitudes HTTP 404 superan el umbral del 5%.

Resumen

El proceso anterior puede extenderse con más verificaciones de métricas personalizadas, Webhooks, aprobaciones manuales y notificaciones en Slack o MS Teams.

Se logra una liberación Canary muy fluida a través de la integración de Apache APISIX y Flagger, lo que mejora la eficiencia de la liberación y reduce los riesgos de la misma. En el futuro, las dos comunidades cooperarán más estrechamente para realizar más capacidades de publicación como Blue/Green Mirroring y A/B Testing.

Tags: