Build automatiche delle immagini con Jenkins, Packer e Kubernetes

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.
Crea immagini personalizzate per avviare le tue istanze Compute Engine o i tuoi container Docker possono ridurre i tempi di avvio e aumentare l'affidabilità. Preinstallando il software in un'immagine personalizzata puoi anche ridurre la dipendenza dalla disponibilità di repository di terze parti fuori dal tuo controllo.

Sei tu a scegliere il software e la configurazione da includere nelle immagini personalizzate. Su un'estremità dello spettro, un'immagine configurata in modo minimo, indicata come immagine di base in questo documento, contiene un'immagine di sistema operativo di base (come Ubuntu 14.10) e potrebbe includere anche software e configurazione di base. Ad esempio, puoi preinstallare dei runtime di linguaggio come Java o Ruby, configurare il logging remoto o applicare patch di sicurezza. Un'immagine di base fornisce un'immagine di base stabile che può essere ulteriormente personalizzata per gestire un'applicazione.

All'altra estremità dello spettro, un'immagine completamente configurata, nota come immagine immutabile in questo documento, contiene non solo un'immagine di base o di base del sistema operativo, ma anche tutto ciò che è necessario per eseguire un'applicazione. La configurazione del runtime, ad esempio informazioni di connessione al database o dati sensibili, può essere inclusa nell'immagine oppure può essere fornita tramite l'ambiente, i metadati o il servizio di gestione delle chiavi al momento del lancio.

Il processo di creazione delle immagini ha molto in comune con la creazione di software: hai del codice (Chef, Puppet, bash e così via) e delle persone che lo scrivono, una build si verifica quando applichi il codice a un'immagine di base, un processo di compilazione riuscito genera un elemento e spesso vuoi sottoporre l'elemento a test. Molte delle best practice che si applicano alla creazione di software si applicano anche alle immagini: puoi utilizzare il controllo della versione per gestire gli script di configurazione delle immagini, attivare le build quando vengono apportate modifiche a tali script, eseguire le build delle immagini automaticamente e controllare la versione e anche testare gli artefatti di immagini risultanti al completamento delle build.

Cosa imparerai a fare

In questa soluzione imparerai a conoscere due approcci generali alla creazione di immagini personalizzate e a utilizzare diversi strumenti open source molto utilizzati, tra cui Jenkins, Packer, Docker e Kubernetes, per creare una pipeline automatizzata in cui creare immagini in modo continuo. Questa pipeline si integra con Cloud Source Repositories in Google Cloud e genera sia immagini Compute Engine che immagini Docker.

Imparerai a creare immagini di base e immutabili e scoprirai le best practice per la gestione dell'accesso a queste immagini in più progetti in Google Cloud. Infine, un tutorial completo alla fine del documento consente di eseguire il deployment e utilizzare un'implementazione di riferimento open source della soluzione.

Tipi di immagini

Nella soluzione Applicazioni web scalabili e resilienti, un'applicazione web Ruby on Rails viene utilizzata come riferimento per l'esecuzione delle applicazioni web su Google Cloud. Il codice sorgente della soluzione non utilizza immagini personalizzate. Quando si avvia un'istanza di Compute Engine, uno script di avvio installa Chef Solo, che installa poi tutto il necessario per eseguire l'applicazione. Sono inclusi nginx, Ruby 2, cURL e altri strumenti di sistema, Unicorn, l'app Rails e tutte le sue gemme, imagemagick e la configurazione dell'app.

Il seguente diagramma descrive la procedura di avvio.

Diagramma che mostra il processo di avvio senza un'immagine personalizzata.

Il processo non è veloce e richiede 10-15 minuti per ciascuna istanza, a seconda della velocità di download dei vari repository richiesti per i pacchetti, supponendo che ogni repository che ospita i pacchetti sia online e disponibile. Nelle sezioni seguenti, scoprirai come un'immagine di base e un'immagine immutabile potrebbero migliorare le prestazioni e l'affidabilità del processo di avvio dell'istanza.

Immagini di base

