Биньо-Π·Π΅Π»Π΅Π½ΠΎ Ρ€Π°Π·ΠΏΠΎΠ»Π°Π³Π°Π½Π΅ Π½Π° ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ Π·Π°ΠΏΠ»Π°Ρ‚ΠΈ

Π’ Ρ‚Π°Π·ΠΈ статия ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ тряскам, SSH, Π΄ΠΎΠΊΠ΅Ρ€ ΠΈ Nginx НиС Ρ‰Π΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ Π±Π΅Π·ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ½ΠΎ ΠΎΡ„ΠΎΡ€ΠΌΠ»Π΅Π½ΠΈΠ΅ Π½Π° ΡƒΠ΅Π± ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ. Биньо-Π·Π΅Π»Π΅Π½ΠΎ Ρ€Π°Π·Π³Ρ€ΡŠΡ‰Π°Π½Π΅ Π΅ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠ°, която Π²ΠΈ позволява Π½Π΅Π·Π°Π±Π°Π²Π½ΠΎ Π΄Π° Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Π±Π΅Π· Π΄Π° ΠΎΡ‚Ρ…Π²ΡŠΡ€Π»ΡΡ‚Π΅ Π½ΠΈΡ‚ΠΎ Π΅Π΄Π½Π° заявка. Π’ΠΎΠ²Π° Π΅ Π΅Π΄Π½Π° ΠΎΡ‚ стратСгиитС Π·Π° внСдряванС с Π½ΡƒΠ»Π΅Π²ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π½Π° прСстой ΠΈ Π΅ Π½Π°ΠΉ-подходяща Π·Π° прилоТСния с Π΅Π΄ΠΈΠ½ СкзСмпляр, Π½ΠΎ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ Π·Π° Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π²Ρ‚ΠΎΡ€ΠΈ, Π³ΠΎΡ‚ΠΎΠ² Π·Π° изпълнСниС СкзСмпляр Π½Π°Π±Π»ΠΈΠ·ΠΎ.

Π”Π° ΠΏΡ€ΠΈΠ΅ΠΌΠ΅ΠΌ, Ρ‡Π΅ ΠΈΠΌΠ°Ρ‚Π΅ ΡƒΠ΅Π± ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, с ΠΊΠΎΠ΅Ρ‚ΠΎ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ работят ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΈ, ΠΈ няма Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ никакъв Π½Π°Ρ‡ΠΈΠ½ Ρ‚ΠΎ Π΄Π° Π»Π΅Π³Π½Π΅ Π·Π° няколко сСкунди. И наистина трябва Π΄Π° пуснСтС актуализация Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°Ρ‚Π°, корСкция Π½Π° Π³Ρ€Π΅ΡˆΠΊΠΈ ΠΈΠ»ΠΈ Π½ΠΎΠ²Π° страхотна функция. Π’ Π½ΠΎΡ€ΠΌΠ°Π»Π½Π° ситуация Ρ‰Π΅ трябва Π΄Π° спрСтС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, Π΄Π° Π³ΠΎ смСнитС ΠΈ Π΄Π° Π³ΠΎ стартиратС ΠΎΡ‚Π½ΠΎΠ²ΠΎ. Π’ случай Π½Π° docker ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡŠΡ€Π²ΠΎ Π΄Π° Π³ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚Π΅, слСд Ρ‚ΠΎΠ²Π° Π΄Π° Π³ΠΎ рСстартиратС, Π½ΠΎ всС ΠΏΠ°ΠΊ Ρ‰Π΅ ΠΈΠΌΠ° ΠΏΠ΅Ρ€ΠΈΠΎΠ΄, Π² ΠΊΠΎΠΉΡ‚ΠΎ заявкитС към ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ няма Π΄Π° сС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π°Ρ‚, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΎΡ‚Π½Π΅ΠΌΠ° извСстно Π²Ρ€Π΅ΠΌΠ΅, Π·Π° Π΄Π° сС Π·Π°Ρ€Π΅Π΄ΠΈ ΠΏΡŠΡ€Π²ΠΎΠ½Π°Ρ‡Π°Π»Π½ΠΎ. Какво Ρ‰Π΅ станС, Π°ΠΊΠΎ Π·Π°ΠΏΠΎΡ‡Π½Π΅, Π½ΠΎ сС ΠΎΠΊΠ°ΠΆΠ΅, Ρ‡Π΅ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚ΠΈ? Π’ΠΎΠ²Π° Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚, Π½Π΅ΠΊΠ° Π³ΠΎ Ρ€Π΅ΡˆΠΈΠΌ с ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ срСдства ΠΈ възмоТно Π½Π°ΠΉ-Π΅Π»Π΅Π³Π°Π½Ρ‚Π½ΠΎ.

ΠžΠ’ΠšΠΠ— ОВ ΠžΠ’Π“ΠžΠ’ΠžΠ ΠΠžΠ‘Π’: По-голямата част ΠΎΡ‚ статията Π΅ прСдставСна Π² СкспСримСнталСн Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ - ΠΏΠΎΠ΄ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° запис Π½Π° ΠΊΠΎΠ½Π·ΠΎΠ»Π½Π° сСсия. НадявамС сС, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° няма Π΄Π° Π΅ Ρ‚Π²ΡŠΡ€Π΄Π΅ Ρ‚Ρ€ΡƒΠ΄Π½ΠΎ Π·Π° Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅ ΠΈ ΠΊΠΎΠ΄ΡŠΡ‚ Ρ‰Π΅ сС Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€Π° Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ. Π—Π° атмосфСра си прСдставСтС, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° Π½Π΅ са просто ΠΊΠΎΠ΄ΠΎΠ²ΠΈ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΈ, Π° хартия ΠΎΡ‚ β€žΠΆΠ΅Π»Π΅Π·Π΅Π½β€œ Ρ‚Π΅Π»Π΅Ρ‚Π°ΠΉΠΏ.

Биньо-Π·Π΅Π»Π΅Π½ΠΎ Ρ€Π°Π·ΠΏΠΎΠ»Π°Π³Π°Π½Π΅ Π½Π° ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ Π·Π°ΠΏΠ»Π°Ρ‚ΠΈ

Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΠ½ΠΈ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ са Ρ‚Ρ€ΡƒΠ΄Π½ΠΈ Π·Π° Google само Ρ‡Ρ€Π΅Π· Ρ‡Π΅Ρ‚Π΅Π½Π΅ Π½Π° ΠΊΠΎΠ΄Π°, са описани Π² Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ Π½Π° всСки Ρ€Π°Π·Π΄Π΅Π». Ако Π½Π΅Ρ‰ΠΎ Π΄Ρ€ΡƒΠ³ΠΎ Π½Π΅ Π΅ ясно, ΠΏΠΎΡ‚ΡŠΡ€ΡΠ΅Ρ‚Π΅ Π³ΠΎ Π² Google ΠΈ Π³ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Ρ‚Π΅. обяснСниС (Π·Π° щастиС ΠΎΡ‚Π½ΠΎΠ²ΠΎ Ρ€Π°Π±ΠΎΡ‚ΠΈ, ΠΏΠΎΡ€Π°Π΄ΠΈ Π΄Π΅Π±Π»ΠΎΠΊΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚Π΅Π»Π΅Π³Ρ€Π°ΠΌΠ°Ρ‚Π°). Ако Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π½Π°ΠΌΠ΅Ρ€ΠΈΡ‚Π΅ Π½ΠΈΡ‰ΠΎ Π² Google, ΠΏΠΎΠΏΠΈΡ‚Π°ΠΉΡ‚Π΅ Π² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΡ‚Π΅. Π©Π΅ сС Ρ€Π°Π΄Π²Π°ΠΌ Π΄Π° добавя към ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ Ρ€Π°Π·Π΄Π΅Π» β€žΠ˜Π½Ρ‚Π΅Ρ€Π΅ΡΠ½ΠΈ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈβ€œ.

Π”Π° Π·Π°ΠΏΠΎΡ‡Π½Π΅ΠΌ.

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

ΠŸΡ€ΠΈΠ½Ρ‚ΠΈΡ€Π°ΠΌ

Π‘ΠΏΠ΅Ρ†ΠΈΠ°Π»Π½ΠΎ ΠΏΡ€Π΅ΠΊΡŠΡΠ²Π°ΠΌ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Π°, Π·Π° Π΄Π° Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€Π°ΠΌ ΠΏΠΎΠ΄Ρ‡Π΅Ρ€Ρ‚Π°Π²Π°Π½Π΅ Π·Π° Python. Накрая Ρ‰Π΅ ΠΈΠΌΠ° ΠΎΡ‰Π΅ Π΅Π΄Π½ΠΎ ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎ ΠΏΠ°Ρ€Ρ‡Π΅. ΠŸΠΎΠΌΠΈΡΠ»Π΅Ρ‚Π΅, Ρ‡Π΅ Π½Π° Ρ‚Π΅Π·ΠΈ мСста хартията Π΅ Π±ΠΈΠ»Π° нарязана, Π·Π° Π΄Π° бъдС ΠΈΠ·ΠΏΡ€Π°Ρ‚Π΅Π½Π° Π² ΠΎΡ‚Π΄Π΅Π»Π° Π·Π° ΠΏΠΎΠ΄Ρ‡Π΅Ρ€Ρ‚Π°Π²Π°Π½Π΅ (ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ ΠΊΠΎΠ΄ΡŠΡ‚ Π΅ Π±ΠΈΠ» ΠΎΡ†Π²Π΅Ρ‚Π΅Π½ Ρ€ΡŠΡ‡Π½ΠΎ с ΠΌΠ°Ρ€ΠΊΠ΅Ρ€ΠΈ), Π° слСд Ρ‚ΠΎΠ²Π° Ρ‚Π΅Π·ΠΈ ΠΏΠ°Ρ€Ρ‡Π΅Ρ‚Π° са Π±ΠΈΠ»ΠΈ Π·Π°Π»Π΅ΠΏΠ΅Π½ΠΈ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ.

$ 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

ΠžΠ±Ρ€Π°Ρ‚Π½ΠΎ прокси

Π—Π° Π΄Π° ΠΌΠΎΠΆΠ΅ Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄Π° сС ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ нСзабСлязано, Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡ€Π΅Π΄ Π½Π΅Π³ΠΎ Π΄Π° ΠΈΠΌΠ° Π΄Ρ€ΡƒΠ³ ΠΎΠ±Π΅ΠΊΡ‚, ΠΊΠΎΠΉΡ‚ΠΎ Π΄Π° скриС замСстващия Π³ΠΎ. МоТС Π΄Π° Π΅ ΡƒΠ΅Π± ΡΡŠΡ€Π²ΡŠΡ€ Nginx Π² Ρ€Π΅ΠΆΠΈΠΌ Π½Π° ΠΎΠ±Ρ€Π°Ρ‚Π΅Π½ прокси. ΠœΠ΅ΠΆΠ΄Ρƒ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ сС установява ΠΎΠ±Ρ€Π°Ρ‚Π΅Π½ прокси. Π’ΠΎΠΉ ΠΏΡ€ΠΈΠ΅ΠΌΠ° заявки ΠΎΡ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΈ ΠΈ Π³ΠΈ ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‰Π° към ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΈ ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‰Π° ΠΎΡ‚Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚Π΅ Π½Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ към ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΈΡ‚Π΅.

ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΈ обратният прокси ΡΡŠΡ€Π²ΡŠΡ€ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ ΡΠ²ΡŠΡ€Π·Π°Π½ΠΈ Π²ΡŠΡ‚Ρ€Π΅ Π² Π΄ΠΎΠΊΠ΅Ρ€ с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° Π΄ΠΎΠΊΠ΅Ρ€ ΠΌΡ€Π΅ΠΆΠ°. По Ρ‚ΠΎΠ·ΠΈ Π½Π°Ρ‡ΠΈΠ½ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΡŠΡ‚ с ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π΄ΠΎΡ€ΠΈ Π½Π΅ сС Π½ΡƒΠΆΠ΄Π°Π΅ ΠΎΡ‚ ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‰Π°Π½Π΅ Π½Π° ΠΏΠΎΡ€Ρ‚ към хост систСмата; Ρ‚ΠΎΠ²Π° позволява ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π΄Π° бъдС максимално ΠΈΠ·ΠΎΠ»ΠΈΡ€Π°Π½ΠΎ ΠΎΡ‚ външни Π·Π°ΠΏΠ»Π°Ρ…ΠΈ.

Ако обратният прокси ΠΆΠΈΠ²Π΅Π΅ Π½Π° Π΄Ρ€ΡƒΠ³ хост, Ρ‰Π΅ трябва Π΄Π° изоставитС Π΄ΠΎΠΊΠ΅Ρ€ ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° ΠΈ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ към обратния прокси ΠΏΡ€Π΅Π· хост ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π°, ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‰Π°ΠΉΠΊΠΈ ΠΏΠΎΡ€Ρ‚Π° прилоТСния ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚ΡŠΡ€ --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 систСма) ΠΈ слСд Ρ‚ΠΎΠ²Π° Π΄Π° Π³ΠΎ ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΠΈΡ‚Π΅ Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π°.

ΠŸΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΡΠ½Π΅ Π½Π° изобраТСния

Π—Π° съТалСниС, няма смисъл Π΄Π° ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΡΡ‚Π΅ изобраТСния ΠΎΡ‚ localhost към localhost, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Ρ‚ΠΎΠ·ΠΈ Ρ€Π°Π·Π΄Π΅Π» ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС изслСдван само Π°ΠΊΠΎ ΠΈΠΌΠ°Ρ‚Π΅ Π΄Π²Π° хоста с 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

Вторият ΠΌΠ΅Ρ‚ΠΎΠ΄ (с Ρ‚Ρ€ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° Π·Π° ΠΏΡ€ΠΈΠ»Π°Π³Π°Π½Π΅Ρ‚ΠΎ ΠΌΡƒ) Π΅ Π΄ΠΎΠ±Ρ€Π΅ описан Π² статията Как Π΄Π° Ρ€Π°Π·ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅ Π½Π° ΠΎΡ‚Π΄Π°Π»Π΅Ρ‡Π΅Π½ΠΈ Docker хостовС с docker-compose.

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, остава Π½Π΅Π΄Π΅Ρ„ΠΈΠ½ΠΈΡ€Π°Π½Π°: get-nginx-config service_name deployment_slot. По аналогия с ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°Ρ‚Π° Π½Π° Π·Π΄Ρ€Π°Π²Π΅Ρ‚ΠΎ, Ρ‚ΡƒΠΊ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π·Π°Π΄Π°Π΄Π΅Ρ‚Π΅ всяка конфигурация Π·Π° всяка услуга. ΠžΡ‚ интСрСснитС Π½Π΅Ρ‰Π° – само cat <<- EOF, ΠΊΠΎΠ΅Ρ‚ΠΎ Π²ΠΈ позволява Π΄Π° ΠΏΡ€Π΅ΠΌΠ°Ρ…Π½Π΅Ρ‚Π΅ всички Ρ€Π°Π·Π΄Π΅Π»ΠΈ Π² Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ. Вярно Π΅, Ρ‡Π΅ Ρ†Π΅Π½Π°Ρ‚Π° Π½Π° Π΄ΠΎΠ±Ρ€ΠΎΡ‚ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€Π°Π½Π΅ са смСсСни Ρ‚Π°Π±ΡƒΠ»Π°Ρ‚ΠΎΡ€ΠΈ с ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»ΠΈ, ΠΊΠΎΠ΅Ρ‚ΠΎ днСс сС счита Π·Π° ΠΌΠ½ΠΎΠ³ΠΎ лоша Ρ„ΠΎΡ€ΠΌΠ°. Но bash ΠΏΡ€ΠΈΠ½ΡƒΠΆΠ΄Π°Π²Π° Ρ€Π°Π·Π΄Π΅Π»ΠΈ ΠΈ Π±ΠΈ Π±ΠΈΠ»ΠΎ Ρ…ΡƒΠ±Π°Π²ΠΎ Π΄Π° ΠΈΠΌΠ° Π½ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€Π°Π½Π΅ Π² конфигурацията Π½Π° nginx. Накратко, смСсванСто Π½Π° Ρ€Π°Π·Π΄Π΅Π»ΠΈ с ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»ΠΈ Ρ‚ΡƒΠΊ наистина ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΊΠ°Ρ‚ΠΎ Π½Π°ΠΉ-Π΄ΠΎΠ±Ρ€ΠΎΡ‚ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΎΡ‚ Π½Π°ΠΉ-Π»ΠΎΡˆΠΎΡ‚ΠΎ. Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‚ΠΎΠ²Π°, няма Π΄Π° Π²ΠΈΠ΄ΠΈΡ‚Π΅ Ρ‚ΠΎΠ²Π° Π² Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Π° ΠΏΠΎ-Π΄ΠΎΠ»Ρƒ, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ 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, Π·Π° Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚Π΅ ΠΊΠΎΠΉ url Ρ‰Π΅ бъдС коя услуга). Π‘ΠΊΡ€ΠΈΠΏΡ‚ΡŠΡ‚ Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π° Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π°, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ Π² Ρ‚ΠΎΠ·ΠΈ случай няма Π΄Π° ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π³ΠΎ Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ (с Ρ†Π΅Π» ΠΊΠΎΡ€Π΅ΠΊΡ†ΠΈΠΈ Π½Π° Π³Ρ€Π΅ΡˆΠΊΠΈ ΠΈ добавянС Π½Π° Π½ΠΎΠ²ΠΈ услуги) ΠΈ ΠΊΠ°Ρ‚ΠΎ цяло ΡΡŠΡΡ‚ΠΎΡΠ½ΠΈΠ΅ = Π·Π»ΠΎ.

РСшСниС 1: ВсС ΠΎΡ‰Π΅ ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°ΠΉΡ‚Π΅ скрипта Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π°, Π½ΠΎ Π³ΠΎ ΠΊΠΎΠΏΠΈΡ€Π°ΠΉΡ‚Π΅ всСки ΠΏΡŠΡ‚ scp. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° сС ΡΠ²ΡŠΡ€ΠΆΠ΅Ρ‚Π΅ Ρ‡Ρ€Π΅Π· ssh ΠΈ ΠΈΠ·ΠΏΡŠΠ»Π½Π΅Ρ‚Π΅ скрипта с Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΈΡ‚Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΈ.

ΠΏΡ€ΠΎΡ‚ΠΈΠ²:

  • Π”Π²Π΅ дСйствия вмСсто Π΅Π΄Π½ΠΎ
  • Π’ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎ Π΅ Π΄Π° няма място, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ ΠΊΠΎΠΏΠΈΡ€Π°Ρ‚Π΅, ΠΈΠ»ΠΈ ΠΌΠΎΠΆΠ΅ Π΄Π° няма Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ Π½Π΅Π³ΠΎ, ΠΈΠ»ΠΈ ΡΠΊΡ€ΠΈΠΏΡ‚ΡŠΡ‚ ΠΌΠΎΠΆΠ΅ Π΄Π° сС изпълни ΠΏΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π½Π° замСстванСто.
  • ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ Π΅ Π΄Π° почиститС слСд сСбС си (ΠΈΠ·Ρ‚Ρ€ΠΈΠΉΡ‚Π΅ скрипта).
  • Π’Π΅Ρ‡Π΅ Ρ‚Ρ€ΠΈ дСйствия.

РСшСниС 2:

  • Π—Π°ΠΏΠ°Π·Π΅Ρ‚Π΅ само Π΄Π΅Ρ„ΠΈΠ½ΠΈΡ†ΠΈΠΈ Π½Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² скрипта ΠΈ Π½Π΅ ΠΈΠ·ΠΏΡŠΠ»Π½ΡΠ²Π°ΠΉΡ‚Π΅ Π½ΠΈΡ‰ΠΎ
  • Π‘ sed Π΄ΠΎΠ±Π°Π²Π΅Ρ‚Π΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅ Π½Π° функция към края
  • Π˜Π·ΠΏΡ€Π°Ρ‚Π΅Ρ‚Π΅ всичко Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ Π½Π° shh Ρ‡Ρ€Π΅Π· Ρ‚Ρ€ΡŠΠ±Π° (|)

плюсовС:

  • Наистина Π±Π΅Π· граТданство
  • Няма шаблонни ΠΎΠ±Π΅ΠΊΡ‚ΠΈ
  • Чувствам сС Π³ΠΎΡ‚ΠΈΠ½ΠΎ

НСка просто Π³ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ Π±Π΅Π· Ansible. Π”Π°, всичко Π²Π΅Ρ‡Π΅ Π΅ измислСно. Π”Π°, вСлосипСд. Π’ΠΈΠΆΡ‚Π΅ ΠΊΠΎΠ»ΠΊΠΎ прост, Π΅Π»Π΅Π³Π°Π½Ρ‚Π΅Π½ ΠΈ минималистичСн Π΅ Π²Π΅Π»ΠΎΡΠΈΠΏΠ΅Π΄ΡŠΡ‚:

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

НС ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠ±Π°Ρ‡Π΅ Π΄Π° смС сигурни, Ρ‡Π΅ отдалСчСният хост ΠΈΠΌΠ° Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π΅Π½ bash, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Ρ‰Π΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΌΠ°Π»ΠΊΠ° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π² Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ (Ρ‚ΠΎΠ²Π° Π΅ вмСсто Ρ‡Π΅Ρ€ΡƒΠΏΠΊΠΎΠ² ΡƒΠ΄Π°Ρ€):

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