Kicsinyített Blue-Green Deployment

Ebben a cikkben segítségével bash, ssh, docker és nginx sorozatmentes telepítést valósítunk meg egy webalkalmazás számára. Kék-zöld telepítés — egy technika, amely lehetővé teszi az alkalmazás azonnali frissítését anélkül, hogy bármilyen kérést elutasítanánk. Ez az egyik stratégiája a nulladik leállási idővel történő telepítésnek, és legjobban azokhoz az alkalmazásokhoz illik, amelyeknek egy példánya van, de van lehetőség egy második, működő példány bevezetésére.

Tegyük fel, hogy van egy webalkalmazás, amelyet sok ügyfél aktívan használ, és amelyet semmilyen körülmények között nem lehet néhány másodpercre leállítani. Önnek nagyon szüksége van egy könyvtár frissítésére, egy hiba javítására vagy egy új, menő funkció bevezetésére. Normális helyzetben meg kell állítani az alkalmazást, kicserélni és újraindítani. Docker esetén először kicserélheti, majd újraindíthatja, de ennek ellenére létezik egy időszak, amikor a kérések nem lesznek feldolgozva, mert az alkalmazásnak általában időre van szüksége az első betöltéshez. Mi van, ha elindul, de nem működik? Ilyen feladat elé nézünk, próbáljuk meg a lehető legkevesebb eszközzel és a lehető legkifinomultabban megoldani.

FELHÍVÁS: A cikk nagy része kísérleti formátumban van, konzolos munkamenet felvételében. Remélem, nem lesz túl nehéz ezt követni, és ez a kód kellőképpen dokumentálja önmagát. Az élmény kedvéért képzelje el, hogy ez nem csupán kódrészletek, hanem egy "acéltípusú" teletávirat papírja.

Kicsinyített Blue-Green Deployment

Érdekes technikák, amelyeket nehéz csak a kód olvasásával megtalálni, minden szakasz elején szerepelnek. Ha valami nem világos, kérdezzen a explainshell (szerencsére, ismét működik a Telegram feloldása miatt). Amit nem talál meg a Google-ban, kérdezzen a kommentekben. Szívesen kiegészítem az "Érdekes technikák" című szakaszt.

Kezdjük.

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

Szolgáltatás

Készítünk egy tesztszolgáltatást, és egy konténerbe helyezzük.

Érdekes technikák

  • cat < file-name (Here Document + I/O Redirection) — egy módja annak, hogy több soros fájlt hozzunk létre egy parancssal. Minden, amit a bash olvas a /dev/stdin e sor után és az EOF sor előtt, az a file-name.
  • wget -qO- URL (explainshell) — a HTTP-n keresztül kapott dokumentum megjelenítése /dev/stdout (analóg curl URL).

Kinyomtatás

Kifejezetten megszakítom a snippetet, hogy kiemelést tegyek a Python számára. A végén még egy ilyen darab lesz. Tekintse úgy, hogy ezeken a helyeken a papírt vágták el a kiemelés osztály számára (ahol a kódot manuálisan színeztek kiemelőkkel), majd ezeket a darabokat vissza ragasztották.

$ cat < uptimer.py
a http.server importálásához használja a BaseHTTPRequestHandler-t és a HTTPServer-t
az idő importálásához használja a monotonic-t

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} {t:3.1f} másodpercig fut.</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} (betöltési idő: {loading_seconds} sec.) indítva.)
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 (10 másodperc alatt töltődik) elindult.

$ wget -qSO- http:\/\/localhost:8080
  HTTP\/1.0 503 Szolgáltatás nem elérhető
  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>Az Uptimer v1.0 15,4 másodperc óta fut.</h2>

$ docker rm --force uptimer
uptimer

Fordított proxy

Ahhoz, hogy az alkalmazásunk észrevétlenül megváltozhasson, szükség van egy másik entitásra előtte, amely eltakarja a cseréjét. Ez lehet egy webkiszolgáló. nginx be. fordított proxy módban. A fordított proxy a kliens és az alkalmazás között helyezkedik el. Elfogadja a kliens által indított kéréseket, és átirányítja őket az alkalmazás felé, míg az alkalmazás válaszait a kliensek felé továbbítja.

Az alkalmazást és a fordított proxy-t összekapcsolhatjuk a Docker segítségével docker network. Így az alkalmazás konténerének még portot sem kell megnyitnia a hoszt rendszeren; ez lehetővé teszi az alkalmazás maximális izolálását a külső fenyegetésektől.

Ha a fordított proxy másik hoszton él, el kell hagyni a docker network-öt, és összekapcsolni az alkalmazást a fordított proxy-val a hoszthálózaton keresztül, a port megnyitásával. alkalmazás paraméterrel --publish, mint az első indításnál és a fordított proxy esetében.

A fordított proxy-t a 80-as porton fogjuk futtatni, mert ez az a entitás, amelynek hallgatnia kell a külső forgalomra. Ha a 80-as port a teszt hoszton foglalt, módosítsa a paramétert. --publish 80:80 Gyorsan emlékezzünk vissza, hogyan működik a moduláris aritmetika. Az óra már egy ismert koncepció. Az órák használata --publish ANY_FREE_PORT:80.

Érdekes technikák

Kinyomtatás

$ 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>Az Uptimer v1.0 már 11,5 másodperce fut.</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 másodperce     Futtatás alatt 25 másodperce       0.0.0.0:80-&gt;80\/tcp   reverse-proxy
a1105f1b583d        uptimer             "python -u .\/app.py"     Körülbelül egy perce   Futtatás alatt Körülbelül egy perce   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: process startelt

$ 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>Az Uptimer v1.0 már 104,1 másodperce fut.</h2>

Zökkenőmentes telepítés

Ki fogjuk adni az alkalmazás új verzióját (dupla teljesítménynöveléssel az indítási teljesítményben), és megpróbáljuk zökkenőmentesen telepíteni.

Érdekes technikák

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' — Írja be a szöveget my text a fájlba /my-file.txt a konténeren belül my-container.
  • cat > /my-file.txt — Írja be a szabványos bemenet tartalmát a fájlba /dev/stdin.

Kinyomtatás

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

$ docker build --tag uptimer .
A build context sending to Docker daemon  39.94kB
Step 1/4 : FROM python:alpine
 ---&gt; 8ecf5a48c789
Step 2/4 : EXPOSE 8080
 ---&gt; Cache használata
 ---&gt; cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
 ---&gt; 3eca6a51cb2d
Step 4/4 : CMD [ "python", "-u", ".\/app.py" ]
 ---&gt; Futtatás 8f13c6d3d9e7
Közbenső konténer eltávolítása 8f13c6d3d9e7
 ---&gt; 1d56897841ec
Sikeresen építve 1d56897841ec
Sikeresen címkézve uptimer:latest

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

