Blue-Green Deployment i minimalt format

I den hÀr artikeln kommer vi att bash, ssh, docker och nginx organisera en sömlös distribusion av webbapplikationen. Blue-green deployment Àr en teknik som gör det möjligt att omedelbart uppdatera en applikation utan att avvisa nÄgra begÀrningar. Det Àr en av strategierna för zero downtime deployment och passar bÀst för applikationer med en instans, men med möjligheten att ladda en andra, redo att anvÀnda instans bredvid.

Anta att du har en webbapplikation som mÄnga anvÀndare aktivt anvÀnder, och den kan inte stanna i nÄgra sekunder. Du mÄste verkligen rulla ut en uppdatering, fixa en bugg eller introducera en ny spÀnnande funktion. I vanliga fall skulle du behöva stoppa applikationen, byta ut den och starta om den. Med Docker kan du först byta ut den och sedan starta om, men det kommer fortfarande att finnas en period dÄ begÀrningar till applikationen inte kommer att behandlas, eftersom applikationen vanligtvis behöver lite tid för att initialt ladda. Men vad om den startas, men Àr oanvÀndbar? SÄdana uppgifter, lÄt oss lösa dem med minimala medel och sÄ elegant som möjligt.

DISCLAIMER: Största delen av artikeln presenteras i ett experimentellt format – som en inspelning av en konsolsession. Jag hoppas att detta inte blir för svĂ„rt att ta till sig, och att denna kod dokumenterar sig sjĂ€lv tillrĂ€ckligt. För stĂ€mningens skull, förestĂ€ll dig att det inte bara Ă€r kodsnuttar, utan papper frĂ„n en "jĂ€rnhĂ„rd" teletype.

Blue-Green Deployment i minimalt format

Intressanta tekniker som Ă€r svĂ„ra att googla bara genom att lĂ€sa koden beskrivs i början av varje avsnitt. Om nĂ„got fortfarande Ă€r oklart – googla och kolla i explainshell (lyckligtvis fungerar den igen pĂ„ grund av att Telegram har avblockerats). Det som inte kan googlas – frĂ„ga i kommentarerna. Jag fyller gĂ€rna pĂ„ den relevanta sektionen "Intressanta tekniker".

LÄt oss börja.

$ mkdir blue-green-deployment && cd $_

TjÀnsten

LÄt oss skapa en försöksservice och placera den i en container.

Intressanta tekniker

  • cat < file-name (Here Document + I/O Redirection) – ett sĂ€tt att skapa en flerradig fil med ett kommando. Allt som bash lĂ€ser frĂ„n /dev/stdin efter denna rad och fram till raden EOF kommer att skrivas till file-name.
  • wget -qO- URL (explainshell) – skriver ut det mottagna dokumentet via HTTP till /dev/stdout (motsvarande curl URL).

Utskrift

Jag delar medvetet upp snippeten för att inkludera syntaxmarkering för Python. I slutet kommer det att finnas en sÄdan bit till. TÀnk pÄ det som att papperet har klippts vid dessa stÀllen för att överlÀmnas till avdelningen för highlightning (dÀr koden fÀrgades manuellt med highlighters), och sedan klipptes dessa bitar tillbaka.