Quando crei un'immagine di base, puoi decidere quale software e pacchetti includere nell'immagine. Di seguito sono riportati alcuni aspetti da considerare nel decidere:

  • Velocità di installazione. Il download di pacchetti di grandi dimensioni può essere lento; il software che deve essere creato dall'origine può richiedere molto tempo e i pacchetti con molte dipendenze costituiscono il problema. Valuta la possibilità di includere questi tipi di software e pacchetti nell'immagine di base.
  • Affidabilità di Repository remoto. Se non includi il software nell'immagine di base e lo scarichi al momento dell'avvio, ritieni attendibile la disponibilità del repository remoto? Se il repository non è disponibile durante l'avvio, impedirà il funzionamento dell'applicazione? Per ridurre la tua dipendenza da repository remoti che potrebbero essere fuori dal tuo controllo, valuta la possibilità di includere dipendenze critiche in un'immagine di base.
  • Frequenza di modifica. Il software o il pacchetto cambiano molto spesso? In questo caso, valuta la possibilità di escludere l'immagine di base e di archiviarla in una posizione affidabile e accessibile, ad esempio in un bucket Cloud Storage.
  • Obbligatorio o obbligatorio per la sicurezza. Se per ogni istanza della tua organizzazione è necessario eseguire determinati pacchetti (come logging, OSSEC e così via), con una configurazione specifica, questi dovrebbero essere installati in un'immagine di base che tutte le altre immagini estendono. Un team di sicurezza potrebbe utilizzare uno strumento più avanzato come Chef o Puppet per creare un'immagine di base Docker, mentre gli sviluppatori downstream potrebbero utilizzare un Dockerfile per estendere facilmente la base.

Questi criteri suggeriscono che un'immagine di base per l'applicazione Ruby on Rails dalla soluzione per applicazioni web scalabili e resilienti potrebbe includere Chef Solo, nginx, Ruby, cURL e altri strumenti di sistema e Unicorn. Le altre dipendenze verranno installate al momento dell'avvio.

Il seguente diagramma descrive il processo di avvio con un'immagine di base:

Diagramma che mostra il processo di avvio con un'immagine di base.

L'istanza funzionale in questo esempio recupera la sua configurazione (ad esempio, stringa di connessione al database, chiavi API e così via) dal servizio metadati di Compute Engine. Puoi scegliere di utilizzare un servizio diverso come etcd o un semplice bucket Cloud Storage per gestire la configurazione.

Le prossime sezioni saranno incentrate sugli strumenti utilizzati per automatizzare la creazione dell'immagine di Ruby di base.

Immagini immutabili

A differenza di un'immagine di base, un'immagine immutabile include tutto il suo software incluso. Quando un'istanza o un container viene avviato dall'immagine, non ci sono pacchetti da scaricare o software da installare. Un'immagine immutabile per l'applicazione Ruby on Rails dalla soluzione per le applicazioni web scalabili e resilienti includerà tutto il software e l'istanza sarà pronta per gestire il traffico all'avvio.

Diagramma che mostra il processo di avvio con un'immagine immutabile.

Configurazione e immagini immutabili

Puoi scegliere che l'applicazione acceda ai dati di configurazione di cui ha bisogno da un servizio di configurazione oppure puoi includere tutta la configurazione nell'immagine immutabile. Se scegli quest'ultimo approccio, assicurati di considerare le implicazioni relative alla sicurezza relative all'inclusione dei secret nelle immagini. Se stai eseguendo il push di immagini immutabili nei repository pubblici in Docker Hub, queste sono accessibili a tutti e non devono contenere informazioni sensibili o segrete.

Immagini immutabili come unità di deployment

L'uso di immagini immutabili come unità di deployment elimina la possibilità di deviazione della configurazione, in cui una o più istanze si trovano in uno stato diverso dal previsto. Questo può accadere, ad esempio, quando applichi una patch di sicurezza a 100 container in esecuzione e alcuni non vengono aggiornati. L'immagine diventa ciò che esegui il deployment quando viene apportata una qualsiasi modifica. Se il sistema operativo richiede una patch software o la configurazione di logging deve essere aggiornata, crei una nuova immagine per includere queste modifiche e la implementi lanciando nuove istanze o container e sostituendo tutte quelle precedenti. Se scegli di raggruppare la configurazione dell'applicazione in un'immagine immutabile, anche una semplice modifica, come l'aggiornamento di una stringa di connessione del database, comporta la creazione e il rilascio di una nuova immagine.

