Eng kam ish haqi bo'yicha ko'k-yashil joylashtirish

Ushbu maqolada biz foydalanamiz bosh, sSH, docker ΠΈ nginx Biz veb-ilovaning uzluksiz tartibini tashkil qilamiz. Moviy-yashil joylashtirish bitta so'rovni rad etmasdan dasturni bir zumda yangilash imkonini beruvchi texnikadir. Bu nol uzilish vaqtini tarqatish strategiyalaridan biri bo'lib, bir nusxasi bo'lgan ilovalar uchun eng mos keladi, lekin ikkinchi, ishga tayyor namunani yaqin atrofda yuklash imkoniyati.

Aytaylik, sizda ko'plab mijozlar faol ishlayotgan veb-ilovangiz bor va u bir necha soniya davomida yotishning mutlaqo imkoni yo'q. Va siz haqiqatan ham kutubxonani yangilash, xatolarni tuzatish yoki yangi ajoyib xususiyatni ishga tushirishingiz kerak. Oddiy holatda, dasturni to'xtatish, uni almashtirish va qayta ishga tushirish kerak bo'ladi. Docker bo'lsa, avval uni almashtirishingiz mumkin, keyin uni qayta ishga tushirishingiz mumkin, lekin hali ham dasturga so'rovlar ko'rib chiqilmaydigan muddat bo'ladi, chunki odatda dasturni dastlab yuklash uchun biroz vaqt ketadi. Agar u boshlansa, lekin ishlamay qolsa-chi? Bu muammo, keling, uni minimal vositalar bilan va iloji boricha oqlangan holda hal qilaylik.

Ogohlantirish: Maqolaning ko'p qismi eksperimental formatda - konsol seansini yozib olish shaklida taqdim etilgan. Umid qilamanki, buni tushunish juda qiyin bo'lmaydi va kod o'zini etarli darajada hujjatlashtiradi. Atmosfera uchun bu shunchaki kod parchalari emas, balki "temir" teletayp qog'ozi ekanligini tasavvur qiling.

Eng kam ish haqi bo'yicha ko'k-yashil joylashtirish

Kodni o'qish orqali Google uchun qiyin bo'lgan qiziqarli texnikalar har bir bo'limning boshida tasvirlangan. Agar biror narsa tushunarsiz bo'lsa, uni Google-ga kiriting va tekshiring. tushuntirish qobig'i (xayriyatki, telegramma blokdan chiqarilganligi sababli u yana ishlaydi). Agar siz Google-da hech narsa qila olmasangiz, sharhlarda so'rang. Tegishli bo'limga "Qiziqarli texnikalar" qo'shishdan xursand bo'laman.

Keling, boshlaymiz.

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

xizmat

Keling, eksperimental xizmat ko'rsatamiz va uni idishga joylashtiramiz.

Qiziqarli texnikalar

  • cat << EOF > file-name (Mana Hujjat + I/U qayta yo'naltirish) - bitta buyruq bilan ko'p qatorli fayl yaratish usuli. Barcha bash o'qiydi /dev/stdin bu qatordan keyin va qatordan oldin EOF da qayd etiladi file-name.
  • wget -qO- URL (tushuntirish qobig'i) β€” HTTP orqali olingan hujjatni chiqarish /dev/stdout (analog curl URL).

Chop etish

Python uchun ajratib ko'rsatishni yoqish uchun men alohida parchani buzaman. Oxirida shunga o'xshash yana bir parcha bo'ladi. Tasavvur qiling-a, bu joylarda qog'oz ajratib ko'rsatish bo'limiga jo'natish uchun kesilgan (bu erda kod yoritgichlar bilan qo'lda bo'yalgan) va keyin bu qismlar orqaga yopishtirilgan.

$ 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

Teskari proksi

Ilovamiz sezilmasdan o'zgarishi uchun uning oldida uning o'rnini yashiradigan boshqa ob'ekt bo'lishi kerak. Bu veb-server bo'lishi mumkin nginx Π² teskari proksi rejimi. Mijoz va dastur o'rtasida teskari proksi-server o'rnatiladi. U mijozlarning so'rovlarini qabul qiladi va ularni ilovaga yuboradi va arizaning javoblarini mijozlarga yuboradi.

Ilova va teskari proksi-server yordamida docker ichida bog'lanishi mumkin docker tarmog'i. Shunday qilib, ilova bilan konteyner host tizimidagi portni yo'naltirishga hojat yo'q, bu ilovani tashqi tahdidlardan maksimal darajada ajratish imkonini beradi.

Agar teskari proksi boshqa xostda yashasa, siz docker tarmog'idan voz kechishingiz va dasturni portni yo'naltirish orqali xost tarmog'i orqali teskari proksi-serverga ulashingiz kerak bo'ladi. ilovalar parametr --publish, birinchi boshlang'ichda va teskari proksi-serverda bo'lgani kabi.

Biz teskari proksi-serverni 80-portda ishga tushiramiz, chunki bu tashqi tarmoqni tinglashi kerak bo'lgan ob'ekt. Sinov xostingizda 80 port band bo'lsa, parametrni o'zgartiring --publish 80:80 haqida --publish ANY_FREE_PORT:80.

Qiziqarli texnikalar

Chop etish

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

Uzluksiz joylashtirish

Keling, ilovaning yangi versiyasini chiqaraylik (boshlash unumdorligini ikki baravar oshirish bilan) va uni muammosiz joylashtirishga harakat qilaylik.

Qiziqarli texnikalar

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' - Matn yozing my text faylga /my-file.txt konteyner ichida my-container.
  • cat > /my-file.txt β€” Faylga standart kiritish mazmunini yozing /dev/stdin.

Chop etish

$ 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

Ushbu bosqichda tasvir to'g'ridan-to'g'ri serverda quriladi, bu dastur manbalarining u erda bo'lishini talab qiladi, shuningdek serverni keraksiz ish bilan yuklaydi. Keyingi qadam, tasvirni yig'ishni alohida mashinaga (masalan, CI tizimiga) ajratish va keyin uni serverga o'tkazishdir.

Tasvirlarni uzatish

Afsuski, tasvirlarni localhost-dan localhost-ga o'tkazish mantiqqa to'g'ri kelmaydi, shuning uchun bu bo'lim faqat sizning qo'lingizda Docker-ga ega ikkita xostingiz bo'lsa, o'rganilishi mumkin. Hech bo'lmaganda u shunday ko'rinadi:

$ 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

komanda docker save tasvir ma'lumotlarini .tar arxivida saqlaydi, ya'ni u siqilgan shakldagi og'irlikdan taxminan 1.5 baravar ko'proq og'irlik qiladi. Keling, vaqt va trafikni tejash uchun uni silkitamiz:

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

Yuklab olish jarayonini ham kuzatishingiz mumkin (garchi bu uchinchi tomon yordam dasturini talab qilsa ham):

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

Maslahat: Agar serverga SSH orqali ulanish uchun bir qator parametrlar kerak boΚ»lsa, siz fayldan foydalanmayotgan boΚ»lishingiz mumkin. ~/.ssh/config.

Tasvirni uzatish orqali docker image save/load - Bu eng minimalist usul, ammo yagona emas. Boshqalar ham bor:

  1. Konteynerlar reestri (sanoat standarti).
  2. Boshqa xostdan docker demon serveriga ulaning:
    1. atrof-muhit o'zgaruvchisi DOCKER_HOST.
    2. Buyruqlar qatori opsiyasi -H yoki --host asbob docker-compose.
    3. docker context

Ikkinchi usul (uni amalga oshirishning uchta varianti bilan) maqolada yaxshi tasvirlangan Docker-compose yordamida masofaviy Docker xostlarida qanday joylashtirish mumkin.

deploy.sh

Keling, qo'lda qilgan hamma narsani bitta skriptga to'playmiz. Keling, yuqori darajadagi funksiyadan boshlaylik, keyin esa unda ishlatiladigan boshqa funksiyalarni ko'rib chiqamiz.

Qiziqarli texnikalar

  • ${parameter?err_msg} - bash sehrli afsunlardan biri (aka parametrlarni almashtirish). Agar parameter belgilanmagan, chiqish err_msg va 1-kod bilan chiqing.
  • docker --log-driver journald β€” sukut bo'yicha, docker jurnali drayveri hech qanday aylanishsiz matn faylidir. Ushbu yondashuv bilan jurnallar tezda butun diskni to'ldiradi, shuning uchun ishlab chiqarish muhiti uchun drayverni aqlliroqqa o'zgartirish kerak.

O'rnatish skripti

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
}

Foydalanilgan xususiyatlar:

  • ensure-reverse-proxy β€” Teskari proksi-server ishlayotganiga ishonch hosil qiladi (birinchi joylashtirish uchun foydali)
  • get-active-slot service_name β€” Berilgan xizmat uchun hozirda qaysi slot faol ekanligini aniqlaydi (BLUE yoki GREEN)
  • get-service-status service_name deployment_slot β€” Xizmat kiruvchi soβ€˜rovlarni koβ€˜rib chiqishga tayyormi yoki yoβ€˜qligini aniqlaydi
  • set-active-slot service_name deployment_slot β€” Teskari proksi-konteynerdagi nginx konfiguratsiyasini o'zgartiradi

Tartibda:

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
}

vazifa get-active-slot bir oz tushuntirishni talab qiladi:

Nima uchun u raqamni qaytaradi va satrni chiqarmaydi?

Qanday bo'lmasin, chaqiruv funktsiyasida biz uning ishining natijasini tekshiramiz va bash yordamida chiqish kodini tekshirish satrni tekshirishdan ko'ra osonroqdir. Bundan tashqari, undan satr olish juda oddiy:
get-active-slot service && echo BLUE || echo GREEN.

Barcha davlatlarni farqlash uchun uchta shart haqiqatan ham etarlimi?

Eng kam ish haqi bo'yicha ko'k-yashil joylashtirish

Hatto ikkitasi etarli bo'ladi, oxirgisi yozmaslik uchun faqat to'liqlik uchun else.

Faqat nginx konfiguratsiyasini qaytaruvchi funksiya aniqlanmagan: get-nginx-config service_name deployment_slot. Sog'liqni saqlash tekshiruviga o'xshab, bu erda istalgan xizmat uchun istalgan konfiguratsiyani o'rnatishingiz mumkin. Qiziqarli narsalardan - faqat cat <<- EOF, bu sizga boshida barcha yorliqlarni olib tashlash imkonini beradi. To'g'ri, yaxshi formatlash narxi bugungi kunda juda yomon shakl deb hisoblanadigan bo'shliqlar bilan aralash tablardir. Lekin bash tablarni majburlaydi va nginx konfiguratsiyasida normal formatlash ham yaxshi bo'lardi. Muxtasar qilib aytganda, bu erda bo'shliqlar bilan yorliqlarni aralashtirish, albatta, eng yomonidan eng yaxshi yechim kabi ko'rinadi. Biroq, siz buni quyidagi parchada ko'rmaysiz, chunki Habr barcha yorliqlarni 4 bo'sh joyga o'zgartirib, EOFni yaroqsiz qilib "yaxshi qiladi". Va bu erda u sezilarli.

Ikki marta turmaslik uchun men sizga darhol aytib beraman cat << 'EOF', bu keyinroq duch keladi. Agar oddiygina yozsangiz cat << EOF, keyin heredoc ichida satr interpolyatsiya qilinadi (o'zgaruvchilar kengaytiriladi ($foo), buyruq chaqiruvlari ($(bar)) va hokazo) va agar siz hujjatning oxirini bitta tirnoq ichiga qo'ysangiz, interpolyatsiya o'chiriladi va belgi $ kabi ko'rsatiladi. Boshqa skript ichiga skript kiritish uchun nima kerak.

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
}

Bu butun skript. Va hokazo Ushbu skript bilan asosiy wget yoki curl orqali yuklab olish uchun.

Uzoq serverda parametrlangan skriptlarni bajarish

Maqsadli serverni taqillatish vaqti keldi. Bu safar localhost juda mos:

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

Biz maqsadli serverga oldindan yaratilgan tasvirni yuklaydigan va xizmat ko'rsatish konteynerini muammosiz almashtiradigan joylashtirish skriptini yozdik, ammo uni masofaviy kompyuterda qanday bajarishimiz mumkin? Skriptda argumentlar mavjud, chunki u universal va bir vaqtning o'zida bir nechta xizmatlarni bitta teskari proksi-server ostida joylashtirishi mumkin (qaysi url qaysi xizmat bo'lishini aniqlash uchun nginx konfiguratsiyasidan foydalanishingiz mumkin). Skriptni serverda saqlab bo'lmaydi, chunki bu holda biz uni avtomatik ravishda yangilay olmaymiz (xatolarni tuzatish va yangi xizmatlarni qo'shish uchun) va umuman, davlat = yomon.

Yechim 1: Skriptni hali ham serverda saqlang, lekin uni har safar nusxalang scp. Keyin orqali ulaning ssh va skriptni kerakli argumentlar bilan bajaring.

Kamchiliklari:

  • Bitta o'rniga ikkita harakat
  • Siz nusxa ko'chiradigan joy bo'lmasligi yoki unga kirish imkoni bo'lmasligi yoki almashtirish vaqtida skript bajarilgan bo'lishi mumkin.
  • O'zingizdan keyin tozalash tavsiya etiladi (skriptni o'chirish).
  • Allaqachon uchta harakat.

Yechim 2:

  • Skriptda faqat funksiya ta'riflarini saqlang va hech narsani ishga tushirmang
  • Yordamida sed oxirigacha funktsiya chaqiruvini qo'shing
  • Hammasini quvur orqali to'g'ridan-to'g'ri shhga yuboring (|)

Taroziga soling:

  • Haqiqatan ham vatansiz
  • Boilerplate ob'ektlari yo'q
  • Salqin his

Keling, buni Ansiblesiz qilaylik. Ha, hamma narsa allaqachon ixtiro qilingan. Ha, velosiped. Velosiped qanchalik sodda, oqlangan va minimalist ekanligini ko'ring:

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

Biroq, biz masofaviy xostda etarli bash borligiga ishonchimiz komil emas, shuning uchun biz boshida kichik chek qo'shamiz (bu o'rniga 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

Va endi bu haqiqiy:

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

Endi siz ochishingiz mumkin http://localhost/ brauzerda joylashtirishni qayta ishga tushiring va tartib davomida sahifani CD ga muvofiq yangilash orqali uning muammosiz ishlashiga ishonch hosil qiling.

Ishdan keyin tozalashni unutmang: 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

Manba: www.habr.com