$ cat < uptimer.py
från http.server importera BaseHTTPRequestHandler, HTTPServer
från time importera 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 &lt; loading_seconds:
                    self.send_error(503)
                else:
                    self.send_response(200)
                    self.send_header(&#039;Content-Type&#039;, &#039;text\/html&#039;)
                    self.end_headers()
                    response = f&#039;<h2>{app_name} körs i {t:3.1f} sekunder.</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} (laddas på {loading_seconds} sek.) startad.')
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
 ---&gt; 8ecf5a48c789
Step 2\/4 : EXPOSE 8080
 ---&gt; Using cache
 ---&gt; cf92d174c9d3
Step 3\/4 : COPY uptimer.py app.py
 ---&gt; a7fbb33d6b7e
Step 4\/4 : CMD [ "python", "-u", ".\/app.py" ]
 ---&gt; Running in 1906b4bd9fdf
Removing intermediate container 1906b4bd9fdf
 ---&gt; 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-&gt;8080\/tcp   uptimer

$ docker logs uptimer
Uptimer v1.0 (laddar på 10 sek.) startad.

$ wget -qSO- http:\/\/localhost:8080
  HTTP\/1.0 503 Tjänsten är inte tillgänglig
  Server: BaseHTTP\/0.6 Python\/3.8.3
  Date: Lör, 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: Lör, 22 Aug 2020 19:52:45 GMT
  Content-Type: text\/html
<h2>Uptimer v1.0 har kört i 15.4 sekunder.</h2>

$ docker rm --force uptimer
uptimer

Reverse proxy

För att vÄr applikation ska kunna bytas ut osynligt, mÄste det finnas en annan enhet framför den som döljer dess byte. Det kan vara en webbserver. nginx pÄ i reverse proxy-lÀge. Reverse proxy installeras mellan klienten och applikationen. Den tar emot förfrÄgningar frÄn klienterna och vidarebefordrar dem till applikationen, och svaren frÄn applikationen skickas till klienterna.

Applikationen och reverse proxy kan kopplas samman inom Docker med hjÀlp av docker network. PÄ sÄ sÀtt behöver inte applikationscontainern ens ha en port exponerad i vÀrdsystemet, vilket gör det möjligt att isolera applikationen maximalt frÄn externa hot.

Om reverse proxy kommer att bo pÄ en annan vÀrd mÄste vi avstÄ frÄn docker network och koppla applikationen till reverse proxy via vÀrdets nÀtverk genom att vidarebefordra en port. applikationen parametern --publish, precis som vid första starten och som hos reverse proxy.

Reverse proxy kommer att köras pÄ port 80, eftersom det Àr just denna enhet som bör lyssna pÄ det externa. Om port 80 Àr upptagen pÄ din testvÀrd, Àndra parametern. --publish 80:80 till --publish ANY_FREE_PORT:80.

Intressanta tekniker

Utskrift

$ 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 har kört i 11,5 sekunder.</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 sekunder sedan       Up 25 sekunder       0.0.0.0:80-&gt;80\/tcp   reverse-proxy
a1105f1b583d        uptimer             "python -u .\/app.py"     För ungefär en minut sedan   Up För ungefär en minut   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 har kört i 104,1 sekunder.</h2>

Sömlös deployment

Vi kommer att lansera en ny version av applikationen (med dubbelt sÄ hög startup-prestanda) och försöka deploya den sömlöst.

Intressanta tekniker

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' — Spara text my text till filen /my-file.txt inuti containern my-container.
  • cat > /my-file.txt — Skriv till filen innehĂ„llet frĂ„n standardingĂ„ngen /dev/stdin.

Utskrift

$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py

$ docker build --tag uptimer .
Skickar byggkontext till Docker-damon  39.94kB
Steg 1/4 : FRÅN python:alpine
 ---&gt; 8ecf5a48c789
Steg 2/4 : EXPOSE 8080
 ---&gt; Använder cache
 ---&gt; cf92d174c9d3
Steg 3/4 : COPY uptimer.py app.py
 ---&gt; 3eca6a51cb2d
Steg 4/4 : CMD [ "python", "-u", ".\/app.py" ]
 ---&gt; Körs i 8f13c6d3d9e7
Tar bort mellanliggande container 8f13c6d3d9e7
 ---&gt; 1d56897841ec
Framgångsrikt byggd 1d56897841ec
Framgångsrikt märkt uptimer:latest

$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer
96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02

$ docker logs uptimer_BLUE
Uptimer v2.0 (laddar på 5 sek.) startad.

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 har varit igång i 23.9 sekunder.</h2>

$ sed s/uptimer/uptimer_BLUE/ uptimer.conf | docker exec --interactive reverse-proxy sh -c 'cat &gt; /etc/nginx/conf.d/default.conf'

$ docker exec reverse-proxy cat /etc/nginx/conf.d/default.conf
server {
    lyssna 80;
    location / {
        proxy_pass http://uptimer_BLUE:8080;
    }
}

$ docker exec reverse-proxy nginx -s reload
2020/06/25 21:22:23 [notis] 68#68: signalprocessen startad

$ wget -qO- http://localhost
<h2>Uptimer v2.0 har varit igång i 63.4 sekunder.</h2>

$ docker rm -f uptimer
uptimer

$ wget -qO- http://localhost
<h2>Uptimer v2.0 har varit igång i 84.8 sekunder.</h2>

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES
96932d4ca97a        uptimer             "python -u .\/app.py"     För cirka en minut sedan   Aktiv För cirka en minut   8080/tcp             uptimer_BLUE
80695a822c19        nginx:alpine        "\/docker-entrypoint.…"   8 minuter sedan        Aktiv 8 minuter        0.0.0.0:80-&gt;80/tcp   reverse-proxy

I detta skede byggs bilden direkt pÄ servern, vilket krÀver att applikationens kÀllkod finns dÀr samt belastar servern med extra arbete. NÀsta steg kommer att vara att dedikera byggandet av bilden till en separat maskin (t.ex. i CI-systemet) med efterföljande överföring till servern.

Överföring av bilder

TyvÀrr har det ingen mening att överföra bilder frÄn localhost till localhost, sÄ den hÀr sektionen kan endast utforskas om man har tvÄ vÀrdar med Docker tillgÀngliga. PÄ en minimal nivÄ ser det ut ungefÀr sÄ hÀr:

$ 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

Team docker save sparar bilddata i en .tar-arkiv, vilket betyder att den vÀger ungefÀr 1,5 gÄnger mer Àn den skulle göra i komprimerad form. SÄ lÄt oss komprimera den för att spara tid och trafik:

$ docker image save uptimer | gzip | ssh production-server 'zcat | docker image load'
Loaded image: uptimer:latest

Och dessutom kan man övervaka överföringsprocessen (dock krÀvs en extern nyttjandeprogram för detta):

$ docker image save uptimer | gzip | pv | ssh production-server 'zcat | docker image load'
25,7MiB 0:01:01 [ 425KiB/s] [                       ]
Loaded image: uptimer:latest

Tips: Om du behöver massor av parametrar för att ansluta till servern via SSH, kanske du inte anvÀnder filen ~/.ssh/config.

Överföring av en bild genom docker image save/load Ă€r den mest minimalistiska metoden, men inte den enda. Det finns andra:

  1. Container Registry (branschstandard).
  2. Anslut till Docker-daemonen pÄ servern frÄn en annan vÀrd:
    1. Miljövariabel DOCKER_HOST.
    2. Kommandoradparameter -H eller --host verktyget docker-compose.
    3. docker context

Ett andra sÀtt (med tre varianter av dess implementering) beskrivs vÀl i artikeln How to deploy on remote Docker hosts with docker-compose.

deploy.sh

Nu ska vi samla allt vi har gjort manuellt i ett skript. Vi börjar med toppnivÄfunktionen och ser sedan pÄ de andra som anvÀnds inom den.

Intressanta tekniker

  • ${parameter?err_msg} — en av bash-magi (aka parameter substitution). Om parameter inte Ă€r angiven, skriv ut err_msg och avsluta med kod 1.
  • docker --log-driver journald — som standard Ă€r Docker-loggdrivaren en textfil utan nĂ„gon rotation. Med detta tillvĂ€gagĂ„ngssĂ€tt fylls loggarna snabbt pĂ„ hela disken, sĂ„ för produktionsmiljöer mĂ„ste man byta loggdrivare till nĂ„got mer intelligent.

Deploymentskript

deploy() {
    local usage_msg="AnvÀndning: ${FUNCNAME[0]} bild_namn"
    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 "Installerar '$NEW' istÀllet för '$OLD'..."
    docker run 
        --detach 
        --restart always 
        --log-driver journald 
        --name $NEW 
        --network web-gateway 
        $image_name || return 3
    echo "BehÄllare startad. Kontrollerar hÀlsa..."
    for i in {1..20}
    do
        sleep 1
        if get-service-status $image_name $new_slot
        then
            echo "Ny '$NEW' tjÀnst verkar vara okej. VÀxlar huvud..."
            sleep 2  # SÀkerstÀll att tjÀnsten Àr redo
            set-active-slot $image_name $new_slot || return 4
            echo "'$NEW' tjÀnst Àr live!"
            sleep 2  # SÀkerstÀll att alla förfrÄgningar har behandlats
            echo "Dödar '$OLD'..."
            docker rm -f $OLD
            docker image prune -f
            echo "UppstÀllning lyckad!"
            return 0
        fi
        echo "Ny '$NEW' tjÀnst Àr inte redo Ànnu. VÀntar ($i)..."
    done
    echo "Ny '$NEW' tjÀnst kunde inte startas, dödar den. Misslyckades med att installera T_T"
    docker rm -f $NEW
    return 5
}

AnvÀnda funktioner:

  • ensure-reverse-proxy — SĂ€kerstĂ€ller att reversproxy fungerar (nyttigt för första installationen)
  • get-active-slot service_name — BestĂ€mmer vilken slot som för nĂ€rvarande Ă€r aktiv för den angivna tjĂ€nsten (BLUE eller GREEN)
  • get-service-status service_name deployment_slot — BestĂ€mmer om tjĂ€nsten Ă€r redo att ta emot inkommande förfrĂ„gningar
  • set-active-slot service_name deployment_slot — Ändrar nginx-konfigurationen i reversproxycontainern

I ordning:

ensure-reverse-proxy() {
    is-container-up reverse-proxy && return 0
    echo "Implementerar 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?"AnvÀndning: ${FUNCNAME[0]} container_name"}

    [ -n "$(docker ps -f name=${container} -q)" ]
    return $?
}

get-active-slot() {
    local service=${1?"AnvÀndning: ${FUNCNAME[0]} service_name"}

    if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then
        echo "Kollision upptÀckt! Stoppar ${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="AnvÀndning: ${FUNCNAME[0]} service_name deployment_slot"
    local service=${1?usage_msg}
    local slot=${2?$usage_msg}

    case $service in
        # LÀgg till specifika hÀlsokontrollvÀgar för dina tjÀnster hÀr
        *) local health_check_port_path=":8080/" ;;
    esac
    local health_check_address="http://${service}_${slot}${health_check_port_path}"
    echo "BegÀr '$health_check_address' inom 'web-gateway' docker-nÀtverket:"
    docker run --rm --network web-gateway alpine 
        wget --timeout=1 --quiet --server-response $health_check_address
    return $?
}

