Blue-Green Deployment sa minimum nga sweldo

Niini nga artikulo among gigamit bash, ssh, docker ΠΈ nginx Mag-organisar kami og usa ka seamless nga layout sa web application. Asul-berde nga pag-deploy usa ka teknik nga nagtugot kanimo sa pag-update dayon sa usa ka aplikasyon nga dili isalikway ang usa ka hangyo. Usa kini sa mga estratehiya sa pag-deploy sa zero downtime ug labing haum alang sa mga aplikasyon nga adunay usa ka higayon, apan ang abilidad sa pag-load sa usa ka ikaduha, andam-padagan nga pananglitan sa duol.

Ingnon ta nga ikaw adunay usa ka aplikasyon sa web diin daghang mga kliyente ang aktibo nga nagtrabaho, ug wala’y hingpit nga paagi aron kini makahigda sulod sa pipila ka segundo. Ug kinahanglan gyud nimo nga ilunsad ang usa ka update sa librarya, usa ka pag-ayo sa bug, o usa ka bag-ong cool nga bahin. Sa usa ka normal nga sitwasyon, kinahanglan nimo nga hunongon ang aplikasyon, pulihan kini ug sugdi kini pag-usab. Sa kaso sa docker, mahimo nimo una nga ilisan kini, unya i-restart kini, apan adunay usa ka panahon diin ang mga hangyo sa aplikasyon dili maproseso, tungod kay kasagaran ang aplikasyon nagkinahanglag panahon sa una nga pagkarga. Unsa kaha kung kini magsugod, apan nahimo nga dili magamit? Kini ang problema, sulbaron nato kini sa gamay nga paagi ug ingon ka elegante kutob sa mahimo.

DISCLAIMER: Kadaghanan sa artikulo gipresentar sa usa ka eksperimento nga format - sa porma sa usa ka pagrekord sa usa ka sesyon sa console. Hinaut nga kini dili kaayo lisud sabton ug ang code magdokumento sa kaugalingon nga igo. Alang sa atmospera, hunahunaa nga kini dili lamang mga tipik sa code, apan papel gikan sa usa ka "puthaw" nga teletype.

Blue-Green Deployment sa minimum nga sweldo

Ang makapainteres nga mga teknik nga lisud sa Google pinaagi lang sa pagbasa sa code gihulagway sa sinugdanan sa matag seksyon. Kung adunay lain nga dili klaro, i-google kini ug tan-awa kini. explainshell (maayo na lang, kini nagtrabaho pag-usab, tungod sa pag-unblock sa telegrama). Kung dili nimo mahimo ang Google bisan unsa, pangutana sa mga komento. Malipayon ako nga idugang sa katugbang nga seksyon nga "Makaikag nga mga pamaagi".

Atong sugdan

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

nga pag-alagad

Magbuhat ta ug eksperimento nga serbisyo ug ibutang kini sa sudlanan.

Makapainteres nga mga teknik

  • cat << EOF > file-name (Ania ang Dokumento + I/O Redirection) maoy usa ka paagi sa paghimo og multi-line file nga adunay usa ka command. Ang tanan nga bash mabasa gikan sa /dev/stdin pagkahuman niini nga linya ug sa wala pa ang linya EOF irekord sa file-name.
  • wget -qO- URL (explainshell) β€” magpagawas ug dokumento nga nadawat pinaagi sa HTTP sa /dev/stdout (analogue curl URL).

Printout

Espesyal nga gibuak nako ang snippet aron mahimo ang pag-highlight alang sa Python. Sa katapusan adunay laing piraso nga sama niini. Hunahunaa nga niining mga dapita ang papel giputol aron ipadala ngadto sa highlighting department (diin ang code gikoloran sa kamot gamit ang mga highlighter), ug unya kini nga mga piraso gipapilit balik.

$ 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

Balikbalik nga proxy

Aron ang atong aplikasyon makahimo sa pagbag-o nga dili mamatikdan, gikinahanglan nga adunay laing entidad sa atubangan niini nga magtago sa pagpuli niini. Kini mahimo nga usa ka web server nginx Π² reverse proxy mode. Ang usa ka reverse proxy gitukod tali sa kliyente ug sa aplikasyon. Gidawat niini ang mga hangyo gikan sa mga kliyente ug gipasa kini sa aplikasyon ug gipasa ang mga tubag sa aplikasyon sa mga kliyente.

Ang aplikasyon ug reverse proxy mahimong ma-link sa sulod sa docker gamit network sa pantalan. Sa ingon, ang sudlanan nga adunay aplikasyon dili kinahanglan nga mag-forward sa usa ka pantalan sa host system; kini nagtugot sa aplikasyon nga labi nga mahimulag gikan sa mga hulga sa gawas.

Kung ang reverse proxy nagpuyo sa laing host, kinahanglan nimong biyaan ang docker network ug ikonektar ang aplikasyon sa reverse proxy pinaagi sa host network, ipasa ang pantalan apps parametro --publish, sama sa unang pagsugod ug sama sa reverse proxy.

