En ĉi tiu artikolo ni uzas bash, ssh, docker и nginx Ni organizos senjuntan aranĝon de la retejo-aplikaĵo. Bluverda deplojo estas tekniko, kiu ebligas al vi tuj ĝisdatigi aplikaĵon sen malakcepti eĉ unu peton. Ĝi estas unu el la nulmalfunkciaj deplojstrategioj kaj plej taŭgas por aplikoj kun unu kazo, sed la kapablo ŝargi duan, pretan por funkciiga kazon proksime.
Ni diru, ke vi havas TTT-aplikaĵon kun kiu multaj klientoj aktive laboras, kaj estas absolute neniu maniero por ke ĝi kuŝu dum kelkaj sekundoj. Kaj vi vere bezonas lanĉi bibliotekan ĝisdatigon, korekton de cimoj aŭ novan bonegan funkcion. En normala situacio, vi devos ĉesigi la aplikaĵon, anstataŭigi ĝin kaj rekomenci ĝin. En la kazo de docker, vi povas unue anstataŭigi ĝin, poste rekomenci ĝin, sed ankoraŭ restos periodo en kiu petoj al la aplikaĵo ne estos procesitaj, ĉar kutime la aplikaĵo prenas iom da tempo por komence ŝargi. Kio se ĝi komenciĝas, sed montriĝas nefunkciebla? Jen la problemo, ni solvu ĝin per minimumaj rimedoj kaj kiel eble plej elegante.
Malgarantio: Plejparto de la artikolo estas prezentita en eksperimenta formato - en formo de registrado de konzola sesio. Espereble ĉi tio ne estos tro malfacile komprenebla kaj la kodo sufiĉe dokumentas sin. Por atmosfero, imagu, ke ĉi tiuj ne estas nur kodaj fragmentoj, sed papero el "fera" teletipo.
Interesaj teknikoj, kiuj malfacilas por Guglo nur per legado de la kodo, estas priskribitaj komence de ĉiu sekcio. Se io alia estas neklara, guglos ĝin kaj kontrolu ĝin. klarigi ŝelo (feliĉe ĝi funkcias denove, pro la malblokado de la telegramo). Se vi ne povas Guglo ion, demandu en la komentoj. Mi volonte aldonos al la responda sekcio "Interesaj teknikoj".
Ni komencu.
$ mkdir blue-green-deployment && cd $_
servo
Ni faru eksperimentan servon kaj metu ĝin en ujon.
Interesaj teknikoj
cat << EOF > file-name (Jen Dokumento + I/O Redirekto) estas maniero krei plurlinian dosieron per unu komando. Ĉio el bash legas /dev/stdin post ĉi tiu linio kaj antaŭ la linio EOF estos registrita en file-name.
wget -qO- URL (klarigi ŝelo) — eligi dokumenton ricevitan per HTTP al /dev/stdout (analogo curl URL).
Presaĵo
Mi specife rompas la fragmenton por ebligi reliefigon por Python. Je la fino estos alia peco tia. Konsideru, ke en ĉi tiuj lokoj la papero estis tranĉita por esti sendita al la elstara fako (kie la kodo estis mane kolorigita per lumigiloj), kaj tiam ĉi tiuj pecoj estis gluitaj reen.
$ 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
Inversa prokurilo
Por ke nia aplikaĵo povu ŝanĝi nerimarkite, necesas, ke antaŭ ĝi estu iu alia ento, kiu kaŝos ĝian anstataŭaĵon. Ĝi povus esti retservilo nginx в inversa prokura reĝimo. Inversa prokurilo estas establita inter la kliento kaj la aplikaĵo. Ĝi akceptas petojn de klientoj kaj plusendas ilin al la aplikaĵo kaj plusendas la respondojn de la aplikaĵo al la klientoj.
La aplikaĵo kaj inversa prokurilo povas esti ligitaj ene de docker uzante docker-reto. Tiel, la ujo kun la aplikiĝo eĉ ne bezonas plusendi havenon sur la mastro-sistemo; tio permesas al la aplikaĵo esti maksimume izolita de eksteraj minacoj.
Se la inversa prokurilo loĝas sur alia gastiganto, vi devos forlasi la docker-reton kaj konekti la aplikaĵon al la inversa prokurilo per la gastiga reto, plusendante la havenon. apps parametro --publish, kiel ĉe la unua komenco kaj kiel ĉe la inversa prokurilo.
Ni rulos la inversan prokurilon sur la haveno 80, ĉar ĉi tio estas ĝuste la ento, kiu devus aŭskulti la eksteran reton. Se la haveno 80 estas okupata en via testa gastiganto, ŝanĝu la parametron --publish 80:80 sur --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>
Senjunta deplojo
Ni lanĉu novan version de la aplikaĵo (kun duobla ekfunkcio-akcelo) kaj provu disfaldi ĝin perfekte.
Interesaj teknikoj
echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' — Skribu tekston my text arkivi /my-file.txt ene de la ujo my-container.
cat > /my-file.txt — Skribu la enhavon de norma enigo al dosiero /dev/stdin.
Presaĵo
$ 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
En ĉi tiu etapo, la bildo estas konstruita rekte sur la servilo, kio postulas ke la aplikaĵfontoj estu tie, kaj ankaŭ ŝarĝas la servilon per nenecesa laboro. La sekva paŝo estas asigni la bildan asembleon al aparta maŝino (ekzemple al CI-sistemo) kaj poste transdoni ĝin al la servilo.
Transdono de bildoj
Bedaŭrinde, ne havas sencon transdoni bildojn de loka gastiganto al loka gastiganto, do ĉi tiu sekcio nur povas esti esplorita se vi havas du gastigantojn kun Docker ĉemane. Minimume ĝi aspektas kiel ĉi tio:
$ 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
teamo docker save konservas la bildajn datumojn en .tar-arkivo, kio signifas, ke ĝi pezas ĉirkaŭ 1.5 fojojn pli ol ĝi pezus en kunpremita formo. Do ni skuu ĝin en la nomo de ŝpari tempon kaj trafikon:
Nun ni kolektu ĉion, kion ni faris permane en unu skripton. Ni komencu per la ĉefnivela funkcio, kaj poste rigardu la aliajn uzatajn en ĝi.
Interesaj teknikoj
${parameter?err_msg} - unu el la bash-magiosorĉoj (alinome anstataŭigo de parametroj). Se parameter ne specifita, eligo err_msg kaj eliru per kodo 1.
docker --log-driver journald — defaŭlte, la docker-logging-ŝoforo estas tekstdosiero sen ajna rotacio. Kun ĉi tiu aliro, la ŝtipoj rapide plenigas la tutan diskon, do por produktadmedio necesas ŝanĝi la ŝoforon al pli inteligenta.
Deploja skripto
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
}
Trajtoj uzataj:
ensure-reverse-proxy — Certigas, ke la inversa prokurilo funkcias (utila por la unua deplojo)
get-active-slot service_name — Determinas kiu fendo estas nuntempe aktiva por antaŭfiksita servo (BLUE aŭ GREEN)
get-service-status service_name deployment_slot — Determinas ĉu la servo pretas prilabori envenantajn petojn
set-active-slot service_name deployment_slot — Ŝanĝas la nginx-agordon en la inversa prokura ujo
En ordo:
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
}
funkcio get-active-slot postulas iom da klarigo:
Kial ĝi resendas nombron kaj ne eligas ĉenon?
Ĉiuokaze, en la voka funkcio ni kontrolas la rezulton de ĝia laboro, kaj kontroli elirkodon per bash estas multe pli facila ol kontroli ĉenon. Krome, ricevi ŝnuron de ĝi estas tre simpla: get-active-slot service && echo BLUE || echo GREEN.
Ĉu tri kondiĉoj vere sufiĉas por distingi ĉiujn ŝtatojn?
Eĉ du sufiĉos, la lasta estas ĉi tie nur por kompleteco, por ne skribi else.
Nur la funkcio, kiu resendas nginx-agordojn, restas nedifinita: get-nginx-config service_name deployment_slot. Analogie kun sankontrolo, ĉi tie vi povas agordi ajnan agordon por iu ajn servo. El la interesaj aferoj — nur cat <<- EOF, kiu permesas forigi ĉiujn langetojn komence. Vere, la prezo de bona formatado estas miksitaj langetoj kun spacoj, kio hodiaŭ estas konsiderata tre malbona formo. Sed bash devigas langetojn, kaj ankaŭ estus bone havi normalan formatadon en la nginx-agordo. Resume, miksi langetojn kun spacoj ĉi tie vere ŝajnas la plej bona solvo el la plej malbona. Tamen, vi ne vidos ĉi tion en la suba fragmento, ĉar Habr "faras ĝin bone" ŝanĝante ĉiujn langetojn al 4 spacoj kaj malvalidigante EOF. Kaj ĉi tie ĝi estas rimarkebla.
Por ne ellitiĝi dufoje, mi tuj rakontos al vi pri cat << 'EOF', kiu estos renkontita poste. Se vi skribas simple cat << EOF, tiam ene de heredoc la ĉeno estas interpolata (variabloj estas vastigitaj ($foo), komandvokoj ($(bar)) ktp.), kaj se vi enmetas la finon de dokumento inter unuopaj citiloj, tiam interpolado estas malŝaltita kaj la simbolo $ estas montrata kiel estas. Kion vi bezonas por enmeti skripton ene de alia skripto.
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
}
Efektivigo de parametrizitaj skriptoj sur fora servilo
Estas tempo frapi la celservilon. Ĉifoje localhost sufiĉe taŭga:
$ 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.
Ni skribis deplojan skripton kiu elŝutas antaŭkonstruitan bildon al la cela servilo kaj perfekte anstataŭigas la servan ujon, sed kiel ni povas ekzekuti ĝin sur fora maŝino? La skripto havas argumentojn, ĉar ĝi estas universala kaj povas disfaldi plurajn servojn samtempe sub unu inversa prokurilo (vi povas uzi nginx-agordojn por determini kiu url estos kiu servo). La skripto ne povas esti konservita en la servilo, ĉar ĉi-kaze ni ne povos ĝin aŭtomate ĝisdatigi (por korekti cimojn kaj aldoni novajn servojn), kaj ĝenerale, stato = malbono.
Solvo 1: Ankoraŭ konservu la skripton sur la servilo, sed kopiu ĝin ĉiufoje scp. Poste konekti per ssh kaj ekzekuti la skripton kun la necesaj argumentoj.
Kons:
Du agoj anstataŭ unu
Eble ne estas loko, kie vi kopias, aŭ eble ne estas aliro al ĝi, aŭ la skripto povas esti efektivigita en la momento de anstataŭigo.
Estas konsilinde purigi post vi mem (forigu la skripton).
Jam tri agoj.
Solvo 2:
Konservu nur funkciodifinojn en la skripto kaj rulu nenion
Kun la helpo de sed aldonu funkciovokon ĝis la fino
Sendu ĉion rekte al shh per pipo (|)
Pros:
Vere sennacia
Neniuj kaldronaj entoj
Sentante malvarmeta
Ni simple faru ĝin sen Ansible. Jes, ĉio jam estas inventita. Jes, biciklo. Rigardu kiel simpla, eleganta kaj minimumisma estas la biciklo:
$ 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'
Tamen, ni ne povas esti certaj, ke la fora gastiganto havas adekvatan bash, do ni aldonos malgrandan kontrolon ĉe la komenco (ĉi tio estas anstataŭe de ŝelbango):
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
Kaj nun ĝi estas reala:
$ 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!
Nun vi povas malfermi http://localhost/ en la retumilo, rulu la deplojon denove kaj certigu, ke ĝi funkcias perfekte ĝisdatigante la paĝon laŭ la KD dum la aranĝo.