Implementa aplicacións en varios clústeres de Kubernetes con Helm

Como usa Dailymotion Kubernetes: Implementación de aplicacións

En Dailymotion comezamos a usar Kubernetes na produción hai 3 anos. Pero despregar aplicacións en varios clústeres é divertido, polo que nos últimos anos estivemos tentando mellorar as nosas ferramentas e fluxos de traballo.

Onde comezou

Aquí explicaremos como implementamos as nosas aplicacións en varios clústeres de Kubernetes en todo o mundo.

Para implementar varios obxectos de Kubernetes á vez, usamos Leme, e todos os nosos gráficos almacénanse nun repositorio git. Para implementar unha pila de aplicacións completa desde varios servizos, utilizamos o chamado gráfico de resumo. Esencialmente, este é un gráfico que declara dependencias e permite inicializar a API e os seus servizos cun só comando.

Tamén escribimos un pequeno script de Python encima de Helm para facer comprobacións, crear gráficos, engadir segredos e implementar aplicacións. Todas estas tarefas realízanse nunha plataforma de CI central mediante unha imaxe docker.

Imos ao grano.

Nota. Mentres ledes isto, xa se anunciou o primeiro candidato para Helm 3. A versión principal contén unha serie de melloras para resolver algúns dos problemas que atopamos no pasado.

Fluxo de traballo de desenvolvemento de gráficos

Usamos ramificación para aplicacións e decidimos aplicar o mesmo enfoque aos gráficos.

  • Rama dev usado para crear gráficos que se probarán en clústeres de desenvolvemento.
  • Cando se envía unha solicitude de extracción a mestre, compróbanse na posta en escena.
  • Finalmente, creamos unha solicitude de extracción para confirmar os cambios na rama produción e aplicalos na produción.

Cada entorno ten o seu propio repositorio privado que almacena os nosos gráficos, e nós utilizamos Chartmuseum con APIs moi útiles. Deste xeito, garantimos un estrito illamento entre os ambientes e as probas no mundo real dos gráficos antes de utilizalos na produción.

Repositorios de gráficos en diferentes ambientes

Paga a pena notar que cando os desenvolvedores impulsan unha rama de desenvolvemento, unha versión do seu gráfico envíase automaticamente ao Chartmuseum de desenvolvemento. Así, todos os desenvolvedores usan o mesmo repositorio de dev, e cómpre especificar coidadosamente a súa versión do gráfico para non usar accidentalmente os cambios doutra persoa.

Ademais, o noso pequeno script de Python valida os obxectos de Kubernetes contra as especificacións de Kubernetes OpenAPI usando Kubeval, antes de publicalos en Chartmusem.

Descrición xeral do fluxo de traballo de desenvolvemento de gráficos

  1. Configurar tarefas de canalización segundo especificacións gazr.io para o control de calidade (pelusa, proba unitaria).
  2. Empuxando unha imaxe docker con ferramentas de Python que despregan as nosas aplicacións.
  3. Configurar o entorno polo nome da sucursal.
  4. Validación de ficheiros yaml de Kubernetes usando Kubeval.
  5. Aumenta automaticamente a versión dun gráfico e dos seus gráficos principais (gráficos que dependen do gráfico que se está modificando).
  6. Enviar un gráfico a un Chartmuseum que coincida co seu entorno

Xestionar as diferenzas entre clusters

Federación de Clústeres

Houbo un tempo no que usamos federación de clústeres de Kubernetes, onde os obxectos de Kubernetes poderían declararse desde un único punto final da API. Pero xurdiron problemas. Por exemplo, algúns obxectos de Kubernetes non se puideron crear no punto final de federación, o que dificulta o mantemento de obxectos federados e outros obxectos para clústeres individuais.

Para solucionar o problema, comezamos a xestionar os clústeres de forma independente, o que simplificou moito o proceso (utilizamos a primeira versión de federación; algo puido cambiar na segunda).

Plataforma xeodistribuída

A nosa plataforma distribúese actualmente en 6 rexións: 3 localmente e 3 na nube.


Implementación distribuída

Valores globais de Helm

Os 4 valores globais de Helm permítenche identificar as diferenzas entre os clústeres. Todos os nosos gráficos teñen valores mínimos predeterminados.

global:
  cloud: True
  env: staging
  region: us-central1
  clusterName: staging-us-central1

Valores globais

Estes valores axudan a definir o contexto das nosas aplicacións e utilízanse para diversos fins: seguimento, rastrexo, rexistro, realizar chamadas externas, escalar, etc.

  • "cloud": temos unha plataforma híbrida Kubernetes. Por exemplo, a nosa API está implantada nas zonas GCP e nos nosos centros de datos.
  • "env": algúns valores poden cambiar para ambientes que non sexan de produción. Por exemplo, definicións de recursos e configuracións de escalado automático.
  • "rexión": esta información axuda a determinar a localización do clúster e pódese usar para determinar puntos finais próximos para servizos externos.
  • "clusterName": se e cando queremos definir un valor para un clúster individual.

Aquí tes un exemplo 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 -}}

Exemplo de modelo de Helm

Esta lóxica defínese nun modelo de axuda para evitar desordenar Kubernetes YAML.

Anuncio de solicitude

As nosas ferramentas de implementación baséanse en varios ficheiros YAML. A continuación móstrase un exemplo de como declaramos un servizo e a súa topoloxía de escalado (número de réplicas) nun clúster.

releases:
  - foo.world

foo.world:                # Release name
  services:               # List of dailymotion's apps/projects
    foobar:
      chart_name: foo-foobar
      repo: [email protected]:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3

Definición do servizo

Este é un esbozo de todos os pasos que definen o noso fluxo de traballo de implantación. O último paso desprega a aplicación en varios clústeres de traballadores simultaneamente.


Pasos de implementación de Jenkins

E os segredos?

En canto á seguridade, rastrexamos todos os segredos de diferentes lugares e gardámolos nunha bóveda única Bóveda en París.

As nosas ferramentas de despregamento extraen valores secretos de Vault e, cando chegue o momento de implantación, insíreos en Helm.

Para iso, definimos un mapeo entre os segredos de Vault e os que precisan as nosas aplicacións:

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"

  • Definimos regras xerais a seguir ao gravar segredos en Vault.
  • Se se aplica o segredo a un contexto o clúster específico, cómpre engadir unha entrada específica. (Aquí o contexto cluster1 ten o seu propio valor para o secreto stack-app1-password).
  • En caso contrario, utilízase o valor predeterminado.
  • Para cada elemento desta lista en Segredo de Kubernetes insírese un par clave-valor. Polo tanto, o modelo secreto dos nosos gráficos é moi sinxelo.

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: Opaque

Problemas e limitacións

Traballar con varios repositorios

Agora separamos o desenvolvemento de gráficos e aplicacións. Isto significa que os desenvolvedores teñen que traballar en dous repositorios git: un para a aplicación e outro para definir a súa implantación en Kubernetes. 2 repositorios git significan 2 fluxos de traballo, e é fácil que un novato se confunda.

Xestionar gráficos xeneralizados é un problema

Como xa dixemos, os gráficos xenéricos son moi útiles para identificar dependencias e despregar rapidamente varias aplicacións. Pero usamos --reuse-valuespara evitar pasar todos os valores cada vez que despregamos unha aplicación que forma parte deste gráfico xeneralizado.

Nun fluxo de traballo de entrega continua, só temos dous valores que cambian regularmente: o número de réplicas e a etiqueta da imaxe (versión). Outros valores máis estables cámbianse manualmente, e isto é bastante difícil. Ademais, un erro ao despregar un gráfico xeneralizado pode levar a graves fallos, como vimos pola nosa propia experiencia.

Actualizando varios ficheiros de configuración

Cando un desenvolvedor engade unha nova aplicación, ten que cambiar varios ficheiros: a declaración da aplicación, a lista de segredos, engadindo a aplicación como dependencia se está incluída no cadro xeralizado.

Os permisos de Jenkins están demasiado estendidos en Vault

Agora temos un AppRole, que le todos os segredos da Bóveda.

O proceso de recuperación non está automatizado

Para revertir, cómpre executar o comando en varios clústeres, e isto está cheo de erros. Realizamos esta operación manualmente para asegurarnos de que se especifica o ID de versión correcto.

Avanzamos cara a GitOps

O noso obxectivo

Queremos devolver o gráfico ao repositorio da aplicación que desprega.

O fluxo de traballo será o mesmo que para o desenvolvemento. Por exemplo, cando se empurra unha rama para dominar, o despregamento activarase automaticamente. A principal diferenza entre este enfoque e o fluxo de traballo actual sería esa todo será xestionado en git (a propia aplicación e a forma en que se desprega en Kubernetes).

Hai varias vantaxes:

  • Moito máis claro para o desenvolvedor. É máis fácil aprender a aplicar cambios nun gráfico local.
  • Pódese especificar a definición de implantación do servizo mesmo lugar que o código servizo.
  • Xestionar a eliminación de gráficos xeneralizados. O servizo terá a súa propia versión Helm. Isto permitirá xestionar o ciclo de vida da aplicación (reversión, actualización) ao menor nivel, para non afectar a outros servizos.
  • Beneficios de git para a xestión de gráficos: desfacer cambios, rexistro de auditoría, etc. Se precisa desfacer un cambio nun gráfico, pode facelo usando git. O despregamento comeza automaticamente.
  • Podes considerar mellorar o teu fluxo de traballo de desenvolvemento con ferramentas como Patín, co que os desenvolvedores poden probar os cambios nun contexto próximo á produción.

Migración en dous pasos

Os nosos desenvolvedores levan 2 anos usando este fluxo de traballo, polo que queremos que a migración sexa o máis sinxela posible. Por iso, decidimos engadir un paso intermedio no camiño cara á meta.
A primeira etapa é sinxela:

  • Mantemos unha estrutura similar para configurar a implantación de aplicacións, pero nun único obxecto chamado 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 (sen gráficos xeneralizados).
  • Gráficos no repositorio git da aplicación.

Falamos con todos os desenvolvedores, polo que xa comezou o proceso de migración. A primeira fase aínda se controla mediante a plataforma CI. Pronto escribirei outra publicación sobre a segunda fase: como nos movemos a un fluxo de traballo de GitOps Fluxo. Vouvos contar como configuramos todo e que dificultades nos atopamos (múltiples repositorios, segredos, etc.). Siga as noticias.

Aquí tentamos describir o noso progreso no fluxo de traballo de implantación de aplicacións nos últimos anos, o que levou a pensar sobre o enfoque de GitOps. Aínda non chegamos ao obxectivo e informaremos dos resultados, pero agora estamos convencidos de que fixemos o correcto cando decidimos simplificalo todo e achegalo aos hábitos dos desenvolvedores.

Fonte: www.habr.com

Engadir un comentario