Espansione e integrazione di Kubernetes (revisione e rapporto video)

Espansione e integrazione di Kubernetes (revisione e rapporto video)

8 aprile alla conferenza San HighLoad++ 2019, nell'ambito della sezione “DevOps e Operations”, è stato presentato il rapporto “Espansione e integrazione di Kubernetes”, alla cui creazione hanno partecipato tre dipendenti dell'azienda Flant. In esso parliamo di numerose situazioni in cui volevamo espandere e integrare le capacità di Kubernetes, ma per le quali non abbiamo trovato una soluzione semplice e pronta. Abbiamo le soluzioni necessarie sotto forma di progetti Open Source, e questo discorso è dedicato anche a loro.

Per tradizione, siamo lieti di presentare video del resoconto (50 minuti, molto più informativo dell'articolo) e il riassunto principale in forma testuale. Andare!

Core e aggiunte nei K8

Kubernetes sta cambiando il settore e gli approcci all'amministrazione consolidati da tempo:

  • Grazie a lui astrazioni, non operiamo più con concetti come l'impostazione di una configurazione o l'esecuzione di un comando (Chef, Ansible...), ma utilizziamo il raggruppamento di contenitori, servizi, ecc.
  • Possiamo preparare le applicazioni senza pensare alle sfumature del sito specifico, su cui verrà lanciato: bare metal, cloud di uno dei fornitori, ecc.
  • Con K8 non sei mai stato così accessibile migliori pratiche sull’organizzazione delle infrastrutture: tecniche di scalabilità, autoriparazione, tolleranza agli errori, ecc.

Ma ovviamente non tutto è così liscio: anche Kubernetes ha portato con sé nuove sfide.

kubernetes no è una mietitrebbia che risolve tutti i problemi di tutti gli utilizzatori. nucleo Kubernetes è responsabile solo di una serie di funzioni minime necessarie presenti in ogni grappolo:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Il core Kubernetes definisce un set di base di primitive per raggruppare i contenitori, gestire il traffico e così via. Ne abbiamo parlato più approfonditamente in rapporto 2 anni fa.

Espansione e integrazione di Kubernetes (revisione e rapporto video)

D'altra parte, K8 offre grandi opportunità per espandere le funzioni disponibili, che aiutano a chiuderne altre: specifica — esigenze degli utenti. Le aggiunte a Kubernetes sono responsabilità degli amministratori del cluster, che devono installare e configurare tutto il necessario per ottenere il proprio cluster “nella giusta forma” [per risolvere i loro problemi specifici]. Che tipo di aggiunte sono queste? Diamo un'occhiata ad alcuni esempi.

Esempi di componenti aggiuntivi

Avendo installato Kubernetes, potremmo essere sorpresi che la rete così necessaria per l'interazione dei pod sia all'interno di un nodo che tra i nodi non funzioni da sola. Il kernel Kubernetes non garantisce le connessioni necessarie; determina invece la rete интерфейс (CNI) per componenti aggiuntivi di terze parti. Dobbiamo installare uno di questi componenti aggiuntivi, che sarà responsabile della configurazione della rete.

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Un esempio ravvicinato sono le soluzioni di archiviazione dei dati (disco locale, dispositivo a blocchi di rete, Ceph...). Inizialmente erano nel nucleo, ma con l'avvento CSI la situazione cambia in qualcosa di simile a quello già descritto: l'interfaccia è in Kubernetes e la sua implementazione è in moduli di terze parti.

Altri esempi includono:

  • Ingresso-controller (vedi la loro recensione in il nostro recente articolo).
  • manager-cert:

    Espansione e integrazione di Kubernetes (revisione e rapporto video)

  • Operatori è un'intera classe di componenti aggiuntivi (che include il citato certificato-manager), definiscono primitivi e controller. La logica del loro lavoro è limitata solo dalla nostra immaginazione e ci consente di trasformare i componenti dell'infrastruttura già pronti (ad esempio un DBMS) in primitivi con cui è molto più facile lavorare (rispetto a un insieme di contenitori e le loro impostazioni). È stato scritto un numero enorme di operatori - anche se molti di loro non sono ancora pronti per la produzione, è solo questione di tempo:

    Espansione e integrazione di Kubernetes (revisione e rapporto video)

  • Metrica - un'altra illustrazione di come Kubernetes ha separato l'interfaccia (API Metrics) dall'implementazione (add-on di terze parti come adattatore Prometheus, agente cluster Datadog...).
  • per monitoraggio e statistica, dove in pratica non solo sono necessari Prometeo e Grafana, ma anche kube-state-metrics, node-exporter, ecc.

E questo non è un elenco completo delle aggiunte... Ad esempio, presso la società Flant attualmente installiamo 29 aggiunte (tutti creano un totale di 249 oggetti Kubernetes). In poche parole, non possiamo vedere la vita di un cluster senza aggiunte.

Automazione

Gli operatori sono progettati per automatizzare le operazioni di routine che incontriamo ogni giorno. Ecco alcuni esempi di vita reale per i quali scrivere un operatore sarebbe un'ottima soluzione:

  1. Esiste un registro privato (ovvero che richiede un accesso) con immagini per l'applicazione. Si presuppone che a ciascun pod venga assegnato un segreto speciale che consente l'autenticazione nel registro. Il nostro compito è garantire che questo segreto venga trovato nello spazio dei nomi in modo che i pod possano scaricare le immagini. Possono esserci molte applicazioni (ognuna delle quali necessita di un segreto), ed è utile aggiornare regolarmente i segreti stessi, quindi viene eliminata la possibilità di disporre i segreti manualmente. È qui che arriva l'operatore in soccorso: creiamo un controller che attenderà la comparsa del namespace e, in base a questo evento, aggiungerà un segreto al namespace.
  2. Per impostazione predefinita, l'accesso dai pod a Internet è vietato. Ma a volte può essere necessario: è logico che il meccanismo di autorizzazione all'accesso funzioni in modo semplice, senza richiedere competenze specifiche, ad esempio la presenza di una determinata etichetta nel namespace. Come può aiutarci l'operatore in questo caso? Viene creato un controller che attende la visualizzazione dell'etichetta nello spazio dei nomi e aggiunge la policy appropriata per l'accesso a Internet.
  3. Una situazione simile: supponiamo di dover aggiungere un certo contaminazione, se ha un'etichetta simile (con qualche tipo di prefisso). Le azioni con l'operatore sono evidenti...

In qualsiasi cluster, i compiti di routine devono essere risolti e correttamente questo può essere fatto utilizzando gli operatori.

Riassumendo tutte le storie descritte, siamo giunti alla conclusione che per lavorare comodamente in Kubernetes di cui hai bisogno: ma) installare componenti aggiuntivi, B) sviluppare gli operatori (per risolvere le attività amministrative quotidiane).

Come scrivere una dichiarazione per Kubernetes?

In generale lo schema è semplice:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

... ma poi si scopre che:

  • L'API Kubernetes è una cosa piuttosto non banale che richiede molto tempo per essere padroneggiata;
  • anche la programmazione non è per tutti (la lingua Go è stata scelta come lingua preferita perché esiste un framework speciale per essa - SDK dell'operatore);
  • La situazione è simile con il quadro stesso.

La linea di fondo: per scrivere un controller (l'operatore) deve spendere risorse significative studiare materiale. Ciò sarebbe giustificato per gli operatori "grandi", ad esempio per il DBMS MySQL. Ma se ricordiamo gli esempi sopra descritti (svelamento di segreti, accesso ai pod su Internet...), che vogliamo fare correttamente, allora capiremo che lo sforzo compiuto supererà il risultato di cui abbiamo bisogno ora:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

In generale, sorge un dilemma: spendere molte risorse e trovare lo strumento giusto per scrivere dichiarazioni, oppure farlo alla vecchia maniera (ma velocemente). Per risolverlo - per trovare un compromesso tra questi estremi - abbiamo creato il nostro progetto: operatore di shell (vedi anche il suo recente annuncio sul mozzo).

Operatore di shell

Come lavora? Il cluster ha un pod contenente un file binario Go con un operatore shell. Accanto ad esso c'è una serie di ganci (maggiori dettagli su di loro - vedi sotto). L'operatore shell stesso si iscrive a determinati sviluppi nell'API Kubernetes, al verificarsi della quale lancia gli hook corrispondenti.

Come fa l'operatore shell a sapere quali hook chiamare su quali eventi? Questa informazione viene trasmessa all'operatore shell dagli stessi hook, e lo fanno in modo molto semplice.

Un hook è uno script Bash o qualsiasi altro file eseguibile che accetta un singolo argomento --config e risponde con JSON. Quest'ultimo determina quali oggetti gli interessano e a quali eventi (per questi oggetti) si dovrebbe rispondere:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Illustrerò l'implementazione sull'operatore shell di uno dei nostri esempi: la scomposizione dei segreti per l'accesso a un registro privato con le immagini dell'applicazione. Si compone di due fasi.

Esercizio: 1. Scrivi un hook

Prima di tutto, elaboreremo nel gancio --config, indicando che siamo interessati ai namespace e, nello specifico, al momento della loro creazione:

[[ $1 == "--config" ]] ; then
  cat << EOF
{
  "onKubernetesEvent": [
    {
      "kind": "namespace",
      "event": ["add"]
    }
  ]
}
EOF
…

Come sarebbe la logica? Anche abbastanza semplice:

…
else
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  kubectl create -n ${createdNamespace} -f - << EOF
Kind: Secret
...
EOF
fi

Il primo passo è scoprire quale spazio dei nomi è stato creato, il secondo è crearlo utilizzando kubectl segreto per questo spazio dei nomi.

Esercizio: 2. Assemblare l'immagine

Tutto ciò che resta è passare l'hook creato all'operatore shell: come farlo? L'operatore shell stesso si presenta come un'immagine Docker, quindi il nostro compito è aggiungere l'hook a una directory speciale in questa immagine:

FROM flant/shell-operator:v1.0.0-beta.1
ADD my-handler.sh /hooks

Non resta che montarlo e spingerlo:

$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

Il tocco finale consiste nel distribuire l'immagine nel cluster. Per fare questo, scriviamo Distribuzione:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1 # 1
      serviceAccountName: my-operator              # 2

Ci sono due punti a cui prestare attenzione:

  1. indicazione dell'immagine appena creata;
  2. Questo è un componente di sistema che (come minimo) necessita dei diritti per sottoscrivere eventi in Kubernetes e per allocare segreti agli spazi dei nomi, quindi creiamo un ServiceAccount (e un insieme di regole) per l'hook.

Risultato: abbiamo risolto il nostro problema ai parenti per Kubernetes in modo da creare un operatore per la scomposizione dei segreti.

Altre funzionalità dell'operatore shell

Per limitare gli oggetti del tipo scelto con cui funzionerà il gancio, possono essere filtrati, selezionando in base a determinate etichette (o utilizzando matchExpressions):

"onKubernetesEvent": [
  {
    "selector": {
      "matchLabels": {
        "foo": "bar",
       },
       "matchExpressions": [
         {
           "key": "allow",
           "operation": "In",
           "values": ["wan", "warehouse"],
         },
       ],
     }
     …
  }
]

Fornito meccanismo di deduplicazione, che, utilizzando un filtro jq, consente di convertire oggetti JSON di grandi dimensioni in oggetti piccoli, dove rimangono solo i parametri di cui vogliamo monitorare le modifiche.

Quando viene chiamato un hook, l'operatore shell lo passa dati dell'oggetto, che può essere utilizzato per qualsiasi esigenza.

Gli eventi che attivano gli hook non si limitano agli eventi Kubernetes: l'operatore shell fornisce il supporto chiamando gli hook in base al tempo (simile a crontab in uno scheduler tradizionale), nonché un evento speciale all'avvio. Tutti questi eventi possono essere combinati e assegnati allo stesso hook.

E altre due funzionalità dell'operatore shell:

  1. lui lavora in modo asincrono. Poiché è stato ricevuto un evento Kubernetes (come un oggetto in fase di creazione), altri eventi (come ad esempio l'eliminazione dello stesso oggetto) potrebbero essersi verificati nel cluster e gli hook devono tenerne conto. Se l'hook è stato eseguito con un errore, lo sarà per impostazione predefinita richiamare fino al completamento con successo (questo comportamento può essere modificato).
  2. Esporta metrica per Prometheus, con il quale puoi capire se l'operatore shell funziona, scoprire il numero di errori per ciascun hook e la dimensione attuale della coda.

Per riassumere questa parte del rapporto:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Installazione componenti aggiuntivi

Per lavorare comodamente con Kubernetes, è stata menzionata anche la necessità di installare componenti aggiuntivi. Te lo parlerò usando l’esempio del percorso della nostra azienda su come lo facciamo ora.

Abbiamo iniziato a lavorare con Kubernetes con diversi cluster, l'unica aggiunta ai quali è stata Ingress. Doveva essere installato in modo diverso in ciascun cluster e abbiamo realizzato diverse configurazioni YAML per ambienti diversi: bare metal, AWS...

Poiché c'erano più cluster, c'erano più configurazioni. Inoltre, abbiamo migliorato queste stesse configurazioni, per cui sono diventate piuttosto eterogenee:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Per mettere tutto in ordine, abbiamo iniziato con uno script (install-ingress.sh), che ha preso come argomento il tipo di cluster in cui eseguiremo il deploy, ha generato la configurazione YAML necessaria e l'ha distribuita a Kubernetes.

In breve, il nostro ulteriore percorso e il ragionamento ad esso associato sono stati i seguenti:

  • per lavorare con le configurazioni YAML è necessario un template engine (nelle prime fasi si tratta di semplice sed);
  • con l'aumento del numero dei cluster è arrivata la necessità dell'aggiornamento automatico (la prima soluzione è stata mettere lo script in Git, aggiornarlo tramite cron ed eseguirlo);
  • uno script simile era richiesto per Prometeo (install-prometheus.sh), tuttavia, si distingue per il fatto che richiede molti più dati di input, nonché la loro archiviazione (in modo corretto, centralizzato e in un cluster), e alcuni dati (password) potrebbero essere generati automaticamente:

    Espansione e integrazione di Kubernetes (revisione e rapporto video)

  • il rischio di implementare qualcosa di sbagliato in un numero crescente di cluster era in costante aumento, quindi ci siamo resi conto che gli installatori (cioè due script: per Ingress e Prometheus) era necessario lo staging (diversi rami in Git, diversi cron per aggiornarli nei corrispondenti cluster: stabili o di test);
  • с kubectl apply è diventato difficile lavorarci perché non è dichiarativo e può solo creare oggetti, ma non prendere decisioni sul loro stato/eliminarli;
  • Ci mancavano alcune funzioni che all'epoca non avevamo implementato affatto:
    • pieno controllo sul risultato degli aggiornamenti del cluster,
    • determinazione automatica di alcuni parametri (input per script di installazione) in base ai dati ottenibili dal cluster (discovery),
    • il suo sviluppo logico sotto forma di scoperta continua.

Abbiamo implementato tutta questa esperienza accumulata nel quadro del nostro altro progetto: operatore-add-on.

Operatore aggiuntivo

Si basa sul già citato operatore shell. L'intero sistema si presenta così:

Quanto segue viene aggiunto agli hook dell'operatore shell:

  • conservazione dei valori,
  • Grafico del timone,
  • componente quello monitora l'archivio valori e, in caso di modifiche, chiede a Helm di rilanciare il grafico.

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Pertanto, possiamo reagire a un evento in Kubernetes, lanciare un hook e da questo hook possiamo apportare modifiche allo spazio di archiviazione, dopodiché il grafico verrà scaricato nuovamente. Nel diagramma risultante, separiamo l'insieme di hook e il grafico in un unico componente, che chiameremo modulo:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Possono esserci molti moduli e ad essi aggiungiamo hook globali, un archivio di valori globali e un componente che monitora questo archivio globale.

Ora, quando succede qualcosa in Kubernetes, possiamo reagire utilizzando un hook globale e modificare qualcosa nell'archivio globale. Questo cambiamento verrà notato e causerà l'implementazione di tutti i moduli nel cluster:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Questo schema soddisfa tutti i requisiti per l'installazione dei componenti aggiuntivi sopra indicati:

  • Helm è responsabile dei modelli e della dichiaratività.
  • Il problema dell'aggiornamento automatico è stato risolto utilizzando un hook globale, che si reca al registro secondo una pianificazione e, se vede lì una nuova immagine di sistema, la distribuisce (cioè "se stessa").
  • La memorizzazione delle impostazioni nel cluster viene implementata utilizzando Mappa di configurazione, che contiene i dati primari per gli archivi (all'avvio vengono caricati negli archivi).
  • I problemi con la generazione, il rilevamento e il rilevamento continuo delle password sono stati risolti utilizzando gli hook.
  • La stadiazione viene ottenuta grazie ai tag, che Docker supporta immediatamente.
  • Il risultato viene monitorato utilizzando metriche in base alle quali possiamo comprendere lo stato.

L'intero sistema è implementato sotto forma di un unico binario in Go, chiamato addon-operator. Questo rende il diagramma più semplice:

Espansione e integrazione di Kubernetes (revisione e rapporto video)

Il componente principale in questo diagramma è un insieme di moduli (evidenziato in grigio sotto). Ora possiamo scrivere un modulo per il componente aggiuntivo richiesto con un piccolo sforzo ed essere sicuri che verrà installato in ciascun cluster, verrà aggiornato e risponderà agli eventi di cui ha bisogno nel cluster.

Usi "Flant". operatore-add-on su oltre 70 cluster Kubernetes. Stato attuale - versione alfa. Ora stiamo preparando la documentazione per rilasciare la beta, ma per ora nel repository esempi disponibili, sulla base del quale puoi creare il tuo componente aggiuntivo.

Dove posso trovare i moduli per addon-operator? La pubblicazione della nostra biblioteca è la fase successiva per noi; prevediamo di farlo in estate.

Video e diapositive

Video dello spettacolo (~50 minuti):

Presentazione del rapporto:

PS

Altre segnalazioni sul nostro blog:

Potrebbero interessarti anche le seguenti pubblicazioni:

Fonte: habr.com

Aggiungi un commento