Atong ipadagan ang reverse proxy sa port 80, tungod kay mao kini ang entidad nga kinahanglan mamati sa eksternal nga network. Kung ang port 80 busy sa imong test host, usba ang parameter --publish 80:80 sa --publish ANY_FREE_PORT:80.

Makapainteres nga mga teknik

Printout

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

Seamless nga pagdeploy

Atong ilunsad ang usa ka bag-ong bersyon sa aplikasyon (nga adunay duha ka pilo nga pagpausbaw sa performance sa pagsugod) ug sulayi nga i-deploy kini nga hapsay.

Makapainteres nga mga teknik

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' β€” Pagsulat og teksto my text sa pag-file /my-file.txt sulod sa sudlanan my-container.
  • cat > /my-file.txt β€” Isulat ang sulod sa standard input sa usa ka file /dev/stdin.

Printout

$ 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

Niini nga yugto, ang imahe gitukod direkta sa server, nga nanginahanglan nga ang mga gigikanan sa aplikasyon naa didto, ug nag-load usab sa server nga wala kinahanglana nga trabaho. Ang sunod nga lakang mao ang paggahin sa image assembly sa usa ka bulag nga makina (pananglitan, sa usa ka CI system) ug dayon ibalhin kini sa server.

Pagbalhin sa mga hulagway

Ikasubo, dili makatarunganon nga ibalhin ang mga imahe gikan sa localhost ngadto sa localhost, mao nga kini nga seksyon mahimo ra nga masusi kung adunay ka duha ka host nga adunay Docker sa kamot. Sa labing gamay kini tan-awon sama niini:

$ 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 gitipigan ang datos sa imahe sa usa ka .tar archive, nagpasabut nga kini motimbang ug mga 1.5 ka pilo nga labaw pa kaysa kini motimbang sa compressed nga porma. Busa atong uyogon kini sa ngalan sa pagdaginot sa oras ug trapiko:

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

Mahimo usab nimo bantayan ang proseso sa pag-download (bisan kung kini nanginahanglan usa ka ikatulo nga partido nga utility):

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

Sugyot: Kung kinahanglan nimo ang usa ka hugpong sa mga parameter aron makonektar sa usa ka server pinaagi sa SSH, mahimong dili nimo gigamit ang file ~/.ssh/config.

Pagbalhin sa imahe pinaagi sa docker image save/load - Kini ang labing minimalistic nga pamaagi, apan dili ang usa ra. Adunay uban:

  1. Container Registry (standard sa industriya).
  2. Sumpaysumpaya ang docker daemon server gikan sa laing host:
    1. variable sa palibot DOCKER_HOST.
    2. Opsyon sa command line -H o --host instrumento docker-compose.
    3. docker context

Ang ikaduha nga pamaagi (nga adunay tulo nga mga kapilian alang sa pagpatuman niini) maayo nga gihulagway sa artikulo Giunsa ang pag-deploy sa hilit nga mga host sa Docker nga adunay docker-compose.

deploy.sh

Karon atong kolektahon ang tanan nga atong gibuhat sa mano-mano ngadto sa usa ka script. Magsugod ta sa top-level function, ug dayon tan-awon ang uban nga gigamit niini.

Makapainteres nga mga teknik

  • ${parameter?err_msg} - usa sa mga bash magic spelling (aka pag-ilis sa mga parameter). Kung ang parameter wala gipiho, output err_msg ug paggawas gamit ang code 1.
  • docker --log-driver journald β€” sa default, ang driver sa pag-log sa docker usa ka text file nga wala’y bisan unsang rotation. Uban niini nga pamaagi, ang mga troso dali nga napuno ang tibuuk nga disk, mao nga alang sa usa ka palibot sa produksiyon kinahanglan nga usbon ang drayber sa usa ka mas maalamon.

Pag-deploy nga script

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
}

Mga gamit nga gigamit:

  • ensure-reverse-proxy β€” Siguruha nga ang reverse proxy nagtrabaho (mapuslan alang sa una nga pag-deploy)
  • get-active-slot service_name β€” Pagtino kung unsang slot ang aktibo karon para sa gihatag nga serbisyo (BLUE o GREEN)
  • get-service-status service_name deployment_slot β€” Pagtino kung ang serbisyo andam na ba sa pagproseso sa umaabot nga mga hangyo
  • set-active-slot service_name deployment_slot - Gibag-o ang nginx config sa reverse proxy nga sudlanan

Aron:

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
}

function get-active-slot nagkinahanglan og gamay nga katin-awan:

Ngano nga kini nagbalik sa usa ka numero ug dili nagpagawas sa usa ka hilo?

Bisan pa, sa function sa pagtawag gisusi namon ang resulta sa trabaho niini, ug ang pagsusi sa exit code gamit ang bash labi ka dali kaysa pagsusi sa usa ka pisi. Dugang pa, ang pagkuha sa usa ka pisi gikan niini yano ra kaayo:
get-active-slot service && echo BLUE || echo GREEN.

Ang tulo ka kondisyon ba igo na kaayo aron mailhan ang tanang estado?

Blue-Green Deployment sa minimum nga sweldo

Bisan ang duha igo na, ang katapusan ania ra para sa pagkakompleto, aron dili magsulat else.

Ang function nga nagbalik sa nginx configs nagpabilin nga wala mahibal-an: get-nginx-config service_name deployment_slot. Pinaagi sa analohiya sa pagsusi sa kahimsog, dinhi mahimo nimong itakda ang bisan unsang config alang sa bisan unsang serbisyo. Sa makapaikag nga mga butang - lamang cat <<- EOF, nga nagtugot kanimo sa pagtangtang sa tanan nga mga tab sa sinugdanan. Tinuod, ang presyo sa maayo nga pag-format gisagol nga mga tab nga adunay mga wanang, nga karon giisip nga dili maayo nga porma. Apan ang mga tab nga pwersa sa bash, ug maayo usab nga adunay normal nga pag-format sa nginx config. Sa laktud, ang pagsagol sa mga tab nga adunay mga luna dinhi ingon gyud ang labing kaayo nga solusyon gikan sa labing daotan. Bisan pa, dili nimo kini makita sa snippet sa ubos, tungod kay ang Habr "maayo ang pagbuhat niini" pinaagi sa pagbag-o sa tanan nga mga tab sa 4 nga mga wanang ug paghimo sa EOF nga dili balido. Ug dinhi kini mamatikdan.

Aron dili makabangon kaduha, sultihan ko ikaw dayon cat << 'EOF', nga masugatan unya. Kung magsulat ka sa yano cat << EOF, unya sa sulod sa heredoc ang string gi-interpolated (ang mga variable gipalapdan ($foo), command calls ($(bar)) ug uban pa), ug kung imong ilakip ang katapusan sa dokumento sa usa ka kinutlo, nan ang interpolation dili magamit ug ang simbolo $ gipakita ingon nga mao. Unsa ang imong kinahanglan nga sal-ot sa usa ka script sa sulod sa laing script.

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
}

Kini ang tibuok script. Unya gist uban niini nga script alang sa pag-download pinaagi sa wget o curl.

Pagpatuman sa mga parameterized nga mga script sa usa ka hilit nga server

Panahon na sa pagtuktok sa target nga server. Niining higayona localhost angayan kaayo:

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

Nagsulat kami usa ka script sa pag-deploy nga nag-download sa usa ka pre-built nga imahe sa target nga server ug hapsay nga gipuli ang sulud sa serbisyo, apan giunsa namon kini mahimo sa usa ka hilit nga makina? Ang script adunay mga argumento, tungod kay kini unibersal ug mahimong mag-deploy sa daghang mga serbisyo sa usa ka higayon ubos sa usa ka reverse proxy (mahimo nimong gamiton ang nginx configs aron mahibal-an kung unsang url ang mahimong serbisyo). Ang script dili matipigan sa server, tungod kay sa kini nga kaso dili namon kini awtomatiko nga ma-update (alang sa katuyoan sa pag-ayo sa bug ug pagdugang bag-ong mga serbisyo), ug sa kinatibuk-an, estado = daotan.

Solusyon 1: Itago gihapon ang script sa server, apan kopyaha kini matag higayon scp. Dayon magkonektar pinaagi sa ssh ug ipatuman ang script gamit ang gikinahanglan nga mga argumento.

Kahinumduman:

  • Duha ka aksyon imbis usa
  • Mahimong walay dapit diin imong kopyahon, o walay access niini, o ang script mahimong ipatuman sa panahon sa pag-ilis.
  • Kini mao ang advisable sa paglimpyo human sa imong kaugalingon (pagtangtang sa script).
  • Tulo na ka aksyon.

Solusyon 2:

  • Itago lamang ang mga kahulugan sa function sa script ug wala’y bisan unsa
  • Uban sa tabang sa sed pagdugang usa ka tawag sa function hangtod sa katapusan
  • Ipadala kining tanan direkta sa shh pinaagi sa tubo (|)

Mga Pro:

  • Stateless gyud
  • Walay boilerplate entity
  • Gibati nga cool

Buhaton na lang nato nga walay Ansible. Oo, ang tanan naimbento na. Oo, usa ka bisikleta. Tan-awa kung unsa ka yano, elegante ug minimalistic ang bike:

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

Bisan pa, dili kami makasiguro nga ang layo nga host adunay igo nga bash, mao nga magdugang kami usa ka gamay nga tseke sa sinugdanan (kini imbes nga shellbang):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."
    exit 1
fi

Ug karon tinuod na:

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

Karon mahimo nimong ablihan http://localhost/ sa browser, padagana pag-usab ang deployment ug siguruha nga kini nagdagan nga hapsay pinaagi sa pag-update sa panid sumala sa CD sa panahon sa layout.

Ayaw kalimot sa pagpanglimpyo human sa trabaho :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

Source: www.habr.com