рдпрд╛ рд▓реЗрдЦрд╛рдд рдЖрдореНрд╣реА рд╡рд╛рдкрд░рддреЛ
рд╕рдордЬрд╛ рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдПрдХ рд╡реЗрдм рдНрдкреНрд▓рд┐рдХреЗрд╢рди рдЖрд╣реЗ рдЬреНрдпрд╛рд╕рд╣ рдмрд░реЗрдЪ рдХреНрд▓рд╛рдпрдВрдЯ рд╕рдХреНрд░рд┐рдпрдкрдгреЗ рдХрд╛рдо рдХрд░рдд рдЖрд╣реЗрдд рдЖрдгрд┐ рдХрд╛рд╣реА рд╕реЗрдХрдВрджрд╛рдВрд╕рд╛рдареА рдЭреЛрдкрдгреНрдпрд╛рдЪрд╛ рдХреЛрдгрддрд╛рд╣реА рдорд╛рд░реНрдЧ рдирд╛рд╣реА. рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рдЦрд░реЛрдЦрд░ рд▓рд╛рдпрдмреНрд░рд░реА рдЕрдкрдбреЗрдЯ, рдмрдЧ рдлрд┐рдХреНрд╕ рдХрд┐рдВрд╡рд╛ рдирд╡реАрди рдЫрд╛рди рд╡реИрд╢рд┐рд╖реНрдЯреНрдп рд░реЛрд▓ рдЖрдЙрдЯ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ. рд╕рд╛рдорд╛рдиреНрдп рдкрд░рд┐рд╕реНрдерд┐рддреАрдд, рддреБрдореНрд╣рд╛рд▓рд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдерд╛рдВрдмрд╡рд╛рд╡рд╛ рд▓рд╛рдЧреЗрд▓, рддреЛ рдкреБрдирд░реНрд╕реНрдерд┐рдд рдХрд░рд╛ рдЖрдгрд┐ рддреЛ рдкреБрдиреНрд╣рд╛ рд╕реБрд░реВ рдХрд░рд╛. рдбреЙрдХрд░рдЪреНрдпрд╛ рдмрд╛рдмрддреАрдд, рддреБрдореНрд╣реА рдкреНрд░рдердо рддреЗ рдкреБрдирд░реНрд╕реНрдерд┐рдд рдХрд░реВ рд╢рдХрддрд╛, рдирдВрддрд░ рддреЗ рд░реАрд╕реНрдЯрд╛рд░реНрдЯ рдХрд░реВ рд╢рдХрддрд╛, рдкрд░рдВрддреБ рддрд░реАрд╣реА рдПрдХ рдХрд╛рд▓рд╛рд╡рдзреА рдЕрд╕реЗрд▓ рдЬреНрдпрд╛рдордзреНрдпреЗ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд╛рдЪреНрдпрд╛ рд╡рд┐рдирдВрддреНрдпрд╛рдВрд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗрд▓реА рдЬрд╛рдгрд╛рд░ рдирд╛рд╣реА, рдХрд╛рд░рдг рд╕рд╣рд╕рд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд╛рд╕ рдкреНрд░рд╛рд░рдВрднреА рд▓реЛрдб рд╣реЛрдгреНрдпрд╛рд╕ рдереЛрдбрд╛ рд╡реЗрд│ рд▓рд╛рдЧрддреЛ. рдЬрд░ рддреЗ рд╕реБрд░реВ рдЭрд╛рд▓реЗ, рдкрд░рдВрддреБ рдЕрдХреНрд╖рдо рдЭрд╛рд▓реЗ рддрд░? рд╣реА рд╕рдорд╕реНрдпрд╛ рдЖрд╣реЗ, рддреА рдХрдореАрддрдХрдореА рдорд╛рдзреНрдпрдорд╛рдВрдиреА рдЖрдгрд┐ рд╢рдХреНрдп рддрд┐рддрдХреНрдпрд╛ рд╕реБрдВрджрд░рдкрдгреЗ рд╕реЛрдбрд╡реВрдпрд╛.
рдЕрд╕реНрд╡реАрдХрд░рдг: рдмрд╣реБрддреЗрдХ рд▓реЗрдЦ рдкреНрд░рд╛рдпреЛрдЧрд┐рдХ рд╕реНрд╡рд░реВрдкрд╛рдд рд╕рд╛рджрд░ рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд - рдХрдиреНрд╕реЛрд▓ рд╕рддреНрд░рд╛рдЪреНрдпрд╛ рд░реЗрдХреЙрд░реНрдбрд┐рдВрдЧрдЪреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд. рдЖрд╢рд╛ рдЖрд╣реЗ рдХреА рд╣реЗ рд╕рдордЬрдгреЗ рдлрд╛рд░ рдХрдареАрдг рд╣реЛрдгрд╛рд░ рдирд╛рд╣реА рдЖрдгрд┐ рдХреЛрдб рд╕реНрд╡рддрдГрдЪ рдкреБрд░реЗрд╕реЗ рджрд╕реНрддрдРрд╡рдЬреАрдХрд░рдг рдХрд░реЗрд▓. рд╡рд╛рддрд╛рд╡рд░рдгрд╛рд╕рд╛рдареА, рдХрд▓реНрдкрдирд╛ рдХрд░рд╛ рдХреА рд╣реЗ рдлрдХреНрдд рдХреЛрдб рд╕реНрдирд┐рдкреЗрдЯ рдирд╛рд╣реАрдд рддрд░ тАЬрд▓реЛрд╣тАЭ рдЯреЗрд▓рд┐рдЯрд╛рдЗрдкрдордзреАрд▓ рдХрд╛рдЧрдж рдЖрд╣реЗрдд.
рдлрдХреНрдд рдХреЛрдб рд╡рд╛рдЪреВрди 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
рдЙрд▓рдЯ рдкреНрд░реЙрдХреНрд╕реА
рдЖрдордЪрд╛ рдЕрд░реНрдЬ рдХреЛрдгрд╛рдЪреНрдпрд╛рд╣реА рд▓рдХреНрд╖рд╛рдд рди рдпреЗрддрд╛ рдмрджрд▓рдгреНрдпрд╛рдд рд╕рдХреНрд╖рдо рд╣реЛрдгреНрдпрд╛рд╕рд╛рдареА, рддреНрдпрд╛рдЪреНрдпрд╛рд╕рдореЛрд░ рджреБрд╕рд░реА рдПрдЦрд╛рджреА рд╕рдВрд╕реНрдерд╛ рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдЬреА рддреНрдпрд╛рдЪреА рдмрджрд▓реА рд▓рдкрд╡реЗрд▓. рд╣реЗ рд╡реЗрдм рд╕рд░реНрд╡реНрд╣рд░ рдЕрд╕реВ рд╢рдХрддреЗ
рдЕреЕрдкреНрд▓рд┐рдХреЗрд╢рди рдЖрдгрд┐ рд░рд┐рд╡реНрд╣рд░реНрд╕ рдкреНрд░реЙрдХреНрд╕реА рдбреЙрдХрд░рдЪреНрдпрд╛ рдЖрдд рдЬреЛрдбрд▓реЗ рдЬрд╛рдК рд╢рдХрддрд╛рдд
рд░рд┐рд╡реНрд╣рд░реНрд╕ рдкреНрд░реЙрдХреНрд╕реА рджреБрд╕рд░реНтАНрдпрд╛ рд╣реЛрд╕реНрдЯрд╡рд░ рд░рд╛рд╣рдд рдЕрд╕рд▓реНрдпрд╛рд╕, рддреБрдореНрд╣рд╛рд▓рд╛ рдбреЙрдХрд░ рдиреЗрдЯрд╡рд░реНрдХ рд╕реЛрдбреВрди рджреНрдпрд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓ рдЖрдгрд┐ рдкреЛрд░реНрдЯ рдлреЙрд░рд╡рд░реНрдб рдХрд░реВрди рд╣реЛрд╕реНрдЯ рдиреЗрдЯрд╡рд░реНрдХрджреНрд╡рд╛рд░реЗ рд░рд┐рд╡реНрд╣рд░реНрд╕ рдкреНрд░реЙрдХреНрд╕реАрд╢реА рдНрдкреНрд▓рд┐рдХреЗрд╢рди рдХрдиреЗрдХреНрдЯ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓. рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдкреЕрд░рд╛рдореАрдЯрд░ --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
, рдЬреЗ рддреБрдореНрд╣рд╛рд▓рд╛ рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рд╕рд░реНрд╡ рдЯреЕрдм рдХрд╛рдврдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ. рдЦрд░реЗ рдЖрд╣реЗ, рдЪрд╛рдВрдЧрд▓реНрдпрд╛ рд╕реНрд╡рд░реВрдкрдирд╛рдЪреА рдХрд┐рдВрдордд рд╕реНрдкреЗрд╕рд╕рд╣ рдорд┐рд╢реНрд░рд┐рдд рдЯреЕрдм рдЖрд╣реЗ, рдЬреА рдЖрдЬ рдЦреВрдк рд╡рд╛рдИрдЯ рдлреЙрд░реНрдо рдорд╛рдирд▓реА рдЬрд╛рддреЗ. рдкрдг рдмреЕрд╢ рдЯреЕрдмрд▓рд╛ рд╕рдХреНрддреА рдХрд░рддреЗ, рдЖрдгрд┐ 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
рдЖрдгрд┐ рдЖрд╡рд╢реНрдпрдХ рдпреБрдХреНрддрд┐рд╡рд╛рджрд╛рдВрд╕рд╣ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рд╛.
рдмрд╛рдзрдХ
- рдПрдХрд╛ рдРрд╡рдЬреА рджреЛрди рдХреНрд░рд┐рдпрд╛
- рддреБрдореНрд╣реА рдХреЙрдкреА рдХрд░рд╛рд▓ рдЕрд╢реА рдЬрд╛рдЧрд╛ рдирд╕реЗрд▓, рдХрд┐рдВрд╡рд╛ рддрд┐рдереЗ рдкреНрд░рд╡реЗрд╢ рдирд╕реЗрд▓, рдХрд┐рдВрд╡рд╛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрдирд╛рдЪреНрдпрд╛ рд╡реЗрд│реА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЕрдВрдорд▓рд╛рдд рдЖрдгрд▓реА рдЬрд╛рдИрд▓.
- рд╕реНрд╡рдд: рдирдВрддрд░ рд╕рд╛рдл рдХрд░рдгреНрдпрд╛рдЪрд╛ рд╕рд▓реНрд▓рд╛ рджрд┐рд▓рд╛ рдЬрд╛рддреЛ (рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╣рдЯрд╡рд╛).
- рдЖрдзреАрдЪ рддреАрди рдХреНрд░рд┐рдпрд╛.
рдЙрдкрд╛рдп реи:
- рд╕реНрдХреНрд░рд┐рдкреНрдЯрдордзреНрдпреЗ рдлрдХреНрдд рдлрдВрдХреНрд╢рди рд╡реНрдпрд╛рдЦреНрдпрд╛ рдареЗрд╡рд╛ рдЖрдгрд┐ рдХрд╛рд╣реАрд╣реА рдЪрд╛рд▓рд╡реВ рдирдХрд╛
- рдЪреНрдпрд╛ рдорджрддреАрдиреЗ
sed
рд╢реЗрд╡рдЯреА рдлрдВрдХреНрд╢рди рдХреЙрд▓ рдЬреЛрдбрд╛ - рд╣реЗ рд╕рд░реНрд╡ рдереЗрдЯ рдкрд╛рдИрдкрджреНрд╡рд╛рд░реЗ shh рд╡рд░ рдкрд╛рдард╡рд╛ (
|
)
рд╕рд╛рдзрдХ:
- рдЦрд░рдЪ рд╕реНрдЯреЗрдЯрд▓реЗрд╕
- рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдирд╛рд╣реА
- рдорд╕реНрдд рд╡рд╛рдЯрддрдВрдп
рдлрдХреНрдд Ansible рд╢рд┐рд╡рд╛рдп рдХрд░реВ. рд╣реЛрдп, рд╕рд░реНрд╡ рдХрд╛рд╣реА рдЖрдзреАрдЪ рд╢реЛрдзрд▓реЗ рдЧреЗрд▓реЗ рдЖрд╣реЗ. рд╣реЛрдп, рд╕рд╛рдпрдХрд▓. рдмрд╛рдЗрдХ рдХрд┐рддреА рд╕реЛрдкреА, рдореЛрд╣рдХ рдЖрдгрд┐ рдХрд┐рдорд╛рди рдЖрд╣реЗ рддреЗ рдкрд╣рд╛:
$ cat << 'EOF' > deploy.sh
#!/bin/bash
usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}
echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPT
EOF
$ chmod +x deploy.sh
$ ./deploy.sh localhost magic-porridge-pot
Connecting to localhost...
Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'
рддрдерд╛рдкрд┐, рдЖрдореНрд╣реА рдЦрд╛рддреНрд░реА рдмрд╛рд│рдЧреВ рд╢рдХрдд рдирд╛рд╣реА рдХреА рд░рд┐рдореЛрдЯ рд╣реЛрд╕реНрдЯрдХрдбреЗ рдкреБрд░реЗрд╕рд╛ рдмреЕрд╢ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдЖрдореНрд╣реА рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рдПрдХ рд▓рд╣рд╛рди рдЪреЗрдХ рдЬреЛрдбреВ (рд╣реЗ рддреНрдпрд╛рдРрд╡рдЬреА рдЖрд╣реЗ
if [ "$SHELL" != "/bin/bash" ]
then
echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."
exit 1
fi
рдЖрдгрд┐ рдЖрддрд╛ рддреЗ рдЦрд░реЗ рдЖрд╣реЗ:
$ docker exec reverse-proxy rm /etc/nginx/conf.d/default.conf
$ wget -qO deploy.sh https://git.io/JUURc
$ chmod +x deploy.sh
$ ./deploy.sh localhost uptimer
Sending gzipped image 'uptimer' to 'localhost' via ssh...
Loaded image: uptimer:latest
Connecting to 'localhost' via ssh to seamlessly deploy 'uptimer'...
Deploying 'uptimer_GREEN' in place of 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Container started. Checking health...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (1)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (2)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.8.3
Date: Sat, 22 Aug 2020 20:15:50 GMT
Content-Type: text/html
New 'uptimer_GREEN' service seems OK. Switching heads...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2020/08/22 20:15:54 [notice] 97#97: signal process started
The 'uptimer_GREEN' service is live!
Killing 'uptimer_BLUE'...
uptimer_BLUE
Total reclaimed space: 0B
Deployment successful!
рдЖрддрд╛ рддреБрдореНрд╣реА рдЙрдШрдбреВ рд╢рдХрддрд╛
рдХрд╛рдорд╛рдирдВрддрд░ рд╕рд╛рдлрд╕рдлрд╛рдИ рдХрд░рд╛рдпрд▓рд╛ рд╡рд┐рд╕рд░реВ рдирдХрд╛ :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