set-active-slot() {
    local usage_msg="AnvÀndning: ${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
}

Funktionen get-active-slot krÀver nÄgra förtydliganden:

Varför returnerar den ett nummer istÀllet för att skriva ut en strÀng?

I alla fall kontrollerar vi resultatet av dess körning i den anropande funktionen, och det Àr mycket enklare att kontrollera exit-koden med hjÀlp av bash Àn en strÀng. Dessutom Àr det mycket enkelt att fÄ en strÀng frÄn den:
get-active-slot service && echo BLUE || echo GREEN.

Är tre villkor verkligen tillrĂ€ckligt för att sĂ€rskilja alla tillstĂ„nd?

Blue-Green Deployment i minimalt format

TvÄ rÀcker, det sista hÀr Àr bara för fullstÀndighetens skull, för att slippa skriva. else.

Den Ă„terstĂ„ende odefinierade funktionen Ă€r den som returnerar nginx-konfigurationer: get-nginx-config service_name deployment_slot. PĂ„ liknande sĂ€tt som hĂ€lsokontrollen kan vilken konfiguration som helst för vilken tjĂ€nst som helst anges. Intressant nog — bara cat <<- EOF, vilket gör att man kan ta bort alla tabbkaraktĂ€rer i början. Men priset för fin formatering Ă€r blandade tabbkaraktĂ€rer med mellanslag, vilket idag anses vara vĂ€ldigt dĂ„ligt. Men bash tvingar fram tabbar, och i nginx-konfigurationen skulle det ocksĂ„ vara bra att ha en korrekt formatering. Kort sagt, hĂ€r verkar blandningen av tabbar och mellanslag vara den bĂ€sta lösningen av de dĂ„liga alternativen. Men i snippeten nedan kommer du inte att se detta, eftersom Habr "gör det bra" genom att byta ut alla tabbar mot fyra mellanslag och gör EOF ogiltigt. HĂ€r Ă€r det mĂ€rkbart.

