ื‘ืœื•ื™-ื’ืจื™ืŸ ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ื‘ื™ื™ ืžื™ื ื™ืžื•ื ืœื•ื™ืŸ

ืื™ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ ืžื™ืจ ื ื•ืฆืŸ ื‘ืึทืฉ, ืกืฉ, ื“ืึธืงืงืขืจ ะธ ื ื’ื™ื ืงืก ืžื™ืจ ื•ื•ืขืœืŸ ืึธืจื’ืึทื ื™ื–ื™ืจืŸ ืึท ืกื™ืžืœืึทืก ืื•ื™ืกืœื™ื™ื’ ืคื•ืŸ ื“ื™ ื•ื•ืขื‘ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ. ื‘ืœื•ื™-ื’ืจื™ืŸ ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ืื™ื– ืึท ื˜ืขื›ื ื™ืง ื•ื•ืึธืก ืึทืœืึทื•ื– ืื™ืจ ืฆื• ื’ืœื™ื™ืš ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ืึท ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืึธืŸ ืจื™ื“ื–ืฉืขืงื˜ื™ื ื’ ืึท ืื™ื™ืŸ ื‘ืงืฉื”. ืขืก ืื™ื– ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ื ื•ืœ ื“ืึทื•ื ื˜ื™ื™ื ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ืกื˜ืจืึทื˜ืขื’ื™ืขืก ืื•ืŸ ืื™ื– ื‘ืขืกื˜ืขืจ ืคึผืึทืกื™ืง ืคึฟืึทืจ ืึทืคึผืœืึทืงื™ื™ืฉืึทื ื– ืžื™ื˜ ืื™ื™ืŸ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืึธื‘ืขืจ ื“ื™ ืคื™ื™ื™ืงื™ื™ื˜ ืฆื• ืœืึธื“ืŸ ืึท ืจื’ืข, ื’ืจื™ื™ื˜-ืฆื•-ืœื•ื™ืคืŸ ื‘ื™ื™ึทืฉืคึผื™ืœ ื ื™ืจื‘ื™ื™.

ื–ืืœ ืก ื–ืึธื’ืŸ ืื™ืจ ื”ืึธื‘ืŸ ืึท ื•ื•ืขื‘ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืžื™ื˜ ื•ื•ืึธืก ืคื™ืœืข ืงืœื™ื™ืึทื ืฅ ื–ืขื ืขืŸ ืึทืงื˜ื™ื•ื•ืœื™ ืืจื‘ืขื˜ืŸ, ืื•ืŸ ืขืก ืื™ื– ืœืขื’ืึทืžืจืข ืงื™ื™ืŸ ื•ื•ืขื’ ืคึฟืึทืจ ืขืก ืฆื• ืœื™ื’ืŸ ืึทืจืึธืคึผ ืคึฟืึทืจ ืึท ืคึผืึธืจ ืคื•ืŸ ืกืขืงื•ื ื“ืขืก. ืื•ืŸ ืื™ืจ ื˜ืึทืงืข ื“ืึทืจืคึฟืŸ ืฆื• ืจืึธื•ืœื“ ืึท ื‘ื™ื‘ืœื™ืึธื˜ืขืง ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ, ืึท ื–ืฉื•ืง ืคืึทืจืจื™ื›ื˜ืŸ ืึธื“ืขืจ ืึท ื ื™ื™ึทืข ืงื™ืœ ืฉื˜ืจื™ืš. ืื™ืŸ ืึท ื ืึธืจืžืึทืœ ืกื™ื˜ื•ืึทืฆื™ืข, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื”ืึทืœื˜ืŸ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ, ืคืึทืจื‘ื™ื™ึทื˜ืŸ ืขืก ืื•ืŸ ืึธื ื”ื™ื™ื‘ืŸ ืขืก ื•ื•ื™ื“ืขืจ. ืื™ืŸ ื“ืขื ืคืึทืœ ืคื•ืŸ ื“ืึธืงืขืจ, ืื™ืจ ืงืขื ืขืŸ ืขืจืฉื˜ืขืจ ืคืึทืจื‘ื™ื™ึทื˜ืŸ ืขืก, ืื•ืŸ ืจื™ืกื˜ืึทืจื˜ ืขืก, ืึธื‘ืขืจ ืขืก ื•ื•ืขื˜ ื ืึธืš ื–ื™ื™ืŸ ืึท ืคึผืขืจื™ืึธื“ ืื™ืŸ ื•ื•ืึธืก ืจื™ืงื•ื•ืขืก ืฆื• ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ืคึผืจืึทืกืขืกื˜, ื•ื•ื™ื™ึทืœ ื™ื•ื–ืฉืึทื•ื•ืึทืœื™ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื ืขืžื˜ ืขื˜ืœืขื›ืข ืžืึธืœ ืฆื• ื˜ื›ื™ืœืขืก ืœืึธื“ืŸ. ื•ื•ืึธืก ืื•ื™ื‘ ืขืก ืกื˜ืึทืจืฅ, ืึธื‘ืขืจ ื˜ื•ืจื ืก ืื•ื™ืก ืฆื• ื–ื™ื™ืŸ ื™ื ืึทืคึผืขืจืึทื‘ืึทืœ? ื“ืึธืก ืื™ื– ื“ืขืจ ืคึผืจืึธื‘ืœืขื, ืœืึธื–ืŸ ืก ืกืึธืœื•ื•ืข ืขืก ืžื™ื˜ ืžื™ื ื™ืžืึทืœ ืžื™ื˜ืœืขืŸ ืื•ืŸ ื•ื•ื™ ืขืœืขื’ืึทื ื˜ ื•ื•ื™ ืžืขื’ืœืขืš.

ืึธืคึผืœื™ื™ืงืขื ื•ื ื’: ืจื•ื‘ึฟ ืคื•ืŸ ื“ื™ ืึทืจื˜ื™ืงืœ ืื™ื– ื“ืขืจืœืื ื’ื˜ ืื™ืŸ ืึทืŸ ื™ืงืกืคึผืขืจืžืขื ืึทืœ ืคึฟืึธืจืžืึทื˜ - ืื™ืŸ ื“ื™ ืคืึธืจืขื ืคื•ืŸ ืึท ืจืขืงืึธืจื“ื™ื ื’ ืคื•ืŸ ืึท ืงืึทื ืกืึธื•ืœ ืกืขืกื™ืข. ืึทืœืขื•ื•ื™ื™ึท ื“ืึธืก ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ืฆื• ืฉื•ื•ืขืจ ืฆื• ืคึฟืึทืจืฉื˜ื™ื™ืŸ ืื•ืŸ ื“ื™ ืงืึธื“ ื•ื•ืขื˜ ื“ืึธืงื•ืžืขื ื˜ ื–ื™ืš ื’ืขื ื•ื’. ืคึฟืึทืจ ืึทื˜ืžืึธืกืคืขืจ, ื™ืžืึทื“ื–ืฉืึทืŸ ืึทื– ื“ืึธืก ื–ืขื ืขืŸ ื ื™ืฉื˜ ื‘ืœื•ื™ื– ืงืึธื“ ืกื ื™ืคึผืึทืฅ, ืึธื‘ืขืจ ืคึผืึทืคึผื™ืจ ืคื•ืŸ ืึท "ืื™ื™ึทื–ืŸ" ื˜ืขืœืขื˜ื™ืคึผืข.

ื‘ืœื•ื™-ื’ืจื™ืŸ ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ื‘ื™ื™ ืžื™ื ื™ืžื•ื ืœื•ื™ืŸ

ื˜ืฉื™ืงืึทื•ื•ืข ื˜ืขืงื ื™ืงืก ื•ื•ืึธืก ื–ืขื ืขืŸ ืฉื•ื•ืขืจ ืฆื• Google ื ืึธืจ ื“ื•ืจืš ืœื™ื™ืขื ืขืŸ ื“ืขื ืงืึธื“ ื–ืขื ืขืŸ ื“ื™ืกืงืจื™ื™ื‘ื“ ืื™ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘ ืคื•ืŸ ื™ืขื“ืขืจ ืึธืคึผื˜ื™ื™ืœื•ื ื’. ืื•ื™ื‘ ืขืคึผืขืก ืึทื ื“ืขืจืฉ ืื™ื– ื•ืžืงืœืึธืจ, ื’ื•ื’ืœ ืขืก ืื•ืŸ ื˜ืฉืขืง ืขืก ืื•ื™ืก. ื“ืขืจืงืœืขืจืŸ (ืฆื•ื ื’ืœื™ืง, ืขืก ืึทืจื‘ืขื˜ ื•ื•ื™ื“ืขืจ, ืจืขื›ื˜ ืฆื• ื“ืขืจ ื•ื ื‘ืœืึธืงืงื™ื ื’ ืคื•ืŸ ื“ื™ ื˜ืขืœืขื’ืจืึทื). ืื•ื™ื‘ ืื™ืจ ืงืขื ืขืŸ ื ื™ืฉื˜ Google ืขืคึผืขืก, ืคืจืขื’ืŸ ืื™ืŸ ื“ื™ ื‘ืึทืžืขืจืงื•ื ื’ืขืŸ. ืื™ืš ื•ื•ืขืœ ื–ื™ื™ืŸ ืฆื•ืคืจื™ื“ืŸ ืฆื• ืœื™ื™ื’ืŸ ืฆื• ื“ื™ ืงืึธืจืึทืกืคึผืึทื ื“ื™ื ื’ ืึธืคึผื˜ื™ื™ืœื•ื ื’ "ืื™ื ื˜ืขืจืขืกืึทื ื˜ ื˜ืขืงื ื™ืงืก".

ื–ืืœ ืก ื‘ืึทืงื•ืžืขืŸ ืกื˜ืึทืจื˜ืขื“.

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

ื“ื™ื ืกื˜

ืœืึธืžื™ืจ ืžืึทื›ืŸ ืึทืŸ ื™ืงืกืคึผืขืจืžืขื ืึทืœ ื“ื™ื ืกื˜ ืื•ืŸ ืฉื˜ืขืœืŸ ืขืก ืื™ืŸ ืึท ืงืึทื ื˜ื™ื™ื ืขืจ.

ื˜ืฉื™ืงืึทื•ื•ืข ื˜ืขืงื ื™ืงืก

  • cat << EOF > file-name (ื“ืึธ ื“ืึธืงื•ืžืขื ื˜ + ืื™ืš / ืึธ ืจื™ื“ืขืจืขืงืฉืึทืŸ) ืื™ื– ืึท ื•ื•ืขื’ ืฆื• ืฉืึทืคึฟืŸ ืึท ืžืึทืœื˜ื™-ืฉื•ืจื” ื˜ืขืงืข ืžื™ื˜ ืื™ื™ืŸ ื‘ืึทืคึฟืขืœ. ืึทืœืฅ ื‘ืึทืฉ ืœื™ื™ืขื ื˜ ืคื•ืŸ /dev/stdin ื ืึธืš ื“ืขื ืฉื•ืจื” ืื•ืŸ ืื™ื™ื“ืขืจ ื“ื™ ืฉื•ืจื” EOF ื•ื•ืขื˜ ื–ื™ื™ืŸ ืจืขืงืึธืจื“ืขื“ ืื™ืŸ file-name.
  • wget -qO- URL (ื“ืขืจืงืœืขืจืŸ) - ืจืขื–ื•ืœื˜ืึทื˜ ืึท ื“ืึธืงื•ืžืขื ื˜ ื‘ืืงื•ืžืขืŸ ื“ื•ืจืš ื”ื˜ื˜ืคึผ ืฆื• /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

ืคืึทืจืงืขืจื˜ ืคืจืืงืกื™

ื›ึผื“ื™ ืื•ื ื“ื–ืขืจ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื–ืึธืœ ื–ื™ื™ืŸ ื‘ื™ื›ื•ืœืช ืฆื• ื˜ื•ื™ืฉืŸ ืึทื ื ืึธื•ื˜ื™ืกื˜, ืขืก ืื™ื– ื ื™ื™ื˜ื™ืง ืึทื– ืขืก ืื™ื– ืขื˜ืœืขื›ืข ืื ื“ืขืจืข ืขื ื˜ื™ื˜ื™ ืื™ืŸ ืคืจืึธื ื˜ ืคื•ืŸ ืขืก ื•ื•ืึธืก ื•ื•ืขื˜ ื‘ืึทื”ืึทืœื˜ืŸ ื–ื™ื™ึทืŸ ืคืึทืจื‘ื™ื™ึทื˜. ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืึท ื•ื•ืขื‘ ืกืขืจื•ื•ืขืจ ื ื’ื™ื ืงืก ะฒ ืคืึทืจืงืขืจื˜ ืคึผืจืึทืงืกื™ ืžืึธื“ืข. ื ืคืึทืจืงืขืจื˜ ืคึผืจืึทืงืกื™ ืื™ื– ื’ืขื’ืจื™ื ื“ืขื˜ ืฆื•ื•ื™ืฉืŸ ื“ืขื ืงืœื™ืขื ื˜ ืื•ืŸ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ. ืขืก ืึทืงืกืขืคึผืฅ ืจื™ืงื•ื•ืขืก ืคื•ืŸ ืงืœื™ื™ืึทื ืฅ ืื•ืŸ ืคืึธืจื•ื•ืขืจื“ื– ื–ื™ื™ ืฆื• ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืื•ืŸ ืคืึธืจื•ื•ืขืจื“ื– ื“ื™ ืจืขืกืคึผืึธื ืกืขืก ืคื•ืŸ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืฆื• ื“ื™ ืงืœื™ื™ืึทื ืฅ.

ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืื•ืŸ ื“ื™ ืคืึทืจืงืขืจื˜ ืคึผืจืึทืงืกื™ ืงืขื ืขืŸ ื–ื™ื™ืŸ ืœื™ื ื’ืงื˜ ืื™ืŸ ื“ื™ ื“ืึธืงืงืขืจ ื“ืึธืงืงืขืจ ื ืขืฅ. ืื–ื•ื™, ื“ืขืจ ืงืึทื ื˜ื™ื™ื ืขืจ ืžื™ื˜ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื˜ื•ื˜ ื ื™ืฉื˜ ืืคื™ืœื• ื“ืึทืจืคึฟืŸ ืฆื• ืคืึธืจื•ื•ืขืจื“ื™ื“ ืึท ืคึผืึธืจื˜ ืื•ื™ืฃ ื“ืขืจ ื‘ืึทืœืขื‘ืึธืก ืกื™ืกื˜ืขื; ื“ืึธืก ืึทืœืึทื•ื– ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืฆื• ื–ื™ื™ืŸ ืžืึทืงืกื™ืžืึทืœืœื™ ืืคื’ืขื–ื•ื ื“ืขืจื˜ ืคื•ืŸ ืคื•ื ื“ืจื•ื™ืกื ื“ื™ืง ื˜ืจืขืฅ.

ืื•ื™ื‘ ื“ื™ ืคืึทืจืงืขืจื˜ ืคึผืจืึทืงืกื™ ืœืขื‘ืŸ ืื•ื™ืฃ ืืŸ ืื ื“ืขืจ ื‘ืึทืœืขื‘ืึธืก, ืื™ืจ ื•ื•ืขื˜ ื”ืึธื‘ืŸ ืฆื• ืคืึทืจืœืึธื–ืŸ ื“ื™ ื“ืึธืงืงืขืจ ื ืขืฅ ืื•ืŸ ืคืึทืจื‘ื™ื ื“ืŸ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืฆื• ื“ื™ ืคืึทืจืงืขืจื˜ ืคึผืจืึทืงืกื™ ื“ื•ืจืš ื“ื™ ื‘ืึทืœืขื‘ืึธืก ื ืขืฅ, ืคืึธืจื•ื•ืขืจื“ื™ื ื’ ื“ื™ ืคึผืึธืจื˜. annexes ืคึผืึทืจืึทืžืขื˜ืขืจ --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

ืื™ืŸ ื“ืขื ื‘ื™ื ืข, ื“ื™ ื‘ื™ืœื“ ืื™ื– ื’ืขื‘ื•ื™ื˜ ื’ืœื™ื™ึทืš ืื•ื™ืฃ ื“ื™ ืกืขืจื•ื•ืขืจ, ื•ื•ืึธืก ืจื™ืงื•ื•ื™ื™ืขืจื– ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืงื•ื•ืืœืŸ ืฆื• ื–ื™ื™ืŸ ื“ืึธืจื˜, ืื•ืŸ ืื•ื™ืš ืœืึธื•ื“ื– ื“ื™ ืกืขืจื•ื•ืขืจ ืžื™ื˜ ื•ืžื ื™ื™ื˜ื™ืง ืึทืจื‘ืขื˜. ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ืฉืจื™ื˜ ืื™ื– ืฆื• ืึทืœืึทืงื™ื™ื˜ ื“ื™ ื‘ื™ืœื“ ืคึฟืึทืจื–ืึทืžืœื•ื ื’ ืฆื• ืึท ื‘ืึทื–ื•ื ื“ืขืจ ืžืึทืฉื™ืŸ (ืœืžืฉืœ ืฆื• ืึท ืกื™ ืกื™ืกื˜ืขื) ืื•ืŸ ื“ืขืžืึธืœื˜ ืึทืจื™ื‘ืขืจืคื™ืจืŸ ืขืก ืฆื• ื“ื™ ืกืขืจื•ื•ืขืจ.

ื˜ืจืึทื ืกืคืขืจื™ื ื’ ื‘ื™ืœื“ืขืจ

ืฆื•ื ื‘ืึทื“ื•ื™ืขืจืŸ, ืขืก ืื™ื– ื ื™ืฉื˜ ื–ื™ื ืขืŸ ืฆื• ืึทืจื™ื‘ืขืจืคื™ืจืŸ ื‘ื™ืœื“ืขืจ ืคื•ืŸ ืœืึธืงืึทืœื”ืึธืกื˜ ืฆื• ืœืึธืงืึทืœื”ืึธืกื˜, ืึทื–ื•ื™ ื“ืขื ืึธืคึผื˜ื™ื™ืœื•ื ื’ ืงืขื ืขืŸ ื–ื™ื™ืŸ ื™ืงืกืคึผืœืึธืจื“ ื‘ืœื•ื™ื– ืื•ื™ื‘ ืื™ืจ ื”ืึธื‘ืŸ ืฆื•ื•ื™ื™ ืžื—ื ื•ืช ืžื™ื˜ ื“ืึธืงืขืจ ืื™ืŸ ื”ืึทื ื˜. ืื™ืŸ ืžื™ื ื™ืžื•ื ืขืก ืงื•ืงื˜ ืขืคึผืขืก ื•ื•ื™ ื“ืึธืก:

$ 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 ืกืึทื•ื•ืขืก ื“ื™ ื‘ื™ืœื“ ื“ืึทื˜ืŸ ืื™ืŸ ืึท .ื˜ืึทืจ ืึทืจืงื™ื™ื•ื•, ื˜ื™ื™ึทื˜ืฉ ืขืก ื•ื•ื™ื™ื– ื•ื•ืขื’ืŸ 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, ื•ื•ืึธืก ืึทืœืึทื•ื– ืื™ืจ ืฆื• ื‘ืึทื–ื™ื™ึทื˜ื™ืงืŸ ืึทืœืข ื˜ืึทื‘ืก ืื™ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘. ืืžืช, ื“ื™ ืคึผืจื™ื™ึทื– ืคื•ืŸ ื’ื•ื˜ ืคืึธืจืžืึทื˜ื˜ื™ื ื’ ืื™ื– ื’ืขืžื™ืฉื˜ ื˜ืึทื‘ืก ืžื™ื˜ ืกืคึผื™ื™ืกืึทื–, ื•ื•ืึธืก ื”ื™ื™ึทื ื˜ ืื™ื– ื’ืขื”ืืœื˜ืŸ ื–ื™ื™ืขืจ ืฉืœืขื›ื˜ ืคืึธืจืขื. ืึธื‘ืขืจ ื‘ืึทืฉ ืคืึธืจืกืขืก ื˜ืึทื‘ืก, ืื•ืŸ ืขืก ื•ื•ืึธืœื˜ ืื•ื™ืš ื–ื™ื™ืŸ ืคื™ื™ึทืŸ ืฆื• ื”ืึธื‘ืŸ ื ืึธืจืžืึทืœ ืคืึธืจืžืึทื˜ื˜ื™ื ื’ ืื™ืŸ ื“ื™ nginx ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ. ืื™ืŸ ืงื•ืจืฅ, ืžื™ืงืกื™ื ื’ ื˜ืึทื‘ืก ืžื™ื˜ ืกืคึผื™ื™ืกืึทื– ื“ืึธ ื˜ืึทืงืข ืžื™ื™ื ื˜ ื•ื•ื™ ื“ืขืจ ื‘ืขืกื˜ืขืจ ืœื™ื™ื–ื•ื ื’ ืคื•ืŸ ื“ื™ ืขืจื’ืกื˜. ืึธื‘ืขืจ, ืื™ืจ ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ืขืŸ ื“ืขื ืื™ืŸ ื“ื™ ืกื ื™ืคึผืึทื˜ ืื•ื ื˜ืŸ, ื–ื™ื ื˜ Habr "ื˜ื•ื˜ ืขืก ื’ื•ื˜" ื“ื•ืจืš ื˜ืฉืึทื ื’ื™ื ื’ ืึทืœืข ื˜ืึทื‘ืก ืฆื• 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 ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทื ื– ืฆื• ื‘ืึทืฉืœื™ืกืŸ ื•ื•ืึธืก 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'

ืึธื‘ืขืจ, ืžื™ืจ ืงืขื ืขืŸ ื ื™ืฉื˜ ื–ื™ื™ืŸ ื–ื™ื›ืขืจ ืึทื– ื“ืขืจ ื•ื•ื™ื™ึทื˜ ื‘ืึทืœืขื‘ืึธืก ื”ืื˜ ื˜ื•ื™ื’ืŸ ื‘ืึทืฉ, ืึทื–ื•ื™ ืžื™ืจ ื•ื•ืขืœืŸ ืœื™ื™ื’ืŸ ืึท ืงืœื™ื™ืŸ ื˜ืฉืขืง ืื™ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘ (ื“ืึธืก ืื™ื– ืึทื ืฉื˜ืึธื˜ ืคื•ืŸ ืฉืขืœื‘ืึทื ื’):

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