Architettura e implementazione di una pipeline di creazione di immagini automatizzata

Questa sezione include dettagli di implementazione di una pipeline di creazione di immagini automatica che utilizza Jenkins, Packer, Docker e Google Kubernetes Engine (GKE) per creare automaticamente immagini personalizzate. Ogni sezione include un'introduzione, un diagramma di architettura e un'analisi dettagliata dei componenti di quel diagramma.

Software e servizi utilizzati

Questi software e servizi vengono utilizzati per creare lo strumento per la creazione di immagini automatizzato.

Software Utilizzo
Jenkins Jenkins è un popolare server di integrazione continua (CI) open source. Utilizzerai Jenkins per eseguire il polling dei repository Git in altri progetti che contengono script di configurazione delle immagini, quindi per creare immagini basate su tali repository.
Packer Packer è uno strumento per creare immagini di macchine identiche per più piattaforme da una singola configurazione di origine. Supporta molte origini di configurazione diverse, tra cui Shell, Chef, Puppet, Ansible e Salt, e può generare immagini per Compute Engine, Docker e altri. Packer viene utilizzato dagli agenti di Jenkins per creare immagini dalla configurazione nei repository Git.
Docker Docker è uno strumento open source per la pacchettizzazione e il deployment di applicazioni come container. Il deployment di Jenkins (inclusi il nodo leader e gli agenti di build) in questa architettura e in questo tutorial viene eseguito come deployment di container Docker. Anche gli agenti di build generano immagini Docker come una delle loro architetture.
GKE GKE, basato sulla tecnologia open source Kubernetes, consente di eseguire e gestire container Docker sulle macchine virtuali Google Cloud.
Container Registry Container Registry fornisce uno spazio di archiviazione sicuro per le immagini Docker private su Google Cloud. Viene eseguito su Google Cloud ed è accessibile tramite un endpoint HTTPS.
Compute Engine GKE utilizza le VM di Compute Engine per eseguire Kubernetes e per ospitare i leader Jenkins e i container di agenti agenti. Il processo di compilazione di Jenkins genera anche immagini VM di Compute Engine oltre alle immagini Docker.
Cloud Storage Utilizzerai Cloud Storage per archiviare i backup della tua configurazione Jenkins.
Nginx Nginx fornisce una funzionalità proxy inversa; inoltra le richieste in entrata all'interfaccia web dei leader Jenkins. Può essere configurato per terminare le connessioni SSL e fornire l'autenticazione di base.

Panoramica del generatore di immagini

Il seguente diagramma mostra in che modo i vari componenti interagiscono per creare un sistema che crei automaticamente immagini VM e Docker.

Diagramma che mostra i vari componenti del progetto di creazione delle immagini.

Puoi definire un job nel leader Jenkins per ogni immagine che vuoi creare. Il job raccoglie un repository di codice sorgente, Git in questa illustrazione, che contiene gli script di configurazione e un modello Packer che descrive come creare un'immagine. Quando il processo di polling rileva una modifica, il leader Jenkins assegna il job a un agente di build. L'agente utilizza Packer per eseguire la build, che genera un'immagine Docker nel Container Registry e un'immagine VM in Compute Engine.

Script di packer e configurazione

Un modello Packer e gli script di configurazione associati definiscono insieme come creare un'immagine. Vengono trattate come software e archiviate nel loro repository Git. Ogni immagine creata avrà il proprio repository con un modello Packer e script di configurazione.

Questa sezione fornisce una panoramica di una possibile configurazione di Packer che utilizza Chef per personalizzare Ubuntu 14.04 aggiungendo Ruby e rbenv. Per la copertura completa di Packer, consulta la sua eccellente documentazione all'indirizzo https://www.packer.io/docs.

Denominazione delle immagini e variabili dei pacchetti

Il generatore di immagini crea un'immagine ogni volta che viene apportata una modifica al repository Git contenente il modello Packer e gli script di configurazione dell'immagine. È una buona idea assegnare un nome o un tag alle immagini con il ramo Git e l'ID commit da cui sono stati creati. I modelli di packer ti consentono di definire le variabili e fornire i relativi valori in fase di runtime:

{
...
  "variables": {
      "Git_commit": "",
      "Git_branch": "",
      "ruby_version_name": "212",
      "project_id": "null"
  }
...
}

L'agente di build Jenkins può trovare il ramo Git e l'ID commit e fornirli come variabili allo strumento a riga di comando Packer. Vedrai questo in azione più avanti nella sezione Tutorial di questo documento.

Configurazione programmatica con provisioner

Un modello Packer definisce uno o più provisioner che descrivono come utilizzare uno strumento come chef, burattini o script shell per configurare un'istanza. Packer supporta molti provisioner; consulta il sommario nella documentazione di Packer per un elenco completo. Questo snippet definisce un provisioner chef-solo con percorso di ricetta e ricette da eseguire per configurare un'immagine:

{
  ...
  "provisioners": [
    {
      "type": "chef-solo",
      "install_command": "apt-get install -y curl && curl -L https://www.opscode.com/chef/install.sh | {{if .Sudo}}sudo{{end}} bash",
      "cookbook_paths": ["chef/site-cookbooks"],
      "run_list": [{{
        "recipe[ruby]",
        "recipe[ruby::user]",
        "recipe[ruby::ruby212]"
      ]
    }
  ],
  ...
}

Il libro di ricette e le ricette dello chef vengono archiviati nello stesso repository Git del modello Packer.

Definizione degli output delle immagini con i builder

La sezione builders del modello definisce dove verranno eseguiti i provisioner per creare nuove immagini. Per creare sia un'immagine Compute Engine sia un'immagine Docker, definisci due builder:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [
    {
      "type": "googlecompute",
      "project_id": "{{user `project_id`}}",
      "source_image": "ubuntu-1410-utopic-v20150202",
      "zone": "us-central1-a",
      "image_name": "{{user `ruby_version_name`}}-{{user `Git_branch`}}-{{user `Git_commit`}}"
    },
    {
      "type": "docker",
      "image": "ubuntu:14.10",
      "commit": "true"
    }
  ],
 ...
}

Lo strumento di creazione googlecompute include un attributo project_id che indica dove verrà archiviata l'immagine risultante. L'attributo image_name, che assegna un nome all'immagine risultante, concatena le variabili per creare un nome con informazioni sull'immagine: la versione di Ruby, il ramo Git e l'ID commit Git utilizzato per creare l'immagine. Un URI di esempio per un'immagine creata dal generatore googlecompute può avere il seguente aspetto:

https://www.googleapis.com/compute/v1/projects/image-builder-project-name/global/images/ruby212-master-9909043

Il generatore docker deve includere un attributo post-processors per codificare l'immagine con il registro Docker e il repository a cui verrà eseguito il push:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [...],
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "gcr.io/{{user `project_id`}}/ruby212",
        "tag": "{{user `Git_branch`}}-{{user `Git_commit`}}",
        "only": ["docker"]
      }
    ]
  ]
}

Questo post-processor tagga l'immagine per l'archiviazione in Container Registry utilizzando il project_id fornito durante l'esecuzione della build. Una volta eseguito il push di questa immagine Docker, puoi recuperarla:

docker pull gcr.io/image-builder-project-name/ruby212:master-9909043

Ogni immagine che vuoi creare avrà un modello Packer e script di configurazione nel proprio repository di codice sorgente e il leader Jenkins avrà un job definito per ciascuno, come mostrato nel diagramma seguente.

Un diagramma che mostra il progetto del generatore di immagini con immagini personalizzate.

Uno dei vantaggi dell'utilizzo combinato di Jenkins e Packer è che Jenkins può rilevare e rispondere agli eventuali aggiornamenti apportati ai modelli o agli script di configurazione di Packer. Ad esempio, se aggiorni la versione di Ruby installata nell'immagine di base di Ruby, il leader Jenkins risponde assegnando un agente a clonare il repository, eseguire Packer sul modello e creare le immagini.

Il tutorial alla fine di questa soluzione illustra nel dettaglio il processo di configurazione di un job Jenkins per l'esecuzione della build Packer.

Isolamento dei progetti

Il leader Jenkins e gli agenti di build vengono eseguiti insieme nello stesso progetto Cloud Platform, e le immagini che creano sono archiviate in questo progetto. I progetti consentono di isolare le applicazioni in base alla funzione. Non sono previsti costi per un progetto; ti vengono addebitati solo i costi delle risorse che utilizzi. In questa soluzione, l'infrastruttura Jenkins verrà eseguita nel proprio progetto, separato dai repository di controllo del codice sorgente che utilizza. I backup di Jenkins, descritti in una sezione futura, vengono archiviati in un bucket Google Cloud Storage all'interno del progetto. Ciò consente a Jenkins di agire come un "hub immagine"; la condivisione di immagini in altri progetti, consentendo ad altri progetti di gestire i propri repository di codice con controlli di accesso separati.

Creazione e condivisione di immagini in un'organizzazione

Per facilitare la condivisione delle immagini, questa soluzione posiziona ogni immagine build creata in Git in un progetto di configurazione delle immagini separato. Questa separazione consente l'isolamento dei progetti tra il progetto di creazione delle immagini e le immagini di build. Con questa architettura hub-and-soke, in cui il progetto del generatore di immagini è l'hub e i progetti di configurazione delle immagini sono gli spoke, team separati possono possedere e gestire più facilmente le configurazioni delle immagini.

L'architettura di hub e spoke è illustrata nel diagramma seguente.

Diagramma che mostra il progetto del generatore di immagini come hub e sistema di spoke.

Il controllo dell'accesso (concedendo al cluster Jenkins l'accesso a ogni progetto di immagine e l'accesso ad altri progetti alle immagini create da Jenkins) verrà esaminato di seguito.

Un progetto per immagine

A ogni progetto creato è associato un Cloud Repository basato su Git dedicato. Non c'è limite al numero di progetti che crei e paghi solo per le risorse che usi in un progetto, ad esempio le istanze di Compute Engine. Ad esempio, se hai immagini PHP, Ruby e Wordpress, ognuna di queste avrà il proprio progetto visibile in Google Cloud Console, come mostrato nel diagramma seguente.

Un diagramma che mostra il progetto di creazione delle immagini con progetti separati per ciascuna immagine personalizzata.

Puoi accedere a Cloud Repository di un progetto dalla voce di menu Codice sorgente. Per i nuovi progetti, scegli come inizializzare il repository: puoi eseguire il mirroring di un repository GitHub o Bitbucket esistente, eseguire il push di un repository Git locale esistente o creare un nuovo repository Git locale da Cloud Source Repositories, come mostrato nell'immagine seguente.

Un'immagine di schermata che mostra come sfogliare il codice sorgente con
      Google Cloud Console.

L'immagine seguente mostra il progetto Image di Ruby Foundation inizializzato con un modello Pack e ricette di chef che definiscono la build.

L'immagine di Ruby Foundation con il modello Packer e le ricette di chef.

Per visualizzare l'URL del repository, fai clic su Impostazioni. Questo URL è necessario quando crei un job di compilazione per il repository sul leader Jenkins, come mostrato nell'immagine seguente.

Le impostazioni del repository di codice sorgente per il leader Jenkins.

Controllo dell'accesso al repository di Cloud

Il generatore di immagini Jenkins deve disporre delle autorizzazioni Può visualizzare per ogni progetto di configurazione delle immagini Cloud Repository. Il seguente diagramma mostra una visualizzazione semplificata dell'architettura di hub e spoke mostrata in precedenza.

Progetto del generatore di immagini con le autorizzazioni necessarie.

Ogni progetto deve concedere l'accesso al progetto Jenkins del generatore di immagini utilizzando l'indirizzo email dell'account di servizio di computing del progetto di creazione di immagini. Il formato dell'indirizzo è \{PROJECT_ID\}-compute@developer.gserviceaccount.com ed è disponibile per essere copiato nella sezione Autorizzazioni di tale progetto in Google Cloud Console, come mostrato nell'immagine seguente.

Indirizzo da copiare dalla sezione Autorizzazioni di un progetto.

Dopo aver creato l'indirizzo email dell'account di servizio Compute per il progetto che esegue il generatore di immagini Jenkins, vai alla sezione Autorizzazioni di ogni progetto con un repository Cloud da cui vuoi creare immagini, seleziona Aggiungi entità e concedi l'autorizzazione Può visualizzare, come mostrato nell'immagine seguente.

L'impostazione può visualizzare le autorizzazioni in un progetto.

Il leader Jenkins in esecuzione nel progetto di creazione delle immagini ora potrà eseguire il pull e il pull dal Cloud Repository in questi progetti e creare nuove immagini man mano che vengono apportate modifiche.

Condivisione di immagini Compute Engine e Docker

Le immagini Compute Engine e Docker create dal generatore di immagini vengono archiviate nello stesso progetto del generatore di immagini. Le immagini verranno utilizzate dalle applicazioni in altri progetti per avviare le istanze Compute Engine e i container Docker e ogni progetto applicativo che desidera accedere a queste immagini deve avere l'autorizzazione Può visualizzare per il progetto di creazione delle immagini. Segui la procedura definita nella sezione precedente, questa volta individuando l'account di servizio Compute di ogni progetto dell'applicazione e aggiungendolo come membro con autorizzazioni di visualizzazione per il progetto di creazione immagini, come mostrato nel diagramma seguente.

L'aggiunta di un altro progetto può visualizzare le autorizzazioni per il progetto del generatore di immagini.

Backup e ripristino di Jenkins

Il leader Jenkins include un job predefinito per il backup periodico della configurazione Jenkins e della cronologia dei job in Google Cloud Storage. Per impostazione predefinita, il job viene eseguito periodicamente (una volta ogni due ore, ogni giorno feriale), come mostrato nell'immagine seguente.

le impostazioni di build automatizzate per il leader di Jenkins.

Il passaggio di build del job esegue uno script shell che archivia secret, utenti, job e cronologia in un tarball. Le copie dell'archivio create sono due: una ha un nome con il timestamp, l'altra è LULTI, che ti consente di ripristinare facilmente e automaticamente il backup più recente. Puoi personalizzare questo passaggio per aggiungere o rimuovere elementi di cui eseguire il backup, come mostrato nell'immagine seguente.

Come personalizzare lo script di build.

Un'azione post-build utilizza il plug-in Cloud Storage e le credenziali dei metadati Google che hai creato per interagire con le API di Google e caricare l'archivio di backup in Cloud Storage. Carica sia gli archivi che la data ULTIMI archivi. L'immagine seguente mostra la definizione del passaggio.

Interfaccia per definire le azioni post-build.

L'immagine seguente mostra un bucket con alcuni backup accumulati:

Elenco dei backup accumulati per un progetto.

Ripristino di un backup

Nello stesso modo in cui utilizzi le variabili di ambiente per abilitare SSL o autenticazione di base sul proxy inverso Nginx in una sezione precedente, puoi utilizzare una variabile di ambiente per configurare la definizione del controller di replica del leader Jenkins in modo che ripristini un backup all'avvio del servizio. Il codice seguente è uno snippet della definizione del controller di replica:

{
  "kind": "ReplicationController",
  ...
  "spec": {
    ...
    "template": {
      "spec": {
        "containers": [
            {
              "name": "jenkins",
              "env": [
                {
                  "name": "GCS_RESTORE_URL",
                  "value": "gs://your-backup-bucket/jenkins-backup/LATEST.tar.gz"
                }
              ],
             ...
           }
        ]
      }
    }
  }
}

L'immagine Docker di leader Jenkins controlla l'esistenza della variabile di ambiente GCS_RESTORE_URL all'avvio. Se lo trovi, il valore è considerato l'URL del backup (incluso lo schema gs://) e lo script utilizza lo strumento a riga di comando gsutil installato sull'immagine lead di Jenkins per scaricare e ripristinare il backup in modo sicuro.

Il processo di ripristino avviene solo all'avvio di un contenitore. Per ripristinare un backup dopo aver avviato un leader Jenkins, ridimensiona il controller di replica in 0, aggiorna la definizione del controller in modo che punti all'URL del backup, quindi imposta le dimensioni nuovamente su 1. Ne parleremo nel tutorial.

Tutorial

I contenuti completi del tutorial, incluse le istruzioni e il codice sorgente, sono disponibili su GitHub all'indirizzo https://github.com/GoogleCloudPlatform/kube-jenkins-imager.