Cómo Dailymotion utiliza Kubernetes: implementación de aplicaciones
En Dailymotion comenzamos a utilizar Kubernetes en producción hace 3 años. Pero implementar aplicaciones en múltiples clústeres es divertido, por lo que durante los últimos años hemos intentado mejorar nuestras herramientas y flujos de trabajo.
donde empezó
Aquí cubriremos cómo implementamos nuestras aplicaciones en múltiples clústeres de Kubernetes en todo el mundo.
Para implementar múltiples objetos de Kubernetes a la vez, usamos y todos nuestros gráficos se almacenan en un repositorio de git. Para implementar una pila de aplicaciones completa de varios servicios, utilizamos el llamado cuadro de resumen. Básicamente, este es un gráfico que declara dependencias y le permite inicializar la API y sus servicios con un solo comando.
También escribimos un pequeño script de Python sobre Helm para realizar comprobaciones, crear gráficos, agregar secretos e implementar aplicaciones. Todas estas tareas se realizan en una plataforma de CI central utilizando una imagen acoplable.
Vayamos al grano.
Nota. Mientras lees esto, ya se ha anunciado el primer lanzamiento candidato para Helm 3. La versión principal contiene una gran cantidad de mejoras para abordar algunos de los problemas que hemos encontrado en el pasado.
Flujo de trabajo de desarrollo de gráficos
Usamos ramificaciones para aplicaciones y decidimos aplicar el mismo enfoque a los gráficos.
- Rama dev Se utiliza para crear gráficos que se probarán en grupos de desarrollo.
- Cuando se envía una solicitud de extracción a dominar, se controlan en puesta en escena.
- Finalmente, creamos una solicitud de extracción para confirmar los cambios en la rama. pinchar y aplicarlos en producción.
Cada entorno tiene su propio repositorio privado que almacena nuestros gráficos, y usamos con API muy útiles. De esta manera garantizamos un aislamiento estricto entre entornos y pruebas de gráficos en el mundo real antes de usarlos en producción.
Repositorios de gráficos en diferentes entornos
Vale la pena señalar que cuando los desarrolladores envían una rama de desarrollo, una versión de su gráfico se envía automáticamente al dev Chartmuseum. Por lo tanto, todos los desarrolladores utilizan el mismo repositorio de desarrollo y usted debe especificar cuidadosamente su versión del gráfico para no utilizar accidentalmente los cambios de otra persona.
Además, nuestro pequeño script de Python valida los objetos de Kubernetes con las especificaciones de Kubernetes OpenAPI utilizando , antes de publicarlos en Chartmusem.
Descripción general del flujo de trabajo de desarrollo de gráficos.
- Configuración de tareas de canalización según las especificaciones para control de calidad (pelusa, prueba unitaria).
- Enviar una imagen de Docker con herramientas de Python que implementan nuestras aplicaciones.
- Configurar el entorno por nombre de sucursal.
- Validación de archivos yaml de Kubernetes usando Kubeval.
- Aumentar automáticamente la versión de un gráfico y sus gráficos principales (gráficos que dependen del gráfico que se modifica).
- Enviar un gráfico a un Chartmuseum que coincida con su entorno
Gestionar las diferencias entre clústeres
Federación de Clústeres
Hubo un tiempo en que usábamos , donde los objetos de Kubernetes podrían declararse desde un único punto final API. Pero surgieron problemas. Por ejemplo, algunos objetos de Kubernetes no se pudieron crear en el punto final de la federación, lo que dificulta el mantenimiento de objetos federados y otros objetos para clústeres individuales.
Para solucionar el problema, comenzamos a gestionar los clústeres de forma independiente, lo que simplificó enormemente el proceso (utilizamos la primera versión de federación; algo podría haber cambiado en la segunda).
Plataforma geodistribuida
Actualmente, nuestra plataforma está distribuida en 6 regiones: 3 localmente y 3 en la nube.
Implementación distribuida
Valores globales del timón
4 valores globales de Helm le permiten identificar diferencias entre grupos. Todos nuestros gráficos tienen valores mínimos predeterminados.
global:
cloud: True
env: staging
region: us-central1
clusterName: staging-us-central1Valores globales
Estos valores ayudan a definir el contexto de nuestras aplicaciones y se utilizan para diversos fines: monitorear, rastrear, registrar, realizar llamadas externas, escalar, etc.
- "nube": Tenemos una plataforma híbrida de Kubernetes. Por ejemplo, nuestra API se implementa en zonas de GCP y en nuestros centros de datos.
- "env": algunos valores pueden cambiar para entornos que no son de producción. Por ejemplo, definiciones de recursos y configuraciones de escalado automático.
- "región": esta información ayuda a determinar la ubicación del clúster y se puede utilizar para determinar puntos finales cercanos para servicios externos.
- "clusterName": si queremos definir un valor para un clúster individual.
Aquí hay un ejemplo específico:
{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}Ejemplo de plantilla de timón
Esta lógica se define en una plantilla auxiliar para evitar saturar Kubernetes YAML.
Anuncio de solicitud
Nuestras herramientas de implementación se basan en múltiples archivos YAML. A continuación se muestra un ejemplo de cómo declaramos un servicio y su topología de escalamiento (número de réplicas) en un clúster.
releases:
- foo.world
foo.world: # Release name
services: # List of dailymotion's apps/projects
foobar:
chart_name: foo-foobar
repo: git@github.com:dailymotion/foobar
contexts:
prod-europe-west1:
deployments:
- name: foo-bar-baz
replicas: 18
- name: another-deployment
replicas: 3Definición de servicio
Este es un resumen de todos los pasos que definen nuestro flujo de trabajo de implementación. El último paso implementa la aplicación en varios clústeres de trabajadores simultáneamente.
Pasos de implementación de Jenkins
¿Qué pasa con los secretos?
En cuanto a seguridad, rastreamos todos los secretos de diferentes lugares y los almacenamos en una bóveda única. en París.
Nuestras herramientas de implementación extraen valores secretos de Vault y, cuando llega el momento de la implementación, los insertan en Helm.
Para hacer esto, definimos un mapeo entre los secretos en Vault y los secretos que nuestras aplicaciones necesitan:
secrets:
- secret_id: "stack1-app1-password"
contexts:
- name: "default"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"
- name: "cluster1"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"- Hemos definido reglas generales a seguir al registrar secretos en Vault.
- Si el secreto se aplica a un contexto o grupo específico, debe agregar una entrada específica. (Aquí el contexto cluster1 tiene su propio valor para la contraseña secreta de la aplicación 1 de la pila).
- De lo contrario se utiliza el valor por defecto.
- Para cada elemento de esta lista en secreto de kubernetes Se inserta un par clave-valor. Por tanto, la plantilla secreta de nuestros gráficos es muy sencilla.
apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
{{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
name: "{{ .Chart.Name }}"
labels:
chartVersion: "{{ .Chart.Version }}"
tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: OpaqueDesafíos y limitaciones
Trabajar con múltiples repositorios
Ahora separamos el desarrollo de gráficos y aplicaciones. Esto significa que los desarrolladores tienen que trabajar en dos repositorios de git: uno para la aplicación y otro para definir su implementación en Kubernetes. 2 repositorios de git significan 2 flujos de trabajo y es fácil que un novato se confunda.
Administrar gráficos generalizados es una molestia
Como ya dijimos, los gráficos genéricos son muy útiles para identificar dependencias e implementar rápidamente múltiples aplicaciones. pero usamos --reuse-valuespara evitar pasar todos los valores cada vez que implementamos una aplicación que forma parte de este cuadro generalizado.
En un flujo de trabajo de entrega continua, solo tenemos dos valores que cambian periódicamente: el número de réplicas y la etiqueta de la imagen (versión). Otros valores más estables se cambian manualmente, lo cual es bastante complicado. Además, un error al desplegar un gráfico generalizado puede provocar fallos graves, como hemos visto por propia experiencia.
Actualización de múltiples archivos de configuración
Cuando un desarrollador agrega una nueva aplicación, tiene que cambiar varios archivos: la declaración de la aplicación, la lista de secretos, agregar la aplicación como dependencia si está incluida en el cuadro generalizado.
Los permisos de Jenkins están demasiado extendidos en Vault
Ahora tenemos uno , que lee todos los secretos de la Bóveda.
El proceso de reversión no está automatizado.
Para revertir, debe ejecutar el comando en varios clústeres, y esto está plagado de errores. Realizamos esta operación manualmente para asegurarnos de que se especifique el ID de versión correcto.
Avanzamos hacia GitOps
Nuestro objetivo
Queremos devolver el gráfico al repositorio de la aplicación que implementa.
El flujo de trabajo será el mismo que para el desarrollo. Por ejemplo, cuando una rama se envía a master, la implementación se activará automáticamente. La principal diferencia entre este enfoque y el flujo de trabajo actual sería que todo se gestionará en git (la aplicación en sí y la forma en que se implementa en Kubernetes).
Hay varias ventajas:
- Mucho más claro para el desarrollador. Es más fácil aprender a aplicar cambios en un gráfico local.
- La definición de implementación del servicio se puede especificar. mismo lugar que el código servicio
- Gestionar la eliminación de gráficos generalizados. El servicio tendrá su propia versión de Helm. Esto le permitirá administrar el ciclo de vida de la aplicación (revertir, actualizar) en el nivel más pequeño, para no afectar otros servicios.
- Beneficios de git para gestión de gráficos: deshacer cambios, auditar registro, etc. Si necesita deshacer un cambio en un gráfico, puede hacerlo usando git. La implementación comienza automáticamente.
- Podría considerar mejorar su flujo de trabajo de desarrollo con herramientas como patín, con el que los desarrolladores pueden probar cambios en un contexto cercano al de producción.
Migración en dos pasos
Nuestros desarrolladores han estado utilizando este flujo de trabajo durante 2 años, por lo que queremos que la migración sea lo más sencilla posible. Por eso, decidimos añadir un paso intermedio en el camino hacia la meta.
La primera etapa es simple:
- Mantenemos una estructura similar para configurar la implementación de aplicaciones, pero en un único objeto llamado DailymotionRelease.
apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
name: "app1.ns1"
environment: "dev"
branch: "mybranch"
spec:
slack_channel: "#admin"
chart_name: "app1"
scaling:
- context: "dev-us-central1-0"
replicas:
- name: "hermes"
count: 2
- context: "dev-europe-west1-0"
replicas:
- name: "app1-deploy"
count: 2
secrets:
- secret_id: "app1"
contexts:
- name: "default"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"
- name: "dev-europe-west1-0"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"- 1 versión por aplicación (sin cuadros generalizados).
- Gráficos en el repositorio git de la aplicación.
Hemos hablado con todos los desarrolladores, por lo que el proceso de migración ya ha comenzado. La primera etapa todavía se controla mediante la plataforma CI. Pronto escribiré otra publicación sobre la fase dos: cómo pasamos a un flujo de trabajo GitOps con . Te contaré cómo configuramos todo y qué dificultades encontramos (múltiples repositorios, secretos, etc.). Sigue las noticias.
Aquí hemos intentado describir nuestro progreso en el flujo de trabajo de implementación de aplicaciones durante los últimos años, lo que nos llevó a reflexionar sobre el enfoque GitOps. Aún no hemos alcanzado la meta e informaremos sobre los resultados, pero ahora estamos convencidos de que hicimos lo correcto cuando decidimos simplificar todo y acercarlo a los hábitos de los desarrolladores.
Fuente: habr.com
