In questo articolo useremo , , e per organizzare un deployment senza interruzioni dell'applicazione web. è una tecnica che consente di aggiornare immediatamente un'applicazione senza rifiutare alcuna richiesta. È una delle strategie di zero downtime deployment e si adatta meglio ad applicazioni con un'istanza singola, ma con la possibilità di caricarne una seconda, pronta per l'uso, accanto alla prima.
Supponiamo che tu abbia un'applicazione web con cui molti clienti stanno interagendo attivamente e che non possa assolutamente essere fermata anche solo per qualche secondo. E tu hai davvero bisogno di rilasciare un aggiornamento della libreria, una correzione di un bug o una nuova funzionalità straordinaria. Di solito, in una situazione normale, dovresti fermare l'applicazione, sostituirla e poi riavviarla. Con Docker, puoi prima sostituire e poi riavviare, ma ci sarà comunque un periodo in cui le richieste all'applicazione non verranno elaborate, poiché di solito l'applicazione richiede del tempo per il caricamento iniziale. E se si avvia, ma risulta non funzionante? Ecco un compito: vediamo come affrontarlo con il minimo sforzo e con il massimo di eleganza.
DISCLAIMER: La maggior parte dell'articolo è presentata in un formato sperimentale — sotto forma di registrazione di una sessione della console. Spero che non sia troppo difficile da comprendere e che questo codice si auto-documenti a sufficienza. Per creare un'atmosfera, immaginate che non siano solo dei frammenti di codice, ma la carta di un teletipo "metallico".
Tecniche interessanti, che sono difficili da trovare semplicemente leggendo il codice, sono descritte all'inizio di ogni sezione. Se ci fosse qualcosa che non è chiaro — cercate su Google e controllate in (fortunatamente, è di nuovo funzionante grazie alla sblocco di Telegram). Ciò che non è ricercabile — chiedete nei commenti. Sarò felice di integrare la sezione "Tecniche interessanti".
Iniziamo.
$ mkdir blue-green-deployment && cd $_Servizio
Creeremo un servizio di prova e lo inseriremo in un contenitore.
Tecniche interessanti
cat < file-name( + ) — è un modo per creare un file multilinea con un solo comando. Tutto ciò che bash leggerà da/dev/stdindopo questa riga e fino alla rigaEOFverrà scritto infile-name.wget -qO- URL() — per stampare il documento ottenuto tramite HTTP in/dev/stdout(analogamente acurl URL).
Stampa
Sto appositamente rompendo il snippet per includere l'evidenziazione per Python. Alla fine ci sarà un altro pezzo simile. Considerate che in questi punti la carta è stata tagliata per il trasferimento al reparto evidenziazione (dove il codice è stato colorato a mano con evidenziatori), e poi questi pezzi sono stati reinseriti.
$ cat < uptimer.pyfrom http.server import BaseHTTPRequestHandler, HTTPServer
from time import monotonic
app_version = 1
app_name = f'Uptimer v{app_version}.0'
loading_seconds = 15 - app_version * 5
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
try:
t = monotonic() - server_start
if t < loading_seconds:
self.send_error(503)
else:
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
response = f'<h2>{app_name} è in esecuzione da {t:3.1f} secondi.</h2>n'
self.wfile.write(response.encode('utf-8'))
except Exception:
self.send_error(500)
else:
self.send_error(404)
httpd = HTTPServer(('', 8080), Handler)
server_start = monotonic()
print(f'{app_name} (si carica in {loading_seconds} sec.) avviato.')
httpd.serve_forever()EOF
$ cat << EOF > Dockerfile
FROM python:alpine
EXPOSE 8080
COPY uptimer.py app.py
CMD [ "python", "-u", "./app.py" ]
EOF
$ docker build --tag uptimer .
Invio del contesto di build al demone Docker 39.42kB
Passo 1/4 : FROM python:alpine
---> 8ecf5a48c789
Passo 2/4 : EXPOSE 8080
---> Utilizzando la cache
---> cf92d174c9d3
Passo 3/4 : COPY uptimer.py app.py
---> a7fbb33d6b7e
Passo 4/4 : CMD [ "python", "-u", "./app.py" ]
---> Esecuzione in 1906b4bd9fdf
Rimozione del container intermedio 1906b4bd9fdf
---> c1655b996fe8
Build completato con successo c1655b996fe8
Tag completato con successo uptimer:latest
$ docker run --rm --detach --name uptimer --publish 8080:8080 uptimer
8f88c944b8bf78974a5727070a94c76aa0b9bb2b3ecf6324b784e782614b2fbf
$ docker ps
ID CONTENITORE IMMAGINE COMANDO CREATO STATO PORTE NOME
8f88c944b8bf uptimer "python -u ./app.py" 3 secondi fa In esecuzione da 5 secondi 0.0.0.0:8080->8080/tcp uptimer
$ docker logs uptimer
Uptimer v1.0 (carica in 10 sec.) avviato.
$ wget -qSO- http://localhost:8080
HTTP/1.0 503 Servizio non disponibile
Server: BaseHTTP/0.6 Python/3.8.3
Data: Sab, 22 Ago 2020 19:52:40 GMT
Connessione: chiusa
Content-Type: text/html;charset=utf-8
Content-Length: 484
$ wget -qSO- http://localhost:8080
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.8.3
Data: Sab, 22 Ago 2020 19:52:45 GMT
Content-Type: text/html
<h2>Uptimer v1.0 è in esecuzione da 15.4 secondi.</h2>
$ docker rm --force uptimer
uptimerReverse proxy
Per permettere alla nostra applicazione di avere la possibilità di cambiare senza essere notata, è necessario che ci sia un'altra entità davanti a essa, che nasconda il suo cambio. Questo può essere un server web in . Il reverse proxy si colloca tra il cliente e l'applicazione. Riceve richieste dai clienti e le inoltra all'applicazione, mentre le risposte dell'applicazione sono inviate ai clienti.
L'applicazione e il reverse proxy possono essere collegati all'interno di Docker tramite . In questo modo, il contenitore con l'applicazione potrebbe anche non esporre porte nel sistema host, permettendo di isolare l'applicazione dalle minacce esterne.
Se il reverse proxy deve trovarsi su un host diverso, sarà necessario rinunciare alla docker network e collegare l'applicazione al reverse proxy tramite la rete dell'host, esponendo la porta applicativi parametro --publish, come al primo avvio e come per il reverse proxy.
Il reverse proxy verrà avviato sulla porta 80, poiché è proprio questa l'entità che deve ascoltare l'esterno. Se la porta 80 è occupata sul tuo host di test, cambia il parametro --publish 80:80 con --publish ANY_FREE_PORT:80.
Tecniche interessanti
- «Nelle reti Docker create dall'utente, i container possono essere connessi non solo tramite indirizzo IP. Anche il nome del container si risolve nel suo indirizzo IP» (, punto 5 del codice Docker).
Stampa
$ docker network create web-gateway
5dba128fb3b255b02ac012ded1906b7b4970b728fb7db3dbbeccc9a77a5dd7bd
$ docker run --detach --rm --name uptimer --network web-gateway uptimer
a1105f1b583dead9415e99864718cc807cc1db1c763870f40ea38bc026e2d67f
$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer:8080
<h2>Uptimer v1.0 è in esecuzione da 11,5 secondi.</h2>
$ docker run --detach --publish 80:80 --network web-gateway --name reverse-proxy nginx:alpine
80695a822c19051260c66bf60605dcb4ea66802c754037704968bc42527bf120
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80695a822c19 nginx:alpine "/docker-entrypoint.…" 27 secondi fa In esecuzione da 25 secondi 0.0.0.0:80->80/tcp reverse-proxy
a1105f1b583d uptimer "python -u ./app.py" Circa un minuto fa In esecuzione da circa un minuto 8080/tcp uptimer
$ cat << EOF > uptimer.conf
server {
listen 80;
location / {
proxy_pass http://uptimer:8080;
}
}
EOF
$ docker cp ./uptimer.conf reverse-proxy:/etc/nginx/conf.d/default.conf
$ docker exec reverse-proxy nginx -s reload
2020/06/23 20:51:03 [notice] 31#31: signal process started
$ wget -qSO- http://localhost
HTTP/1.1 200 OK
Server: nginx/1.19.0
Date: Sab, 22 Ago 2020 19:56:24 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
<h2>Uptimer v1.0 è in esecuzione da 104,1 secondi.</h2>Deploy senza interruzioni
Distribuiremo una nuova versione dell'applicazione (con un raddoppio delle prestazioni all'avvio) e cercheremo di effettuare il deploy senza interruzioni.
Tecniche interessanti
echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt'— Scrivere il testomy textnel file/my-file.txtall'interno del containermy-container.cat > /my-file.txt— Scrivere nel file il contenuto dell'input standard/dev/stdin.
Stampa
$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py
$ docker build --tag uptimer .
Invio contesto di build al daemon Docker 39.94kB
Passo 1/4 : FROM python:alpine
---> 8ecf5a48c789
Passo 2/4 : EXPOSE 8080
---> Uso della cache
---> cf92d174c9d3
Passo 3/4 : COPY uptimer.py app.py
---> 3eca6a51cb2d
Passo 4/4 : CMD [ "python", "-u", "./app.py" ]
---> Esecuzione in 8f13c6d3d9e7
Rimozione del contenitore intermedio 8f13c6d3d9e7
---> 1d56897841ec
Costruito con successo 1d56897841ec
Taggato con successo uptimer:latest
$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer
96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02
$ docker logs uptimer_BLUE
Uptimer v2.0 (carica in 5 sec.) avviato.
$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 è in esecuzione da 23.9 secondi.</h2>
$ sed s/uptimer/uptimer_BLUE/ uptimer.conf | docker exec --interactive reverse-proxy sh -c 'cat > /etc/nginx/conf.d/default.conf'
$ docker exec reverse-proxy cat /etc/nginx/conf.d/default.conf
server {
listen 80;
location / {
proxy_pass http://uptimer_BLUE:8080;
}
}
$ docker exec reverse-proxy nginx -s reload
2020/06/25 21:22:23 [notice] 68#68: il processo del segnale è avviato
$ wget -qO- http://localhost
<h2>Uptimer v2.0 è in esecuzione da 63.4 secondi.</h2>
$ docker rm -f uptimer
uptimer
$ wget -qO- http://localhost
<h2>Uptimer v2.0 è in esecuzione da 84.8 secondi.</h2>
$ docker ps
ID CONTENITORE IMMAGINE COMANDO CREATO STATO PORTE NOMI
96932d4ca97a uptimer "python -u ./app.py" Circa un minuto fa In esecuzione da circa un minuto 8080/tcp uptimer_BLUE
80695a822c19 nginx:alpine "/docker-entrypoint.…" 8 minuti fa In esecuzione da 8 minuti 0.0.0.0:80->80/tcp reverse-proxyA questo punto, l'immagine viene costruita direttamente sul server, il che richiede che i sorgenti dell'applicazione siano presenti lì e carica il server di lavoro superfluo. Il passo successivo sarà l'assegnazione della costruzione dell'immagine a una macchina separata (ad esempio, in un sistema CI) e la successiva trasmissione al server.
Trasferimento delle immagini
Sfortunatamente, non ha senso trasferire immagini da localhost a localhost, quindi questa sezione può essere esplorata solo avendo a disposizione due host con Docker. In minima parte, si presenta così:
$ ssh production-server docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
$ docker image save uptimer | ssh production-server 'docker image load'
Loaded image: uptimer:latest
$ ssh production-server docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
uptimer latest 1d56897841ec 5 minutes ago 78.9MBTeam docker save salva i dati dell'immagine in un archivio .tar, il che significa che pesa circa 1,5 volte di più di quanto potrebbe pesare in forma compressa. Quindi, comprimiamolo in nome del risparmio di tempo e traffico:
$ docker image save uptimer | gzip | ssh production-server 'zcat | docker image load'
Loaded image: uptimer:latestInoltre, è possibile monitorare il processo di trasferimento (anche se per questo è necessaria un'utilità esterna):
$ docker image save uptimer | gzip | pv | ssh production-server 'zcat | docker image load'
25,7MiB 0:01:01 [ 425KiB/s] [ <= ]
Loaded image: uptimer:latestSuggerimento: Se per connetterti al server tramite SSH hai bisogno di molti parametri, potrebbe essere che non stai usando il file
~/.ssh/config.
Trasferimento dell'immagine tramite docker image save/load è il metodo più minimale, ma non l'unico. Ce ne sono altri:
- Container Registry (standard di settore).
- Connettersi al docker daemon del server da un altro host:
- Variabile d'ambiente
DOCKER_HOST. - Parametro della riga di comando
-Ho--hostuno strumentodocker-compose. contesto docker
- Variabile d'ambiente
Il secondo metodo (con tre varianti di implementazione) è ben descritto nell'articolo .
deploy.sh
Ora uniremo tutto ciò che abbiamo fatto manualmente in un unico script. Iniziamo con la funzione principale, poi daremo un'occhiata alle altre utilizzate in essa.
Tecniche interessanti
${parameter?err_msg}— uno degli incantesimi della magia bash (aka ). Seil parametronon è specificato, mostrareerr_msged uscire con codice 1.docker --log-driver journald— per impostazione predefinita, il driver di logging di docker è un file di testo senza alcuna rotazione. Con questo approccio, i log riempiono rapidamente l'intero disco, quindi per un ambiente di produzione è necessario cambiare il driver con uno più intelligente.
Script di deployment
deploy() {
local usage_msg="Usage: ${FUNCNAME[0]} image_name"
local image_name=${1?$usage_msg}
ensure-reverse-proxy || return 2
if get-active-slot $image_name
then
local OLD=${image_name}_BLUE
local new_slot=GREEN
else
local OLD=${image_name}_GREEN
local new_slot=BLUE
fi
local NEW=${image_name}_${new_slot}
echo "Deploying '$NEW' in place of '$OLD'..."
docker run
--detach
--restart always
--log-driver journald
--name $NEW
--network web-gateway
$image_name || return 3
echo "Container started. Checking health..."
for i in {1..20}
do
sleep 1
if get-service-status $image_name $new_slot
then
echo "New '$NEW' service seems OK. Switching heads..."
sleep 2 # Ensure service is ready
set-active-slot $image_name $new_slot || return 4
echo "'$NEW' service is live!"
sleep 2 # Ensure all requests were processed
echo "Killing '$OLD'..."
docker rm -f $OLD
docker image prune -f
echo "Deployment successful!"
return 0
fi
echo "New '$NEW' service is not ready yet. Waiting ($i)..."
done
echo "New '$NEW' service did not raise, killing it. Failed to deploy T_T"
docker rm -f $NEW
return 5
}Funzioni utilizzate:
ensure-reverse-proxy— Si assicura che il reverse proxy sia attivo (utile per il primo deployment)get-active-slot service_name— Determina quale slot è attualmente attivo per il servizio specificato (BLUEoGREEN)get-service-status service_name deployment_slot— Verifica se il servizio è pronto a gestire le richieste in ingressoset-active-slot service_name deployment_slot— Modifica la configurazione di nginx nel container reverse proxy
In ordine:
assicurati-ritorno-proxy() {
è-contenitore-in-esecuzione ritornare-proxy && return 0
echo "Distribuendo ritorno-proxy..."
docker network create web-gateway
docker run
--detached
--restart sempre
--log-driver journald
--name ritorno-proxy
--network web-gateway
--publish 80:80
nginx:alpine || return 1
docker exec --interactive ritorno-proxy sh -c "> /etc/nginx/conf.d/default.conf"
docker exec ritorno-proxy nginx -s reload
}
è-contenitore-in-esecuzione() {
local contenitore=${1?"Utilizzo: ${FUNCNAME[0]} nome_contenitore"}
[ -n "$(docker ps -f name=${contenitore} -q)" ]
return $?
}
ottieni-slot-attivo() {
local servizio=${1?"Utilizzo: ${FUNCNAME[0]} nome_servizio"}
if è-contenitore-in-esecuzione ${servizio}_BLU && è-contenitore-in-esecuzione ${servizio}_VERDE; then
echo "Collisione rilevata! Arrestando ${servizio}_VERDE..."
docker rm -f ${servizio}_VERDE
return 0 # BLU
fi
if è-contenitore-in-esecuzione ${servizio}_BLU && ! è-contenitore-in-esecuzione ${servizio}_VERDE; then
return 0 # BLU
fi
if ! è-contenitore-in-esecuzione ${servizio}_BLU; then
return 1 # VERDE
fi
}
ottieni-stato-servizio() {
local msg_utilizzo="Utilizzo: ${FUNCNAME[0]} nome_servizio slot_distribuzione"
local servizio=${1?msg_utilizzo}
local slot=${2?$msg_utilizzo}
case $servizio in
# Aggiungi percorsi di controllo salute specifici per i tuoi servizi qui
*) local percorso_controllo_salute=":8080/" ;;
esac
local indirizzo_controllo_salute="http://${servizio}_${slot}${percorso_controllo_salute}"
echo "Richiesta '$indirizzo_controllo_salute' all'interno della rete 'web-gateway' docker:"
docker run --rm --network web-gateway alpine
wget --timeout=1 --quiet --server-response $indirizzo_controllo_salute
return $?
}
imposta-slot-attivo() {
local msg_utilizzo="Utilizzo: ${FUNCNAME[0]} nome_servizio slot_distribuzione"
local servizio=${1?$msg_utilizzo}
local slot=${2?$msg_utilizzo}
[ "$slot" == BLU ] || [ "$slot" == VERDE ] || return 1
ottieni-config-nginx $servizio $slot | docker exec --interactive ritorno-proxy sh -c "cat > /etc/nginx/conf.d/$servizio.conf"
docker exec ritorno-proxy nginx -t || return 2
docker exec ritorno-proxy nginx -s reload
}Funzione get-active-slot richiede alcune spiegazioni:
Perché restituisce un numero invece di stampare una stringa?
In ogni caso, nella funzione chiamante controlliamo il risultato del suo lavoro, e controllare il codice di uscita tramite bash è molto più semplice rispetto a una stringa. Inoltre, ottenerne una stringa è molto facile:
get-active-slot service && echo BLUE || echo GREEN.
Ma tre condizioni sono davvero sufficienti per distinguere tutti gli stati?
Anche due sarebbero sufficienti, l'ultima è qui solo per completezza, per non scrivere else.
Rimane indefinita solo la funzione che restituisce le configurazioni nginx: get-nginx-config service_name deployment_slot. Analogamente al health check, qui è possibile impostare qualsiasi configurazione per qualsiasi servizio. Interessante è solo cat <<- EOF, che consente di rimuovere tutte le tabulazioni all'inizio. Tuttavia, il prezzo di una formattazione decente è che sono mischiate tabulazioni e spazi, il che oggi è considerato un grave difetto. Ma bash forza le tabulazioni, e anche nella configurazione nginx sarebbe utile avere una buona formattazione. In ogni caso, qui l'accoppiamento di tabulazioni con spazi sembra davvero la migliore soluzione tra le peggiori. Tuttavia, nel frammento qui sotto non lo vedrete, poiché Habr 'fa bene', cambiando tutte le tabulazioni in 4 spazi e rendendo EOF non valido. .
Per non dover ripetere, vi racconterò subito di
cat << 'EOF', che incontreremo più avanti. Se si scrive semplicementecat << EOF, all'interno dell'heredoc avviene l'interpolazione della stringa (vengono espansi le variabili ($foo), le chiamate di comando ($(bar)), ecc., e se si racchiude il delimitatore di fine documento tra virgolette singole, allora l'interpolazione è disattivata e il carattere$viene stampato così com'è. Questo è necessario per inserire uno script all'interno di un altro script.
get-nginx-config() {
local usage_msg="Uso: ${FUNCNAME[0]} nome_servizio slot_distribuzione"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
local container_name=${service}_${slot}
case $service in
# Aggiungi configurazioni nginx specifiche per i tuoi servizi qui
*) nginx-config-simple-service $container_name:8080 ;;
esac
}
nginx-config-simple-service() {
local usage_msg="Uso: ${FUNCNAME[0]} proxy_pass"
local proxy_pass=${1?$usage_msg}
cat << EOF
server {
listen 80;
location / {
proxy_pass http://$proxy_pass;
}
}
EOF
}Questo è tutto lo script. Ecco per il download tramite wget o curl.
Esecuzione di script parametrizzati su un server remoto
È il momento di contattare il server di destinazione. Questa volta localhost andrà benissimo:
$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
himura@localhost's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.Abbiamo scritto uno script di deployment che trasferisce un'immagine precompilata sul server di destinazione e sostituisce senza interruzioni il container del servizio. Ma come eseguirlo su una macchina remota? Lo script ha degli argomenti, essendo universale e in grado di gestire più servizi per un unico reverse proxy (le configurazioni di nginx possono gestire quale servizio utilizzare a seconda dell'URL). Non possiamo conservare lo script sul server, poiché in questo modo non riusciremo ad aggiornarlo automaticamente (per bug fix e per l'aggiunta di nuovi servizi), e in generale, lo stato è male.
Soluzione 1: Conservare lo script sul server, ma copiarlo ogni volta tramite scp. Poi connettersi tramite ssh ed eseguire lo script con gli argomenti richiesti.
Contro:
- Due azioni anziché una
- Il percorso dove copi si potrebbe non esistere, o non si potrebbe avere accesso, oppure lo script potrebbe essere eseguito durante la sostituzione.
- È preferibile pulire dopo di sé (rimuovere lo script).
- Già tre azioni.
Soluzione 2:
- Nello script tenere solo le definizioni delle funzioni e non eseguire nulla.
- Con
sedaggiungere alla fine la chiamata della funzione. - Inviare tutto questo direttamente a shh attraverso pipe (
|)
Vantaggi:
- Veramente senza stato.
- Nessuna entità boilerplate.
- Sembra interessante.
Bene, facciamo senza Ansible. Sì, tutto è già stato inventato. Sì, la bicicletta. Guardate, che bicicletta semplice, elegante e minimalista:
$ cat < deploy.sh#!/bin/bash
usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}
echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPTEOF
$ chmod +x deploy.sh
$ ./deploy.sh localhost magic-porridge-pot
Collegamento a localhost...
Evviva! La funzione 'deploy' sta eseguendo su 'hut' con argomento 'magic-porridge-pot'Tuttavia, non possiamo essere certi che sul server remoto ci sia un bash adeguato, quindi aggiungiamo un piccolo controllo all'inizio (questo invece di ):
if [ "$SHELL" != "/bin/bash" ]
then
echo "La shell '$SHELL' non è supportata da 'deploy.sh'. Imposta una shell '/bin/bash' per '$USER@$HOSTNAME'."
exit 1
fiE ora tutto è reale:
$ docker exec reverse-proxy rm /etc/nginx/conf.d/default.conf
$ wget -qO deploy.sh https://git.io/JUURc
$ chmod +x deploy.sh
$ ./deploy.sh localhost uptimer
Invio dell'immagine compressa 'uptimer' a 'localhost' tramite ssh...
Immagine caricata: uptimer:latest
Connessione a 'localhost' tramite ssh per distribuire senza interruzioni 'uptimer'...
Distribuzione di 'uptimer_GREEN' al posto di 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Contenitore avviato. Verifica dello stato...
Richiesta a 'http://uptimer_GREEN:8080/' all'interno della rete docker 'web-gateway':
HTTP/1.0 503 Servizio non disponibile
wget: il server ha restituito errore: HTTP/1.0 503 Servizio non disponibile
Il nuovo servizio 'uptimer_GREEN' non è ancora pronto. Attendere (1)...
Richiesta a 'http://uptimer_GREEN:8080/' all'interno della rete docker 'web-gateway':
HTTP/1.0 503 Servizio non disponibile
wget: il server ha restituito errore: HTTP/1.0 503 Servizio non disponibile
Il nuovo servizio 'uptimer_GREEN' non è ancora pronto. Attendere (2)...
Richiesta a 'http://uptimer_GREEN:8080/' all'interno della rete docker 'web-gateway':
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.8.3
Data: Sab, 22 Ago 2020 20:15:50 GMT
Content-Type: text/html
Il nuovo servizio 'uptimer_GREEN' sembra OK. Switching heads...
nginx: il file di configurazione /etc/nginx/nginx.conf ha una sintassi corretta
nginx: il file di configurazione /etc/nginx/nginx.conf ha superato il test con successo
2020/08/22 20:15:54 [notice] 97#97: processo segnale avviato
Il servizio 'uptimer_GREEN' è attivo!
Terminazione di 'uptimer_BLUE'...
uptimer_BLUE
Spazio totale recuperato: 0B
Distribuzione riuscita!Ora puoi aprire nel browser, riavviare il deployment e verificare che avvenga senza problemi aggiornando la pagina a intervalli regolari durante la distribuzione.
Non dimentichiamo di pulire dopo il lavoro :3
$ docker rm -f uptimer_GREEN reverse-proxy
uptimer_GREEN
reverse-proxy
$ docker network rm web-gateway
web-gateway
$ cd ..
$ rm -r blue-green-deploymentFonte: habr.com
