แž€แžถแžšแžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž–แžŽแŸŒแžแŸ€แžœแž”แŸƒแžแž„แž“แŸ…แž”แŸ’แžšแžถแž€แŸ‹แžˆแŸ’แž“แžฝแž›แžขแž”แŸ’แž”แž”แžšแž˜แžถ

แž“แŸ…แž€แŸ’แž“แžปแž„แžขแžแŸ’แžแž”แž‘แž“แŸแŸ‡แž™แžพแž„แž”แŸ’แžšแžพ bash, ssh, docker ะธ nginx แž™แžพแž„โ€‹แž“แžนแž„โ€‹แžšแŸ€แž”แž…แŸ†โ€‹แž”แŸ’แž›แž„แŸ‹โ€‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธโ€‹แž”แžŽแŸ’แžŠแžถแž‰โ€‹แžŠแŸ‚แž›โ€‹แž‚แŸ’แž˜แžถแž“โ€‹แžแŸ’แž“แŸแžšแŸ” แž€แžถแžšแžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž–แžŽแŸŒแžแŸ€แžœแž”แŸƒแžแž„ แž‚แžบแž‡แžถแž”แž…แŸ’แž…แŸแž€แž‘แŸแžŸแžŠแŸ‚แž›แžขแž“แžปแž‰แŸ’แž‰แžถแžแžฑแŸ’แž™แžขแŸ’แž“แž€แž’แŸ’แžœแžพแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แž—แžถแž–แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž—แŸ’แž›แžถแž˜แŸ—แžŠแŸ„แž™แž˜แžทแž“แž”แžŠแžทแžŸแŸแž’แžŸแŸ†แžŽแžพแžแŸ‚แž˜แžฝแž™แŸ” แžœแžถแž‚แžบแž‡แžถแž™แžปแž‘แŸ’แž’แžŸแžถแžŸแŸ’แžšแŸ’แžแž˜แžฝแž™แž€แŸ’แž“แžปแž„แž…แŸ†แž“แŸ„แž˜แž™แžปแž‘แŸ’แž’แžŸแžถแžŸแŸ’แžแŸ’แžšแžŠแžถแž€แŸ‹แžฑแŸ’แž™แžŠแŸ†แžŽแžพแžšแž€แžถแžšแž‚แŸ’แž˜แžถแž“แž–แŸแž›แž‘แŸ†แž“แŸแžš แž แžพแž™แžŸแŸแž€แŸ’แžแžทแžŸแž˜แž”แŸ†แž•แžปแžแžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžŠแŸ‚แž›แž˜แžถแž“แžงแž‘แžถแž แžšแžŽแŸแž˜แžฝแž™ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แžŸแž˜แžแŸ’แžแž—แžถแž–แž€แŸ’แž“แžปแž„แž€แžถแžšแž•แŸ’แž‘แžปแž€แžœแžแŸ’แžแžปแž‘แžธแž–แžธแžšแžŠแŸ‚แž›แžแŸ’แžšแŸ€แž˜แžšแžฝแž…แž‡แžถแžŸแŸ’แžšแŸแž…แžŠแžพแž˜แŸ’แž”แžธแžŠแŸ†แžŽแžพแžšแž€แžถแžšแž“แŸ…แž€แŸ’แž”แŸ‚แžšแž“แŸ„แŸ‡แŸ”

