рдпрд╕ рд▓реЗрдЦрдорд╛ рд╣рд╛рдореА рдкреНрд░рдпреЛрдЧ рдЧрд░реНрджрдЫреМрдВ
рдорд╛рдиреМрдВ рддрдкрд╛рдИрдВрд╕рдБрдЧ рдПрдЙрдЯрд╛ рд╡реЗрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЫ рдЬрд╕рд╕рдБрдЧ рдзреЗрд░реИ рдЧреНрд░рд╛рд╣рдХрд╣рд░реВ рд╕рдХреНрд░рд┐рдп рд░реВрдкрдорд╛ рдХрд╛рдо рдЧрд░рд┐рд░рд╣реЗрдХрд╛ рдЫрдиреН, рд░ рддреНрдпрд╣рд╛рдБ рдХреЗрд╣реА рд╕реЗрдХреЗрдиреНрдбрдХреЛ рд▓рд╛рдЧрд┐ рд╕реБрддреНрдиреЗ рдХреБрдиреИ рддрд░рд┐рдХрд╛ рдЫреИрдиред рд░ рддрдкрд╛рдИрдВрд▓реЗ рд╡рд╛рд╕реНрддрд╡рдореИ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдЕрдкрдбреЗрдЯ, рдмрдЧ рдлрд┐рдХреНрд╕, рд╡рд╛ рдирдпрд╛рдБ рд░рд╛рдореНрд░реЛ рд╕реБрд╡рд┐рдзрд╛ рд░реЛрд▓ рдЖрдЙрдЯ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫред рд╕рд╛рдорд╛рдиреНрдп рдЕрд╡рд╕реНрдерд╛рдорд╛, рддрдкрд╛рдИрдВрд▓реЗ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд▓рд╛рдИ рд░реЛрдХреНрди, рдпрд╕рд▓рд╛рдИ рдмрджрд▓реНрди рд░ рдлреЗрд░рд┐ рд╕реБрд░реБ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рд╣реБрдиреЗрдЫред рдбрдХрд░рдХреЛ рдЕрд╡рд╕реНрдерд╛рдорд╛, рддрдкрд╛рдЗрдБ рдкрд╣рд┐рд▓реЗ рдпрд╕рд▓рд╛рдИ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ, рддреНрдпрд╕рдкрдЫрд┐ рдпрд╕рд▓рд╛рдИ рдкреБрди: рд╕реБрд░реБ рдЧрд░реНрдиреБрд╣реЛрд╕реН, рддрд░ рддреНрдпрд╣рд╛рдБ рдЕрдЭреИ рдкрдирд┐ рдПрдХ рдЕрд╡рдзрд┐ рд╣реБрдиреЗрдЫ рдЬрд╕рдорд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрдХреЛ рдЕрдиреБрд░реЛрдзрд╣рд░реВ рдкреНрд░рд╢реЛрдзрди рдЧрд░рд┐рдиреЗ рдЫреИрди, рдХрд┐рдирднрдиреЗ рд╕рд╛рдорд╛рдиреНрдпрддрдпрд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд▓реЗ рд╕реБрд░реБрдорд╛ рд▓реЛрдб рд╣реБрди рдХреЗрд╣реА рд╕рдордп рд▓рд┐рдиреНрдЫред рдХреЗ рд╣реБрдиреНрдЫ рдпрджрд┐ рдпреЛ рд╕реБрд░реБ рд╣реБрдиреНрдЫ, рддрд░ рдирд┐рд╖реНрдХреНрд░рд┐рдп рд╣реБрди рдЬрд╛рдиреНрдЫ? рдпреЛ рд╕рдорд╕реНрдпрд╛ рд╣реЛ, рдпрд╕рд▓рд╛рдИ рдиреНрдпреВрдирддрдо рдорд╛рдзреНрдпрдордмрд╛рдЯ рд░ рд╕рдореНрднрд╡ рднрдПрд╕рдореНрдо рд░рд╛рдореНрд░рд░реА рд╕рдорд╛рдзрд╛рди рдЧрд░реМрдВред
рдЕрд╕реНрд╡реАрдХрд░рдг: рдЕрдзрд┐рдХрд╛рдВрд╢ рд▓реЗрдЦ рдкреНрд░рдпреЛрдЧрд╛рддреНрдордХ рдврд╛рдБрдЪрд╛рдорд╛ рдкреНрд░рд╕реНрддреБрдд рдЧрд░рд┐рдПрдХреЛ рдЫ - рдХрдиреНрд╕реЛрд▓ рд╕рддреНрд░рдХреЛ рд░реЗрдХрд░реНрдбрд┐рдЩрдХреЛ рд░реВрдкрдорд╛ред рдЖрд╢рд╛ рдЫ рдХрд┐ рдпреЛ рдмреБрдЭреНрди рдзреЗрд░реИ рдЧрд╛рд╣реНрд░реЛ рд╣реБрдиреЗрдЫреИрди рд░ рдХреЛрдб рдЖрдлреИрд▓рд╛рдИ рдкрд░реНрдпрд╛рдкреНрдд рдХрд╛рдЧрдЬрд╛рдд рд╣реБрдиреЗрдЫред рд╡рд╛рддрд╛рд╡рд░рдгрдХреЛ рд▓рд╛рдЧрд┐, рдХрд▓реНрдкрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН рдХрд┐ рдпреА рдХреЗрд╡рд▓ рдХреЛрдб рд╕реНрдирд┐рдкреЗрдЯрд╣рд░реВ рд╣реЛрдЗрдирдиреН, рддрд░ "рдлрд▓рд╛рдо" рдЯреЗрд▓рд┐рдЯрд╛рдЗрдкрдмрд╛рдЯ рдХрд╛рдЧрдЬред
рдХреЛрдб рдкрдвреЗрд░ рдорд╛рддреНрд░ рдЧреБрдЧрд▓рд▓рд╛рдИ рдЧрд╛рд╣реНрд░реЛ рд╣реБрдиреЗ рд░реЛрдЪрдХ рдкреНрд░рд╡рд┐рдзрд┐рд╣рд░реВ рдкреНрд░рддреНрдпреЗрдХ рдЦрдгреНрдбрдХреЛ рд╕реБрд░реБрдорд╛ рд╡рд░реНрдгрди рдЧрд░рд┐рдПрдХреЛ рдЫред рдпрджрд┐ рдХреЗрд╣рд┐ рдЕрд╕реНрдкрд╖реНрдЯ рдЫ рднрдиреЗ, рдпрд╕рд▓рд╛рдИ рдЧреБрдЧрд▓ рдЧрд░реНрдиреБрд╣реЛрд╕реН рд░ рдпрд╕рд▓рд╛рдИ рдЬрд╛рдБрдЪ рдЧрд░реНрдиреБрд╣реЛрд╕реНред
рд╕реБрд░реБ рдЧрд░реМрдВред
$ 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
рдЙрд▓реНрдЯреЛ рдкреНрд░реЛрдХреНрд╕реА
рд╣рд╛рдореНрд░реЛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд▓рд╛рдИ рдзреНрдпрд╛рди рдирджрд┐рдИ рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрди рд╕рдХреНрд╖рдо рд╣реБрдирдХреЛ рд▓рд╛рдЧрд┐, рдпреЛ рдЖрд╡рд╢реНрдпрдХ рдЫ рдХрд┐ рдпрд╕рдХреЛ рдЕрдЧрд╛рдбрд┐ рдХреБрдиреИ рдЕрдиреНрдп рд╕рдВрд╕реНрдерд╛ рд╣реБрдиреБ рдкрд░реНрдЫ рдЬрд╕рд▓реЗ рдпрд╕рдХреЛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рд▓реБрдХрд╛рдЙрдиреЗрдЫред рдпреЛ рд╡реЗрдм рд╕рд░реНрднрд░ рд╣реБрди рд╕рдХреНрдЫ
рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд░ рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реА рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдбрдХрд░ рднрд┐рддреНрд░ рд▓рд┐рдЩреНрдХ рдЧрд░реНрди рд╕рдХрд┐рдиреНрдЫ
рдпрджрд┐ рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реА рдЕрд░реНрдХреЛ рд╣реЛрд╕реНрдЯрдорд╛ рд░рд╣рдиреНрдЫ рднрдиреЗ, рддрдкрд╛рдИрдВрд▓реЗ рдбрдХрд░ рдиреЗрдЯрд╡рд░реНрдХ рддреНрдпрд╛рдЧреНрдиреБрдкрд░реНрдиреЗрдЫ рд░ рдкреЛрд░реНрдЯ рдлрд░реНрд╡рд╛рд░реНрдб рдЧрд░реНрджреИ рд╣реЛрд╕реНрдЯ рдиреЗрдЯрд╡рд░реНрдХ рдорд╛рд░реНрдлрдд рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реАрдорд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЬрдбрд╛рди рдЧрд░реНрдиреБрдкрд░реНрдиреЗрдЫред рдЕрдиреБрдкреНрд░рдпреЛрдЧрд╣рд░реВ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░ --publish
, рдкрд╣рд┐рд▓реЛ рд╕реБрд░реБрдорд╛ рдЬрд╕реНрддреИ рд░ рдЙрд▓реНрдЯреЛ рдкреНрд░реЛрдХреНрд╕реАрдХреЛ рд╕рд╛рдеред
рд╣рд╛рдореА рдкреЛрд░реНрдЯ 80 рдорд╛ рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реА рдЪрд▓рд╛рдЙрдиреЗрдЫреМрдВ, рдХрд┐рдирднрдиреЗ рдпреЛ рд╡рд╛рд╕реНрддрд╡рдорд╛ рдмрд╛рд╣реНрдп рдиреЗрдЯрд╡рд░реНрдХ рд╕реБрдиреНрдиреИ рдкрд░реНрдиреЗ рдирд┐рдХрд╛рдп рд╣реЛред рдпрджрд┐ рдкреЛрд░реНрдЯ 80 рддрдкрд╛рдИрдВрдХреЛ рдкрд░реАрдХреНрд╖рдг рд╣реЛрд╕реНрдЯрдорд╛ рд╡реНрдпрд╕реНрдд рдЫ рднрдиреЗ, рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░ рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрдиреБрд╣реЛрд╕реН --publish 80:80
рдорд╛ --publish ANY_FREE_PORT:80
.
рд░реЛрдЪрдХ рдкреНрд░рд╡рд┐рдзрд┐рд╣рд░реВ
- "рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд▓реЗ рдмрдирд╛рдПрдХреЛ рдбрдХрд░ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрдорд╛, рддрдкрд╛рдЗрдБ рдХрдиреНрдЯреЗрдирд░рд╣рд░реВрд╕рдБрдЧ рдорд╛рддреНрд░ рдЖрдИрдкреА рдареЗрдЧрд╛рдирд╛рджреНрд╡рд╛рд░рд╛ рд╕рдЮреНрдЪрд╛рд░ рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫред рдХрдиреНрдЯреЗрдирд░рдХреЛ рдирд╛рдо рдкрдирд┐ рдпрд╕рдХреЛ IP рдареЗрдЧрд╛рдирд╛рдорд╛ рд╕рдорд╛рдзрд╛рди рдЧрд░рд┐рдПрдХреЛ рдЫ" (
рд▓реЗрдЦ "рд╕реНрдЯреНрдпрд╛рдиреНрдбрдЕрд▓реЛрди рдХрдиреНрдЯреЗрдирд░рд╣рд░реВрд╕рдБрдЧ рдиреЗрдЯрд╡рд░реНрдХрд┐рдЩ", рднрд╛рдЧ "рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛-рдкрд░рд┐рднрд╛рд╖рд┐рдд рдмреНрд░рд┐рдЬ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреБрд╣реЛрд╕реН" , рдбрдХрд░ рдХреЛрдбрдХреЛ рдмрд┐рдиреНрджреБ 5)ред
рдкреНрд░рд┐рдиреНрдЯрдЖрдЙрдЯ
$ 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
- рдпреЛ рд╕рдмреИрднрдиреНрджрд╛ рдиреНрдпреВрдирддрдо рд╡рд┐рдзрд┐ рд╣реЛ, рддрд░ рдПрдХ рдорд╛рддреНрд░ рд╣реЛрдЗрдиред рддреНрдпрд╣рд╛рдБ рдЕрдиреНрдп рдЫрдиреН:
- рдХрдиреНрдЯреЗрдирд░ рд░рдЬрд┐рд╕реНрдЯреНрд░реА (рдЙрджреНрдпреЛрдЧ рдорд╛рдирдХ)ред
- рдЕрд░реНрдХреЛ рд╣реЛрд╕реНрдЯрдмрд╛рдЯ рдбрдХрд░ рдбреЗрдорди рд╕рд░реНрднрд░рдорд╛ рдЬрдбрд╛рди рдЧрд░реНрдиреБрд╣реЛрд╕реН:
- рдкрд░рд┐рд╡реЗрд╢ рдЪрд░
DOCKER_HOST
. - рдЖрджреЗрд╢ рд░реЗрдЦрд╛ рд╡рд┐рдХрд▓реНрдк
-H
рд╡рд╛--host
рд╕рд╛рдзрдиdocker-compose
. 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
}
рдпреЛ рд╕рдореНрдкреВрд░реНрдг рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╣реЛред рд░ рддреНрдпрд╕реИрд▓реЗ
рд░рд┐рдореЛрдЯ рд╕рд░реНрднрд░рдорд╛ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╛рдЗрдЬреНрдб рд╕реНрдХреНрд░рд┐рдкреНрдЯрд╣рд░реВ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░реНрджреИ
рдпреЛ рд▓рдХреНрд╖реНрдп рд╕рд░реНрднрд░рдорд╛ рджрд╕реНрддрдХ рдЧрд░реНрдиреЗ рд╕рдордп рд╣реЛред рдпреЛ рд╕рдордп 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
рд░ рдЖрд╡рд╢реНрдпрдХ рддрд░реНрдХ рд╕рдВрдЧ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░реНрдиреБрд╣реЛрд╕реНред
Cons:
- рдПрдХрдХреЛ рд╕рдЯреНрдЯрд╛ рджреБрдИ рдХрд╛рд░реНрдпрд╣рд░реВ
- рддреНрдпрд╣рд╛рдБ рддрдкрд╛рдИрдВрд▓реЗ рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдЧрд░реНрдиреЗ рдард╛рдЙрдБ рдирд╣реБрди рд╕рдХреНрдЫ, рд╡рд╛ рддреНрдпрд╣рд╛рдБ рдкрд╣реБрдБрдЪ рдирд╣реБрди рд╕рдХреНрдЫ, рд╡рд╛ рд▓рд┐рдкрд┐ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрдирдХреЛ рд╕рдордпрдорд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реБрди рд╕рдХреНрдЫред
- рдпреЛ рдЖрдлреИрдВ рдкрдЫрд┐ рд╕рдлрд╛ рдЧрд░реНрди рд╕рд▓реНрд▓рд╛рд╣ рджрд┐рдЗрдиреНрдЫ (рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдореЗрдЯрд╛рдЙрдиреБрд╣реЛрд╕реН)ред
- рдкрд╣рд┐рд▓реЗ рдиреИ рддреАрди рдХрд╛рд░реНрдпрд╣рд░реВред
рд╕рдорд╛рдзрд╛рди реи:
- рд╕реНрдХреНрд░рд┐рдкреНрдЯрдорд╛ рдХреЗрд╡рд▓ рдкреНрд░рдХрд╛рд░реНрдп рдкрд░рд┐рднрд╛рд╖рд╛рд╣рд░реВ рд░рд╛рдЦреНрдиреБрд╣реЛрд╕реН рд░ рдХреЗрд╣рд┐ рдкрдирд┐ рдЪрд▓рд╛рдЙрдиреБрд╣реЛрд╕реН
- рд╕рд╣рдпреЛрдЧрдХреЛ рд╕рд╛рде
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!
рдЕрдм рддрдкрд╛рдИрдВ рдЦреЛрд▓реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ
рдХрд╛рдо рдкрдЫрд┐ рд╕рдлрд╛ рдЧрд░реНрди рдирдмрд┐рд░реНрд╕рдиреБрд╣реЛрд╕реН: 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