$ docker logs uptimer_BLUE
Uptimer v2.0 (betöltés 5 sec.) elindult.

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Az Uptimer v2.0 23,9 másodperce fut.</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 {
    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: jelzésfolyamat elindult

$ wget -qO- http://localhost
<h2>Az Uptimer v2.0 63,4 másodperce fut.</h2>

$ docker rm -f uptimer
uptimer

$ wget -qO- http://localhost
<h2>Az Uptimer v2.0 84,8 másodperce fut.</h2>

$ docker ps
KONTÉNER AZONOSÍTÓ        KÉP               PARANCS                  LÉTREHOZVA              ÁLLAPOT              PORTOK                NEVEK
96932d4ca97a        uptimer             "python -u .\/app.py"     Körülbelül egy perce   Fenn Körülbelül egy perce   8080/tcp             uptimer_BLUE
80695a822c19        nginx:alpine        "\/docker-entrypoint.…"   8 perce            Fenn 8 perce        0.0.0.0:80-&gt;80/tcp   reverse-proxy

Ezen a ponton a kép közvetlenül a szerveren készül, amely megköveteli, hogy az alkalmazás forrásai ott legyenek, és felesleges munkát ad a szervernek. A következő lépés egy külön gépre (például CI-rendszerbe) történő képkészítési folyamat kiemelése, majd ennek átköltöztetése a szerverre.

Képek átköltöztetése

Sajnálom, de a localhostról a localhostra történő képfeltöltésnek nincs értelme, így ezt a részt csak akkor tudod kipróbálni, ha két docker hostod van. Minimális beállítások mellett ez körülbelül így néz ki:

$ 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

A parancs docker save a képadatokat egy .tar archívumba menti, tehát körülbelül 1,5-ször nagyobb lesz, mint amennyi lehetne tömörítve. Akkor tömörítsük, hogy időt és forgalmat takarítsunk meg:

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

A folyamat megfigyelésére is van lehetőség (bár ehhez külső segédprogram szükséges):

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

Tipp: Ha az SSH-kapcsolathoz sok paraméterre van szükséged, lehet, hogy nem használod a fájlt ~/.ssh/config.

Kép átvitele a docker image save/load — ez a legminimalista módszer, de nem az egyetlen. Más módszerek is léteznek:

  1. Container Registry (iparági szabvány).
  2. Csatlakozás a server docker daemonhoz egy másik hostból:
    1. Környezeti változó DOCKER_HOST.
    2. Parancssori paraméter -H vagy --host eszköz docker-compose.
    3. docker context

A második módszer (három megvalósítási lehetőséggel) jól le van írva a cikkben Hogyan telepítsünk távoli Docker hostokra a docker-compose segítségével.

deploy.sh

Most mindazt, amit kézzel csináltunk, összevonjuk egy scriptbe. Kezdjük a legfelső szintű funkcióval, majd nézzük meg a többi, benne használtat.

Érdekes technikák

  • ${parameter?err_msg} — a bash-mágia egyik varázsigéje (aka parameter substitution). Ha a parameter nincs megadva, akkor kiírja err_msg és kilép 1-es kóddal.
  • docker --log-driver journald — alapértelmezés szerint a docker naplózásának meghajtója egy szöveges fájl bármilyen forgatás nélkül. Ezzel a megközelítéssel a naplók gyorsan elfoglalják az egész lemezt, ezért produkciós környezetben a meghajtót intelligensebbre kell váltani.

Telepítési script

deploy() {
    local usage_msg="Használat: ${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 "Telepítés '$NEW' helyett '$OLD'..."
    docker run 
        --detach 
        --restart always 
        --log-driver journald 
        --name $NEW 
        --network web-gateway 
        $image_name || return 3
    echo "Konténer elindítva. Egészség ellenőrzése..."
    for i in {1..20}
    do
        sleep 1
        if get-service-status $image_name $new_slot
        then
            echo "Az új '$NEW' szolgáltatás rendben van. Fejváltás..."
            sleep 2  # Gondoskodik arról, hogy a szolgáltatás készen álljon
            set-active-slot $image_name $new_slot || return 4
            echo "A '$NEW' szolgáltatás él!"
            sleep 2  # Gondoskodik arról, hogy minden kérés feldolgozva legyen
            echo "'Killing '$OLD'..."
            docker rm -f $OLD
            docker image prune -f
            echo "Telepítés sikeres!"
            return 0
        fi
        echo "Az új '$NEW' szolgáltatás még nem készen áll. Várakozás ($i)..."
    done
    echo "Az új '$NEW' szolgáltatás nem indult el, megszüntetve. A telepítés nem sikerült T_T"
    docker rm -f $NEW
    return 5
}

Használt funkciók:

  • ensure-reverse-proxy — Ellenőrzi, hogy a fordított proxy működik (hasznos az első telepítéshez)
  • get-active-slot service_name — Meghatározza, hogy melyik slot aktív a megadott szolgáltatás számára (BLUE vagy GREEN)
  • get-service-status service_name deployment_slot — Meghatározza, hogy a szolgáltatás készen áll-e bejövő kérések feldolgozására
  • set-active-slot service_name deployment_slot — Megváltoztatja az nginx konfigurációt a fordított proxy konténerében

Részletesen:

ensure-reverse-proxy() {
    is-container-up reverse-proxy && return 0
    echo "Visszapillantó telepítése..."
    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?"Használat: ${FUNCNAME[0]} container_name"}

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

get-active-slot() {
    local service=${1?"Használat: ${FUNCNAME[0]} service_name"}

    if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then
        echo "Ütközés történt! ${service}_GREEN leállítása..."
        docker rm -f ${service}_GREEN
        return 0  # KÉK
    fi
    if is-container-up ${service}_BLUE && ! is-container-up ${service}_GREEN; then
        return 0  # KÉK
    fi
    if ! is-container-up ${service}_BLUE; then
        return 1  # ZÖLD
    fi
}

get-service-status() {
    local usage_msg="Használat: ${FUNCNAME[0]} service_name deployment_slot"
    local service=${1?usage_msg}
    local slot=${2?$usage_msg}

    case $service in
        # Adjon meg konkrét egészségellenőrzési útvonalakat a szolgáltatásaihoz itt
        *) local health_check_port_path=":8080/" ;;
    esac
    local health_check_address="http://${service}_${slot}${health_check_port_path}"
    echo "'${health_check_address}' címre kérés a 'web-gateway' docker hálózaton:"
    docker run --rm --network web-gateway alpine 
        wget --timeout=1 --quiet --server-response $health_check_address
    return $?
}

