کم از کم اجرت پر بلیو گرین تعیناتی۔

اس مضمون میں ہم استعمال کرتے ہیں۔ مار, 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

ریورس پراکسی

ہماری درخواست کو کسی کا دھیان نہ دینے کے قابل ہونے کے لیے، یہ ضروری ہے کہ اس کے سامنے کوئی اور ادارہ ہو جو اس کے متبادل کو چھپائے۔ یہ ایک ویب سرور ہو سکتا ہے۔ نگنکس в ریورس پراکسی موڈ. کلائنٹ اور ایپلیکیشن کے درمیان ایک ریورس پراکسی قائم ہوتی ہے۔ یہ کلائنٹس کی درخواستوں کو قبول کرتا ہے اور انہیں ایپلی کیشن کو آگے بھیجتا ہے اور درخواست کے جوابات کلائنٹس کو بھیجتا ہے۔

ایپلیکیشن اور ریورس پراکسی کو استعمال کرتے ہوئے ڈوکر کے اندر منسلک کیا جا سکتا ہے۔ ڈاکر نیٹ ورک. اس طرح، ایپلی کیشن کے ساتھ کنٹینر کو ہوسٹ سسٹم پر پورٹ فارورڈ کرنے کی بھی ضرورت نہیں ہے؛ یہ ایپلیکیشن کو زیادہ سے زیادہ بیرونی خطرات سے الگ تھلگ کرنے کی اجازت دیتا ہے۔

اگر ریورس پراکسی کسی دوسرے میزبان پر رہتی ہے، تو آپ کو ڈوکر نیٹ ورک کو ترک کرنا ہوگا اور پورٹ کو فارورڈ کرتے ہوئے میزبان نیٹ ورک کے ذریعے ایپلی کیشن کو ریورس پراکسی سے جوڑنا ہوگا۔ ایپس پیرامیٹر --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 - یہ سب سے کم سے کم طریقہ ہے، لیکن صرف ایک نہیں. دیگر ہیں:

  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 ایک چھوٹی سی وضاحت کی ضرورت ہے:

یہ نمبر کیوں واپس کرتا ہے اور اسٹرنگ کو آؤٹ پٹ کیوں نہیں کرتا ہے؟

ویسے بھی کالنگ فنکشن میں ہم اس کے کام کا نتیجہ چیک کرتے ہیں، اور bash کا استعمال کرتے ہوئے ایگزٹ کوڈ کو چیک کرنا سٹرنگ کو چیک کرنے سے کہیں زیادہ آسان ہے۔ اس کے علاوہ، اس سے تار حاصل کرنا بہت آسان ہے:
get-active-slot service && echo BLUE || echo GREEN.

کیا واقعی تین شرائط تمام ریاستوں کو الگ کرنے کے لیے کافی ہیں؟

کم از کم اجرت پر بلیو گرین تعیناتی۔

یہاں تک کہ دو ہی کافی ہوں گے، آخری یہاں صرف مکمل ہونے کے لیے ہے، تاکہ نہ لکھیں۔ else.

صرف وہ فنکشن جو nginx configs کو واپس کرتا ہے غیر متعینہ رہتا ہے: get-nginx-config service_name deployment_slot. صحت کی جانچ کے ساتھ مشابہت کے ساتھ، یہاں آپ کسی بھی سروس کے لیے کوئی بھی ترتیب ترتیب دے سکتے ہیں۔ دلچسپ چیزوں میں سے - صرف cat <<- EOF، جو آپ کو شروع میں تمام ٹیبز کو ہٹانے کی اجازت دیتا ہے۔ سچ ہے، اچھی فارمیٹنگ کی قیمت خالی جگہوں کے ساتھ ملے جلے ٹیبز ہیں، جسے آج بہت خراب شکل سمجھا جاتا ہے۔ لیکن bash ٹیبز کو مجبور کرتا ہے، اور یہ بھی اچھا ہو گا کہ nginx config میں نارمل فارمیٹنگ ہو۔ مختصراً، یہاں خالی جگہوں کے ساتھ ٹیبز کو ملانا واقعی بدترین حل کی طرح لگتا ہے۔ تاہم، آپ کو ذیل کے ٹکڑوں میں یہ نظر نہیں آئے گا، کیونکہ Habr تمام ٹیبز کو 4 اسپیس میں تبدیل کرکے اور EOF کو غلط بنا کر "اچھی طرح سے کرتا ہے"۔ اور یہاں یہ قابل دید ہے۔.

تاکہ دو بار نہ اٹھیں، میں آپ کو فوراً بتاؤں گا۔ cat << 'EOF'، جس کا سامنا بعد میں کیا جائے گا۔ اگر آپ سادگی سے لکھیں۔ cat << EOF، پھر heredoc کے اندر اسٹرنگ کو انٹرپولیٹ کیا جاتا ہے (متغیرات کو بڑھایا جاتا ہے ($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 کون سا سروس ہو گا)۔ اسکرپٹ کو سرور پر محفوظ نہیں کیا جا سکتا، کیونکہ اس صورت میں ہم اسے خود بخود اپ ڈیٹ نہیں کر سکیں گے (بگ فکس کرنے اور نئی سروسز شامل کرنے کے مقصد سے) اور عام طور پر، state = برائی۔

حل 1: پھر بھی اسکرپٹ کو سرور پر اسٹور کریں، لیکن اسے ہر بار کاپی کریں۔ scp. پھر اس کے ذریعے جڑیں۔ ssh اور ضروری دلائل کے ساتھ اسکرپٹ پر عمل کریں۔

Cons:

  • ایک کے بجائے دو اعمال
  • ہو سکتا ہے کہ کوئی ایسی جگہ نہ ہو جہاں آپ کاپی کرتے ہوں، یا اس تک رسائی نہ ہو، یا اسکرپٹ کو متبادل کے وقت عمل میں لایا جائے۔
  • اپنے بعد صاف کرنے کا مشورہ دیا جاتا ہے (اسکرپٹ کو حذف کریں)۔
  • پہلے ہی تین اعمال۔

حل 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