แž…แžผแžšแž“แžทแž™แžถแž™แžแžถแžขแŸ’แž“แž€แž˜แžถแž“แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž”แžŽแŸ’แžแžถแž‰แžŠแŸ‚แž›แžขแžแžทแžแžทแž‡แž“แž‡แžถแž…แŸ’แžšแžพแž“แž€แŸ†แž–แžปแž„แž’แŸ’แžœแžพแž€แžถแžšแž™แŸ‰แžถแž„แžŸแž€แž˜แŸ’แž˜ แž แžพแž™แžœแžถแž–แžทแžแž‡แžถแž‚แŸ’แž˜แžถแž“แž•แŸ’แž›แžผแžœแžŸแž˜แŸ’แžšแžถแž”แŸ‹แžœแžถแžŠแŸแž€แž–แžธแžšแž”แžธแžœแžทแž“แžถแž‘แžธแž“แŸ„แŸ‡แž‘แŸแŸ” แž แžพแž™โ€‹แžขแŸ’แž“แž€โ€‹แž–แžทแžโ€‹แž‡แžถโ€‹แžแŸ’แžšแžผแžœโ€‹แžŠแžถแž€แŸ‹โ€‹แž…แŸแž‰โ€‹แž€แžถแžšโ€‹แžขแžถแž”แŸ‹แžŠแŸแžโ€‹แž”แžŽแŸ’แžŽแžถแž›แŸแž™ แž€แžถแžšโ€‹แž‡แžฝแžŸแž‡แžปแž›โ€‹แž”แž‰แŸ’แž แžถ แžฌโ€‹แž˜แžปแžแž„แžถแžšโ€‹แž–แžทแžŸแŸแžŸโ€‹แžแŸ’แž˜แžธแŸ” แž€แŸ’แž“แžปแž„แžŸแŸ’แžแžถแž“แž—แžถแž–แž’แž˜แŸ’แž˜แžแžถ แžขแŸ’แž“แž€แž“แžนแž„แžแŸ’แžšแžผแžœแž”แž‰แŸ’แžˆแž”แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธ แž‡แŸ†แž“แžฝแžŸแžœแžถ แž แžพแž™แž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แžœแžถแž˜แŸ’แžแž„แž‘แŸ€แžแŸ” แž€แŸ’แž“แžปแž„แž€แžšแžŽแžธ docker แžŠแŸ†แž”แžผแž„แžขแŸ’แž“แž€แžขแžถแž…แž‡แŸ†แž“แžฝแžŸแžœแžถ แž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แžœแžถแžกแžพแž„แžœแžทแž‰ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แžœแžถแž“แžนแž„แž“แŸ…แžแŸ‚แž˜แžถแž“แžšแž™แŸˆแž–แŸแž›แžŠแŸ‚แž›แžŸแŸ†แžŽแžพแž‘แŸ…แž€แžถแž“แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž“แžนแž„แž˜แžทแž“แžŠแŸ†แžŽแžพแžšแž€แžถแžšแž‘แŸ แž–แŸ’แžšแŸ„แŸ‡แž‡แžถแž’แž˜แŸ’แž˜แžแžถแž€แž˜แŸ’แž˜แžœแžทแž’แžธแžแŸ’แžšแžผแžœแž…แŸ†แžŽแžถแž™แž–แŸแž›แžแŸ’แž›แŸ‡แžŠแžพแž˜แŸ’แž”แžธแžŠแŸ†แžŽแžพแžšแž€แžถแžšแžŠแŸ†แž”แžผแž„แŸ” แž…แžปแŸ‡โ€‹แž”แžพโ€‹แžœแžถโ€‹แž…แžถแž”แŸ‹โ€‹แž•แŸ’แžŠแžพแž˜ แž”แŸ‰แžปแž“แŸ’แžแŸ‚โ€‹แžœแžถโ€‹แž˜แžทแž“โ€‹แžขแžถแž…โ€‹แž”แŸ’แžšแžพโ€‹แž”แžถแž“? แž“แŸแŸ‡โ€‹แž‡แžถโ€‹แž”แž‰แŸ’แž แžถ แžŸแžผแž˜โ€‹แžŠแŸ„แŸ‡แžŸแŸ’แžšแžถแž™โ€‹แžœแžถโ€‹แžŠแŸ„แž™โ€‹แž˜แž’แŸ’แž™แŸ„แž”แžถแž™โ€‹แžแžทแž…แžแžฝแž…โ€‹แž”แŸ†แž•แžปแž แž“แžทแž„โ€‹แž†แžพแžแž†แžถแž™โ€‹แžแžถแž˜โ€‹แžŠแŸ‚แž›โ€‹แžขแžถแž…โ€‹แž’แŸ’แžœแžพโ€‹แž‘แŸ…โ€‹แž”แžถแž“แŸ”

แž€แžถแžšแž”แžŠแžทแžŸแŸแž’แŸ– แžขแžแŸ’แžแž”แž‘แž—แžถแž‚แž…แŸ’แžšแžพแž“แžแŸ’แžšแžผแžœแž”แžถแž“แž”แž„แŸ’แž แžถแž‰แž‡แžถแž‘แž˜แŸ’แžšแž„แŸ‹แž–แžทแžŸแŸ„แž’แž“แŸ - แž‡แžถแž‘แž˜แŸ’แžšแž„แŸ‹แž“แŸƒแž€แžถแžšแžแžแžŸแž˜แŸแž™แž€แžปแž„แžŸแžผแž›แŸ” แžŸแž„แŸ’แžƒแžนแž˜แžแžถแž“แŸแŸ‡แž“แžนแž„แž˜แžทแž“แž–แžทแž”แžถแž€แž™แž›แŸ‹แž–แŸแž€แž‘แŸ แž แžพแž™แž›แŸแžแž€แžผแžŠแž“แžนแž„แž…แž„แž€แŸ’แžšแž„แžŠแŸ„แž™แžแŸ’แž›แžฝแž“แžœแžถแž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแžถแž“แŸ‹แŸ” แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž”แžšแžทแž™แžถแž€แžถแžŸ แžŸแžผแž˜แžŸแŸ’แžšแž˜แŸƒแžแžถ แž‘แžถแŸ†แž„แž“แŸแŸ‡แž˜แžทแž“แž˜แŸ‚แž“แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แž‡แžถแžขแžแŸ’แžแž”แž‘แž€แžผแžŠแž”แŸ‰แžปแžŽแŸ’แžŽแŸ„แŸ‡แž‘แŸ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แž‡แžถแž€แŸ’แžšแžŠแžถแžŸแž–แžธแž‘แžผแžšแž›แŸแž "แžŠแŸ‚แž€"แŸ”

แž€แžถแžšแžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž–แžŽแŸŒแžแŸ€แžœแž”แŸƒแžแž„แž“แŸ…แž”แŸ’แžšแžถแž€แŸ‹แžˆแŸ’แž“แžฝแž›แžขแž”แŸ’แž”แž”แžšแž˜แžถ

แž”แž…แŸ’แž…แŸแž€แž‘แŸแžŸแž‚แžฝแžšแžฑแŸ’แž™แž…แžถแž”แŸ‹แžขแžถแžšแž˜แŸ’แž˜แžŽแŸแžŠแŸ‚แž›แž–แžทแž”แžถแž€แžŸแž˜แŸ’แžšแžถแž”แŸ‹ Google แžŠแŸ„แž™แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แžขแžถแž“แž€แžผแžŠแžแŸ’แžšแžผแžœแž”แžถแž“แž–แžทแž–แžŽแŸŒแž“แžถแž“แŸ…แžŠแžพแž˜แž•แŸ’แž“แŸ‚แž€แž“แžธแž˜แžฝแž™แŸ—แŸ” แž”แŸ’แžšแžŸแžทแž“แž”แžพแž˜แžถแž“แžขแŸ’แžœแžธแž•แŸ’แžŸแŸแž„แž‘แŸ€แžแž˜แžทแž“แž…แŸ’แž”แžถแžŸแŸ‹แž›แžถแžŸแŸ‹, google แžœแžถแž แžพแž™แž–แžทแž“แžทแžแŸ’แž™แž˜แžพแž›แžœแžถแž…แŸแž‰แŸ” แž–แž“แŸ’แž™แž›แŸ‹แžŸแŸ‚แž› (แž‡แžถแžŸแŸ†แžŽแžถแž„แž›แŸ’แžข แžœแžถแžŠแŸ†แžŽแžพแžšแž€แžถแžšแž˜แŸ’แžแž„แž‘แŸ€แž แžŠแŸ„แž™แžŸแžถแžšแžแŸ‚แž€แžถแžšแž”แžทแž‘แž‘แžผแžšแž›แŸแž)แŸ” แž”แŸ’แžšแžŸแžทแž“แž”แžพแžขแŸ’แž“แž€แž˜แžทแž“แžขแžถแž… Google แžขแŸ’แžœแžธแž”แžถแž“แž‘แŸ แžŸแžผแž˜แžŸแžฝแžšแž“แŸ…แž€แŸ’แž“แžปแž„แž˜แžแžทแž™แŸ„แž”แž›แŸ‹แŸ” แžแŸ’แž‰แžปแŸ†แž“แžนแž„แžšแžธแž€แžšแžถแž™แž€แŸ’แž“แžปแž„แž€แžถแžšแž”แž“แŸ’แžแŸ‚แž˜แž‘แŸ…แž•แŸ’แž“แŸ‚แž€แžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž‚แŸ’แž“แžถ "แž”แž…แŸ’แž…แŸแž€แž‘แŸแžŸแž‚แžฝแžšแžฑแŸ’แž™แž…แžถแž”แŸ‹แžขแžถแžšแž˜แŸ’แž˜แžŽแŸ" แŸ”

แžแŸ„แŸ‡แž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แŸ”

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

RะŽRฮผSะ‚Rะ†Rั‘Sะƒ

แž…แžผแžšแž™แžพแž„แž”แž„แŸ’แž€แžพแžแžŸแŸแžœแžถแž€แž˜แŸ’แž˜แž–แžทแžŸแŸ„แž’แž“แŸ แž แžพแž™แžŠแžถแž€แŸ‹แžœแžถแž“แŸ…แž€แŸ’แž“แžปแž„แž’แžปแž„แž˜แžฝแž™แŸ”

แž”แž…แŸ’แž…แŸแž€แž‘แŸแžŸแž‚แžฝแžšแžฑแŸ’แž™แž…แžถแž”แŸ‹แžขแžถแžšแž˜แŸ’แž˜แžŽแŸ

  • 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

แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸ

แžŠแžพแž˜แŸ’แž”แžธแžฑแŸ’แž™แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแž”แžŸแŸ‹แž™แžพแž„แžขแžถแž…แž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžšแžŠแŸ„แž™แž˜แžทแž“แž˜แžถแž“แž“แžšแžŽแžถแž€แžแŸ‹แžŸแž˜แŸ’แž‚แžถแž›แŸ‹ แžœแžถแž…แžถแŸ†แž”แžถแž…แŸ‹แžแžถแž˜แžถแž“แžขแž„แŸ’แž‚แž—แžถแž–แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแž“แŸ…แž–แžธแž˜แžปแžแžœแžถแžŠแŸ‚แž›แž“แžนแž„แž›แžถแž€แŸ‹แž€แžถแžšแž‡แŸ†แž“แžฝแžŸแžšแž”แžŸแŸ‹แžœแžถแŸ” แžœแžถแžขแžถแž…แž‡แžถ web server nginx ะฒ แžšแž”แŸ€แž”แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸ. แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸแžแŸ’แžšแžผแžœแž”แžถแž“แž”แž„แŸ’แž€แžพแžแžกแžพแž„แžšแžœแžถแž„แžขแžแžทแžแžทแž‡แž“ แž“แžทแž„แž€แž˜แŸ’แž˜แžœแžทแž’แžธแŸ” แžœแžถแž‘แž‘แžฝแž›แž™แž€แžŸแŸ†แžŽแžพแž–แžธแžขแžแžทแžแžทแž‡แž“ แž“แžทแž„แž”แž‰แŸ’แž‡แžผแž“แž–แžฝแž€แž‚แŸแž‘แŸ…แž€แžถแž“แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธ แž“แžทแž„แž”แž‰แŸ’แž‡แžผแž“แž€แžถแžšแž†แŸ’แž›แžพแž™แžแž”แžšแž”แžŸแŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž‘แŸ…แžขแžแžทแžแžทแž‡แž“แŸ”

แž€แž˜แŸ’แž˜แžœแžทแž’แžธ แž“แžทแž„แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸแžขแžถแž…แžแŸ’แžšแžผแžœแž”แžถแž“แž—แŸ’แž‡แžถแž”แŸ‹แž“แŸ…แžแžถแž„แž€แŸ’แž“แžปแž„ docker แžŠแŸ„แž™แž”แŸ’แžšแžพ แž”แžŽแŸ’แžแžถแž‰ docker. แžŠแžผแž…แŸ’แž“แŸแŸ‡ แž€แžปแž„แžแžบแž“แŸแžšแžŠแŸ‚แž›แž˜แžถแž“แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž˜แžทแž“แž…แžถแŸ†แž”แžถแž…แŸ‹แž”แž‰แŸ’แž‡แžผแž“แž…แŸ’แžšแž€แž“แŸ…แž›แžพแž”แŸ’แžšแž–แŸแž“แŸ’แž’แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž‘แŸ แžœแžถแžขแž“แžปแž‰แŸ’แž‰แžถแžแžฑแŸ’แž™แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžŠแžถแž…แŸ‹แž†แŸ’แž„แžถแž™แž–แžธแž€แžถแžšแž‚แŸ†แžšแžถแž˜แž€แŸ†แž แŸ‚แž„แž–แžธแžแžถแž„แž€แŸ’แžšแŸ…แŸ”

แž”แŸ’แžšแžŸแžทแž“แž”แžพแž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸแžšแžŸแŸ‹แž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž•แŸ’แžŸแŸแž„แž‘แŸ€แž แžขแŸ’แž“แž€แž“แžนแž„แžแŸ’แžšแžผแžœแž”แŸ„แŸ‡แž”แž„แŸ‹แž…แŸ„แž›แž”แžŽแŸ’แžแžถแž‰ docker แž แžพแž™แž—แŸ’แž‡แžถแž”แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž‘แŸ…แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸแžแžถแž˜แžšแž™แŸˆแž”แžŽแŸ’แžแžถแž‰แž˜แŸ‰แžถแžŸแŸŠแžธแž“ แžŠแŸ„แž™แž”แž‰แŸ’แž‡แžผแž“แž”แž“แŸ’แžแž…แŸ’แžšแž€แŸ” แž€แž˜แŸ’แž˜แžœแžทแž’แžธ แž”แŸ‰แžถแžšแŸ‰แžถแž˜แŸ‰แŸ‚แžแŸ’แžš --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. แž—แŸ’แž‡แžถแž”แŸ‹แž‘แŸ…แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ docker daemon แž–แžธแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแŸ–
    1. แž”แžšแžทแžŸแŸ’แžแžถแž“แž”แŸ’แžšแŸ‚แž”แŸ’แžšแžฝแž› DOCKER_HOST.
    2. แž‡แž˜แŸ’แžšแžพแžŸแž”แž“แŸ’แž‘แžถแžแŸ‹แž–แžถแž€แŸ’แž™แž”แž‰แŸ’แž‡แžถ -H แžฌ --host แžงแž”แž€แžšแžŽแŸ docker-compose.
    3. docker context

แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแž‘แžธแž–แžธแžš (แž‡แžถแž˜แžฝแž™แž“แžนแž„แž‡แž˜แŸ’แžšแžพแžŸแž”แžธแžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แžถแžšแžขแž“แžปแžœแžแŸ’แžแžšแž”แžŸแŸ‹แžœแžถ) แžแŸ’แžšแžผแžœแž”แžถแž“แž–แžทแž–แžŽแŸŒแž“แžถแž™แŸ‰แžถแž„แž›แŸ’แžขแž“แŸ…แž€แŸ’แž“แžปแž„แžขแžแŸ’แžแž”แž‘ แžšแž”แŸ€แž”แžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž“แŸ…แž›แžพ Docker hosts แž–แžธแž…แž˜แŸ’แž„แžถแž™แž‡แžถแž˜แžฝแž™ docker-compose.

deploy.sh

แžฅแžกแžผแžœแž“แŸแŸ‡ แž…แžผแžšแž™แžพแž„แž”แŸ’แžšแž˜แžผแž›แžขแŸ’แžœแžธแž‚แŸ’แžšแž”แŸ‹แž™แŸ‰แžถแž„แžŠแŸ‚แž›แž™แžพแž„แž”แžถแž“แž’แŸ’แžœแžพแžŠแŸ„แž™แžŠแŸƒแž‘แŸ…แž€แŸ’แž“แžปแž„แžŸแŸ’แž‚แŸ’แžšแžธแž”แžแŸ‚แž˜แžฝแž™แŸ” แž…แžผแžšแž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แž‡แžถแž˜แžฝแž™แž“แžนแž„แž˜แžปแžแž„แžถแžšแž€แž˜แŸ’แžšแžทแžแž€แŸ†แž–แžผแž› แž แžพแž™แž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž˜แžพแž›แž˜แžปแžแž„แžถแžšแž•แŸ’แžŸแŸแž„แž‘แŸ€แžแžŠแŸ‚แž›แž”แžถแž“แž”แŸ’แžšแžพแž“แŸ…แž€แŸ’แž“แžปแž„แžœแžถแŸ”

แž”แž…แŸ’แž…แŸแž€แž‘แŸแžŸแž‚แžฝแžšแžฑแŸ’แž™แž…แžถแž”แŸ‹แžขแžถแžšแž˜แŸ’แž˜แžŽแŸ

  • ${parameter?err_msg} - แž˜แžฝแž™แž“แŸƒแžขแž€แŸ’แžแžšแžถแžœแžทแžšแžปแž‘แŸ’แž’แžœแŸแž‘แž˜แž“แŸ’แž bash (aka แž€แžถแžšแž‡แŸ†แž“แžฝแžŸแž”แŸ‰แžถแžšแŸ‰แžถแž˜แŸ‰แŸ‚แžแŸ’แžš) แž”แŸ’แžšแžŸแžทแž“แž”แžพ parameter แž˜แžทแž“แž”แžถแž“แž”แž‰แŸ’แž‡แžถแž€แŸ‹, แž‘แžทแž“แŸ’แž“แž•แž› err_msg แž แžพแž™แž…แŸแž‰แžŠแŸ„แž™แž›แŸแžแž€แžผแžŠ 1 แŸ”
  • docker --log-driver journald - แžแžถแž˜แž›แŸ†แž“แžถแŸ†แžŠแžพแž˜ แž€แž˜แŸ’แž˜แžœแžทแž’แžธแž”แž‰แŸ’แž‡แžถแž€แžถแžšแž€แžแŸ‹แžแŸ’แžšแžถ docker แž‚แžบแž‡แžถแžฏแž€แžŸแžถแžšแžขแžแŸ’แžแž”แž‘แžŠแŸ„แž™แž‚แŸ’แž˜แžถแž“แž€แžถแžšแž”แž„แŸ’แžœแžทแž›แžŽแžถแž˜แžฝแž™แžกแžพแž™แŸ” แž‡แžถแž˜แžฝแž™แž“แžนแž„แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแž“แŸแŸ‡ แž€แŸ†แžŽแžแŸ‹แž แŸแžแžปแžขแžถแž…แž”แŸ†แž–แŸแž‰แžแžถแžŸแž‘แžถแŸ†แž„แž˜แžผแž›แž”แžถแž“แž™แŸ‰แžถแž„แž†แžถแž”แŸ‹แžšแž แŸแžŸ แžŠแžผแž…แŸ’แž“แŸแŸ‡แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž”แžšแžทแž™แžถแž€แžถแžŸแž•แž›แžทแžแž€แž˜แŸ’แž˜ แž…แžถแŸ†แž”แžถแž…แŸ‹แžแŸ’แžšแžผแžœแž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžšแž€แž˜แŸ’แž˜แžœแžทแž’แžธแž”แž‰แŸ’แž‡แžถแžฑแŸ’แž™แž€แžถแž“แŸ‹แžแŸ‚แž†แŸ’แž›แžถแžแžœแŸƒแŸ”

แžŸแŸ’แž‚แŸ’แžšแžธแž”แžŠแžถแž€แŸ‹แžฑแŸ’แž™แž”แŸ’แžšแžพแž”แŸ’แžšแžถแžŸแŸ‹

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 spaces แž“แžทแž„แž’แŸ’แžœแžพแžฑแŸ’แž™ EOF แž˜แžทแž“แžแŸ’แžšแžนแž˜แžแŸ’แžšแžผแžœแŸ” แž แžพแž™แž“แŸ…แž‘แžธแž“แŸแŸ‡แžœแžถแž‚แžฝแžšแžฑแŸ’แž™แž€แžแŸ‹แžŸแž˜แŸ’แž‚แžถแž›แŸ‹.

แžŠแžพแž˜แŸ’แž”แžธแž€แžปแŸ†แžฑแŸ’แž™แž€แŸ’แžšแŸ„แž€แžกแžพแž„แž–แžธแžšแžŠแž„ แžแŸ’แž‰แžปแŸ†แž“แžนแž„แž”แŸ’แžšแžถแž”แŸ‹แžขแŸ’แž“แž€แž—แŸ’แž›แžถแž˜แŸ—แžขแŸ†แž–แžธ cat << 'EOF'แžŠแŸ‚แž›แž“แžนแž„แž‡แžฝแž”แž”แŸ’แžšแž‘แŸ‡แž“แŸ…แž–แŸแž›แž€แŸ’แžšแŸ„แž™แŸ” แž”แŸ’แžšแžŸแžทแž“แž”แžพแžขแŸ’แž“แž€แžŸแžšแžŸแŸแžšแž™แŸ‰แžถแž„แžŸแžถแž˜แž‰แŸ’แž‰ cat << EOFแž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž“แŸ…แžแžถแž„แž€แŸ’แž“แžปแž„ heredoc แžแŸ’แžŸแŸ‚แžขแž€แŸ’แžŸแžšแžแŸ’แžšแžผแžœแž”แžถแž“ interpolated (แžขแžแŸแžšแžแŸ’แžšแžผแžœแž”แžถแž“แž–แž„แŸ’แžšแžธแž€ ($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
}

แž“แŸแŸ‡แž‚แžบแž‡แžถแžŸแŸ’แž‚แŸ’แžšแžธแž”แž‘แžถแŸ†แž„แž˜แžผแž›แŸ” แž แžพแž™โ€‹แžŠแžผแž…แŸ’แž“แŸแŸ‡ gist แž‡แžถแž˜แžฝแž™แžŸแŸ’แž‚แŸ’แžšแžธแž”แž“แŸแŸ‡แŸ” แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แžถแžšแž‘แžถแž‰แž™แž€แžแžถแž˜แžšแž™แŸˆ wget แžฌ curl แŸ”

แžŠแŸ†แžŽแžพแžšแž€แžถแžšแžŸแŸ’แž‚แŸ’แžšแžธแž”แžŠแŸ‚แž›แž”แžถแž“แž€แŸ†แžŽแžแŸ‹แž”แŸ‰แžถแžšแŸ‰แžถแž˜แŸ‰แŸ‚แžแŸ’แžšแž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž–แžธแž…แž˜แŸ’แž„แžถแž™

แžœแžถแžŠแž›แŸ‹แž–แŸแž›แž แžพแž™แžŠแžพแž˜แŸ’แž”แžธแž‚แŸ„แŸ‡แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž‚แŸ„แž›แžŠแŸ…แŸ” แž–แŸแž›แž“แŸแŸ‡ localhost แžŸแž˜แžšแž˜แŸ’แž™แžŽแžถแžŸแŸ‹แŸ–

$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
himura@localhost's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.

แž™แžพแž„แž”แžถแž“แžŸแžšแžŸแŸแžšแžŸแŸ’แž‚แŸ’แžšแžธแž”แžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แžŠแŸ‚แž›แž‘แžถแž‰แž™แž€แžšแžผแž”แž—แžถแž–แžŠแŸ‚แž›แž”แžถแž“แž”แž„แŸ’แž€แžพแžแž‡แžถแž˜แžปแž“แž‘แŸ…แž€แžถแž“แŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž‚แŸ„แž›แžŠแŸ… แž แžพแž™แž‡แŸ†แž“แžฝแžŸแž€แž“แŸ’แž›แŸ‚แž„แž•แŸ’แž‘แžปแž€แžŸแŸแžœแžถแž€แž˜แŸ’แž˜แž™แŸ‰แžถแž„แžšแž›แžผแž“ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แžแžพแž™แžพแž„แžขแžถแž…แž”แŸ’แžšแžแžทแž”แžแŸ’แžแžทแžœแžถแžŠแŸ„แž™แžšแž”แŸ€แž”แžŽแžถแž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž–แžธแž…แž˜แŸ’แž„แžถแž™? แžŸแŸ’แž‚แŸ’แžšแžธแž”แž˜แžถแž“แžขแžถแž‚แžปแž™แž˜แŸ‰แž„แŸ‹ แž–แŸ’แžšแŸ„แŸ‡แžœแžถแž˜แžถแž“แž›แž€แŸ’แžแžŽแŸˆแž‡แžถแžŸแž€แž› แž แžพแž™แžขแžถแž…แž”แŸ’แžšแžพแž”แŸ’แžšแžถแžŸแŸ‹แžŸแŸแžœแžถแž€แž˜แŸ’แž˜แž‡แžถแž…แŸ’แžšแžพแž“แž€แŸ’แž“แžปแž„แž–แŸแž›แžแŸ‚แž˜แžฝแž™แž€แŸ’แžšแŸ„แž˜แž”แŸ’แžšแžผแž€แžŸแŸŠแžธแž”แž‰แŸ’แž…แŸ’แžšแžถแžŸแž˜แžฝแž™ (แžขแŸ’แž“แž€แžขแžถแž…แž”แŸ’แžšแžพ nginx configs แžŠแžพแž˜แŸ’แž”แžธแž€แŸ†แžŽแžแŸ‹ url แž˜แžฝแž™แžŽแžถแž‡แžถแžŸแŸแžœแžถแž€แž˜แŸ’แž˜แž˜แžฝแž™แžŽแžถ)แŸ” แžŸแŸ’แž‚แŸ’แžšแžธแž”แž˜แžทแž“แžขแžถแž…แžšแž€แŸ’แžŸแžถแž‘แžปแž€แž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž”แžถแž“แž‘แŸ แž–แŸ’แžšแŸ„แŸ‡แž€แŸ’แž“แžปแž„แž€แžšแžŽแžธแž“แŸแŸ‡ แž™แžพแž„แž“แžนแž„แž˜แžทแž“แžขแžถแž…แžขแžถแž”แŸ‹แžŠแŸแžแžœแžถแžŠแŸ„แž™แžŸแŸ’แžœแŸแž™แž”แŸ’แžšแžœแžแŸ’แžแžทแž”แžถแž“แž‘แŸ (แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž‚แŸ„แž›แž”แŸ†แžŽแž„แž‡แžฝแžŸแž‡แžปแž›แž€แŸ†แž แžปแžŸ แž“แžทแž„แž”แž“แŸ’แžแŸ‚แž˜แžŸแŸแžœแžถแž€แž˜แŸ’แž˜แžแŸ’แž˜แžธ) แž แžพแž™แž‡แžถแž‘แžผแž‘แŸ… แžŸแŸ’แžแžถแž“แž—แžถแž– = แžขแžถแž€แŸ’แžšแž€แŸ‹แŸ”

แžŠแŸ†แžŽแŸ„แŸ‡แžŸแŸ’แžšแžถแž™แž‘แžธ 1: แž“แŸ…แžแŸ‚แžšแž€แŸ’แžŸแžถแž‘แžปแž€แžŸแŸ’แž‚แŸ’แžšแžธแž”แž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แž…แž˜แŸ’แž›แž„แžœแžถแžšแžถแž›แŸ‹แž–แŸแž›แž†แŸ’แž›แž„แž€แžถแžแŸ‹ scp. แž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž—แŸ’แž‡แžถแž”แŸ‹แžแžถแž˜แžšแž™แŸˆ ssh แž“แžทแž„แž”แŸ’แžšแžแžทแž”แžแŸ’แžแžทแžŸแŸ’แž‚แŸ’แžšแžธแž”แž‡แžถแž˜แžฝแž™แž“แžนแž„แžขแžถแž‚แžปแž™แž˜แŸ‰แž„แŸ‹แž…แžถแŸ†แž”แžถแž…แŸ‹แŸ”

แž‚แžปแžŽแžœแžทแž”แžแŸ’แžแžท:

  • แžŸแž€แž˜แŸ’แž˜แž—แžถแž–แž–แžธแžšแž‡แŸ†แž“แžฝแžŸแžฑแŸ’แž™แž˜แžฝแž™แŸ”
  • แž”แŸ’แžšแž แŸ‚แž›แž‡แžถแž˜แžทแž“แž˜แžถแž“แž€แž“แŸ’แž›แŸ‚แž„แžŠแŸ‚แž›แžขแŸ’แž“แž€แž…แž˜แŸ’แž›แž„ แžฌแž”แŸ’แžšแž แŸ‚แž›แž‡แžถแž˜แžทแž“แž˜แžถแž“แžŸแžทแž‘แŸ’แž’แžทแž…แžผแž›แž”แŸ’แžšแžพแžœแžถ แžฌแžŸแŸ’แž‚แŸ’แžšแžธแž”แžขแžถแž…แžแŸ’แžšแžผแžœแž”แžถแž“แž”แŸ’แžšแžแžทแž”แžแŸ’แžแžทแž“แŸ…แž–แŸแž›แž‡แŸ†แž“แžฝแžŸแŸ”
  • แžœแžถแžแŸ’แžšแžผแžœแž”แžถแž“แžŽแŸ‚แž“แžถแŸ†แžฑแŸ’แž™แžŸแž˜แŸ’แžขแžถแžแž”แž“แŸ’แž‘แžถแž”แŸ‹แž–แžธแžแŸ’แž›แžฝแž“แžขแŸ’แž“แž€ (แž›แžปแž”แžŸแŸ’แž‚แŸ’แžšแžธแž”) แŸ”
  • แžŸแž€แž˜แŸ’แž˜แž—แžถแž–แž”แžธแžšแžฝแž…แž แžพแž™แŸ”

แžŠแŸ†แžŽแŸ„แŸ‡แžŸแŸ’แžšแžถแž™ 2:

  • แž‘แžปแž€แžแŸ‚แž“แžทแž™แž˜แž“แŸแž™แž˜แžปแžแž„แžถแžšแž“แŸ…แž€แŸ’แž“แžปแž„แžŸแŸ’แž‚แŸ’แžšแžธแž” แž แžพแž™แž˜แžทแž“แžŠแŸ†แžŽแžพแžšแž€แžถแžšแžขแŸ’แžœแžธแž‘แžถแŸ†แž„แžขแžŸแŸ‹แŸ”
  • แžŠแŸ„แž™แž˜แžถแž“แž‡แŸ†แž“แžฝแž™แž–แžธ sed แž”แž“แŸ’แžแŸ‚แž˜แž˜แžปแžแž„แžถแžšแž แŸ…แž‘แŸ…แž…แžปแž„แž”แž‰แŸ’แž…แž”แŸ‹
  • แž•แŸ’แž‰แžพแžœแžถแž‘แžถแŸ†แž„แžขแžŸแŸ‹แžŠแŸ„แž™แž•แŸ’แž‘แžถแž›แŸ‹แž‘แŸ… shh แžแžถแž˜แžšแž™แŸˆแž”แŸ†แž–แž„แŸ‹ (|)

แž”แŸ’แžšแžปแžŸ:

  • แž–แžทแžแž‡แžถแž‚แŸ’แž˜แžถแž“แžšแžŠแŸ’แž‹
  • แž˜แžทแž“แž˜แžถแž“แž’แžถแžแžป boilerplate
  • แž˜แžถแž“แžขแžถแžšแž˜แŸ’แž˜แžŽแŸแžแŸ’แžšแž‡แžถแž€แŸ‹

แž…แžผแžšแž™แžพแž„แž’แŸ’แžœแžพแžœแžถแžŠแŸ„แž™แž‚แŸ’แž˜แžถแž“ 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 แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแžถแž“แŸ‹แž‘แŸ แžŠแžผแž…แŸ’แž“แŸแŸ‡แž™แžพแž„แž“แžนแž„แž”แž“แŸ’แžแŸ‚แž˜แž€แžถแžšแžแŸ’แžšแžฝแžแž–แžทแž“แžทแžแŸ’แž™แžแžผแž…แž˜แžฝแž™แž“แŸ…แžŠแžพแž˜แžŠแŸ†แž”แžผแž„ (แž“แŸแŸ‡แž‚แžบแž‡แŸ†แž“แžฝแžŸแžฑแŸ’แž™ shellbang):

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

แž แžพแž™แžฅแžกแžผแžœแž“แŸแŸ‡แžœแžถแž‡แžถแž€แžถแžšแž–แžทแžแŸ–

$ 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/ แž“แŸ…แž€แŸ’แž“แžปแž„แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแžปแž€แžšแž€แžแžถแž˜แžขแŸŠแžธแž“แž’แžบแžŽแžทแž แžŠแŸ†แžŽแžพแžšแž€แžถแžšแž€แžถแžšแžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž˜แŸ’แžแž„แž‘แŸ€แž แž แžพแž™แžแŸ’แžšแžผแžœแž”แŸ’แžšแžถแž€แžŠแžแžถแžœแžถแžŠแŸ†แžŽแžพแžšแž€แžถแžšแžŠแŸ„แž™แžšแž›แžผแž“แžŠแŸ„แž™แž’แŸ’แžœแžพแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แž—แžถแž–แž‘แŸ†แž–แŸแžšแžŠแŸ„แž™แž™แŸ„แž„แžแžถแž˜แžŸแŸŠแžธแžŒแžธแž€แŸ†แžกแžปแž„แž–แŸแž›แž”แŸ’แž›แž„แŸ‹แŸ”

แž€แžปแŸ†โ€‹แž—แŸ’แž›แŸแž…โ€‹แžŸแž˜แŸ’แžขแžถแžโ€‹แž€แŸ’แžšแŸ„แž™โ€‹แž–แŸแž›โ€‹แž’แŸ’แžœแžพโ€‹แž€แžถแžšแŸ– แŸฃ

$ 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

แž‘แžทแž‰แž€แžถแžšแž”แž„แŸ’แž แŸ„แŸ‡แžŠแŸ‚แž›แžขแžถแž…แž‘แžปแž€แž…แžทแžแŸ’แžแž”แžถแž“แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž‚แŸแž แž‘แŸ†แž–แŸแžšแžŠแŸ‚แž›แž˜แžถแž“แž€แžถแžšแž€แžถแžšแž–แžถแžš DDoS, แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ VPS VDS ๐Ÿ”ฅ แž‘แžทแž‰แžŸแŸแžœแžถแž”แž„แŸ’แž แŸ„แŸ‡แž‚แŸแž แž‘แŸ†แž–แŸแžšแžŠแŸ‚แž›แžขแžถแž…แž‘แžปแž€แž…แžทแžแŸ’แžแž”แžถแž“แž‡แžถแž˜แžฝแž™แž“แžนแž„แž€แžถแžšแž€แžถแžšแž–แžถแžš DDoS แž“แžทแž„แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ VPS VDS | ProHoster