گهٽ ۾ گهٽ اجرت تي بليو-گرين ڊيپلائيمينٽ

هن آرٽيڪل ۾ اسان استعمال ڪريون ٿا ڪشادو, ssh, گھڻي и نگنڪس اسان منظم ڪنداسين ويب ايپليڪيشن جي بي ترتيب ترتيب. نيري-سائي ڊيپلائيشن هڪ ٽيڪنڪ آهي جيڪا توهان کي هڪ درخواست رد ڪرڻ کان سواءِ فوري طور تي هڪ ايپليڪيشن کي اپڊيٽ ڪرڻ جي اجازت ڏئي ٿي. اهو هڪ آهي صفر ڊائون ٽائم جي ترتيب واري حڪمت عملي ۽ هڪ مثال سان ايپليڪيشنن لاءِ بهترين موزون آهي، پر هڪ سيڪنڊ کي لوڊ ڪرڻ جي صلاحيت، ويجھي هلڻ لاءِ تيار مثال.

اچو ته چئو ته توهان وٽ هڪ ويب ايپليڪيشن آهي جنهن سان ڪيترائي گراهڪ فعال طور تي ڪم ڪري رهيا آهن، ۽ ان لاءِ ڪجهه سيڪنڊن لاءِ ليٽڻ جو ڪو به طريقو ناهي. ۽ توهان کي حقيقت ۾ هڪ لائبريري تازه ڪاري، هڪ بگ فڪس، يا هڪ نئين سٺي خصوصيت کي رول ڪرڻ جي ضرورت آهي. عام حالت ۾، توهان کي ايپليڪيشن کي روڪڻ جي ضرورت پوندي، ان کي تبديل ڪريو ۽ ان کي ٻيهر شروع ڪريو. ڊاڪر جي صورت ۾، توهان پهريان ان کي تبديل ڪري سگهو ٿا، پوء ان کي ٻيهر شروع ڪريو، پر اڃا به هڪ عرصو هوندو جنهن ۾ ايپليڪيشن جي درخواستن تي عمل نه ڪيو ويندو، ڇاڪاڻ ته عام طور تي ايپليڪيشن کي شروعاتي طور تي لوڊ ڪرڻ ۾ ڪجهه وقت لڳندو آهي. ڇا جيڪڏهن اهو شروع ٿئي ٿو، پر ٻاهر ڦرندو آهي ناقابل؟ اهو مسئلو آهي، اچو ته ان کي حل ڪريون گهٽ ۾ گهٽ طريقن سان ۽ جيترو ٿي سگهي خوبصورتي سان.

ڊسڪليمر: گهڻو ڪري آرٽيڪل هڪ تجرباتي فارميٽ ۾ پيش ڪيو ويو آهي - هڪ ڪنسول سيشن جي رڪارڊنگ جي صورت ۾. اميد آهي ته اهو سمجهڻ ڏکيو نه ٿيندو ۽ ڪوڊ پاڻ کي ڪافي دستاويز ڪندو. ماحول لاءِ، تصور ڪريو ته اهي صرف ڪوڊ جا ٽڪرا نه آهن، پر هڪ ”لوهه“ ٽيلي ٽائپ مان ڪاغذ.

گهٽ ۾ گهٽ اجرت تي بليو-گرين ڊيپلائيمينٽ

دلچسپ ٽيڪنالاجيون جيڪي گوگل لاءِ مشڪل آهن صرف ڪوڊ پڙهڻ سان هر سيڪشن جي شروعات ۾ بيان ڪيل آهن. جيڪڏهن ٻيو ڪجهه واضح ناهي، ان کي گوگل ڪريو ۽ ان کي چيڪ ڪريو. وضاحت شيل (خوشقسمتي سان، اهو ٻيهر ڪم ڪري ٿو، ٽيليگرام کي بلاڪ ڪرڻ جي ڪري). جيڪڏھن توھان ڪجھ به گوگل نٿا ڪري سگھو، تبصرن ۾ پڇو. مان لاڳاپيل سيڪشن ۾ شامل ڪرڻ لاء خوش ٿيندس "دلچسپ ٽيڪنالاجي".

اچو ته شروع ڪريون

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

خدمت

اچو ته هڪ تجرباتي خدمت ٺاهيو ۽ ان کي ڪنٽينر ۾ رکون.

دلچسپ ٽيڪنالاجي

  • cat << EOF > file-name (هتي دستاويز + I/O ريڊائريڪشن) هڪ ڪمانڊ سان ملٽي لائن فائل ٺاهڻ جو هڪ طريقو آهي. سڀڪنھن شيء کي bash کان پڙهڻ /dev/stdin هن لڪير کان پوء ۽ لائن کان اڳ EOF ۾ رڪارڊ ڪيو ويندو file-name.
  • wget -qO- URL (وضاحت شيل) - HTTP ذريعي حاصل ڪيل دستاويز کي ٻاھر ڪڍو /dev/stdout (اينالاگ curl URL).

ڇپائي

مان خاص طور تي پيٿون لاءِ نمايان ڪرڻ کي چالو ڪرڻ لاءِ اسنيپٽ کي ٽوڙيو. آخر ۾ هن طرح هڪ ٻيو ٽڪرو ٿيندو. غور ڪريو ته انهن هنڌن تي ڪاغذ کي نمايان ڪرڻ واري کاتي ڏانهن موڪلڻ لاءِ ڪٽيو ويو هو (جتي ڪوڊ هاءِ لائٽر سان هٿ سان رنگيل هو) ۽ پوءِ اهي ٽڪرا واپس چپڪي ويا.

$ 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

ريورس پراکسي

ان لاءِ ته اسان جي ائپليڪيشن کي بغير ڪنهن نوٽيس ۾ تبديل ڪرڻ جي قابل ٿي وڃي، اهو ضروري آهي ته ان جي سامهون ڪو ٻيو ادارو هجي جيڪو ان جي بدلي کي لڪائي ڇڏيندو. اهو هڪ ويب سرور ٿي سگهي ٿو نگنڪس в ريورس پراکسي موڊ. ڪلائنٽ ۽ ايپليڪيشن جي وچ ۾ هڪ ريورس پراکسي قائم ڪئي وئي آهي. اهو گراهڪن کان درخواستون قبول ڪري ٿو ۽ انهن کي ايپليڪيشن ڏانهن اڳتي وڌائي ٿو ۽ درخواستن جا جواب گراهڪن ڏانهن موڪلي ٿو.

ايپليڪيشن ۽ ريورس پراکسي کي استعمال ڪندي ڊاکر اندر ڳنڍجي سگھجي ٿو docker نيٽ ورڪ. اهڙيء طرح، ايپليڪيشن سان گڏ ڪنٽينر کي ميزبان سسٽم تي بندرگاهن کي اڳتي وڌائڻ جي ضرورت ناهي؛ هي ايپليڪيشن کي خارجي خطرن کان وڌ کان وڌ الڳ ڪرڻ جي اجازت ڏئي ٿو.

جيڪڏهن ريورس پراکسي ڪنهن ٻئي ميزبان تي رهي ٿي، توهان کي ڊاکر نيٽ ورڪ کي ڇڏڻو پوندو ۽ ايپليڪيشن کي ميزبان نيٽ ورڪ ذريعي ريورس پراکسي سان ڳنڍڻو پوندو، پورٽ کي اڳتي وڌايو ايپس پيرا ميٽر --publish، جيئن پهرين شروعات ۾ ۽ جيئن ريورس پراکسي سان.

اسان ريورس پراکسي کي پورٽ 80 تي هلائينداسين، ڇاڪاڻ ته اهو ئي ادارو آهي جيڪو ٻاهرين نيٽ ورڪ کي ٻڌڻ گهرجي. جيڪڏهن پورٽ 80 توهان جي ٽيسٽ ميزبان تي مصروف آهي، پيٽرول تبديل ڪريو --publish 80:80 تي --publish ANY_FREE_PORT:80.

دلچسپ ٽيڪنالاجي

ڇپائي

$ docker network create web-gateway
5dba128fb3b255b02ac012ded1906b7b4970b728fb7db3dbbeccc9a77a5dd7bd

$ docker run --detach --rm --name uptimer --network web-gateway uptimer
a1105f1b583dead9415e99864718cc807cc1db1c763870f40ea38bc026e2d67f

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer:8080
<h2>Uptimer v1.0 is running for 11.5 seconds.</h2>

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

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES
80695a822c19        nginx:alpine        "/docker-entrypoint.…"   27 seconds ago       Up 25 seconds       0.0.0.0:80->80/tcp   reverse-proxy
a1105f1b583d        uptimer             "python -u ./app.py"     About a minute ago   Up About a minute   8080/tcp             uptimer

$ cat << EOF > uptimer.conf
server {
    listen 80;
    location / {
        proxy_pass http://uptimer:8080;
    }
}
EOF

$ docker cp ./uptimer.conf reverse-proxy:/etc/nginx/conf.d/default.conf

$ docker exec reverse-proxy nginx -s reload
2020/06/23 20:51:03 [notice] 31#31: signal process started

$ wget -qSO- http://localhost
  HTTP/1.1 200 OK
  Server: nginx/1.19.0
  Date: Sat, 22 Aug 2020 19:56:24 GMT
  Content-Type: text/html
  Transfer-Encoding: chunked
  Connection: keep-alive
<h2>Uptimer v1.0 is running for 104.1 seconds.</h2>

بي ترتيب لڳائڻ

اچو ته ايپليڪيشن جو هڪ نئون ورزن (ٻه ڀيرا شروعاتي ڪارڪردگي جي واڌاري سان) ۽ ان کي بغير بغير ترتيب ڏيڻ جي ڪوشش ڪريو.

دلچسپ ٽيڪنالاجي

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' - متن لکو my text فائل ڪرڻ /my-file.txt ڪنٽينر اندر my-container.
  • cat > /my-file.txt - معياري ان پٽ جي مواد کي فائل ۾ لکو /dev/stdin.

ڇپائي

$ 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

هن اسٽيج تي، تصوير سڌو سنئون سرور تي ٺهيل آهي، جنهن جي ضرورت آهي ته ايپليڪيشن ذريعن کي اتي هجڻ گهرجي، ۽ پڻ سرور کي غير ضروري ڪم سان لوڊ ڪري ٿو. ايندڙ قدم هڪ الڳ مشين ڏانهن تصويري اسيمبلي کي مختص ڪرڻ آهي (مثال طور، هڪ CI سسٽم ڏانهن) ۽ پوء ان کي سرور ڏانهن منتقل ڪريو.

تصويرن جي منتقلي

بدقسمتي سان، تصويرن کي مقامي هوسٽ کان لوڪل هوسٽ ڏانهن منتقل ڪرڻ جو مطلب ناهي، تنهن ڪري اهو سيڪشن صرف ان صورت ۾ ڳولي سگهجي ٿو جيڪڏهن توهان وٽ ٻه ميزبان هٿ ۾ Docker سان آهن. گهٽ ۾ گهٽ اهو ڪجهه هن طرح نظر اچي ٿو:

$ 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

ٽيم docker save تصوير جي ڊيٽا کي .tar آرڪائيو ۾ محفوظ ڪري ٿو، مطلب ته ان جو وزن تقريباً 1.5 ڀيرا وڌيڪ آهي ان کان به وڌيڪ ته ان جو وزن کمپريس ٿيل شڪل ۾ هوندو. تنهن ڪري اچو ته ان کي وقت ۽ ٽرئفڪ بچائڻ جي نالي تي هلون:

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

توھان پڻ ڊائون لوڊ ڪرڻ جي عمل جي نگراني ڪري سگھو ٿا (جيتوڻيڪ ھن کي ٽئين پارٽي جي افاديت جي ضرورت آھي):

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

ھدايت: جيڪڏھن توھان کي ضرورت آھي پيراميٽرز جي ھڪڙي ھڪڙي سرور سان ڳنڍڻ لاءِ SSH ذريعي، توھان شايد فائل استعمال نه ڪري رھيا آھيو ~/.ssh/config.

ذريعي تصوير جي منتقلي docker image save/load - اهو سڀ کان وڌيڪ minimalistic طريقو آهي، پر صرف هڪ ئي ناهي. ٻيا به آهن:

  1. ڪنٽينر رجسٽري (انڊسٽري معيار).
  2. ٻئي ميزبان کان ڊاکر ڊيمن سرور سان ڳنڍيو:
    1. ماحولياتي متغير DOCKER_HOST.
    2. ڪمانڊ لائن آپشن -H يا --host اوزار docker-compose.
    3. docker context

ٻيو طريقو (ان جي عمل درآمد لاء ٽن اختيارن سان) چڱي طرح مضمون ۾ بيان ڪيو ويو آهي ڊاڪر-ڪپوز سان ريموٽ ڊاڪر هوسٽ تي ڪيئن ترتيب ڏيڻ.

deploy.sh

ھاڻي اچو ته سڀڪنھن شيء کي گڏ ڪريون جيڪو اسان دستي طور تي ڪيو آھي ھڪڙي اسڪرپٽ ۾. اچو ته شروع ڪريون مٿاهين سطح جي فنڪشن سان، ۽ پوءِ ان ۾ استعمال ٿيندڙ ٻين کي ڏسو.

دلچسپ ٽيڪنالاجي

  • ${parameter?err_msg} - بش جادو منتر مان هڪ (اڪا پيٽرول جي متبادل). جيڪڏهن parameter بيان نه ڪيل، ٻاڦ err_msg ۽ ڪوڊ 1 سان نڪرڻ.
  • docker --log-driver journald - ڊفالٽ طور، ڊاکر لاگنگ ڊرائيور هڪ ٽيڪسٽ فائل آهي بغير ڪنهن گردش جي. هن طريقي سان، لاگز جلدي پوري ڊسڪ کي ڀريندا آهن، تنهنڪري پيداوار جي ماحول لاء، ڊرائيور کي هڪ هوشيار ۾ تبديل ڪرڻ ضروري آهي.

مقرري اسڪرپٽ

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
}

استعمال ٿيل خاصيتون:

  • ensure-reverse-proxy - پڪ ڪريو ته ريورس پراکسي ڪم ڪري رهي آهي (پهرين ترتيب ڏيڻ لاءِ مفيد)
  • get-active-slot service_name - بيان ڪري ٿو ته ڪهڙي سلاٽ في الحال ڏنل خدمت لاءِ سرگرم آهي (BLUE يا GREEN)
  • get-service-status service_name deployment_slot - اهو طئي ڪري ٿو ته ڇا خدمت ايندڙ درخواستن تي عمل ڪرڻ لاءِ تيار آهي
  • set-active-slot service_name deployment_slot - ريورس پراکسي ڪنٽينر ۾ nginx ترتيب تبديل ڪريو

جي لاء:

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
}

فعل get-active-slot ٿوري وضاحت جي ضرورت آهي:

ڇو ته اهو هڪ نمبر واپس ڪري ٿو ۽ هڪ اسٽرنگ نه ڪڍي ٿو؟

بهرحال، ڪالنگ فنڪشن ۾ اسان ان جي ڪم جو نتيجو چيڪ ڪندا آهيون، ۽ بيش استعمال ڪندي ايگزٽ ڪوڊ چيڪ ڪرڻ اسٽرنگ چيڪ ڪرڻ کان وڌيڪ آسان آهي. ان کان سواء، ان مان هڪ تار حاصل ڪرڻ بلڪل سادو آهي:
get-active-slot service && echo BLUE || echo GREEN.

ڇا ٽي حالتون حقيقت ۾ سڀني رياستن کي ڌار ڪرڻ لاءِ ڪافي آهن؟

گهٽ ۾ گهٽ اجرت تي بليو-گرين ڊيپلائيمينٽ

جيتوڻيڪ ٻه ڪافي هوندا، آخري هڪ هتي صرف مڪمل ٿيڻ لاء آهي، جيئن لکڻ نه هجي else.

صرف فنڪشن جيڪو nginx ترتيبن کي واپس ڪري ٿو اڻڄاتل رهي ٿو: get-nginx-config service_name deployment_slot. صحت جي چڪاس سان قياس سان، هتي توهان ڪنهن به خدمت لاء ڪا به ترتيب سيٽ ڪري سگهو ٿا. دلچسپ شين مان - صرف cat <<- EOF، جيڪو توهان کي شروع ۾ سڀني ٽيب کي هٽائڻ جي اجازت ڏئي ٿو. سچ پچ، سٺي فارميٽنگ جي قيمت خالن سان ملايو ٽيب آهي، جنهن کي اڄڪلهه تمام خراب فارم سمجهيو ويندو آهي. پر بش قوتن جي ٽيب، ۽ اهو پڻ سٺو لڳندو ته nginx ترتيب ۾ عام فارميٽنگ. مختصر ۾، ٽيب کي ملاڻ واري جاءِ سان هتي واقعي لڳي ٿو بدترين حل جو بهترين حل. بهرحال، توهان هن کي هيٺ ڏنل ٽڪڙي ۾ نه ڏسندا، ڇو ته حبر "اهو سٺو ڪري ٿو" سڀني ٽيب کي 4 اسپيس تي تبديل ڪندي ۽ EOF کي غلط بڻائي ٿو. ۽ هتي اهو قابل ذڪر آهي.

ته جيئن ٻه ڀيرا نه اٿي، مان توهان کي فوري طور تي ٻڌايان ٿو cat << 'EOF'، جيڪو بعد ۾ ملندو. جيڪڏهن توهان صرف لکندا آهيو cat << EOF، پوءِ هيڊروڪ اندر اسٽرنگ کي مداخلت ڪئي وئي آهي (متغير وڌايو ويو آهي)$foo)، حڪم ڪالون ($(bar)) وغيره)، ۽ جيڪڏھن توھان دستاويز جي پڇاڙيءَ کي ھڪڙي اقتباس ۾ ڳنڍيندا آھيو، پوءِ انٽرپوليشن غير فعال آھي ۽ علامت $ جيئن ته ڏيکاريل آهي. ڇا توھان کي ضرورت آھي ھڪڙي اسڪرپٽ کي ٻي اسڪرپٽ اندر داخل ڪرڻ لاءِ.

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
}

هي سڄو اسڪرپٽ آهي. ۽ ائين هن رسم الخط سان خلاصو wget يا curl ذريعي ڊائون لوڊ ڪرڻ لاءِ.

ريموٽ سرور تي پيرا ميٽرائيز اسڪرپٽ کي هلائڻ

اهو ٽارگيٽ سرور تي دستڪ ڏيڻ جو وقت آهي. هن ڀيري localhost ڪافي مناسب:

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

اسان هڪ مقرري اسڪرپٽ لکيو آهي جيڪو اڳ ۾ ٺهيل تصوير کي ٽارگيٽ سرور تي ڊائون لوڊ ڪري ٿو ۽ بغير ڪنهن سروس ڪنٽينر کي تبديل ڪري ٿو، پر اسان ان کي ريموٽ مشين تي ڪيئن عمل ڪري سگهون ٿا؟ اسڪرپٽ ۾ دليل آهن، ڇاڪاڻ ته اها آفاقي آهي ۽ ڪيترن ئي خدمتن کي ترتيب ڏئي سگهي ٿي هڪ ئي وقت ۾ هڪ ريورس پراکسي (توهان استعمال ڪري سگهو ٿا nginx configs جو اندازو لڳائڻ لاءِ ته ڪهڙي url ڪهڙي خدمت هوندي). اسڪرپٽ سرور تي محفوظ نه ٿي ڪري سگھجي، ڇو ته ھن صورت ۾ اسان ان کي خودڪار طور تي اپڊيٽ ڪرڻ جي قابل نه ھونداسين (بگ فيڪس ڪرڻ ۽ نئين خدمتن کي شامل ڪرڻ جي مقصد لاء)، ۽ عام طور تي، رياست = برائي.

حل 1: اڃا تائين اسڪرپٽ کي سرور تي ذخيرو ڪريو، پر ان کي هر وقت نقل ڪريو scp. پوء ذريعي ڳنڍيو ssh ۽ ضروري دليلن سان اسڪرپٽ تي عمل ڪريو.

ڪن

  • هڪ بدران ٻه عمل
  • ٿي سگهي ٿو ته ڪا جاءِ نه هجي جتي توهان نقل ڪيو هجي، يا ٿي سگهي ٿو اتي ان تائين رسائي نه هجي، يا اسڪرپٽ کي متبادل جي وقت تي عمل ڪيو وڃي.
  • اهو مشورو ڏنو ويو آهي ته پاڻ کي صاف ڪرڻ کان پوء (اسڪرپٽ کي ختم ڪريو).
  • اڳ ۾ ئي ٽي عمل.

حل 2:

  • رسم الخط ۾ صرف فنڪشن جي تعريف رکو ۽ ڪجھ به نه هلايو
  • جي مدد سان sed آخر ۾ هڪ فنڪشن ڪال شامل ڪريو
  • اهو سڀ سڌو سنئون پائپ ذريعي shh ڏانهن موڪليو (|)

پرو:

  • واقعي بي وطن
  • ڪو به بوائلر پليٽ ادارو ناهي
  • ٿڌو محسوس ٿيڻ

اچو ته جواب ڏيڻ کان سواءِ ئي ڪريون. ها، هر شيء اڳ ۾ ئي ايجاد ڪئي وئي آهي. ها، هڪ سائيڪل. ڏسو ته سائيڪل ڪيتري سادي، خوبصورت ۽ گھٽ ۾ گھٽ آهي:

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

تنهن هوندي، اسان يقين نه ٿا ڪري سگھون ته ريموٽ ميزبان وٽ مناسب بيش آهي، تنهنڪري اسان شروعات ۾ هڪ ننڍڙو چيڪ شامل ڪنداسين (هي ان جي بدران آهي. شيل بينگ):

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

۽ هاڻي اهو حقيقي آهي:

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

هاڻي توهان کولي سگهو ٿا http://localhost/ برائوزر ۾، ڊيپلائيمينٽ کي ٻيهر هلايو ۽ پڪ ڪريو ته اهو صفحو کي ترتيب ڏيڻ دوران سي ڊي جي مطابق اپڊيٽ ڪندي بنا ڪنهن وقفي سان هلندو.

ڪم کان پوء صاف ڪرڻ نه وساريو: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

جو ذريعو: www.habr.com