เชฒเช˜เซเชคเซเชคเชฎ เชตเซ‡เชคเชจ เชชเชฐ เชฌเซเชฒเซ-เช—เซเชฐเซ€เชจ เชœเชฎเชพเชตเชŸ

เช† เชฒเซ‡เช–เชฎเชพเช‚ เช†เชชเชฃเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช เชฌเชพเชถ, ssh, เช—เซ‹เชฆเซ€ ะธ nginx เช…เชฎเซ‡ เชตเซ‡เชฌ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ‹ เชธเซ€เชฎเชฒเซ‡เชธ เชฒเซ‡เช†เช‰เชŸ เช—เซ‹เช เชตเซ€เชถเซเช‚. เชตเชพเชฆเชณเซ€-เชฒเซ€เชฒเซ‹ เชœเชฎเชพเชตเชŸ เชเช• เชเชตเซ€ เชคเช•เชจเซ€เช• เช›เซ‡ เชœเซ‡ เชคเชฎเชจเซ‡ เชเช• เชชเชฃ เชตเชฟเชจเช‚เชคเซ€เชจเซ‡ เชจเช•เชพเชฐเซเชฏเชพ เชตเชฟเชจเชพ เชคเชฐเชค เชœ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ‡ เช…เชชเชกเซ‡เชŸ เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡. เชคเซ‡ เชถเซ‚เชจเซเชฏ เชกเชพเช‰เชจเชŸเชพเช‡เชฎ เชกเชฟเชชเซเชฒเซ‹เชฏเชฎเซ‡เชจเซเชŸ เชตเซเชฏเซ‚เชนเชฐเชšเชจเชพเช“เชฎเชพเช‚เชจเซ€ เชเช• เช›เซ‡ เช…เชจเซ‡ เชเช• เช‰เชฆเชพเชนเชฐเชฃ เชธเชพเชฅเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเซเชธ เชฎเชพเชŸเซ‡ เชถเซเชฐเซ‡เชทเซเช  เช…เชจเซเชฐเซ‚เชช เช›เซ‡, เชชเชฐเช‚เชคเซ เชจเชœเซ€เช•เชจเชพ เชฌเซ€เชœเชพ, เชšเชฒเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชคเซˆเชฏเชพเชฐ เชฆเชพเช–เชฒเซ‹ เชฒเซ‹เชก เช•เชฐเชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เช›เซ‡.

เชšเชพเชฒเซ‹ เช•เชนเซ€เช เช•เซ‡ เชคเชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เชเช• เชตเซ‡เชฌ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช›เซ‡ เชœเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เช˜เชฃเชพ เช•เซเชฒเชพเชฏเช‚เชŸ เชธเช•เซเชฐเชฟเชฏ เชฐเซ€เชคเซ‡ เช•เชพเชฎ เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เช›เซ‡, เช…เชจเซ‡ เชคเซ‡เชจเชพ เชฎเชพเชŸเซ‡ เชฅเซ‹เชกเซ€ เชธเซ‡เช•เช‚เชก เชฎเชพเชŸเซ‡ เชธเซ‚เชตเชพเชจเซ‹ เช•เซ‹เชˆ เชฐเชธเซเชคเซ‹ เชจเชฅเซ€. เช…เชจเซ‡ เชคเชฎเชพเชฐเซ‡ เช–เชฐเซ‡เช–เชฐ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€ เช…เชชเชกเซ‡เชŸ, เชฌเช— เชซเชฟเช•เซเชธ เช…เชฅเชตเชพ เชจเชตเซ€ เชถเชพเชจเชฆเชพเชฐ เชธเซเชตเชฟเชงเชพ เชฒเชพเชตเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เชธเชพเชฎเชพเชจเซเชฏ เชชเชฐเชฟเชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚, เชคเชฎเชพเชฐเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ‡ เชฐเซ‹เช•เชตเชพเชจเซ€, เชคเซ‡เชจเซ‡ เชฌเชฆเชฒเชตเซ€ เช…เชจเซ‡ เชคเซ‡เชจเซ‡ เชซเชฐเซ€เชฅเซ€ เชถเชฐเซ‚ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡. เชกเซ‹เช•เชฐเชจเชพ เช•เชฟเชธเซเชธเชพเชฎเชพเช‚, เชคเชฎเซ‡ เชชเชนเซ‡เชฒเชพ เชคเซ‡เชจเซ‡ เชฌเชฆเชฒเซ€ เชถเช•เซ‹ เช›เซ‹, เชชเช›เซ€ เชคเซ‡เชจเซ‡ เชซเชฐเซ€เชฅเซ€ เชถเชฐเซ‚ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹, เชชเชฐเช‚เชคเซ เชนเชœเซ€ เชชเชฃ เชเช• เชธเชฎเชฏเช—เชพเชณเซ‹ เชนเชถเซ‡ เชœเซ‡เชฎเชพเช‚ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ€ เชตเชฟเชจเช‚เชคเซ€เช“ เชชเชฐ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ เชจเชนเซ€เช‚, เช•เชพเชฐเชฃ เช•เซ‡ เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ‡ เชถเชฐเซ‚เช†เชคเชฎเชพเช‚ เชฒเซ‹เชก เชฅเชตเชพเชฎเชพเช‚ เชฅเซ‹เชกเซ‹ เชธเชฎเชฏ เชฒเชพเช—เซ‡ เช›เซ‡. เชœเซ‹ เชคเซ‡ เชถเชฐเซ‚ เชฅเชพเชฏ, เชชเชฐเช‚เชคเซ เชจเชฟเชทเซเช•เซเชฐเชฟเชฏ เชฅเชˆ เชœเชพเชฏ เชคเซ‹ เชถเซเช‚? เช† เชธเชฎเชธเซเชฏเชพ เช›เซ‡, เชšเชพเชฒเซ‹ เชคเซ‡เชจเซ‡ เชจเซเชฏเซ‚เชจเชคเชฎ เชฎเชพเชงเซเชฏเชฎเชฅเซ€ เช…เชจเซ‡ เชถเช•เซเชฏ เชคเซ‡เชŸเชฒเซ€ เชธเซเช‚เชฆเชฐ เชฐเซ€เชคเซ‡ เชนเชฒ เช•เชฐเซ€เช.

เช…เชธเซเชตเซ€เช•เชฐเชฃ: เชฎเซ‹เชŸเชพเชญเชพเช—เชจเชพ เชฒเซ‡เช– เชชเซเชฐเชพเชฏเซ‹เช—เชฟเช• เชซเซ‹เชฐเซเชฎเซ‡เชŸเชฎเชพเช‚ เชฐเชœเซ‚ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเชพ เช›เซ‡ - เช•เชจเซเชธเซ‹เชฒ เชธเชคเซเชฐเชจเชพ เชฐเซ‡เช•เซ‹เชฐเซเชกเชฟเช‚เช—เชจเชพ เชฐเซ‚เชชเชฎเชพเช‚. เช†เชถเชพ เช›เซ‡ เช•เซ‡ เช†เชจเซ‡ เชธเชฎเชœเชตเซเช‚ เชฌเชนเซ เชฎเซเชถเซเช•เซ‡เชฒ เชจเชนเซ€เช‚ เชนเซ‹เชฏ เช…เชจเซ‡ เช•เซ‹เชก เชชเซ‹เชคเซ‡ เชชเซ‚เชฐเชคเชพ เชชเซเชฐเชฎเชพเชฃเชฎเชพเช‚ เชฆเชธเซเชคเชพเชตเซ‡เชœ เช•เชฐเชถเซ‡. เชตเชพเชคเชพเชตเชฐเชฃ เชฎเชพเชŸเซ‡, เช•เชฒเซเชชเชจเชพ เช•เชฐเซ‹ เช•เซ‡ เช† เชฎเชพเชคเซเชฐ เช•เซ‹เชก เชธเซเชจเชฟเชชเซ‡เชŸเซเชธ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ "เช†เชฏเชฐเซเชจ" เชŸเซ‡เชฒเชฟเชŸเชพเช‡เชชเชฎเชพเช‚เชฅเซ€ เช•เชพเช—เชณ เช›เซ‡.

เชฒเช˜เซเชคเซเชคเชฎ เชตเซ‡เชคเชจ เชชเชฐ เชฌเซเชฒเซ-เช—เซเชฐเซ€เชจ เชœเชฎเชพเชตเชŸ

เชฎเชพเชคเซเชฐ เช•เซ‹เชก เชตเชพเช‚เชšเซ€เชจเซ‡ 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).

เชชเซเชฐเชฟเชจเซเชŸเช†เช‰เชŸ

เชนเซเช‚ เช–เชพเชธ เช•เชฐเซ€เชจเซ‡ เชชเชพเชฏเชฅเซ‹เชจ เชฎเชพเชŸเซ‡ เชนเชพเช‡เชฒเชพเช‡เชŸเชฟเช‚เช— เชธเช•เซเชทเชฎ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชธเซเชจเชฟเชชเซ‡เชŸ เชคเซ‹เชกเซ€ เชจเชพเช–เซเช‚ เช›เซเช‚. เช…เช‚เชคเซ‡ เช†เชจเชพ เชœเซ‡เชตเซ‹ เชฌเซ€เชœเซ‹ เชญเชพเช— เชนเชถเซ‡. เชงเซเชฏเชพเชจเชฎเชพเช‚ เชฒเซ‹ เช•เซ‡ เช† เชธเซเชฅเชณเซ‹เช เช•เชพเช—เชณเชจเซ‡ เชนเชพเช‡เชฒเชพเช‡เชŸเชฟเช‚เช— เชตเชฟเชญเชพเช—เชฎเชพเช‚ เชฎเซ‹เช•เชฒเชตเชพ เชฎเชพเชŸเซ‡ เช•เชพเชชเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซ‹ เชนเชคเซ‹ (เชœเซเชฏเชพเช‚ เช•เซ‹เชก เชนเชพเช‡เชฒเชพเช‡เชŸเชฐเซเชธ เชธเชพเชฅเซ‡ เชนเชพเชฅเชฅเซ€ เชฐเช‚เช—เซ€เชจ เชนเชคเซ‹), เช…เชจเซ‡ เชชเช›เซ€ เช† เชŸเซเช•เชกเชพเช“ เชชเชพเช›เชพ เช—เซเช‚เชฆเชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเชพ เชนเชคเชพ.

$ 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 เชธเชฟเชธเซเชŸเชฎเชฎเชพเช‚) เชซเชพเชณเชตเชตเชพเชจเซเช‚ เช›เซ‡ เช…เชจเซ‡ เชชเช›เซ€ เชคเซ‡เชจเซ‡ เชธเชฐเซเชตเชฐ เชชเชฐ เชธเซเชฅเชพเชจเชพเช‚เชคเชฐเชฟเชค เช•เชฐเชตเชพเชจเซเช‚ เช›เซ‡.

เช›เชฌเซ€เช“ เชธเซเชฅเชพเชจเชพเช‚เชคเชฐเชฟเชค เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เช›เซ€เช

เช•เชฎเชจเชธเซ€เชฌเซ‡, เชฒเซ‹เช•เชฒเชนเซ‹เชธเซเชŸเชฅเซ€ เชฒเซ‹เช•เชฒเชนเซ‹เชธเซเชŸเชฎเชพเช‚ เชˆเชฎเซ‡เชœเซ‹ เชŸเซเชฐเชพเชจเซเชธเชซเชฐ เช•เชฐเชตเชพเชจเซ‹ เช•เซ‹เชˆ เช…เชฐเซเชฅ เชจเชฅเซ€, เชคเซ‡เชฅเซ€ เชœเซ‹ เชคเชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เชกเซ‹เช•เชฐ เชธเชพเชฅเซ‡ เชฌเซ‡ เชนเซ‹เชธเซเชŸ เชนเซ‹เชฏ เชคเซ‹ เชœ เช† เชตเชฟเชญเชพเช—เชจเซ‡ เชถเซ‹เชงเซ€ เชถเช•เชพเชฏ เช›เซ‡. เช“เช›เชพเชฎเชพเช‚ เช“เช›เซเช‚ เชคเซ‡ เช•เช‚เชˆเช• เช†เชจเชพ เชœเซ‡เชตเซเช‚ เชฒเชพเช—เซ‡ เช›เซ‡:

$ 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 เชฅเซ‹เชกเซ€ เชธเชฎเชœเซ‚เชคเซ€เชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡:

เชถเชพ เชฎเชพเชŸเซ‡ เชคเซ‡ เชจเช‚เชฌเชฐ เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชธเซเชŸเซเชฐเชฟเช‚เช— เช†เช‰เชŸเชชเซเชŸ เช•เชฐเชคเซเช‚ เชจเชฅเซ€?

เช•เซ‹เชˆเชชเชฃ เชฐเซ€เชคเซ‡, เช•เซ‰เชฒเชฟเช‚เช— เชซเช‚เช•เซเชถเชจเชฎเชพเช‚ เช†เชชเชฃเซ‡ เชคเซ‡เชจเชพ เช•เชพเชฐเซเชฏเชจเซเช‚ เชชเชฐเชฟเชฃเชพเชฎ เชคเชชเชพเชธเซ€เช เช›เซ€เช, เช…เชจเซ‡ เชฌเซ‡เชถเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชเช•เซเชเชฟเชŸ เช•เซ‹เชก เชคเชชเชพเชธเชตเซเช‚ เช เชธเซเชŸเซเชฐเชฟเช‚เช— เชคเชชเชพเชธเชตเชพ เช•เชฐเชคเชพเช‚ เชตเชงเซ เชธเชฐเชณ เช›เซ‡. เชตเชงเซเชฎเชพเช‚, เชคเซ‡เชฎเชพเช‚เชฅเซ€ เชธเซเชŸเซเชฐเชฟเช‚เช— เชฎเซ‡เชณเชตเชตเซ€ เช–เซ‚เชฌ เชœ เชธเชฐเชณ เช›เซ‡:
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.

เช…เชฎเซ‡ เชกเชฟเชชเซเชฒเซ‹เชฏเชฎเซ‡เชจเซเชŸ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชฒเช–เซ€ เช›เซ‡ เชœเซ‡ เชŸเชพเชฐเซเช—เซ‡เชŸ เชธเชฐเซเชตเชฐ เชชเชฐ เชชเซเชฐเซ€-เชฌเชฟเชฒเซเชŸ เชˆเชฎเซ‡เชœ เชกเชพเช‰เชจเชฒเซ‹เชก เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชธเชฐเซเชตเชฟเชธ เช•เชจเซเชŸเซ‡เชจเชฐเชจเซ‡ เชธเซ€เชฎเชฒเซ‡เชธ เชฐเซ€เชคเซ‡ เชฐเชฟเชชเซเชฒเซ‡เชธ เช•เชฐเซ‡ เช›เซ‡, เชชเชฐเช‚เชคเซ เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เชฐเชฟเชฎเซ‹เชŸ เชฎเชถเซ€เชจ เชชเชฐ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชเช•เซเชเชฟเช•เซเชฏเซเชŸ เช•เชฐเซ€ เชถเช•เซ€เช? เชธเซเช•เซเชฐเชฟเชชเซเชŸเชฎเชพเช‚ เชฆเชฒเซ€เชฒเซ‹ เช›เซ‡, เช•เชพเชฐเชฃ เช•เซ‡ เชคเซ‡ เชธเชพเชฐเซเชตเชคเซเชฐเชฟเช• เช›เซ‡ เช…เชจเซ‡ เชเช• เชฐเชฟเชตเชฐเซเชธ เชชเซเชฐเซ‹เช•เซเชธเซ€ เชนเซ‡เช เชณ เชเช• เชธเชพเชฅเซ‡ เช…เชจเซ‡เช• เชธเซ‡เชตเชพเช“เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡ (เช•เชฏเซเช‚ url เช•เชˆ เชธเซ‡เชตเชพ เชนเชถเซ‡ เชคเซ‡ เชจเชฟเชฐเซเชงเชพเชฐเชฟเชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชคเชฎเซ‡ nginx เชฐเซ‚เชชเชฐเซ‡เช–เชพเช“เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹). เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชธเชฐเซเชตเชฐ เชชเชฐ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ€ เชถเช•เชพเชคเซ€ เชจเชฅเซ€, เช•เชพเชฐเชฃ เช•เซ‡ เช† เช•เชฟเชธเซเชธเชพเชฎเชพเช‚ เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เช†เชชเชฎเซ‡เชณเซ‡ เช…เชชเชกเซ‡เชŸ เช•เชฐเซ€ เชถเช•เซ€เชถเซเช‚ เชจเชนเซ€เช‚ (เชฌเช— เชซเชฟเช•เซเชธ เช…เชจเซ‡ เชจเชตเซ€ เชธเซ‡เชตเชพเช“ เช‰เชฎเซ‡เชฐเชตเชพเชจเชพ เชนเซ‡เชคเซ เชฎเชพเชŸเซ‡), เช…เชจเซ‡ เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡, เชฐเชพเชœเซเชฏ = เช–เชฐเชพเชฌ.

เช‰เช•เซ‡เชฒ 1: เชนเชœเซ€ เชชเชฃ เชธเซเช•เซเชฐเชฟเชชเซเชŸเชจเซ‡ เชธเชฐเซเชตเชฐ เชชเชฐ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ‹, เชชเชฐเช‚เชคเซ เชฆเชฐ เชตเช–เชคเซ‡ เชคเซ‡เชจเซ€ เชจเช•เชฒ เช•เชฐเซ‹ scp. เชชเช›เซ€ เชฆเซเชตเชพเชฐเชพ เช•เชจเซ‡เช•เซเชŸ เช•เชฐเซ‹ ssh เช…เชจเซ‡ เชœเชฐเซ‚เชฐเซ€ เชฆเชฒเซ€เชฒเซ‹ เชธเชพเชฅเซ‡ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชšเชฒเชพเชตเซ‹.

เชตเชฟเชชเช•เซเชท:

  • เชเช•เชจเซ‡ เชฌเชฆเชฒเซ‡ เชฌเซ‡ เช•เซเชฐเชฟเชฏเชพเช“
  • เชคเซเชฏเชพเช‚ เช•เซ‹เชˆ เชธเซเชฅเชพเชจ เชจ เชนเซ‹เชˆ เชถเช•เซ‡ เชœเซเชฏเชพเช‚ เชคเชฎเซ‡ เชจเช•เชฒ เช•เชฐเซ‹ เช›เซ‹, เช…เชฅเชตเชพ เชคเซเชฏเชพเช‚ เชคเซ‡เชจเซ€ เชเช•เซเชธเซ‡เชธ เชนเซ‹เชˆ เชถเช•เชคเซ€ เชจเชฅเซ€, เช…เชฅเชตเชพ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เช…เชตเซ‡เชœเซ€ เชธเชฎเชฏเซ‡ เช…เชฎเชฒเชฎเชพเช‚ เช†เชตเซ€ เชถเช•เซ‡ เช›เซ‡.
  • เชคเชฎเชพเชฐเชพ เชชเช›เซ€ เชธเชพเชซ เช•เชฐเชตเชพเชจเซ€ เชธเชฒเชพเชน เช†เชชเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ (เชธเซเช•เซเชฐเซ€เชชเซเชŸ เช•เชพเชขเซ€ เชจเชพเช–เซ‹).
  • เชชเชนเซ‡เชฒเซ‡เชฅเซ€ เชœ เชคเซเชฐเชฃ เช•เซเชฐเชฟเชฏเชพเช“.

เช‰เช•เซ‡เชฒ 2:

  • เชธเซเช•เซเชฐเชฟเชชเซเชŸเชฎเชพเช‚ เชฎเชพเชคเซเชฐ เชซเช‚เช•เซเชถเชจ เชตเซเชฏเชพเช–เซเชฏเชพเช“ เชฐเชพเช–เซ‹ เช…เชจเซ‡ เช•เช‚เชˆเชชเชฃ เชšเชฒเชพเชตเชถเซ‹ เชจเชนเซ€เช‚
  • เชจเซ€ เชฎเชฆเชฆ เชธเชพเชฅเซ‡ sed เช…เช‚เชคเชฎเชพเช‚ เชซเช‚เช•เซเชถเชจ เช•เซ‰เชฒ เช‰เชฎเซ‡เชฐเซ‹
  • เชคเซ‡ เชฌเชงเซเช‚ เชชเชพเช‡เชช เชฆเซเชตเชพเชฐเชพ เชธเซ€เชงเชพ shh เชชเชฐ เชฎเซ‹เช•เชฒเซ‹ (|)

เช—เซเชฃ:

  • เช–เชฐเซ‡เช–เชฐ เชธเซเชŸเซ‡เชŸเชฒเซ‡เชธ
  • เช•เซ‹เชˆ เชฌเซ‹เชˆเชฒเชฐเชชเซเชฒเซ‡เชŸ เชเชจเซเชŸเชฟเชŸเซ€ เชจเชฅเซ€
  • เช เช‚เชกเซ€ เชฒเชพเช—เซ‡ เช›เซ‡

เชšเชพเชฒเซ‹ เชœเชตเชพเชฌ เชตเช—เชฐ เชœ เช•เชฐเซ€เช. เชนเชพ, เชฆเชฐเซ‡เช• เชตเชธเซเชคเซเชจเซ€ เชถเซ‹เชง เชฅเชˆ เชšเซ‚เช•เซ€ เช›เซ‡. เชนเชพ, เชธเชพเชฏเช•เชฒ. เชœเซเช“ เช•เซ‡ เชฌเชพเช‡เช• เช•เซ‡เชŸเชฒเซ€ เชธเชฐเชณ, เชญเชตเซเชฏ เช…เชจเซ‡ เชจเซเชฏเซ‚เชจเชคเชฎ เช›เซ‡:

$ cat << 'EOF' > deploy.sh
#!/bin/bash

usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}

echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
    echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPT
EOF

$ chmod +x deploy.sh

$ ./deploy.sh localhost magic-porridge-pot
Connecting to localhost...
Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'

เชœเซ‹ เช•เซ‡, เช…เชฎเซ‡ เช–เชพเชคเชฐเซ€ เช•เชฐเซ€ เชถเช•เชคเชพ เชจเชฅเซ€ เช•เซ‡ เชฐเชฟเชฎเซ‹เชŸ เชนเซ‹เชธเซเชŸ เชชเชพเชธเซ‡ เชชเชฐเซเชฏเชพเชชเซเชค เชฌเซ‡เชถ เช›เซ‡, เชคเซ‡เชฅเซ€ เช…เชฎเซ‡ เชถเชฐเซ‚เช†เชคเชฎเชพเช‚ เชเช• เชจเชพเชจเซ‹ เชšเซ‡เช• เช‰เชฎเซ‡เชฐเซ€เชถเซเช‚ (เช† เชคเซ‡เชจเชพ เชฌเชฆเชฒเซ‡ เช›เซ‡ เชถเซ‡เชฒเชฌเซ‡เช‚เช—):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."
    exit 1
fi

เช…เชจเซ‡ เชนเชตเซ‡ เชคเซ‡ เชตเชพเชธเซเชคเชตเชฟเช• เช›เซ‡:

$ docker exec reverse-proxy rm /etc/nginx/conf.d/default.conf

$ wget -qO deploy.sh https://git.io/JUURc

$ chmod +x deploy.sh

$ ./deploy.sh localhost uptimer
Sending gzipped image 'uptimer' to 'localhost' via ssh...
Loaded image: uptimer:latest
Connecting to 'localhost' via ssh to seamlessly deploy 'uptimer'...
Deploying 'uptimer_GREEN' in place of 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Container started. Checking health...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (1)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (2)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 200 OK
  Server: BaseHTTP/0.6 Python/3.8.3
  Date: Sat, 22 Aug 2020 20:15:50 GMT
  Content-Type: text/html

New 'uptimer_GREEN' service seems OK. Switching heads...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2020/08/22 20:15:54 [notice] 97#97: signal process started
The 'uptimer_GREEN' service is live!
Killing 'uptimer_BLUE'...
uptimer_BLUE
Total reclaimed space: 0B
Deployment successful!

เชนเชตเซ‡ เชคเชฎเซ‡ เช–เซ‹เชฒเซ€ เชถเช•เซ‹ เช›เซ‹ http://localhost/ เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚, เชซเชฐเซ€เชฅเซ€ เชœเชฎเชพเชตเชŸ เชšเชฒเชพเชตเซ‹ เช…เชจเซ‡ เช–เชพเชคเชฐเซ€ เช•เชฐเซ‹ เช•เซ‡ เชคเซ‡ เชฒเซ‡เช†เช‰เชŸ เชฆเชฐเชฎเชฟเชฏเชพเชจ เชธเซ€เชกเซ€ เช…เชจเซเชธเชพเชฐ เชชเซƒเชทเซเช เชจเซ‡ เช…เชชเชกเซ‡เชŸ เช•เชฐเซ€เชจเซ‡ เชเช•เซ€เช•เซƒเชค เชฐเซ€เชคเซ‡ เชšเชพเชฒเซ‡ เช›เซ‡.

เช•เชพเชฎ เช•เชฐเซเชฏเชพ เชชเช›เซ€ เชธเชพเชซ เช•เชฐเชตเชพเชจเซเช‚ เชญเซ‚เชฒเชถเซ‹ เชจเชนเซ€เช‚ :3

$ docker rm -f uptimer_GREEN reverse-proxy 
uptimer_GREEN
reverse-proxy

$ docker network rm web-gateway 
web-gateway

$ cd ..

$ rm -r blue-green-deployment

เชธเซ‹เชฐเซเชธ: www.habr.com