set-active-slot() {
    local usage_msg="Használat: ${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
}

Funkció get-active-slot kisebb magyarázatra szorul:

Miért ad vissza számot, és miért nem szöveget írásba?

Mindazonáltal a hívó függvényben ellenőrizzük a működésének eredményét, és a bash-beli exit kódot ellenőrizni sokkal egyszerűbb, mint egy szöveget. Ezenkívül, nagyon egyszerű kibővíteni szöveggé:
get-active-slot service && echo BLUE || echo GREEN.

De vajon elegendő három feltétel a különböző állapotok megkülönböztetésére?

Kicsinyített Blue-Green Deployment

Még csupán kettő is elegendő, az utolsó itt csak a teljesség kedvéért van, hogy ne kelljen írni. else.

Csak a nginx konfigurációkat visszaadó függvény maradt meghatározatlanul: get-nginx-config service_name deployment_slot. Hasonlóan a healthcheck-hez, itt bármilyen konfigurációt megadhat bármelyik szolgáltatáshoz. Ami érdekes — csak cat <<- EOF, amely lehetővé teszi, hogy eltávolítsuk az összes tabulát az elején. Az igazság az, hogy a megfelelő formázás ára – a különböző tabok és szóközök keveréke, ami manapság nagyon rossz formának számít. De a bash erőlteti a tabokat, és az nginx konfigurációban is jó lenne, ha megfelelő formázás lenne. Röviden, itt a tabok és szóközök keveréke valóban a legrosszabb megoldás legjobbja. Azonban az alábbi snippetben ezt nem fogja látni, mivel a habr „jól csinálja”, mindent 4 szóközre cserél, és érvénytelen EOF-ot készít. Itt jól látható.

Hogy elkerüljük a felesleges ismétlést, azonnal beszélek a cat << 'EOF', amely még találkozni fog később. Ha csak annyit írsz, hogy cat << EOF, akkor a heredoc-on belül a szöveg interpolálódik (kibővülnek a változók ($foo), parancshívások ($(bar)) stb.), azonban ha a dokumentum végét egyes idézőjelek közé zárjuk, akkor az interpoláció ki van kapcsolva, és a karakter $ könnyen kibővíthető. Ez pontosan szükséges ahhoz, hogy egy scriptet beleillesszünk egy másik scriptbe.

get-nginx-config() {
    local usage_msg="Használat: ${FUNCNAME[0]} szolgáltatás_név telepítési_hely"
    local service=${1?$usage_msg}
    local slot=${2?$usage_msg}
    [ "$slot" == KÉK ] || [ "$slot" == ZÖLD ] || return 1

    local container_name=${service}_${slot}
    case $service in
        # Itt add meg a szolgáltatásaid specifikus nginx konfigurációit
        *) nginx-config-simple-service $container_name:8080 ;;
    esac
}

nginx-config-simple-service() {
    local usage_msg="Használat: ${FUNCNAME[0]} proxy_pass"
    local proxy_pass=${1?$usage_msg}

cat << EOF
server {
    listen 80;
    location / {
        proxy_pass http://$proxy_pass;
    }
}
EOF
}

Ez az egész script. És itt van a gitről szóló script letöltéshez wget vagy curl segítségével.

Paraméterezett script-ek végrehajtása távoli szerveren

Itt az idő, hogy belépjünk a célszerverre. Most localhost teljesen megfelelő lesz:

$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: új kulcs(ok)kal próbálkozik a bejelentkezéshez, hogy kiszűrje azokat, amelyek már telepítve vannak
/usr/bin/ssh-copy-id: INFO: 1 kulcs(ok) maradtak telepítésre - ha most megkérdezik, az új kulcsok telepítésére történik
himura@localhost jelszava: 

A telepített kulcs(ok) száma: 1

Most próbálj meg bejelentkezni a gépre: "ssh 'localhost'"
és ellenőrizd, hogy csak a kívánt kulcs(ok) lettek-e telepítve.

Írtunk egy telepítő szkriptet, amely feltölti az előre összeállított képet a cél szerverre, és zökkenőmentesen kicseréli a szolgáltatás konténerét, de hogyan hajtsuk végre azt távoli gépen? A szkriptnek vannak argumentumai, mivel univerzális, és egyszerre több szolgáltatást is képes telepíteni egy fordított proxy alatt (az nginx konfigurációkkal megoldható, hogy mely URL-hez melyik szolgáltatás tartozik). A szkriptet nem lehet a szerveren tárolni, mert ebben az esetben nem tudjuk automatikusan frissíteni (a hibajavítások és új szolgáltatások hozzáadása érdekében), és egyáltalán, a state = rossz dolog.

Megoldás 1: Mégis tárolni a szkriptet a szerveren, de minden alkalommal átmásolni scp. Ezután csatlakozni ssh és végrehajtani a szkriptet a szükséges argumentumokkal.

Hátrányok:

  • Két lépés egy helyett
  • A hely, ahová másolsz, lehet, hogy nem létezik, vagy nem lesz hozzáférésed, vagy a szkript végrehajtása történhet a csere pillanatában.
  • Ajánlott eltakarítani magad után (törölni a szkriptet).
  • Már három lépés.

Megoldás 2:

  • A szkriptben csak a függvények definiálását tartani, és egyáltalán semmit nem végrehajtani
  • A segítségével sed a függvényhívás végére írni
  • Mindezt közvetlenül az shh-n át küldeni pipe-on keresztül (|)

Előnyök:

  • Tényleg stateless
  • Nincsenek sablon entity-k
  • Menőnek lenni

Csak kérlek, Ansible nélkül. Igen, már mindent kitaláltak. Igen, a kerék. Nézd, milyen egyszerű, elegáns és minimalista kerék:

$ 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
Csatlakozás a localhost-hoz...
Hurrá! A 'deploy' függvény a 'hut'-on fut a 'magic-porridge-pot' argumentummal.

Mégsem lehetünk biztosak abban, hogy a távoli hoszton van megfelelő bash, ezért adjunk hozzá egy kis ellenőrzést az elejére (ez a shellbang):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "A '$SHELL' shell nem támogatott a 'deploy.sh'-ben. Állíts be egy '/bin/bash' shellt a '$USER@$HOSTNAME'-nek."
    exit 1
fi

Most pedig minden valódi:

$ 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
Gzipelt képként küldjük az 'uptimer'-t a 'localhost'-ra ssh-n keresztül...
Betöltött kép: uptimer:latest
Kapcsolódás a 'localhost'-hoz ssh-n keresztül az 'uptimer' zökkenőmentes telepítéséhez...
Az 'uptimer_GREEN' telepítése az 'uptimer_BLUE' helyére...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
A konténer elindult. Egészségellenőrzés folyamatban...
'http://uptimer_GREEN:8080/' kérés a 'web-gateway' docker hálózaton belül:
  HTTP/1.0 503 Szolgáltatás nem elérhető
wget: a szerver hibát adott vissza: HTTP/1.0 503 Szolgáltatás nem elérhető
Az új 'uptimer_GREEN' szolgáltatás még nem készen áll. Várakozás (1)...
'http://uptimer_GREEN:8080/' kérés a 'web-gateway' docker hálózaton belül:
  HTTP/1.0 503 Szolgáltatás nem elérhető
wget: a szerver hibát adott vissza: HTTP/1.0 503 Szolgáltatás nem elérhető
Az új 'uptimer_GREEN' szolgáltatás még nem készen áll. Várakozás (2)...
'http://uptimer_GREEN:8080/' kérés a 'web-gateway' docker hálózaton belül:
  HTTP/1.0 200 Rendben
  Szerver: BaseHTTP/0.6 Python/3.8.3
  Dátum: 2020. augusztus 22. 20:15:50 GMT
  Tartalom típusa: text/html

Az új 'uptimer_GREEN' szolgáltatás rendben van. Fejek váltása...
nginx: a konfigurálási fájl /etc/nginx/nginx.conf szintaktikailag helyes
nginx: konfigurálási fájl /etc/nginx/nginx.conf tesztje sikeres
2020/08/22 20:15:54 [notice] 97#97: jelzés folyamat elindítva
Az 'uptimer_GREEN' szolgáltatás aktív!
Az 'uptimer_BLUE' leállítása...
uptimer_BLUE
Teljesen visszanyert terület: 0B
A telepítés sikeres!

Most már megnyithatjuk http://localhost/ a böngészőben, újra indíthatjuk a telepítést, és megbizonyosodhatunk róla, hogy zökkenőmentesen zajlik az oldal frissítési folyamatával a telepítés alatt.

Ne felejtsük el kitakarítani a munka után :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

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster