Në këtë artikull ne përdorim përplas, ssh, cungues и nginx Ne do të organizojmë një paraqitje pa probleme të aplikacionit në internet. Vendosja blu-jeshile është një teknikë që ju lejon të përditësoni në çast një aplikacion pa refuzuar një kërkesë të vetme. Është një nga strategjitë e vendosjes me kohë joproduktive dhe është më e përshtatshme për aplikacionet me një shembull, por aftësinë për të ngarkuar një shembull të dytë, të gatshëm për të ekzekutuar afër.
Le të themi se keni një aplikacion në internet me të cilin shumë klientë punojnë në mënyrë aktive dhe nuk ka absolutisht asnjë mënyrë që ai të shtrihet për disa sekonda. Dhe vërtet duhet të lëshoni një përditësim të bibliotekës, një rregullim të gabimeve ose një veçori të re të lezetshme. Në një situatë normale, do t'ju duhet të ndaloni aplikacionin, ta zëvendësoni dhe ta filloni përsëri. Në rastin e docker-it, fillimisht mund ta zëvendësoni, më pas ta rinisni, por do të ketë ende një periudhë në të cilën kërkesat për aplikacionin nuk do të përpunohen, sepse zakonisht aplikacioni kërkon pak kohë për t'u ngarkuar fillimisht. Po sikur të fillojë, por të rezultojë se nuk funksionon? Ky është problemi, le ta zgjidhim me mjete minimale dhe sa më elegante.
MOFIMI: Shumica e artikullit është paraqitur në një format eksperimental - në formën e një regjistrimi të një sesioni konsol. Shpresojmë se kjo nuk do të jetë shumë e vështirë për t'u kuptuar dhe kodi do të dokumentohet mjaftueshëm. Për atmosferën, imagjinoni që këto nuk janë vetëm copa kodi, por letra nga një teletip "hekuri".
Teknikat interesante që janë të vështira për t'u Google vetëm duke lexuar kodin përshkruhen në fillim të çdo seksioni. Nëse ndonjë gjë tjetër është e paqartë, kërkojeni në google dhe kontrollojeni. shpjegon shell (për fat funksionon sërish, për shkak të zhbllokimit të telegramit). Nëse nuk mund të kërkoni asgjë në Google, pyesni në komente. Do të jem i lumtur të shtoj në seksionin përkatës "Teknika interesante".
Le të fillojmë.
$ mkdir blue-green-deployment && cd $_
Shërbim
Le të bëjmë një shërbim eksperimental dhe ta vendosim në një enë.
Teknika interesante
cat << EOF > file-name (Këtu Dokumenti + Ridrejtimi I/O) është një mënyrë për të krijuar një skedar me shumë rreshta me një komandë. Gjithçka bash lexon nga /dev/stdin pas kësaj rreshti dhe para vijës EOF do të regjistrohet në file-name.
wget -qO- URL (shpjegon shell) — nxirrni një dokument të marrë nëpërmjet HTTP në /dev/stdout (analog curl URL).
Printim
Unë posaçërisht thyej fragmentin për të mundësuar theksimin për Python. Në fund do të ketë një tjetër pjesë si kjo. Konsideroni që në këto vende letra ishte prerë për t'u dërguar në departamentin e theksimit (ku kodi ishte i ngjyrosur me dorë me theksues), dhe më pas këto pjesë u ngjitën pas.
$ cat << EOF > uptimer.py
from 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} is running for {t:3.1f} seconds.</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} (loads in {loading_seconds} sec.) started.')
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 .
Sending build context to Docker daemon 39.42kB
Step 1/4 : FROM python:alpine
---> 8ecf5a48c789
Step 2/4 : EXPOSE 8080
---> Using cache
---> cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
---> a7fbb33d6b7e
Step 4/4 : CMD [ "python", "-u", "./app.py" ]
---> Running in 1906b4bd9fdf
Removing intermediate container 1906b4bd9fdf
---> c1655b996fe8
Successfully built c1655b996fe8
Successfully tagged uptimer:latest
$ docker run --rm --detach --name uptimer --publish 8080:8080 uptimer
8f88c944b8bf78974a5727070a94c76aa0b9bb2b3ecf6324b784e782614b2fbf
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8f88c944b8bf uptimer "python -u ./app.py" 3 seconds ago Up 5 seconds 0.0.0.0:8080->8080/tcp uptimer
$ docker logs uptimer
Uptimer v1.0 (loads in 10 sec.) started.
$ wget -qSO- http://localhost:8080
HTTP/1.0 503 Service Unavailable
Server: BaseHTTP/0.6 Python/3.8.3
Date: Sat, 22 Aug 2020 19:52:40 GMT
Connection: close
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
Date: Sat, 22 Aug 2020 19:52:45 GMT
Content-Type: text/html
<h2>Uptimer v1.0 is running for 15.4 seconds.</h2>
$ docker rm --force uptimer
uptimer
Përfaqësues i kundërt
Në mënyrë që aplikacioni ynë të mund të ndryshojë pa u vënë re, është e nevojshme që të ketë një ent tjetër përpara tij që do të fshehë zëvendësimin e tij. Mund të jetë një server në internet nginx в modaliteti i përfaqësuesit të kundërt. Ndërmjet klientit dhe aplikacionit krijohet një përfaqësues i kundërt. Ai pranon kërkesat nga klientët dhe i përcjell ato në aplikacion dhe ua përcjell përgjigjet e aplikacionit klientëve.
Aplikacioni dhe përfaqësuesi i kundërt mund të lidhen brenda dokerit duke përdorur rrjeti doker. Kështu, kontejneri me aplikacionin nuk ka nevojë as të përcjellë një port në sistemin pritës; kjo lejon që aplikacioni të izolohet maksimalisht nga kërcënimet e jashtme.
Nëse përfaqësuesi i kundërt jeton në një host tjetër, do t'ju duhet të braktisni rrjetin docker dhe të lidhni aplikacionin me përfaqësuesin e kundërt përmes rrjetit pritës, duke përcjellë portin Apps parametri --publish, si në fillimin e parë dhe si me përfaqësuesin e kundërt.
Ne do të ekzekutojmë përfaqësuesin e kundërt në portin 80, sepse ky është pikërisht entiteti që duhet të dëgjojë rrjetin e jashtëm. Nëse porta 80 është e zënë në hostin tuaj të testimit, ndryshoni parametrin --publish 80:80 mbi --publish ANY_FREE_PORT:80.
$ 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 is running for 11.5 seconds.</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 seconds ago Up 25 seconds 0.0.0.0:80->80/tcp reverse-proxy
a1105f1b583d uptimer "python -u ./app.py" About a minute ago Up About a minute 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: Sat, 22 Aug 2020 19:56:24 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
<h2>Uptimer v1.0 is running for 104.1 seconds.</h2>
Vendosja pa probleme
Le të nxjerrim një version të ri të aplikacionit (me një rritje të dyfishtë të performancës së fillimit) dhe të përpiqemi ta vendosim atë pa probleme.
Teknika interesante
echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' - Shkruani tekst my text për të paraqitur /my-file.txt brenda enës my-container.
cat > /my-file.txt — Shkruani përmbajtjen e hyrjes standarde në një skedar /dev/stdin.
Printim
$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py
$ docker build --tag uptimer .
Sending build context to Docker daemon 39.94kB
Step 1/4 : FROM python:alpine
---> 8ecf5a48c789
Step 2/4 : EXPOSE 8080
---> Using cache
---> cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
---> 3eca6a51cb2d
Step 4/4 : CMD [ "python", "-u", "./app.py" ]
---> Running in 8f13c6d3d9e7
Removing intermediate container 8f13c6d3d9e7
---> 1d56897841ec
Successfully built 1d56897841ec
Successfully tagged uptimer:latest
$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer
96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02
$ docker logs uptimer_BLUE
Uptimer v2.0 (loads in 5 sec.) started.
$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 is running for 23.9 seconds.</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: signal process started
$ wget -qO- http://localhost
<h2>Uptimer v2.0 is running for 63.4 seconds.</h2>
$ docker rm -f uptimer
uptimer
$ wget -qO- http://localhost
<h2>Uptimer v2.0 is running for 84.8 seconds.</h2>
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
96932d4ca97a uptimer "python -u ./app.py" About a minute ago Up About a minute 8080/tcp uptimer_BLUE
80695a822c19 nginx:alpine "/docker-entrypoint.…" 8 minutes ago Up 8 minutes 0.0.0.0:80->80/tcp reverse-proxy
Në këtë fazë, imazhi ndërtohet direkt në server, i cili kërkon që burimet e aplikacionit të jenë aty, dhe gjithashtu ngarkon serverin me punë të panevojshme. Hapi tjetër është ndarja e montimit të imazhit në një makinë të veçantë (për shembull, në një sistem CI) dhe më pas transferimi i tij në server.
Transferimi i imazheve
Fatkeqësisht, nuk ka kuptim të transferoni imazhe nga localhost në localhost, kështu që ky seksion mund të eksplorohet vetëm nëse keni dy hoste me Docker në dorë. Të paktën duket diçka si kjo:
$ 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.9MB
Ekip docker save ruan të dhënat e imazhit në një arkiv .tar, që do të thotë se peshon rreth 1.5 herë më shumë se sa do të peshonte në formë të ngjeshur. Pra, le ta tundim atë në emër të kursimit të kohës dhe trafikut:
Tani le të mbledhim gjithçka që bëmë me dorë në një skenar. Le të fillojmë me funksionin e nivelit të lartë dhe më pas të shohim të tjerët që përdoren në të.
Teknika interesante
${parameter?err_msg} - një nga magjitë magjike bash (aka zëvendësimi i parametrave) Nëse parameter nuk është specifikuar, prodhim err_msg dhe dilni me kodin 1.
docker --log-driver journald — si parazgjedhje, drejtuesi i regjistrimit të docker është një skedar teksti pa asnjë rrotullim. Me këtë qasje, regjistrat mbushin shpejt të gjithë diskun, kështu që për një mjedis prodhimi është e nevojshme të ndryshoni drejtuesin në një më të zgjuar.
Skripti i vendosjes
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
}
Karakteristikat e përdorura:
ensure-reverse-proxy — Sigurohet që përfaqësuesi i kundërt të funksionojë (i dobishëm për vendosjen e parë)
get-active-slot service_name — Përcakton se cila slot është aktualisht aktive për një shërbim të caktuar (BLUE ose GREEN)
get-service-status service_name deployment_slot — Përcakton nëse shërbimi është gati për të përpunuar kërkesat hyrëse
set-active-slot service_name deployment_slot — Ndryshon konfigurimin nginx në kontejnerin e përfaqësuesit të kundërt
Në mënyrë që:
ensure-reverse-proxy() {
is-container-up reverse-proxy && return 0
echo "Deploying reverse-proxy..."
docker network create web-gateway
docker run
--detach
--restart always
--log-driver journald
--name reverse-proxy
--network web-gateway
--publish 80:80
nginx:alpine || return 1
docker exec --interactive reverse-proxy sh -c "> /etc/nginx/conf.d/default.conf"
docker exec reverse-proxy nginx -s reload
}
is-container-up() {
local container=${1?"Usage: ${FUNCNAME[0]} container_name"}
[ -n "$(docker ps -f name=${container} -q)" ]
return $?
}
get-active-slot() {
local service=${1?"Usage: ${FUNCNAME[0]} service_name"}
if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then
echo "Collision detected! Stopping ${service}_GREEN..."
docker rm -f ${service}_GREEN
return 0 # BLUE
fi
if is-container-up ${service}_BLUE && ! is-container-up ${service}_GREEN; then
return 0 # BLUE
fi
if ! is-container-up ${service}_BLUE; then
return 1 # GREEN
fi
}
get-service-status() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?usage_msg}
local slot=${2?$usage_msg}
case $service in
# Add specific healthcheck paths for your services here
*) local health_check_port_path=":8080/" ;;
esac
local health_check_address="http://${service}_${slot}${health_check_port_path}"
echo "Requesting '$health_check_address' within the 'web-gateway' docker network:"
docker run --rm --network web-gateway alpine
wget --timeout=1 --quiet --server-response $health_check_address
return $?
}
set-active-slot() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
get-nginx-config $service $slot | docker exec --interactive reverse-proxy sh -c "cat > /etc/nginx/conf.d/$service.conf"
docker exec reverse-proxy nginx -t || return 2
docker exec reverse-proxy nginx -s reload
}
Funksion get-active-slot kërkon pak shpjegim:
Pse kthen një numër dhe nuk nxjerr një varg?
Gjithsesi, në funksionin e thirrjes ne kontrollojmë rezultatin e punës së tij dhe kontrollimi i kodit të daljes duke përdorur bash është shumë më i lehtë sesa kontrollimi i një vargu. Për më tepër, marrja e një vargu prej tij është shumë e thjeshtë: get-active-slot service && echo BLUE || echo GREEN.
A mjaftojnë vërtet tre kushte për të dalluar të gjitha shtetet?
Edhe dy do të mjaftojnë, ky i fundit është këtu vetëm për plotësi, që të mos shkruaj else.
Vetëm funksioni që kthen konfigurimet nginx mbetet i papërcaktuar: get-nginx-config service_name deployment_slot. Për analogji me kontrollin shëndetësor, këtu mund të vendosni çdo konfigurim për çdo shërbim. Nga gjërat interesante - vetëm cat <<- EOF, i cili ju lejon të hiqni të gjitha skedat në fillim. Vërtetë, çmimi i formatimit të mirë është skedat e përziera me hapësirat, të cilat sot konsiderohen si formë shumë e keqe. Por bash detyron skedat, dhe gjithashtu do të ishte mirë të kishim formatim normal në konfigurimin nginx. Me pak fjalë, përzierja e skedave me hapësira këtu duket vërtet si zgjidhja më e mirë nga më e keqja. Sidoqoftë, nuk do ta shihni këtë në fragmentin më poshtë, pasi Habr "e bën mirë" duke ndryshuar të gjitha skedat në 4 hapësira dhe duke e bërë EOF të pavlefshme. Dhe këtu bie në sy.
Për të mos u ngritur dy herë, do t'ju them menjëherë cat << 'EOF', të cilat do të ndeshen më vonë. Nëse shkruani thjesht cat << EOF, pastaj brenda heredoc vargu ndërpritet (variablat zgjerohen ($foo), thirrjet e komandave ($(bar)) etj.), dhe nëse mbyllni fundin e dokumentit në thonjëza të vetme, atëherë interpolimi çaktivizohet dhe simboli $ shfaqet siç është. Çfarë ju nevojitet për të futur një skript brenda një skripti tjetër.
get-nginx-config() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
local container_name=${service}_${slot}
case $service in
# Add specific nginx configs for your services here
*) nginx-config-simple-service $container_name:8080 ;;
esac
}
nginx-config-simple-service() {
local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass"
local proxy_pass=${1?$usage_msg}
cat << EOF
server {
listen 80;
location / {
proxy_pass http://$proxy_pass;
}
}
EOF
}
Ky është i gjithë skenari. Dhe kështu thelbi me këtë skenar për shkarkim përmes wget ose curl.
Ekzekutimi i skripteve të parametrizuara në një server të largët
Është koha për të trokitur në serverin e synuar. Kësaj radhe localhost mjaft i përshtatshëm:
$ 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.
Ne kemi shkruar një skript vendosjeje që shkarkon një imazh të para-ndërtuar në serverin e synuar dhe zëvendëson pa probleme kontejnerin e shërbimit, por si mund ta ekzekutojmë atë në një makinë të largët? Skripti ka argumente, pasi është universal dhe mund të vendosë disa shërbime njëherësh nën një përfaqësues të kundërt (mund të përdorni konfigurimet nginx për të përcaktuar se cila url do të jetë cili shërbim). Skripti nuk mund të ruhet në server, pasi në këtë rast nuk do të jemi në gjendje ta përditësojmë automatikisht (me qëllim të korrigjimit të gabimeve dhe shtimit të shërbimeve të reja), dhe në përgjithësi, gjendje = e keqe.
Zgjidhja 1: Ende ruajeni skriptin në server, por kopjojeni atë çdo herë scp. Më pas lidheni nëpërmjet ssh dhe ekzekutoni skriptin me argumentet e nevojshme.
Cons:
Dy veprime në vend të një
Mund të mos ketë një vend ku ju kopjoni, ose mund të mos ketë akses në të, ose skripti mund të ekzekutohet në momentin e zëvendësimit.
Këshillohet që të pastroni veten (fshini skenarin).
Tashmë tre veprime.
Zgjidhja 2:
Mbani vetëm përkufizimet e funksioneve në skript dhe mos ekzekutoni asgjë
Me sed shtoni një thirrje funksioni në fund
Dërgojini të gjitha drejtpërdrejt në shh përmes tubit (|)
Pro:
Vërtet pa shtetësi
Nuk ka objekte të pllakës së bojlerit
Ndjenja e ftohtë
Le ta bëjmë atë pa Ansible. Po, gjithçka tashmë është shpikur. Po, një biçikletë. Shikoni sa e thjeshtë, elegante dhe minimaliste është biçikleta:
$ cat << 'EOF' > 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_SCRIPT
EOF
$ chmod +x deploy.sh
$ ./deploy.sh localhost magic-porridge-pot
Connecting to localhost...
Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'
Megjithatë, nuk mund të jemi të sigurt që pritësi në distancë ka bash adekuat, kështu që do të shtojmë një kontroll të vogël në fillim (kjo është në vend të shellbang):
if [ "$SHELL" != "/bin/bash" ]
then
echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."
exit 1
fi
Dhe tani është e vërtetë:
$ 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
Sending gzipped image 'uptimer' to 'localhost' via ssh...
Loaded image: uptimer:latest
Connecting to 'localhost' via ssh to seamlessly deploy 'uptimer'...
Deploying 'uptimer_GREEN' in place of 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Container started. Checking health...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (1)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (2)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.8.3
Date: Sat, 22 Aug 2020 20:15:50 GMT
Content-Type: text/html
New 'uptimer_GREEN' service seems OK. Switching heads...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2020/08/22 20:15:54 [notice] 97#97: signal process started
The 'uptimer_GREEN' service is live!
Killing 'uptimer_BLUE'...
uptimer_BLUE
Total reclaimed space: 0B
Deployment successful!
Tani mund të hapni http://localhost/ në shfletues, ekzekutoni përsëri vendosjen dhe sigurohuni që ai të funksionojë pa probleme duke përditësuar faqen sipas CD-së gjatë paraqitjes.