Π ΡΠ°Π·ΠΈ ΡΡΠ°ΡΠΈΡ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ , , ΠΈ ΠΠΈΠ΅ ΡΠ΅ ΠΎΡΠ³Π°Π½ΠΈΠ·ΠΈΡΠ°ΠΌΠ΅ Π±Π΅Π·ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΠΎ ΠΎΡΠΎΡΠΌΠ»Π΅Π½ΠΈΠ΅ Π½Π° ΡΠ΅Π± ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ. Π΅ ΡΠ΅Ρ Π½ΠΈΠΊΠ°, ΠΊΠΎΡΡΠΎ Π²ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π½Π΅Π·Π°Π±Π°Π²Π½ΠΎ Π΄Π° Π°ΠΊΡΡΠ°Π»ΠΈΠ·ΠΈΡΠ°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Π±Π΅Π· Π΄Π° ΠΎΡΡ Π²ΡΡΠ»ΡΡΠ΅ Π½ΠΈΡΠΎ Π΅Π΄Π½Π° Π·Π°ΡΠ²ΠΊΠ°. Π’ΠΎΠ²Π° Π΅ Π΅Π΄Π½Π° ΠΎΡ ΡΡΡΠ°ΡΠ΅Π³ΠΈΠΈΡΠ΅ Π·Π° Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ Ρ Π½ΡΠ»Π΅Π²ΠΎ Π²ΡΠ΅ΠΌΠ΅ Π½Π° ΠΏΡΠ΅ΡΡΠΎΠΉ ΠΈ Π΅ Π½Π°ΠΉ-ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΡΡΠ° Π·Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Ρ Π΅Π΄ΠΈΠ½ Π΅ΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ, Π½ΠΎ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ Π·Π° Π·Π°ΡΠ΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π²ΡΠΎΡΠΈ, Π³ΠΎΡΠΎΠ² Π·Π° ΠΈΠ·ΠΏΡΠ»Π½Π΅Π½ΠΈΠ΅ Π΅ΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ Π½Π°Π±Π»ΠΈΠ·ΠΎ.
ΠΠ° ΠΏΡΠΈΠ΅ΠΌΠ΅ΠΌ, ΡΠ΅ ΠΈΠΌΠ°ΡΠ΅ ΡΠ΅Π± ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Ρ ΠΊΠΎΠ΅ΡΠΎ Π°ΠΊΡΠΈΠ²Π½ΠΎ ΡΠ°Π±ΠΎΡΡΡ ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΠΈΠ΅Π½ΡΠΈ, ΠΈ Π½ΡΠΌΠ° Π°Π±ΡΠΎΠ»ΡΡΠ½ΠΎ Π½ΠΈΠΊΠ°ΠΊΡΠ² Π½Π°ΡΠΈΠ½ ΡΠΎ Π΄Π° Π»Π΅Π³Π½Π΅ Π·Π° Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΡΠ΅ΠΊΡΠ½Π΄ΠΈ. Π Π½Π°ΠΈΡΡΠΈΠ½Π° ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΡΡΠ½Π΅ΡΠ΅ Π°ΠΊΡΡΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°ΡΠ°, ΠΊΠΎΡΠ΅ΠΊΡΠΈΡ Π½Π° Π³ΡΠ΅ΡΠΊΠΈ ΠΈΠ»ΠΈ Π½ΠΎΠ²Π° ΡΡΡΠ°Ρ ΠΎΡΠ½Π° ΡΡΠ½ΠΊΡΠΈΡ. Π Π½ΠΎΡΠΌΠ°Π»Π½Π° ΡΠΈΡΡΠ°ΡΠΈΡ ΡΠ΅ ΡΡΡΠ±Π²Π° Π΄Π° ΡΠΏΡΠ΅ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ, Π΄Π° Π³ΠΎ ΡΠΌΠ΅Π½ΠΈΡΠ΅ ΠΈ Π΄Π° Π³ΠΎ ΡΡΠ°ΡΡΠΈΡΠ°ΡΠ΅ ΠΎΡΠ½ΠΎΠ²ΠΎ. Π ΡΠ»ΡΡΠ°ΠΉ Π½Π° docker ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΏΡΡΠ²ΠΎ Π΄Π° Π³ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΠ΅, ΡΠ»Π΅Π΄ ΡΠΎΠ²Π° Π΄Π° Π³ΠΎ ΡΠ΅ΡΡΠ°ΡΡΠΈΡΠ°ΡΠ΅, Π½ΠΎ Π²ΡΠ΅ ΠΏΠ°ΠΊ ΡΠ΅ ΠΈΠΌΠ° ΠΏΠ΅ΡΠΈΠΎΠ΄, Π² ΠΊΠΎΠΉΡΠΎ Π·Π°ΡΠ²ΠΊΠΈΡΠ΅ ΠΊΡΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ Π½ΡΠΌΠ° Π΄Π° ΡΠ΅ ΠΎΠ±ΡΠ°Π±ΠΎΡΠ²Π°Ρ, ΡΡΠΉ ΠΊΠ°ΡΠΎ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ ΠΎΡΠ½Π΅ΠΌΠ° ΠΈΠ·Π²Π΅ΡΡΠ½ΠΎ Π²ΡΠ΅ΠΌΠ΅, Π·Π° Π΄Π° ΡΠ΅ Π·Π°ΡΠ΅Π΄ΠΈ ΠΏΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΎ. ΠΠ°ΠΊΠ²ΠΎ ΡΠ΅ ΡΡΠ°Π½Π΅, Π°ΠΊΠΎ Π·Π°ΠΏΠΎΡΠ½Π΅, Π½ΠΎ ΡΠ΅ ΠΎΠΊΠ°ΠΆΠ΅, ΡΠ΅ Π½Π΅ ΡΠ°Π±ΠΎΡΠΈ? Π’ΠΎΠ²Π° Π΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡΡ, Π½Π΅ΠΊΠ° Π³ΠΎ ΡΠ΅ΡΠΈΠΌ Ρ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ ΡΡΠ΅Π΄ΡΡΠ²Π° ΠΈ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉ-Π΅Π»Π΅Π³Π°Π½ΡΠ½ΠΎ.
ΠΠ’ΠΠΠ ΠΠ’ ΠΠ’ΠΠΠΠΠ ΠΠΠ‘Π’: ΠΠΎ-Π³ΠΎΠ»ΡΠΌΠ°ΡΠ° ΡΠ°ΡΡ ΠΎΡ ΡΡΠ°ΡΠΈΡΡΠ° Π΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π΅Π½Π° Π² Π΅ΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠ°Π»Π΅Π½ ΡΠΎΡΠΌΠ°Ρ - ΠΏΠΎΠ΄ ΡΠΎΡΠΌΠ°ΡΠ° Π½Π° Π·Π°ΠΏΠΈΡ Π½Π° ΠΊΠΎΠ½Π·ΠΎΠ»Π½Π° ΡΠ΅ΡΠΈΡ. ΠΠ°Π΄ΡΠ²Π°ΠΌΠ΅ ΡΠ΅, ΡΠ΅ ΡΠΎΠ²Π° Π½ΡΠΌΠ° Π΄Π° Π΅ ΡΠ²ΡΡΠ΄Π΅ ΡΡΡΠ΄Π½ΠΎ Π·Π° ΡΠ°Π·Π±ΠΈΡΠ°Π½Π΅ ΠΈ ΠΊΠΎΠ΄ΡΡ ΡΠ΅ ΡΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΈΡΠ° Π΄ΠΎΡΡΠ°ΡΡΡΠ½ΠΎ. ΠΠ° Π°ΡΠΌΠΎΡΡΠ΅ΡΠ° ΡΠΈ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π΅ΡΠ΅, ΡΠ΅ ΡΠΎΠ²Π° Π½Π΅ ΡΠ° ΠΏΡΠΎΡΡΠΎ ΠΊΠΎΠ΄ΠΎΠ²ΠΈ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠΈ, Π° Ρ Π°ΡΡΠΈΡ ΠΎΡ βΠΆΠ΅Π»Π΅Π·Π΅Π½β ΡΠ΅Π»Π΅ΡΠ°ΠΉΠΏ.
ΠΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΈ ΡΠ΅Ρ Π½ΠΈΠΊΠΈ, ΠΊΠΎΠΈΡΠΎ ΡΠ° ΡΡΡΠ΄Π½ΠΈ Π·Π° Google ΡΠ°ΠΌΠΎ ΡΡΠ΅Π· ΡΠ΅ΡΠ΅Π½Π΅ Π½Π° ΠΊΠΎΠ΄Π°, ΡΠ° ΠΎΠΏΠΈΡΠ°Π½ΠΈ Π² Π½Π°ΡΠ°Π»ΠΎΡΠΎ Π½Π° Π²ΡΠ΅ΠΊΠΈ ΡΠ°Π·Π΄Π΅Π». ΠΠΊΠΎ Π½Π΅ΡΠΎ Π΄ΡΡΠ³ΠΎ Π½Π΅ Π΅ ΡΡΠ½ΠΎ, ΠΏΠΎΡΡΡΡΠ΅ΡΠ΅ Π³ΠΎ Π² Google ΠΈ Π³ΠΎ ΠΏΡΠΎΠ²Π΅ΡΠ΅ΡΠ΅. (Π·Π° ΡΠ°ΡΡΠΈΠ΅ ΠΎΡΠ½ΠΎΠ²ΠΎ ΡΠ°Π±ΠΎΡΠΈ, ΠΏΠΎΡΠ°Π΄ΠΈ Π΄Π΅Π±Π»ΠΎΠΊΠΈΡΠ°Π½Π΅ΡΠΎ Π½Π° ΡΠ΅Π»Π΅Π³ΡΠ°ΠΌΠ°ΡΠ°). ΠΠΊΠΎ Π½Π΅ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π½Π°ΠΌΠ΅ΡΠΈΡΠ΅ Π½ΠΈΡΠΎ Π² Google, ΠΏΠΎΠΏΠΈΡΠ°ΠΉΡΠ΅ Π² ΠΊΠΎΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠ΅. Π©Π΅ ΡΠ΅ ΡΠ°Π΄Π²Π°ΠΌ Π΄Π° Π΄ΠΎΠ±Π°Π²Ρ ΠΊΡΠΌ ΡΡΠΎΡΠ²Π΅ΡΠ½ΠΈΡ ΡΠ°Π·Π΄Π΅Π» βΠΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΈ ΡΠ΅Ρ Π½ΠΈΠΊΠΈβ.
ΠΠ° Π·Π°ΠΏΠΎΡΠ½Π΅ΠΌ.
$ mkdir blue-green-deployment && cd $_ΠΠ±ΡΠ»ΡΠΆΠ²Π°Π½Π΅
ΠΠ΅ΠΊΠ° Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ Π΅ΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠ°Π»Π΅Π½ ΡΠ΅ΡΠ²ΠΈΠ· ΠΈ Π³ΠΎ ΠΏΠΎΡΡΠ°Π²ΠΈΠΌ Π² ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ.
ΠΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΈ ΡΠ΅Ρ Π½ΠΈΠΊΠΈ
cat << EOF > file-name( + ) Π΅ Π½Π°ΡΠΈΠ½ Π·Π° ΡΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΌΠ½ΠΎΠ³ΠΎΡΠ΅Π΄ΠΎΠ² ΡΠ°ΠΉΠ» Ρ Π΅Π΄Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°. ΠΡΠΈΡΠΊΠΎ bash ΡΠ΅ΡΠ΅ ΠΎΡ/dev/stdinΡΠ»Π΅Π΄ ΡΠΎΠ·ΠΈ ΡΠ΅Π΄ ΠΈ ΠΏΡΠ΅Π΄ΠΈ ΡΠ΅Π΄Π°EOFΡΠ΅ Π±ΡΠ΄Π°Ρ Π·Π°ΠΏΠΈΡΠ°Π½ΠΈ Π²file-name.wget -qO- URL() β ΠΈΠ·Π²Π΅Π΄Π΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½Ρ, ΠΏΠΎΠ»ΡΡΠ΅Π½ ΡΡΠ΅Π· HTTP ΠΊΡΠΌ/dev/stdout(Π°Π½Π°Π»ΠΎΠ³curl URL).
ΠΡΠΈΠ½ΡΠΈΡΠ°ΠΌ
Π‘ΠΏΠ΅ΡΠΈΠ°Π»Π½ΠΎ ΠΏΡΠ΅ΠΊΡΡΠ²Π°ΠΌ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ°, Π·Π° Π΄Π° Π°ΠΊΡΠΈΠ²ΠΈΡΠ°ΠΌ ΠΏΠΎΠ΄ΡΠ΅ΡΡΠ°Π²Π°Π½Π΅ Π·Π° Python. ΠΠ°ΠΊΡΠ°Ρ ΡΠ΅ ΠΈΠΌΠ° ΠΎΡΠ΅ Π΅Π΄Π½ΠΎ ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎ ΠΏΠ°ΡΡΠ΅. ΠΠΎΠΌΠΈΡΠ»Π΅ΡΠ΅, ΡΠ΅ Π½Π° ΡΠ΅Π·ΠΈ ΠΌΠ΅ΡΡΠ° Ρ Π°ΡΡΠΈΡΡΠ° Π΅ Π±ΠΈΠ»Π° Π½Π°ΡΡΠ·Π°Π½Π°, Π·Π° Π΄Π° Π±ΡΠ΄Π΅ ΠΈΠ·ΠΏΡΠ°ΡΠ΅Π½Π° Π² ΠΎΡΠ΄Π΅Π»Π° Π·Π° ΠΏΠΎΠ΄ΡΠ΅ΡΡΠ°Π²Π°Π½Π΅ (ΠΊΡΠ΄Π΅ΡΠΎ ΠΊΠΎΠ΄ΡΡ Π΅ Π±ΠΈΠ» ΠΎΡΠ²Π΅ΡΠ΅Π½ ΡΡΡΠ½ΠΎ Ρ ΠΌΠ°ΡΠΊΠ΅ΡΠΈ), Π° ΡΠ»Π΅Π΄ ΡΠΎΠ²Π° ΡΠ΅Π·ΠΈ ΠΏΠ°ΡΡΠ΅ΡΠ° ΡΠ° Π±ΠΈΠ»ΠΈ Π·Π°Π»Π΅ΠΏΠ΅Π½ΠΈ ΠΎΠ±ΡΠ°ΡΠ½ΠΎ.
$ cat << EOF > uptimer.pyfrom 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.
ΠΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΈ ΡΠ΅Ρ Π½ΠΈΠΊΠΈ
- βΠ ΡΡΠ·Π΄Π°Π΄Π΅Π½ΠΈΡΠ΅ ΠΎΡ ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΠΈ Docker ΠΌΡΠ΅ΠΆΠΈ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΊΠΎΠΌΡΠ½ΠΈΠΊΠΈΡΠ°ΡΠ΅ Ρ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΈ Π½Π΅ ΡΠ°ΠΌΠΎ ΡΡΠ΅Π· IP Π°Π΄ΡΠ΅Ρ. ΠΠΌΠ΅ΡΠΎ Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° ΡΡΡΠΎ ΡΠ΅ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΡΠ²Π° Π² Π½Π΅Π³ΠΎΠ²ΠΈΡ 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 ΡΠΈΡΡΠ΅ΠΌΠ°) ΠΈ ΡΠ»Π΅Π΄ ΡΠΎΠ²Π° Π΄Π° Π³ΠΎ ΠΏΡΠ΅Ρ Π²ΡΡΠ»ΠΈΡΠ΅ Π½Π° ΡΡΡΠ²ΡΡΠ°.
ΠΡΠ΅Ρ Π²ΡΡΠ»ΡΠ½Π΅ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
ΠΠ° ΡΡΠΆΠ°Π»Π΅Π½ΠΈΠ΅, Π½ΡΠΌΠ° ΡΠΌΠΈΡΡΠ» Π΄Π° ΠΏΡΠ΅Ρ Π²ΡΡΠ»ΡΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΎΡ 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 - Π’ΠΎΠ²Π° Π΅ Π½Π°ΠΉ-ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΠΈΡΡΠΈΡΠ½ΠΈΡΡ ΠΌΠ΅ΡΠΎΠ΄, Π½ΠΎ Π½Π΅ ΠΈ Π΅Π΄ΠΈΠ½ΡΡΠ²Π΅Π½ΠΈΡΡ. ΠΠΌΠ° ΠΈ Π΄ΡΡΠ³ΠΈ:
- Π Π΅Π³ΠΈΡΡΡΡ Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΈ (ΠΈΠ½Π΄ΡΡΡΡΠΈΠ°Π»Π΅Π½ ΡΡΠ°Π½Π΄Π°ΡΡ).
- Π‘Π²ΡΡΠΆΠ΅ΡΠ΅ ΡΠ΅ ΠΊΡΠΌ Π΄ΠΎΠΊΠ΅Ρ Π΄Π΅ΠΌΠΎΠ½ ΡΡΡΠ²ΡΡ ΠΎΡ Π΄ΡΡΠ³ Ρ
ΠΎΡΡ:
- ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π° Π½Π° ΡΡΠ΅Π΄Π°ΡΠ°
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 ΠΈΠ·ΠΈΡΠΊΠ²Π° ΠΌΠ°Π»ΠΊΠΎ ΠΎΠ±ΡΡΠ½Π΅Π½ΠΈΠ΅:
ΠΠ°ΡΠΎ Π²ΡΡΡΠ° ΡΠΈΡΠ»ΠΎ, Π° Π½Π΅ ΠΈΠ·Π²Π΅ΠΆΠ΄Π° Π½ΠΈΠ·?
ΠΠ°ΠΊΡΠΎ ΠΈ Π΄Π° Π΅, Π² ΠΈΠ·Π²ΠΈΠΊΠ²Π°ΡΠ°ΡΠ° ΡΡΠ½ΠΊΡΠΈΡ ΠΏΡΠΎΠ²Π΅ΡΡΠ²Π°ΠΌΠ΅ ΡΠ΅Π·ΡΠ»ΡΠ°ΡΠ° ΠΎΡ Π½Π΅ΠΉΠ½Π°ΡΠ° ΡΠ°Π±ΠΎΡΠ° ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ°ΡΠ° Π½Π° ΠΈΠ·Ρ
ΠΎΠ΄Π½ΠΈΡ ΠΊΠΎΠ΄ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° bash Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-Π»Π΅ΡΠ½Π° ΠΎΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ°ΡΠ° Π½Π° Π½ΠΈΠ·. ΠΡΠ²Π΅Π½ ΡΠΎΠ²Π° ΠΏΠΎΠ»ΡΡΠ°Π²Π°Π½Π΅ΡΠΎ Π½Π° Π½ΠΈΠ· ΠΎΡ Π½Π΅Π³ΠΎ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΡΡΠΎ:
get-active-slot service && echo BLUE || echo GREEN.
ΠΠ°ΠΈΡΡΠΈΠ½Π° Π»ΠΈ ΡΡΠΈ ΡΡΠ»ΠΎΠ²ΠΈΡ ΡΠ° Π΄ΠΎΡΡΠ°ΡΡΡΠ½ΠΈ, Π·Π° Π΄Π° ΡΠ΅ ΡΠ°Π·Π³ΡΠ°Π½ΠΈΡΠ°Ρ Π²ΡΠΈΡΠΊΠΈ ΡΡΡΡΠΎΡΠ½ΠΈΡ?
ΠΠΎΡΠΈ Π΄Π²Π΅ ΡΠ΅ ΡΠ° Π΄ΠΎΡΡΠ°ΡΡΡΠ½ΠΈ, ΠΏΠΎΡΠ»Π΅Π΄Π½Π°ΡΠ° Π΅ ΡΡΠΊ ΡΠ°ΠΌΠΎ Π·Π° ΠΏΡΠ»Π½ΠΎΡΠ°, Π·Π° Π΄Π° Π½Π΅ ΠΏΠΈΡΠ° else.
Π‘Π°ΠΌΠΎ ΡΡΠ½ΠΊΡΠΈΡΡΠ°, ΠΊΠΎΡΡΠΎ Π²ΡΡΡΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈΡΠ΅ Π½Π° nginx, ΠΎΡΡΠ°Π²Π° Π½Π΅Π΄Π΅ΡΠΈΠ½ΠΈΡΠ°Π½Π°: get-nginx-config service_name deployment_slot. ΠΠΎ Π°Π½Π°Π»ΠΎΠ³ΠΈΡ Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ°ΡΠ° Π½Π° Π·Π΄ΡΠ°Π²Π΅ΡΠΎ, ΡΡΠΊ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π·Π°Π΄Π°Π΄Π΅ΡΠ΅ Π²ΡΡΠΊΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π·Π° Π²ΡΡΠΊΠ° ΡΡΠ»ΡΠ³Π°. ΠΡ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΈΡΠ΅ Π½Π΅ΡΠ° β ΡΠ°ΠΌΠΎ cat <<- EOF, ΠΊΠΎΠ΅ΡΠΎ Π²ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° ΠΏΡΠ΅ΠΌΠ°Ρ
Π½Π΅ΡΠ΅ Π²ΡΠΈΡΠΊΠΈ ΡΠ°Π·Π΄Π΅Π»ΠΈ Π² Π½Π°ΡΠ°Π»ΠΎΡΠΎ. ΠΡΡΠ½ΠΎ Π΅, ΡΠ΅ ΡΠ΅Π½Π°ΡΠ° Π½Π° Π΄ΠΎΠ±ΡΠΎΡΠΎ ΡΠΎΡΠΌΠ°ΡΠΈΡΠ°Π½Π΅ ΡΠ° ΡΠΌΠ΅ΡΠ΅Π½ΠΈ ΡΠ°Π±ΡΠ»Π°ΡΠΎΡΠΈ Ρ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»ΠΈ, ΠΊΠΎΠ΅ΡΠΎ Π΄Π½Π΅Ρ ΡΠ΅ ΡΡΠΈΡΠ° Π·Π° ΠΌΠ½ΠΎΠ³ΠΎ Π»ΠΎΡΠ° ΡΠΎΡΠΌΠ°. ΠΠΎ bash ΠΏΡΠΈΠ½ΡΠΆΠ΄Π°Π²Π° ΡΠ°Π·Π΄Π΅Π»ΠΈ ΠΈ Π±ΠΈ Π±ΠΈΠ»ΠΎ Ρ
ΡΠ±Π°Π²ΠΎ Π΄Π° ΠΈΠΌΠ° Π½ΠΎΡΠΌΠ°Π»Π½ΠΎ ΡΠΎΡΠΌΠ°ΡΠΈΡΠ°Π½Π΅ Π² ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡΡΠ° Π½Π° nginx. ΠΠ°ΠΊΡΠ°ΡΠΊΠΎ, ΡΠΌΠ΅ΡΠ²Π°Π½Π΅ΡΠΎ Π½Π° ΡΠ°Π·Π΄Π΅Π»ΠΈ Ρ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»ΠΈ ΡΡΠΊ Π½Π°ΠΈΡΡΠΈΠ½Π° ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΊΠ°ΡΠΎ Π½Π°ΠΉ-Π΄ΠΎΠ±ΡΠΎΡΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΎΡ Π½Π°ΠΉ-Π»ΠΎΡΠΎΡΠΎ. ΠΡΠΏΡΠ΅ΠΊΠΈ ΡΠΎΠ²Π°, Π½ΡΠΌΠ° Π΄Π° Π²ΠΈΠ΄ΠΈΡΠ΅ ΡΠΎΠ²Π° Π² ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ° ΠΏΠΎ-Π΄ΠΎΠ»Ρ, ΡΡΠΉ ΠΊΠ°ΡΠΎ Habr βΡΠ΅ ΡΠΏΡΠ°Π²Ρ Π΄ΠΎΠ±ΡΠ΅β, ΠΊΠ°ΡΠΎ ΠΏΡΠΎΠΌΠ΅Π½Ρ Π²ΡΠΈΡΠΊΠΈ ΡΠ°Π·Π΄Π΅Π»ΠΈ Π½Π° 4 ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»Π° ΠΈ ΠΏΡΠ°Π²ΠΈ EOF Π½Π΅Π²Π°Π»ΠΈΠ΄Π΅Π½. .
ΠΠ° Π΄Π° Π½Π΅ ΡΡΠ°Π²Π°ΠΌ Π΄Π²Π° ΠΏΡΡΠΈ, ΡΠ΅ Π²ΠΈ ΡΠ°Π·ΠΊΠ°ΠΆΠ° Π²Π΅Π΄Π½Π°Π³Π°
cat << 'EOF', ΠΊΠΎΠΈΡΠΎ ΡΠ΅ ΡΠ΅ ΡΡΠ΅ΡΠ½Π°Ρ ΠΏΠΎ-ΠΊΡΡΠ½ΠΎ. ΠΠΊΠΎ ΠΏΠΈΡΠ΅ΡΠ΅ ΠΏΡΠΎΡΡΠΎcat << EOF, ΡΠΎΠ³Π°Π²Π° Π²ΡΡΡΠ΅ Π² heredoc Π½ΠΈΠ·ΡΡ ΡΠ΅ ΠΈΠ½ΡΠ΅ΡΠΏΠΎΠ»ΠΈΡΠ° (ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈΡΠ΅ ΡΠ΅ ΡΠ°Π·ΡΠΈΡΡΠ²Π°Ρ ($foo), ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΈ ΠΏΠΎΠ²ΠΈΠΊΠ²Π°Π½ΠΈΡ ($(bar)) ΠΈ Ρ.Π½.), ΠΈ Π°ΠΊΠΎ ΠΎΠ³ΡΠ°Π΄ΠΈΡΠ΅ ΠΊΡΠ°Ρ Π½Π° Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ° Π² Π΅Π΄ΠΈΠ½ΠΈΡΠ½ΠΈ ΠΊΠ°Π²ΠΈΡΠΊΠΈ, ΡΠΎΠ³Π°Π²Π° ΠΈΠ½ΡΠ΅ΡΠΏΠΎΠ»Π°ΡΠΈΡΡΠ° Π΅ Π΄Π΅Π°ΠΊΡΠΈΠ²ΠΈΡΠ°Π½Π° ΠΈ ΡΠΈΠΌΠ²ΠΎΠ»ΡΡ$ΡΠ΅ ΠΏΠΎΠΊΠ°Π·Π²Π° ΡΠ°ΠΊΠ°, ΠΊΠ°ΠΊΡΠΎ Π΅. ΠΠ°ΠΊΠ²ΠΎ Π²ΠΈ ΡΡΡΠ±Π²Π°, Π·Π° Π΄Π° Π²ΠΌΡΠΊΠ½Π΅ΡΠ΅ ΡΠΊΡΠΈΠΏΡ Π² Π΄ΡΡΠ³ ΡΠΊΡΠΈΠΏΡ.
get-nginx-config() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
local container_name=${service}_${slot}
case $service in
# Add specific nginx configs for your services here
*) nginx-config-simple-service $container_name:8080 ;;
esac
}
nginx-config-simple-service() {
local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass"
local proxy_pass=${1?$usage_msg}
cat << EOF
server {
listen 80;
location / {
proxy_pass http://$proxy_pass;
}
}
EOF
}Π’ΠΎΠ²Π° Π΅ ΡΠ΅Π»ΠΈΡΡ ΡΡΠ΅Π½Π°ΡΠΈΠΉ. Π ΡΠ°ΠΊΠ° Π·Π° ΠΈΠ·ΡΠ΅Π³Π»ΡΠ½Π΅ ΡΡΠ΅Π· wget ΠΈΠ»ΠΈ curl.
ΠΠ·ΠΏΡΠ»Π½Π΅Π½ΠΈΠ΅ Π½Π° ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΈΠ·ΠΈΡΠ°Π½ΠΈ ΡΠΊΡΠΈΠΏΡΠΎΠ²Π΅ Π½Π° ΠΎΡΠ΄Π°Π»Π΅ΡΠ΅Π½ ΡΡΡΠ²ΡΡ
ΠΡΠ΅ΠΌΠ΅ Π΅ Π΄Π° ΠΏΠΎΡΡΠΊΠ°ΡΠ΅ Π½Π° ΡΠ΅Π»Π΅Π²ΠΈΡ ΡΡΡΠ²ΡΡ. Π’ΠΎΠ·ΠΈ ΠΏΡΡ localhost Π΄ΠΎΡΡΠ° ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ΡΡΠΎ:
$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
himura@localhost's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.ΠΠ°ΠΏΠΈΡΠ°Ρ ΠΌΠ΅ ΡΠΊΡΠΈΠΏΡ Π·Π° Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅, ΠΊΠΎΠΉΡΠΎ ΠΈΠ·ΡΠ΅Π³Π»Ρ ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»Π½ΠΎ ΠΈΠ·Π³ΡΠ°Π΄Π΅Π½ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΡΠ΅Π»Π΅Π²ΠΈΡ ΡΡΡΠ²ΡΡ ΠΈ Π±Π΅Π·ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΠΎ Π·Π°ΠΌΠ΅ΡΡΠ²Π° ΡΠ΅ΡΠ²ΠΈΠ·Π½ΠΈΡ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ, Π½ΠΎ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π³ΠΎ ΠΈΠ·ΠΏΡΠ»Π½ΠΈΠΌ Π½Π° ΠΎΡΠ΄Π°Π»Π΅ΡΠ΅Π½Π° ΠΌΠ°ΡΠΈΠ½Π°? Π‘ΠΊΡΠΈΠΏΡΡΡ ΠΈΠΌΠ° Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΈ, ΡΡΠΉ ΠΊΠ°ΡΠΎ Π΅ ΡΠ½ΠΈΠ²Π΅ΡΡΠ°Π»Π΅Π½ ΠΈ ΠΌΠΎΠΆΠ΅ Π΄Π° Π²Π½Π΅Π΄ΡΠΈ Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΡΡΠ»ΡΠ³ΠΈ Π½Π°Π²Π΅Π΄Π½ΡΠΆ ΠΏΠΎΠ΄ Π΅Π΄ΠΈΠ½ ΠΎΠ±ΡΠ°ΡΠ΅Π½ ΠΏΡΠΎΠΊΡΠΈ (ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈΡΠ΅ Π½Π° nginx, Π·Π° Π΄Π° ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ ΠΊΠΎΠΉ url ΡΠ΅ Π±ΡΠ΄Π΅ ΠΊΠΎΡ ΡΡΠ»ΡΠ³Π°). Π‘ΠΊΡΠΈΠΏΡΡΡ Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΡΡΡ ΡΠ°Π½ΡΠ²Π° Π½Π° ΡΡΡΠ²ΡΡΠ°, ΡΡΠΉ ΠΊΠ°ΡΠΎ Π² ΡΠΎΠ·ΠΈ ΡΠ»ΡΡΠ°ΠΉ Π½ΡΠΌΠ° Π΄Π° ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π³ΠΎ Π°ΠΊΡΡΠ°Π»ΠΈΠ·ΠΈΡΠ°ΠΌΠ΅ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ½ΠΎ (Ρ ΡΠ΅Π» ΠΊΠΎΡΠ΅ΠΊΡΠΈΠΈ Π½Π° Π³ΡΠ΅ΡΠΊΠΈ ΠΈ Π΄ΠΎΠ±Π°Π²ΡΠ½Π΅ Π½Π° Π½ΠΎΠ²ΠΈ ΡΡΠ»ΡΠ³ΠΈ) ΠΈ ΠΊΠ°ΡΠΎ ΡΡΠ»ΠΎ ΡΡΡΡΠΎΡΠ½ΠΈΠ΅ = Π·Π»ΠΎ.
Π Π΅ΡΠ΅Π½ΠΈΠ΅ 1: ΠΡΠ΅ ΠΎΡΠ΅ ΡΡΡ
ΡΠ°Π½ΡΠ²Π°ΠΉΡΠ΅ ΡΠΊΡΠΈΠΏΡΠ° Π½Π° ΡΡΡΠ²ΡΡΠ°, Π½ΠΎ Π³ΠΎ ΠΊΠΎΠΏΠΈΡΠ°ΠΉΡΠ΅ Π²ΡΠ΅ΠΊΠΈ ΠΏΡΡ scp. Π‘Π»Π΅Π΄ ΡΠΎΠ²Π° ΡΠ΅ ΡΠ²ΡΡΠΆΠ΅ΡΠ΅ ΡΡΠ΅Π· ssh ΠΈ ΠΈΠ·ΠΏΡΠ»Π½Π΅ΡΠ΅ ΡΠΊΡΠΈΠΏΡΠ° Ρ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΈΡΠ΅ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΈ.
ΠΏΡΠΎΡΠΈΠ²:
- ΠΠ²Π΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ Π²ΠΌΠ΅ΡΡΠΎ Π΅Π΄Π½ΠΎ
- ΠΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π΅ Π΄Π° Π½ΡΠΌΠ° ΠΌΡΡΡΠΎ, ΠΊΡΠ΄Π΅ΡΠΎ ΠΊΠΎΠΏΠΈΡΠ°ΡΠ΅, ΠΈΠ»ΠΈ ΠΌΠΎΠΆΠ΅ Π΄Π° Π½ΡΠΌΠ° Π΄ΠΎΡΡΡΠΏ Π΄ΠΎ Π½Π΅Π³ΠΎ, ΠΈΠ»ΠΈ ΡΠΊΡΠΈΠΏΡΡΡ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΠΈ ΠΏΠΎ Π²ΡΠ΅ΠΌΠ΅ Π½Π° Π·Π°ΠΌΠ΅ΡΡΠ²Π°Π½Π΅ΡΠΎ.
- ΠΡΠ΅ΠΏΠΎΡΡΡΠΈΡΠ΅Π»Π½ΠΎ Π΅ Π΄Π° ΠΏΠΎΡΠΈΡΡΠΈΡΠ΅ ΡΠ»Π΅Π΄ ΡΠ΅Π±Π΅ ΡΠΈ (ΠΈΠ·ΡΡΠΈΠΉΡΠ΅ ΡΠΊΡΠΈΠΏΡΠ°).
- ΠΠ΅ΡΠ΅ ΡΡΠΈ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ.
Π Π΅ΡΠ΅Π½ΠΈΠ΅ 2:
- ΠΠ°ΠΏΠ°Π·Π΅ΡΠ΅ ΡΠ°ΠΌΠΎ Π΄Π΅ΡΠΈΠ½ΠΈΡΠΈΠΈ Π½Π° ΡΡΠ½ΠΊΡΠΈΠΈ Π² ΡΠΊΡΠΈΠΏΡΠ° ΠΈ Π½Π΅ ΠΈΠ·ΠΏΡΠ»Π½ΡΠ²Π°ΠΉΡΠ΅ Π½ΠΈΡΠΎ
- Π‘
sedΠ΄ΠΎΠ±Π°Π²Π΅ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅ Π½Π° ΡΡΠ½ΠΊΡΠΈΡ ΠΊΡΠΌ ΠΊΡΠ°Ρ - ΠΠ·ΠΏΡΠ°ΡΠ΅ΡΠ΅ Π²ΡΠΈΡΠΊΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ Π½Π° shh ΡΡΠ΅Π· ΡΡΡΠ±Π° (
|)
ΠΏΠ»ΡΡΠΎΠ²Π΅:
- ΠΠ°ΠΈΡΡΠΈΠ½Π° Π±Π΅Π· Π³ΡΠ°ΠΆΠ΄Π°Π½ΡΡΠ²ΠΎ
- ΠΡΠΌΠ° ΡΠ°Π±Π»ΠΎΠ½Π½ΠΈ ΠΎΠ±Π΅ΠΊΡΠΈ
- Π§ΡΠ²ΡΡΠ²Π°ΠΌ ΡΠ΅ Π³ΠΎΡΠΈΠ½ΠΎ
ΠΠ΅ΠΊΠ° ΠΏΡΠΎΡΡΠΎ Π³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ Π±Π΅Π· Ansible. ΠΠ°, Π²ΡΠΈΡΠΊΠΎ Π²Π΅ΡΠ΅ Π΅ ΠΈΠ·ΠΌΠΈΡΠ»Π΅Π½ΠΎ. ΠΠ°, Π²Π΅Π»ΠΎΡΠΈΠΏΠ΅Π΄. ΠΠΈΠΆΡΠ΅ ΠΊΠΎΠ»ΠΊΠΎ ΠΏΡΠΎΡΡ, Π΅Π»Π΅Π³Π°Π½ΡΠ΅Π½ ΠΈ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΠΈΡΡΠΈΡΠ΅Π½ Π΅ Π²Π΅Π»ΠΎΡΠΈΠΏΠ΅Π΄ΡΡ:
$ cat << 'EOF' > deploy.sh#!/bin/bash
usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}
echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPTEOF
$ 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, ΡΠ°ΠΊΠ° ΡΠ΅ ΡΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΌΠ°Π»ΠΊΠ° ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° Π² Π½Π°ΡΠ°Π»ΠΎΡΠΎ (ΡΠΎΠ²Π° Π΅ Π²ΠΌΠ΅ΡΡΠΎ ):
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
