GNU/Linux >> Linux Esercitazione >  >> Linux

Aggiornamento dei contenitori Docker con tempi di inattività pari a zero o minimi

Si supponga di eseguire un servizio in un contenitore e che sia disponibile una nuova versione del servizio tramite l'immagine della finestra mobile. In tal caso, vorresti aggiornare il contenitore Docker.

L'aggiornamento di un container Docker non è un problema, ma l'aggiornamento del container Docker senza tempi di inattività è impegnativo.

Confuso? Lascia che ti mostri entrambi i modi uno per uno.

Metodo 1:aggiornamento del container docker all'immagine più recente (risulta in tempi di inattività)

Questo metodo consiste fondamentalmente in questi passaggi:

  • Estrarre l'ultima immagine della finestra mobile
  • Interrompi e rimuovi il contenitore che esegue la vecchia immagine Docker
  • Crea un nuovo contenitore con l'immagine della finestra mobile appena estratta

Vuoi i comandi? Ecco a te.

Elenca le immagini della finestra mobile e ottieni l'immagine della finestra mobile con un aggiornamento. Ottieni le ultime modifiche a questa immagine usando il comando pull della finestra mobile:

docker pull image_name

Ora ottieni l'ID contenitore o il nome del contenitore che esegue l'immagine Docker precedente. Utilizzare il comando docker ps per questo scopo. Ferma questo contenitore:

docker stop container_ID

E rimuovi il contenitore:

docker rm container_id

Il passaggio successivo consiste nell'eseguire un nuovo contenitore con gli stessi parametri utilizzati per l'esecuzione del contenitore precedente. Credo che tu sappia quali sono quei parametri perché li avevi creati in primo luogo.

docker run --name=container_name [options] docker_image

Vedi il problema con questo approccio? Devi interrompere il contenitore in esecuzione e quindi crearne uno nuovo. Ciò comporterà un tempo di inattività per il servizio in esecuzione.

I tempi di inattività, anche se per un minuto, potrebbero avere un grande impatto se si tratta di un progetto mission-critical o di un servizio web ad alto traffico.

Vuoi conoscere un approccio più sicuro e migliore per questo problema? Leggi la prossima sezione.

Metodo 2:aggiornamento del contenitore docker in una configurazione del proxy inverso (con zero o tempi di fermo minimi)

Se stavi cercando una soluzione semplice, mi dispiace deluderti ma non sarà una soluzione perché qui dovrai distribuire i tuoi container in un'architettura proxy inverso con Docker Compose.

Se stai cercando di gestire i servizi critici utilizzando i contenitori docker, il metodo del proxy inverso ti aiuterà molto a lungo termine.

Consentitemi di elencare tre vantaggi principali della configurazione del proxy inverso:

  • Puoi distribuire più servizi rivolti al pubblico sullo stesso server. Nessun blocco delle porte qui.
  • Il server Let's Encrypt si occupa della distribuzione SSL per tutti i servizi, tutti i container.
  • Puoi aggiornare i contenitori senza influire sui servizi in esecuzione (per la maggior parte dei servizi Web).

Se sei curioso di saperne di più, puoi consultare il glossario ufficiale di Nginx che mette in evidenza gli usi comuni di un proxy inverso e come si confronta con un sistema di bilanciamento del carico.

Abbiamo un ottimo tutorial approfondito sulla configurazione del proxy inverso Nginx per ospitare più di un'istanza di servizi Web in esecuzione in contenitori sullo stesso server. Quindi, non ho intenzione di discuterne di nuovo qui. Dovresti prima configurare i tuoi container usando questa architettura. Credimi, ne vale la pena.

In questo tutorial, ho progettato una metodologia passo passo che può essere molto utile nelle tue attività DevOps quotidiane. Questo requisito può non solo essere molto necessario quando aggiorni i tuoi container, ma anche quando desideri apportare una modifica molto necessaria a una qualsiasi delle tue app in esecuzione senza sacrificare il prezioso tempo di attività.

Da qui in poi, assumiamo che tu stia eseguendo le tue applicazioni web con una configurazione del proxy inverso che assicurerebbe che il reindirizzamento funzioni per il nuovo contenitore aggiornato come previsto dopo le modifiche alla configurazione che faremo.

Mostrerò prima i passaggi di questo metodo, seguiti da un esempio di vita reale.

Passaggio 1:aggiorna il file di composizione della finestra mobile

Prima di tutto, devi modificare il file di composizione della finestra mobile esistente con il numero di versione dell'ultima immagine. Può essere visualizzato su Docker Hub, in particolare nella sezione "tag" dell'applicazione.

Spostarsi nella directory dell'applicazione e modificare il file di composizione mobile con un editor di testo della riga di comando. Ho usato Nano qui.

[email protected]:~/web-app$ nano docker-compose.yml

All'interno di services: , aggiorna image: web-app:x.x.x con il numero di versione più recente e salva il file.

Potresti chiederti perché non utilizzare comunque l'ultimo tag invece di specificare manualmente il numero di versione? L'ho fatto apposta poiché ho notato che durante l'aggiornamento dei contenitori, può esserci un ritardo intermittente dell'ultimo tag nel raccogliere effettivamente l'ultima versione dell'applicazione dockerizzata. Quando usi direttamente il numero di versione, puoi sempre essere assolutamente certo.

Passaggio 2:ridimensionamento di un nuovo container

Quando utilizzi il comando seguente, viene creato un nuovo contenitore in base alle nuove modifiche apportate nel file di composizione della finestra mobile.

[email protected]:~/web-app$ docker-compose up -d --scale web-app=2 --no-recreate

Si noti che il contenitore precedente è ancora attivo e funzionante. Il --scale flag viene utilizzato per creare contenitori aggiuntivi come specificato. Qui, web-app è stato impostato come nome del servizio per l'applicazione Web.

Anche se specifichi il ridimensionamento fino a 2 contenitori, --no-recreate assicura che ne venga aggiunto solo uno poiché hai già il tuo vecchio contenitore in esecuzione.

Per saperne di più su --scale e --no-recreate flag, controlla la pagina della documentazione ufficiale per la composizione della finestra mobile.

Fase 3:rimuovi il vecchio contenitore

Dopo il passaggio 2, attendi circa 15-20 secondi affinché le nuove modifiche abbiano effetto, quindi rimuovi il vecchio contenitore:

[email protected]:~/web-app$ docker rm -f old-web-app

In diverse app Web, le modifiche riflesse sono comportamentalmente diverse dopo aver eseguito il comando precedente (discusso come suggerimento bonus nella parte inferiore di questo tutorial).

Passaggio 4:ridimensiona alla configurazione del contenitore singolo come prima

Per il passaggio finale, riduci ancora una volta la configurazione del contenitore singolo:

[email protected]:~/web-app$ docker-compose up -d --scale web-app=1 --no-recreate

Ho testato questo metodo con istanze Ghost, WordPress, Rocket.Chat e Nextcloud. A parte il passaggio di Nextcloud alla modalità di manutenzione per alcuni secondi, la procedura funziona molto bene per gli altri tre.

Il discorso, tuttavia, è un'altra storia e può essere un'eccezione molto delicata in questo caso a causa del suo modello ibrido.

Il risultato finale è:più l'app Web utilizza la pratica docker standard durante la docker, più diventa conveniente gestire tutti i contenitori di app Web su base giornaliera.

Esempio reale:aggiornamento di un'istanza Ghost live senza tempi di inattività

Come promesso, vi mostrerò un esempio di vita reale. Ti mostrerò come aggiornare Ghost in esecuzione nel contenitore Docker a una versione più recente senza tempi di inattività.

Ghost è un CMS e lo usiamo per Linux Handbook. L'esempio mostrato qui è quello che utilizziamo per aggiornare la nostra istanza Ghost che esegue questo sito Web.

Supponiamo di avere una configurazione esistente basata su una versione precedente situata in /home/avimanyu/ghost :

version: '3.5'
services:
  ghost:
    image: ghost:3.36
    volumes:
      - ghost:/var/lib/ghost/content
    environment:
      - VIRTUAL_HOST=blog.domain.com
      - LETSENCRYPT_HOST=blog.domain.com
      - url=https://blog.domain.com
      - NODE_ENV=production
    restart: always
    networks:
      - net

volumes:
  ghost:
    external: true

networks:
  net:
    external: true

Nota che la configurazione di composizione della finestra mobile sopra si basa su una configurazione della finestra mobile Nginx preesistente descritta qui, in esecuzione su una rete denominata net . Anche il volume della finestra mobile è stato creato manualmente con docker volume create ghost-blog .

Quando lo controllo con docker ps :

CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
2df6c27c1fe3        ghost:3.36                             "docker-entrypoint.s…"   9 days ago          Up 7 days           2368/tcp                                   ghost_ghost-blog_1
89a5a7fdcfa4        jrcs/letsencrypt-nginx-proxy-companion   "/bin/bash /app/entr…"   9 days ago          Up 7 days                                                      letsencrypt-helper
90b72e217516        jwilder/nginx-proxy                      "/app/docker-entrypo…"   9 days ago          Up 7 days           0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   reverse-proxy

Al momento in cui scrivo, questa è una versione precedente di Ghost. È ora di aggiornarlo all'ultima versione 3.37.1! Quindi, lo rivedo nella sezione dell'immagine come:

version: '3.5'
services:
  ghost-blog:
    image: ghost:3.37.1
    volumes:
      - ghost-blog:/var/lib/ghost/content
    environment:
      - VIRTUAL_HOST=blog.domain.com
      - LETSENCRYPT_HOST=blog.domain.com
      - url=https://blog.domain.com
      - NODE_ENV=production
    restart: always
    networks:
      - net

volumes:
  ghost-blog:
    external: true

networks:
  net:
    external: true

Ora per sfruttare al meglio il metodo di ridimensionamento:

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=2 --no-recreate

Con il comando precedente, il contenitore più vecchio rimane inalterato ma uno nuovo si unisce con la stessa configurazione ma basato sull'ultima versione di Ghost:

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=2 --no-recreate
Pulling ghost (ghost:3.37.1)...
3.37.1: Pulling from library/ghost
bb79b6b2107f: Already exists
99ce436c3449: Already exists
f7bdc31da5f5: Already exists
7a1300b9ff59: Already exists
a495c68fa838: Already exists
6e362a39ec35: Already exists
b68b4f3c36f7: Already exists
41f8b02d4a71: Pull complete
3ecc736ea4e5: Pull complete
Digest: sha256:595c759980cd22e99037811397012908d89efb799776db222a4be6d4d892917c
Status: Downloaded newer image for ghost:3.37.1
Starting ghost_ghost-blog_1 ... done
Creating ghost_ghost-blog_2 ... done

Se avessi usato l'approccio convenzionale con docker-compose up -d invece, non avrei potuto evitare che la ricreazione del container esistente fosse basata sull'ultima immagine di Ghost.

La ricreazione comporta la rimozione del contenitore più vecchio e la creazione di uno nuovo al suo posto con le stesse impostazioni. Questo è quando si verificano tempi di inattività e il sito diventa inaccessibile.

Questo è il motivo per cui dovresti usare --no-recreate flag durante il ridimensionamento.

Quindi ora ho due contenitori in esecuzione basati sulla stessa configurazione fantasma. Questa è la parte cruciale in cui evitiamo i tempi di fermo:

[email protected]:~/ghost$ docker ps
CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
f239f677de54        ghost:3.37.1                               "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        2368/tcp                                   ghost_ghost-blog_2
2df6c27c1fe3        ghost:3.36                             "docker-entrypoint.s…"   9 days ago          Up 7 days           2368/tcp                                   ghost_ghost-blog_1
89a5a7fdcfa4        jrcs/letsencrypt-nginx-proxy-companion   "/bin/bash /app/entr…"   9 days ago          Up 7 days                                                      letsencrypt-helper
90b72e217516        jwilder/nginx-proxy                      "/app/docker-entrypo…"   9 days ago          Up 7 days           0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   reverse-proxy

Nota che il nome del contenitore precedente è ghost_ghost-blog_1 . Controlla il tuo nome di dominio e lo troverai ancora accessibile su blog.domain.com . Se aggiorni il pannello di amministrazione di Ghost, che si trova su blog.domain.com/ghost dopo il ridimensionamento, continuerebbe a provare a caricarsi fino a quando non rimuovi il vecchio contenitore:

[email protected]:~/ghost$ docker rm -f ghost_ghost-blog_1

Ma per il blog Ghost stesso, non ci sono tempi di inattività! Quindi, in questo modo puoi garantire zero tempi di inattività durante l'aggiornamento dei blog Ghost.

Infine, ridimensiona la configurazione alle impostazioni originali:

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=1 --no-recreate
Starting ghost_ghost-blog_2 ... done

Come accennato in precedenza, dopo aver rimosso i vecchi contenitori, le modifiche si riflettono nelle rispettive app Web, ma ovviamente si comportano in modo diverso a causa dei diversi design delle app.

Ecco alcune osservazioni:

Su WordPress :Assicurati di aggiungere define( 'AUTOMATIC_UPDATER_DISABLED', true ); come riga di fondo sul file wp-config.php che si trova in /var/www/html e monta /var/www/html/wp-content invece di /var/www/html come volume. Controlla qui per i dettagli. Dopo il passaggio 3, il pannello di amministrazione di WordPress mostrerà che il tuo WordPress è aggiornato e ti chiederà di procedere e aggiornare il tuo database. L'aggiornamento avviene rapidamente senza alcun downtime sul sito WordPress e il gioco è fatto!

Su Rocket.Chat :Possono essere necessari circa 15-20 secondi per le Admin>Info pagina per mostrare che stai eseguendo l'ultima versione anche prima di eseguire il passaggio 3. Niente più tempi di inattività!

Su Nextcloud :Dopo il passaggio 2, Nextcloud passerà alla modalità di manutenzione per alcuni secondi e quindi caricherà nuovamente i file. In Administration > Overview > Security & setup warnings , potresti ricevere un avviso del tipo "Il tuo server web non è impostato correttamente per risolvere "./well-known/carddav". Ciò è dovuto al fatto che il tuo vecchio contenitore è ancora in esecuzione. Una volta rimosso con il passaggio 3, questo avviso sarebbe non esiste più. Assicurati di concedergli un po' di tempo prima di accedere al tuo URL Nextcloud in quanto potrebbe mostrare un errore gateway 502 errato finché il tuo contenitore Nginx non vede il contenitore Nextcloud appena ridimensionato in base all'ultima versione.

Suggerimenti bonus

Ecco alcuni suggerimenti e cose da tenere a mente mentre segui questo metodo.

Suggerimento 1

Per ridurre al minimo i tempi di inattività a zero tra le diverse applicazioni, assicurati di fornire tempo sufficiente ai contenitori appena ridimensionati e aggiornati in modo che i contenitori Nginx possano finalmente riconoscerli prima di rimuovere quelli vecchi nel passaggio 2.

Prima di passare al passaggio 2 sopra descritto, è consigliabile osservare il comportamento dell'applicazione sul browser (sia come aggiornamento della pagina dopo l'accesso che come nuovo accesso alla pagina su una finestra del browser privata e priva di cache) dopo aver eseguito il passaggio 1.

Poiché ogni applicazione è progettata in modo diverso, questa misura si rivelerebbe molto utile prima di far cadere i tuoi vecchi contenitori.

Suggerimento 2

Sebbene questo possa essere un metodo molto pieno di risorse per aggiornare i tuoi contenitori alle versioni più recenti delle app che eseguono, tieni presente che puoi utilizzare lo stesso metodo anche per apportare modifiche alla configurazione o modificare le impostazioni ambientali senza dover affrontare problemi di tempi di inattività.

Questo può essere fondamentale se devi risolvere un problema o eseguire una modifica che potresti ritenere necessaria in un contenitore attivo, ma non vuoi eliminarlo mentre lo fai. Dopo aver apportato le modifiche e aver verificato che il problema sia stato risolto, puoi eliminare facilmente il vecchio.

Abbiamo affrontato questo problema quando abbiamo scoperto che la rotazione dei log non era abilitata in uno dei nostri contenitori live. Abbiamo apportato le modifiche necessarie per abilitarlo e allo stesso tempo abbiamo evitato qualsiasi tempo di inattività mentre lo facevamo con questo metodo.

Suggerimento 3

Se hai assegnato un nome specifico al tuo container nel file YML usando container_name , il metodo non funzionerà come previsto perché prevede la creazione di un nuovo nome contenitore.

Puoi evitare questo conflitto lasciando l'attività di denominazione del contenitore su Docker Compose (fatto automaticamente secondo la sua convenzione di denominazione). Docker Compose nomina i suoi contenitori come directory-name_service-name_1 . Il numero alla fine aumenterebbe ogni volta che il contenitore viene aggiornato (fino a quando non usi docker-compose down per qualche motivo).

Nel caso in cui stai già utilizzando un servizio utilizzando contenitori con nome personalizzato nel file di composizione della finestra mobile, per utilizzare questo metodo, è sufficiente commentare la riga (con un prefisso #) che include container_name all'interno della definizione del servizio.

Dopo aver creato il nuovo contenitore per quanto sopra utilizzando il passaggio 1 come descritto in questo tutorial, per il passaggio 2, il nome del vecchio contenitore (che non è stato interrotto per evitare tempi di inattività) sarebbe come specificato in precedenza utilizzando container_name (può anche essere verificato con `docker ps ` prima di rimuovere il vecchio contenitore).

Hai imparato qualcosa di nuovo?

So che questo articolo è un po' lungo, ma spero che aiuti la nostra community DevOps a gestire i problemi di downtime con i server di produzione che eseguono applicazioni Web dockerizzate. I tempi di fermo hanno un enorme impatto sia a livello individuale che commerciale ed è per questo che trattare questo argomento era un'assoluta necessità.

Partecipa alla discussione e condividi i tuoi pensieri, feedback o suggerimenti nella sezione commenti qui sotto.


Linux
  1. Come installare WordPress con Docker su Ubuntu

  2. Installa ModSecurity con Apache in un Docker Container

  3. Come distribuire un container nginx con Docker su Linode

  4. Aggiornamento di un container distribuito in base a un'immagine Docker

  5. Come aggiornare il contenitore Docker senza tempi di inattività

Come modificare il codice nei contenitori Docker con il codice di Visual Studio

Come eseguire i contenitori Docker

Utilizzo dei contenitori Docker dalla riga di comando

Come rinominare o rinominare i contenitori Docker

Procedura:Introduzione a Windows Containers e Docker

Come gestire i container Docker