SÄ att jag slipper upprepa mig, lÄt mig berÀtta om cat << 'EOF', som kommer att dyka upp senare. Om man bara skriver cat << EOF, sÄ görs interpolation av strÀngen (variablerna öppnas ($foo), kommandon anropas ($(bar)) osv.), men om man omger slutmarkeringen av dokumentet med enkla citattecken, dÄ stÀngs interpolation av och tecknet $ skrivs ut som det Àr. Detta Àr precis vad som behövs för att infoga ett skript i ett annat skript.

get-nginx-config() {
    lokal usage_msg="AnvÀndning: ${FUNCNAME[0]} service_name deployment_slot"
    lokal service=${1?$usage_msg}
    lokal slot=${2?$usage_msg}
    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1

    lokal container_name=${service}_${slot}
    case $service in
        # LÀgg till specifika nginx-konfigurationer för dina tjÀnster hÀr
        *) nginx-config-simple-service $container_name:8080 ;;
    esac
}

nginx-config-simple-service() {
    lokal usage_msg="AnvÀndning: ${FUNCNAME[0]} proxy_pass"
    lokal proxy_pass=${1?$usage_msg}

cat << EOF
server {
    lyssna 80;
    plats / {
        proxy_pass http://$proxy_pass;
    }
}
EOF
}

Detta Àr hela skriptet. Och hÀr Àr gist med detta skript för nedladdning via wget eller curl.

Exekvering av parametriserade skript pÄ en fjÀrrserver

Det Àr dags att knacka pÄ mÄlservern. Den hÀr gÄngen localhost funkar det bra:

$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: försöker logga in med de nya nycklarna, för att filtrera bort de som redan Àr installerade
/usr/bin/ssh-copy-id: INFO: 1 nyckel ÄterstÄr att installera -- om du nu blir tillfrÄgad Àr det för att installera de nya nycklarna
himura@localhost's lösenord: 

Antal nycklar tillagda: 1

Försök nu att logga in pÄ maskinen med:   "ssh 'localhost'"
och kontrollera att endast de nycklar du ville ha lades till.

Vi har skrivit ett deploymentskript som överför en förberedd bild till den valda servern och sömlöst byter ut tjÀnstens container, men hur utför man det pÄ en fjÀrrdator? Skriptet har argument eftersom det Àr universellt och kan distribuera flera tjÀnster under en omvÀnd proxy (genom nginx-konfigurationerna kan man stÀlla in vilken URL som korrelerar med vilken tjÀnst). Skriptet kan inte lagras pÄ servern, eftersom vi annars inte skulle kunna uppdatera det automatiskt (i syfte att fixa buggar och lÀgga till nya tjÀnster), och dessutom, state = ont.

Lösning 1: För att lagra skriptet pÄ servern, men kopiera det varje gÄng via scp. Anslut sedan via ssh och kör skriptet med nödvÀndiga argument.

Nackdelar:

  • TvĂ„ Ă„tgĂ€rder istĂ€llet för en.
  • Det kan hĂ€nda att det stĂ€lle dit du kopierar inte finns, eller att det inte gĂ„r att nĂ„, eller att skriptet kan köras vid tidpunkten för bytet.
  • Det Ă€r fördelaktigt att stĂ€da upp efter sig (ta bort skriptet).
  • Redan tre Ă„tgĂ€rder.

Lösning 2:

  • HĂ„lla endast funktionsdefinitioner i skriptet och inte köra nĂ„got.
  • Med hjĂ€lp av sed LĂ€gga till anrop till funktionen i slutet.
  • Skicka allt detta direkt till shh via pipe (|)

Fördelar:

  • Egentligen stateless.
  • Inga boilerplate-enheter.
  • KĂ€nns coolt.

Men lÄt oss bara undvika Ansible. Ja, allt Àr redan uppfunnet. Ja, cykeln. Titta pÄ vilken enkel, elegant och minimalistisk cykel:

$ 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_SCRIPT
EOF

$ chmod +x deploy.sh

$ ./deploy.sh localhost magic-porridge-pot
Ansluter till localhost...
Yay! Funktionen 'deploy' körs pÄ 'hut' med argumentet 'magic-porridge-pot'.

Men vi kan inte vara sÀkra pÄ att det finns en adekvat bash pÄ den fjÀrran vÀrden, sÄ vi lÀgger till en liten kontroll i början (detta istÀllet för shellbang.):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "SkrÀddarsydd '$SHELL' skal stöds inte av 'deploy.sh'. StÀll in en '/bin/bash' skal för '$USER@$HOSTNAME'."
    exit 1
fi

Och nu Àr allt pÄ riktigt:

$ 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!

Nu kan vi öppna http://localhost/ i webblÀsaren, köra deployment igen och sÀkerstÀlla att det gÄr sömlöst genom att uppdatera sidan med F5 under deployment-processen.

Glöm inte att stÀda upp efter dig :3

$ docker rm -f uptimer_GREEN reverse-proxy 
uptimer_GREEN
reverse-proxy

$ docker network rm web-gateway 
web-gateway

$ cd ..

$ rm -r blue-green-deployment

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster