Blue-Green Deployment në minimum

NĂ« kĂ«tĂ« artikull ne do tĂ« bash, ssh, docker dhe nginx organizojmĂ« njĂ« shpĂ«rndarje pa ndĂ«rprerje tĂ« aplikacionit web. ShpĂ«rndarja blu-jeshile Ă«shtĂ« njĂ« teknikĂ« qĂ« lejon tĂ« pĂ«rditĂ«soni menjĂ«herĂ« aplikacionin, pa devijuar as njĂ« kĂ«rkesĂ«. Ajo Ă«shtĂ« njĂ« nga strategjitĂ« e shpĂ«rndarjes me mungesĂ« kohe (zero downtime deployment) dhe Ă«shtĂ« mĂ« e pĂ«rshtatshme pĂ«r aplikacione me njĂ« instancĂ«, por me mundĂ«sinĂ« pĂ«r tĂ« ngarkuar njĂ« instancĂ« tĂ« dytĂ«, gati pĂ«r t’u pĂ«rdorur, afĂ«r.

Supozoni se keni një aplikacion web që ka shumë klientë duke u përdorur aktivisht, dhe nuk mund ta ndaloni as për disa sekonda. Ju nevojitet me urgjencë të publikoni një përditësim të bibliotekës, një rregullim të një gabimi ose një funksionalitet të ri. Në situatën e zakonshme, do të kërkonte të ndalonit aplikacionin, ta zëvendësonit dhe pastaj ta rinisnit. Në rastin e Dockers, mund të zëvendësoni së pari, pastaj ta rinisni, por prapë do të ketë një periudhë, në të cilën kërkesat për aplikacionin nuk do të përpunohen, sepse zakonisht aplikacionit i nevojitet një kohë për ngarkimin e parë. Dhe nëse ai fillon, por rezulton jo funksional? Kjo është një sfidë, le të mundohemi ta zgjidhim me mjete minimale dhe në një mënyrë sa më elegante.

DISCLAMER: Pjesa mĂ« e madhe e artikullit Ă«shtĂ« paraqitur nĂ« njĂ« format eksperimental — si njĂ« regjistrim i njĂ« seance konsolĂ«. Shpresoj se kjo nuk do tĂ« jetĂ« shumĂ« e vĂ«shtirĂ« pĂ«r t'u perceptuar, dhe ky kod e dokumenton veten nĂ« njĂ« masĂ« tĂ« mjaftueshme. PĂ«r atmosferĂ«, imagjinoni se kĂ«to nuk janĂ« thjesht kod snippete, por njĂ« letĂ«r nga njĂ« telegrame "metalike".

Blue-Green Deployment në minimum

Teknikat interesante, tĂ« cilat janĂ« tĂ« vĂ«shtira pĂ«r t'u gjetur vetĂ«m duke lexuar kodin, janĂ« pĂ«rshkruar nĂ« fillim tĂ« çdo seksioni. NĂ«se keni ndonjĂ« paqartĂ«si tjetĂ«r — kĂ«rkoni nĂ« explainshell (me fat, ai po punon pĂ«rsĂ«ri, pĂ«r shkak tĂ« zhbllokimit tĂ« Telegramit). ÇfarĂ« nuk gjen dot — pyesni nĂ« komentet. Me kĂ«naqĂ«si do ta shtoj seksionin pĂ«rkatĂ«s "Teknika interesante".

Le të fillojmë.

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

Shërbimi

Do të krijojmë një shërbim provë dhe do ta vendosim në një kontejner.

Teknika interesante

  • cat < emri-i-fajllit (Dokumenti kĂ«tu + Redirektimi I/O) — njĂ« mĂ«nyrĂ« pĂ«r tĂ« krijuar njĂ« skedar shumĂ«str kinda nĂ« njĂ« komandĂ«. Çdo gjĂ« qĂ« bash do tĂ« lexojĂ« nga /dev/stdin pas kĂ«saj rresht dhe deri nĂ« rreshtin EOF do tĂ« shkruhet nĂ« emri-i-fajllit.
  • wget -qO- URL (explainshell) — pĂ«r tĂ« shfaqur dokumentin e marrĂ« pĂ«rmes HTTP nĂ« /dev/stdout (analog tij curl URL).

Printimi

Unë qëllimisht e ndava snippet-in për të përfshirë ndriçimin për Python. Në fund do të ketë një copë të tillë. Mendoni se në këto vende letra është prerë për t'u kaluar në departamentin e ndriçimit (ku kodi u ngjyros manualisht me highlighter), dhe pastaj këto copa janë ngjitur përsëri.

$ cat < uptimer.py
nga http.server importo BaseHTTPRequestHandler, HTTPServer
nga kohë importo monotonic

versioni_i_aplikacionit = 1
emri_i_aplikacionit = f'Uptimer v{versioni_i_aplikacionit}.0'
sekondat_e_ngarkimit = 15 - versioni_i_aplikacionit * 5

klasa Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        nëse self.path == ' / ':
            përpiquni:
                t = monotonic() - server_start
                nëse t &lt; sekondat_e_ngarkimit:
                    self.send_error(503)
                përndryshe:
                    self.send_response(200)
                    self.send_header(&#039;Content-Type&#039;, &#039;text/html&#039;)
                    self.end_headers()
                    përgjigja = f&#039;<h2>{emri_i_aplikacionit} po funksionon për {t:3.1f} sekonda.</h2>n'
                    self.wfile.write(përgjigja.encode('utf-8'))
            përjashtim Tjetër:
                self.send_error(500)
        përndryshe:
            self.send_error(404)

httpd = HTTPServer(('', 8080), Handler)
server_start = monotonic()
print(f'{emri_i_aplikacionit} (ngarkohet në {sekondat_e_ngarkimit} sek.) nisi.')
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 (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 është duke funksionuar për 15.4 sekonda.</h2>

$ docker rm --force uptimer
uptimer

Reverse proxy

Për të patur mundësinë që aplikacioni ynë të ndryshojë në mënyrë të padukshme, duhet që para tij të ketë një entitet tjetër që do ta fshehë ndërrimin. Kjo mund të jetë një server web nginx në në modin reverse proxy. Reverse proxy vendoset midis klientit dhe aplikacionit. Ai merr kërkesat nga klientët dhe i redirekton ato në aplikacion, ndërsa përgjigjet e aplikacionit i drejton te klientët.

Aplikacioni dhe reverse proxy mund të lidhën brenda docker-it me anë të docker network. Kështu, kontejneri me aplikacionin nuk ka nevojë të kalojë portin në sistemin host, duke e bërë kështu aplikacionin sa më të izoluar nga kërcënimet e jashtme.

Nëse reverse proxy do të jetojë në një host tjetër, do të duhet të heqim dorë nga docker network dhe të lidhim aplikacionin me reverse proxy përmes rrjetit të host-it, duke kaluar portin aplikacione parametrin --publish, si në fillimin origjinal dhe si për reverse proxy.

Reverse proxy do ta nisim në portin 80, sepse kjo është saktësisht ajo entitet që duhet të dëgjojë nga jasht. Nëse porti 80 është i zënë në hostin tuaj testues, ndryshoni parametrin --publish 80:80 në --publish ANY_FREE_PORT:80.

Teknika interesante

Printimi

$ 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 po punon për 11.5 sekonda.</h2>

$ docker run --detach --publish 80:80 --network web-gateway --name reverse-proxy nginx:alpine
80695a822c19051260c66bf60605dcb4ea66802c754037704968bc42527bf120

$ docker ps
KODI I KONTEINERIT        IMAZHI               KOMANDA                  KRIJUAR              STATUS              PORTET                EMRAT
80695a822c19        nginx:alpine        "/docker-entrypoint.…"   27 sekonda më parë       Up 25 sekonda       0.0.0.0:80-&gt;80/tcp   reverse-proxy
a1105f1b583d        uptimer             "python -u ./app.py"     Rreth një minut më parë   Up Rreth një 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: procesi i sinjalit filloi

$ 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 po punon për 104.1 sekonda.</h2>

Deploy i pandërprerë

Do ta nxjerrim versionin e ri të aplikacionit (me një rritje dyfish në performancën e startup) dhe do të përpiqemi ta vendosim atë pa ndërprerje.

Teknika interesante

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' — Shkruaj tekstin my text nĂ« skedarin /my-file.txt brenda kontejnerit my-container.
  • cat > /my-file.txt — Shkruaj nĂ« skedarin pĂ«rmbajtjen e hyrjes standarde /dev/stdin.

Printimi

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

$ docker build --tag uptimer .
Dërgimi i kontekstit të ndërtimit në daemonin Docker  39.94kB
Hapi 1/4 : FROM python:alpine
 ---&gt; 8ecf5a48c789
Hapi 2/4 : EXPOSE 8080
 ---&gt; Përdorimi i cache
 ---&gt; cf92d174c9d3
Hapi 3/4 : COPY uptimer.py app.py
 ---&gt; 3eca6a51cb2d
Hapi 4/4 : CMD [ "python", "-u", "./app.py" ]
 ---&gt; Po ekzekutohet në 8f13c6d3d9e7
Duke hequr kontejnerin e përkohshëm 8f13c6d3d9e7
 ---&gt; 1d56897841ec
Ndërtimi përfundoi me sukses 1d56897841ec
Me etiketë të suksesshme uptimer:latest

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

$ docker logs uptimer_BLUE
Uptimer v2.0 (ngarkohet në 5 sekonda) filloi.

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 është duke funksionuar për 23.9 sekonda.</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: sinjali i procesit filloi

$ wget -qO- http://localhost
<h2>Uptimer v2.0 është duke funksionuar për 63.4 sekonda.</h2>

$ docker rm -f uptimer
uptimer

$ wget -qO- http://localhost
<h2>Uptimer v2.0 është duke funksionuar për 84.8 sekonda.</h2>

$ docker ps
ID KONTEJNERI        IMAZHI               KOMANDA                  KRIJUAR              STATUS              PORTAT                EMRAT
96932d4ca97a        uptimer             "python -u ./app.py"     Rreth një minutë më parë   Up Rreth një minutë   8080/tcp             uptimer_BLUE
80695a822c19        nginx:alpine        "docker-entrypoint.…"   8 minuta më parë        Up 8 minuta        0.0.0.0:80-&gt;80/tcp   reverse-proxy

Në këtë fazë, imazhi ndërtohet direkt në server, gjë që kërkon që aty të ketë origjinalet e aplikacionit, si dhe e ngarkon serverin me punë të panevojshme. Hapi tjetër do të jetë ndarja e ndërtimit të imazhit në një makinë të veçantë (për shembull, në sistemin CI) me transmetimin e mëpasshëm në server.

Rifreskimi i imazheve

Më vjen keq, por nuk ka kuptim të transferosh imazhet nga localhost në localhost, kështu që ky seksion mund të provohet vetëm duke pasur dy host-e me Docker. Në minimum, kjo duket kështu:

$ 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

Ekipa docker save ruan të dhënat e imazhit në një arkiv .tar, që do të thotë se pesha e tij është përafërsisht 1.5 herë më shumë se sa mund të ishte në versionin e kompresuar. Pra, le ta kompresojmë për shkak të kursimit të kohës dhe trafikut:

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

Po ashtu, mund të monitoroni procesin e transferimit (në të vërtetë, për këtë nevojitet një utilitar i jashtëm):

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

Këshillë: Nëse ju duhen shumë parametra për t'u lidhur me serverin përmes SSH, ndoshta nuk po përdorni skedarin ~/.ssh/config.

Transferimi i imazhit pĂ«rmes docker image save/load — kjo Ă«shtĂ« metoda mĂ« minimaliste, por jo e vetmja. Ka edhe tĂ« tjera:

  1. Container Registry (standard i industrisë).
  2. Këtu lidhuni me docker daemon-n e serverit nga një host tjetër:
    1. Variabla DOCKER_HOST.
    2. Parametri i komandës së linjës -H ose --host instrumenti docker-compose.
    3. docker context

Mënyra e dytë (me tri variante të realizimit të saj) është përshkruar mirë në artikullin How to deploy on remote Docker hosts with docker-compose.

deploy.sh

Tani do të grumbullojmë gjithçka që bëmë manualisht në një skenar. Le të fillojmë me funksionin e nivelit të lartë dhe pastaj do të shohim të tjerët që e përdorin atë.

Teknika interesante

  • ${parameter?err_msg} — njĂ« nga magjitĂ« e bash-it (aka substitucioni i parametrave). NĂ«se parameter nuk Ă«shtĂ« caktuar, shfaqni err_msg dhe dilni me kodin 1.
  • docker --log-driver journald — me default, shkalla e regjistrimit tĂ« docker-it Ă«shtĂ« njĂ« skedar tekstual pa ndonjĂ« rotacion. Me kĂ«tĂ« qasje, regjistrat shpejt mbushin gjithĂ« diskun, prandaj pĂ«r ambientet production Ă«shtĂ« e nevojshme tĂ« ndryshoni shkallĂ«n nĂ« njĂ« mĂ« tĂ« mençur.

Skenari i zbarkimit

deploy() {
    local usage_msg="Përdorimi: ${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 "Duke vendosur '$NEW' në vend të '$OLD'..."
    docker run 
        --detach 
        --restart always 
        --log-driver journald 
        --name $NEW 
        --network web-gateway 
        $image_name || return 3
    echo "Kontejneri nisi. Po kontrolloj shëndetin..."
    for i in {1..20}
    do
        sleep 1
        if get-service-status $image_name $new_slot
        then
            echo "Shërbimi i ri '$NEW' duket OK. Duke kaluar në krye..."
            sleep 2  # Sigurohuni që shërbimi është gati
            set-active-slot $image_name $new_slot || return 4
            echo "Shërbimi '$NEW' është në linjë!"
            sleep 2  # Sigurohuni që të gjitha kërkesat janë përpunuar
            echo "Duke mbyllur '$OLD'..."
            docker rm -f $OLD
            docker image prune -f
            echo "Vendosja e suksesshme!"
            return 0
        fi
        echo "Shërbimi i ri '$NEW' nuk është gati ende. Po pres ($i)..."
    done
    echo "Shërbimi i ri '$NEW' nuk u ndez, duke e mbyllur. Dështim në vendosje T_T"
    docker rm -f $NEW
    return 5
}

Funksionet e përdorura:

  • ensure-reverse-proxy — Siguron qĂ« rasti i anĂ«s tjetĂ«r po punon (e dobishme pĂ«r vendosjen e parĂ«)
  • get-active-slot service_name — PĂ«rcakton se cili slot Ă«shtĂ« aktiv tani pĂ«r shĂ«rbimin e dhĂ«nĂ« (BLUE ose GREEN)
  • get-service-status service_name deployment_slot — PĂ«rcakton nĂ«se shĂ«rbimi Ă«shtĂ« i gatshĂ«m pĂ«r tĂ« pĂ«rpunuar kĂ«rkesat e ardhshme
  • set-active-slot service_name deployment_slot — Ndryshon konfigurimin e nginx nĂ« konteinerin e rreth-rjetit

NĂ« rend:

ensure-reverse-proxy() {
    is-container-up reverse-proxy && return 0
    echo "Duke 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
}

Funksioni get-active-slot kërkon disa shpjegime:

Përse ajo kthen një numër dhe jo një varg?

Megjithatë, në funksionin e thirrjes ne kontrollojmë rezultatin e punës së saj, dhe është shumë më e lehtë të kontrollojmë kodin e daljes me bash sesa vargun. Për më tepër, është shumë e lehtë ta marrësh atë në formën e një vargu:
get-active-slot service && echo BLUE || echo GREEN.

A janë të mjaftueshme këto tri kushte për të diferencuar të gjitha gjendjet?

Blue-Green Deployment në minimum

Madje, dy mjaftojnë, e fundit këtu është vetëm për plotësi, që të mos shkruajmë else.

Mbetej e paqartë vetëm funksioni që kthen konfigurimet nginx: get-nginx-config service_name deployment_slot. Në përputhje me healthcheck, këtu mund të caktosh çdo konfigurim për çdo shërbim. Nga interesante, vetëm cat <<- EOF, që lejon të heqni të gjitha tabulaturat në fillim. Megjithatë, çmimi i formatimit të duhur është përzierja e tabulaturave me hapësira, që sot konsiderohet një sjellje shumë e keqe. Por bash forcon tabulat, dhe në konfigurimin e nginx do ishte gjithashtu e dobishme të kishit një formatim normal. Pra, këtu përzierja e tabulaturave me hapësira duket vërtet si zgjidhja më e mirë nga të keqijat. Megjithatë, në snippetin më poshtë, këtë nuk do ta shihni, pasi habr Këtu është e dukshme.

Që të mos bëj dy herë përpjekje, do të flas menjëherë për cat << 'EOF', i cili do të shfaqet më vonë. Nëse shkruani thjesht cat << EOF, atëherë brenda heredoc bëhet interpolimi i vargjeve (shkopen variablet ($foo), thirrjet e komandave ($(bar)), etj.), dhe nëse e mbyllni shenjën e fundit të dokumentit me thonjza të vetme, atëherë interpolimi ndalet dhe simboli $ printohet siç është. Kjo është pikërisht e nevojshmja për të futur një skenar brenda një skenari tjetër.

get-nginx-config() {
    local usage_msg="Përdorimi: ${FUNCNAME[0]} emri_shërbimi slot_investimi"
    local service=${1?$usage_msg}
    local slot=${2?$usage_msg}
    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1

    local container_name=${service}_${slot}
    case $service in
        # Shtoni konfigurimet specifike të nginx për shërbimet tuaja këtu
        *) nginx-config-simple-service $container_name:8080 ;;
    esac
}

nginx-config-simple-service() {
    local usage_msg="Përdorimi: ${FUNCNAME[0]} proxy_pass"
    local proxy_pass=${1?$usage_msg}

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

Kjo është e gjithë skripti. Dhe ja gist-i me këtë skenar për ta shkarkuar përmes wget ose curl.

Ekzekutimi i skenarëve të parametrizuar në një server të largët

Ka ardhur koha për të trokitur në serverin e synuar. Këtë herë localhost do të ishte plotësisht i përshtatshëm:

$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: po përpiqet të regjistrohet me çelësi të rinj, për të filtruar çdo çelës që tashmë është instaluar
/usr/bin/ssh-copy-id: INFO: 1 çelës/çelësa mbeten për t'u instaluar -- nëse ju kërkohet tani është për të instaluar çelësat e rinj
fjalëkalimi për himura@localhost:

Numri i çelësave të shtuar: 1

Tani provoni të regjistroheni në makinë, me: "ssh 'localhost'"
 dhe kontroloni për t'u siguruar që vetëm çelësa/të që dëshironi u shtuan.

Ne kemi shkruar një skenar shpërndarjeje që ngarkon një pamje të paracaktuar në serverin përkatës dhe zëvendëson pa ndërprerje kontejnerin e shërbimit, por si ta ekzekutojmë atë në një makinë të largët? Skripti ka argumente, pasi është universale dhe mund të bëjë shpërndarje të disa shërbimeve nën një rreth-ndihmës (konfigurimet e nginx-it mund ta zgjidhin se për cilin URL do të jetë shërbimi). Skripti nuk mund të ruhet në server, pasi në këtë rast ne nuk do të mund ta azhurnojmë automatikisht (për qëllime rregullimi të gabimeve dhe shtesave të shërbimeve të reja), dhe gjithashtu, qëndrueshmëria = e keqe.

Zgjidhja 1: Të mbani skriptin në server, por ta kopjoni çdo herë përmes scp. Pastaj lidheni përmes ssh dhe ekzekutoni skriptin me argumentet e nevojshme.

Disavantazhet:

  • Dy veprime nĂ« vend tĂ« njĂ«rit
  • Vendi ku po e kopjoni mund tĂ« mos ekzistojĂ«, ose tĂ« ketĂ« qasje tĂ« pamjaftueshme, ose skripti mund tĂ« ekzekutohet gjatĂ« zĂ«vendĂ«simit.
  • Preferohet tĂ« largohet pas (fshihet skripti).
  • TashmĂ« tre veprime.

Zgjidhja 2:

  • NĂ« skript tĂ« mbani vetĂ«m definicionet e funksioneve dhe nĂ« pĂ«rgjithĂ«si tĂ« mos ekzekutoni asgjĂ«,
  • Me ndihmĂ«n e sed shtoni nĂ« fund thirrjen e funksionit
  • DĂ«rgoni gjithçka direkt nĂ« shh pĂ«rmes pipe (|)

Avantazhet:

  • E vĂ«rtetĂ« pa gjendje
  • Nuk ka entitete boilerplate
  • Duke u ndjerĂ« i ftohtĂ«

Por le t'i mbajmë gjërat pa Ansible. Po, gjithçka është tashmë e menduar. Po, bicikleta. Shikoni, sa e lehtë, elegante dhe minimaliste është kjo bicikletë:

$ 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
Duke u lidhur me localhost...
Yay! Funksioni 'deploy' po ekzekutohet në 'hut' me argumentin 'magic-porridge-pot'

Megjithatë, ne nuk mund të jemi të sigurt se në hostin e largët ka një bash të përshtatshëm, kështu që do të shtojmë një verifikim të vogël në fillim (kjo në vend të shellbang):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "Shelli '$SHELL' nuk përkrah 'deploy.sh'. Vendos një shell 'bash' për '$USER@$HOSTNAME'."
    exit 1
fi

Tani gjithçka në të 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
Dërgimi i imazhit gzipped 'uptimer' në 'localhost' përmes ssh...
Imazhi i ngarkuar: uptimer:latest
Po lidhet me 'localhost' përmes ssh për të vendosur pa probleme 'uptimer'...
Duke vendosur 'uptimer_GREEN' në vend të 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Konteineri u nis. Po kontrollojmë shëndetin...
Duke kërkuar 'http://uptimer_GREEN:8080/' brenda rrjetit docker 'web-gateway':
  HTTP/1.0 503 Shërbimi Nuk është i Disponueshëm
wget: serveri ktheu gabim: HTTP/1.0 503 Shërbimi Nuk është i Disponueshëm
Shërbimi i ri 'uptimer_GREEN' nuk është gati akoma. Po presim (1)...
Duke kërkuar 'http://uptimer_GREEN:8080/' brenda rrjetit docker 'web-gateway':
  HTTP/1.0 503 Shërbimi Nuk është i Disponueshëm
wget: serveri ktheu gabim: HTTP/1.0 503 Shërbimi Nuk është i Disponueshëm
Shërbimi i ri 'uptimer_GREEN' nuk është gati akoma. Po presim (2)...
Duke kërkuar 'http://uptimer_GREEN:8080/' brenda rrjetit docker 'web-gateway':
  HTTP/1.0 200 OK
  Server: BaseHTTP/0.6 Python/3.8.3
  Data: Sat, 22 Aug 2020 20:15:50 GMT
  Content-Type: text/html

Shërbimi i ri 'uptimer_GREEN' duket OK. Po kalojmë ndërrimin...
nginx: skedari i konfigurimit /etc/nginx/nginx.conf ka sintaksë të rregullt
nginx: testi i skedarit të konfigurimit /etc/nginx/nginx.conf është i suksesshëm
2020/08/22 20:15:54 [njoftim] 97#97: procesi i sinjalit nisi
Shërbimi 'uptimer_GREEN' është në jetë!
Po vrasim 'uptimer_BLUE'...
uptimer_BLUE
Hapësira totale e rikuperuar: 0B
Vendosja e suksesshme!

Tani mund të hapni http://localhost/ në shfletues, të nisni përsëri vendosjen dhe të siguroheni që ajo kalon pa probleme duke rifreskuar faqen në mënyrë të vazhdueshme gjatë vendosjes.

Mos haroni të pastroni pas punës :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

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster