แแ แกแขแแขแแแจแ แฉแแแ แแแงแแแแแ
แแแแฃแจแแแ, แ แแ แแฅแแแ แแแฅแแ แแแ แแแแแแแชแแ, แ แแแแแแแช แแแแ แ แแแแแแขแ แแฅแขแแฃแ แแ แแฃแจแแแแก แแ แแแกแ แแแฌแแแ แ แแแแแแแแ แฌแแแแก แแแแแแแแแแแจแ แแแกแแแฃแขแฃแ แแ แแ แแ แแก. แแ แแฅแแแ แแแแแแแแแ แแญแแ แแแแแ แแแแแแแแแแแก แแแแแฎแแแแ, แจแแชแแแแแแแก แแแแแกแฌแแ แแแ แแ แแฎแแแ แแแแแ แ แคแฃแแฅแชแแ. แแแ แแแแฃแ แกแแขแฃแแชแแแจแ แแแแแฌแแแ แแแแแแแชแแแก แจแแฉแแ แแแ, แจแแชแแแ แแ แฎแแแแฎแแ แแแฌแงแแแ. แแแแแ แแก แจแแแแฎแแแแแจแ, แแฅแแแ แจแแแแซแแแแ แฏแแ แจแแชแแแแแ แแแ, แจแแแแแ แแแแแขแแแ แแแ, แแแแ แแ แแแแแช แแฅแแแแ แแแ แแแแ, แ แแแแกแแช แแแแแชแฎแแแแก แแแแฎแแแแ แแ แแแแแฎแแแแแ, แ แแแแแ, แ แแแแ แช แฌแแกแ, แแแแแชแฎแแแแก แแแแแแแ แฉแแขแแแ แแแแก แแแ แแแแฃแแ แแ แ แกแญแแ แแแแ. แ แ แแแฎแแแแ, แแฃ แแก แแฌแงแแแ, แแแแ แแ แแฆแแแฉแแแแแ, แ แแ แฃแแแฅแแแแแ? แแก แแ แแก แแ แแแแแแ, แแแแแแแแ แแ แแแแแแแแฃแ แ แกแแจแฃแแแแแแแแ แแ แ แแช แจแแแซแแแแ แแแแแแแขแฃแ แแ.
แแแกแฃแฎแแกแแแแแแแแแก แฃแแ แงแแคแ: แกแขแแขแแแก แฃแแแขแแกแแแ แฌแแ แแแแแแแแแแ แแฅแกแแแ แแแแแขแฃแ แคแแ แแแขแจแ - แแแแกแแแแก แกแแกแแแก แฉแแแแฌแแ แแก แกแแฎแแ. แแแแแแแแแแแ, แ แแ แแก แแ แแฅแแแแ แซแแแแแ แ แแฃแแ แแแกแแแแแ แแ แแแแ แกแแแแแ แแกแแ แแฅแแแแ แแแแฃแแแแขแแ แแแฃแแ. แแขแแแกแคแแ แแกแแแแก แฌแแ แแแแแแแแแ, แ แแ แแก แแ แแ แแก แแฎแแแแ แแแแแก แคแ แแแแแแขแแแ, แแ แแแแ แฅแแฆแแแแ "แ แแแแแก" แขแแแแขแแแแแแ.
แกแแแแขแแ แแกแ แขแแฅแแแแแแ, แ แแแแแแแช แ แแฃแแแ Google-แแกแแแแก แแฎแแแแ แแแแแก แฌแแแแแฎแแแ, แแฆแฌแแ แแแแ แงแแแแแ แแแแงแแคแแแแแแก แแแกแแฌแงแแกแจแ. แแฃ แแแแแ แ แแแ แแแฃแ แแแแแแแแ, แแแแฃแแแ แแ แจแแแแแฌแแ.
แแแแแฌแงแแ
$ mkdir blue-green-deployment && cd $_
แกแแแกแแฎแฃแ แแก
แแแแแแแแแ แแฅแกแแแ แแแแแขแฃแแ แกแแ แแแกแ แแ แแแแแแแแกแแ แแแแขแแแแแ แจแ.
แกแแแแขแแ แแกแ แขแแฅแแแแ
cat << EOF > file-name
(แแฅ แแ แแก แแแแฃแแแแขแ +I/O แแแแแแแกแแแแ แแแแ ) แแ แแก แแแ แแ แแ แแ แซแแแแแแ แแ แแแแแฎแแแแแแ แคแแแแแก แจแแกแแฅแแแแแแ. แงแแแแแคแแ แ แแแจ แแแแแฎแแแ แกแแแแแ/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
แกแแแแ แแกแแแ แ แแ แแฅแกแ
แแแแกแแแแแก, แ แแ แฉแแแแแ แแแแแแแชแแแ แจแแฃแแฉแแแแแแ แจแแแชแแแแแก, แแฃแชแแแแแแแแ, แ แแ แแแก แฌแแ แแงแแก แกแฎแแ แกแฃแแแแฅแขแ, แ แแแแแแช แแแแแแแแก แแแก แฉแแแแชแแแแแแก. แแก แจแแแซแแแแ แแงแแก แแแ แกแแ แแแ แ
แแแแแแแชแแ แแ แกแแแแ แแกแแแ แ แแ แแฅแกแ แจแแแซแแแแ แแแฃแแแแจแแ แแแก แแแแแ แแก แจแแแแแ
แแฃ แกแแแแ แแกแแแ แ แแ แแฅแกแ แชแฎแแแ แแแก แกแฎแแ แฐแแกแขแแ, แแฅแแแ แแแแแฌแแแ แแแแขแแแแ แแแแแ แแก แฅแกแแแ แแ แแแแแแแจแแ แแ แแแแแแแชแแ แกแแแแ แแกแแแ แ แแ แแฅแกแแก แฐแแกแขแแก แฅแกแแแแก แแแจแแแแแแ, แแแ แขแแก แแแแแแแกแแแแ แแแแแ. apps แแแ แแแแขแ แ --publish
, แ แแแแ แช แแแ แแแแ แแแฌแงแแแแกแแก แแ แ แแแแ แช แกแแแแ แแกแแแ แ แแ แแฅแกแแก แจแแแแฎแแแแแจแ.
แฉแแแ แแแแฃแจแแแ แกแแแแ แแกแแแ แ แแ แแฅแกแ 80-แ แแแ แขแแ, แ แแแแแ แแก แแ แแก แแฃแกแขแแ แแก แแแ แ, แ แแแแแแแช แฃแแแ แแแฃแกแแแแแก แแแ แ แฅแกแแแก. แแฃ แแแ แขแ 80 แแแแแแแแฃแแแ แแฅแแแแก แกแแขแแกแขแ แฐแแกแขแแ, แจแแชแแแแแ แแแ แแแแขแ แ --publish 80:80
on --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 แกแแกแขแแแแแ) แแ แจแแแแแ แกแแ แแแ แแ แแแแแขแแแ.
แกแฃแ แแแแแแก แแแแแขแแแ
แกแแแฌแฃแฎแแ แแ, แกแฃแ แแแแแแก แแแแแแฐแแกแขแแแแ แแแแแแฐแแกแขแแ แแแแแขแแแแก แแแ แ แแ แแฅแแก, แแแแขแแ แแ แแแแงแแคแแแแแแก แจแแกแฌแแแแ แจแแกแแซแแแแแแแ แแฎแแแแ แแ แจแแแแฎแแแแแจแ, แแฃ แฎแแแ แแแฅแแ แแ แ แฐแแกแขแ 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 daemon แกแแ แแแ แก แกแฎแแ แฐแแกแขแแแแ:
- แแแ แแแแก แชแแแแแ
DOCKER_HOST
. - แแ แซแแแแแแก แฎแแแแก แแแ แแแแขแ
-H
แแ--host
แแแกแขแ แฃแแแแขแdocker-compose
. docker context
- แแแ แแแแก แชแแแแแ
แแแแ แ แแแแแแ (แแแกแ แแแแฎแแ แชแแแแแแแก แกแแแ แแแ แแแแขแแ) แแแ แแแ แแ แแก แแฆแฌแแ แแแ แกแขแแขแแแจแ
deploy.sh
แแฎแแ แแแแแ แจแแแแแ แแแแ แงแแแแแคแแ แ, แ แแช แฎแแแแ แแแแแแแแแ แแ แ แกแแ แแแขแจแ. แแแแแฌแงแแ แฃแแแฆแแแกแ แแแแแก แคแฃแแฅแชแแแ แแ แจแแแแแ แแแแแแฎแแแแ แแแกแจแ แแแแแงแแแแแฃแ แแแแแ แฉแแแแแก.
แกแแแแขแแ แแกแ แขแแฅแแแแ
${parameter?err_msg}
- แแแจแแก แแ แ-แแ แแ แฏแแแแกแแฃแ แ แจแแแแชแแ (akaแแแ แแแแขแ แแก แฉแแแแชแแแแแ ). แแฃแแ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 แแแแคแแแฃแ แแชแแแจแ. แแแแแแ, แฉแแแแ แแแแแก แจแแ แแแ แกแแแ แชแแแแแแ แแฅ แแแแแแแแแ แฉแแแก แกแแฃแแแแแกแ แแแแแกแแแแแ แฃแแ แแกแแแแ. แแฃแแชแ, แแแแก แแแ แแฎแแแแแ แฅแแแแแ แแแชแแแฃแ แคแ แแแแแแขแจแ, แ แแแแแ แฐแแแ แ โแแแ แแแ แแแแแแแก แแแแกโ แงแแแแ แฉแแแแ แแแก 4 แกแแแ แชแแแ แจแแชแแแแ แแ EOF-แแก แแ แแกแฌแแ แ แฅแชแแแแ.
แแ แฏแแ แ แแ แแ แแแแ, แแแจแแแแ แแแขแงแแ
cat << 'EOF'
, แ แแแแแกแแช แแแแแแแแแแแ แจแแแฎแแแแแแ. แแฃ แฃแแ แแแแ แฌแแcat << EOF
, แจแแแแแ แฐแแ แแแแแแก แจแแแแแ แกแขแ แแฅแแแ แแแขแแ แแแแแ แแแฃแแแ (แชแแแแแแแ แแแคแแ แแแแแฃแแแ ($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.
แฉแแแ แแแแฌแแ แแ แแแแแแแแแแก แกแแ แแแขแ, แ แแแแแแช แฉแแแแขแแแ แแแแก แฌแแแแกแฌแแ แฉแแจแแแแแฃแ แกแฃแ แแแก แกแแแแแแ แกแแ แแแ แแ แแ แจแแฃแคแแ แฎแแแแแ แชแแแแก แกแแ แแแกแแก แแแแขแแแแแ แก, แแแแ แแ แ แแแแ แจแแแแแซแแแ แแแกแ แจแแกแ แฃแแแแ แแแกแขแแแชแแฃแ แแแแฅแแแแแ? แกแแ แแแขแก แแฅแแก แแ แแฃแแแแขแแแ, แ แแแแแ แแก แฃแแแแแ แกแแแฃแ แแ แแ แจแแฃแซแแแ แแ แแแ แแฃแแแ แแแแแแแแกแแก แ แแแแแแแแ แกแแ แแแกแ แแ แแ แกแแแแ แแกแแแ แ แแ แแฅแกแแก แฅแแแจ (แจแแแแซแแแแ แแแแแแงแแแแ nginx แแแแคแแแฃแ แแชแแแแ, แ แแแ แแแแแแแแแ แ แแแแแ url แ แแแแแ แกแแ แแแกแ แแฅแแแแ). แกแแ แแแขแแก แกแแ แแแ แแ แจแแแแฎแแ แจแแฃแซแแแแแแแ, แ แแแแแ แแ แจแแแแฎแแแแแจแ แฉแแแ แแแ แจแแแซแแแแ แแแก แแแขแแแแขแฃแ แแ แแแแแฎแแแแแก (แจแแชแแแแแก แแแแแกแฌแแ แแแแก แแ แแฎแแแ แกแแ แแแกแแแแก แแแแแขแแแแก แแแแแแ) แแ แแแแแแแ, แแแแแแแ แแแแ = แแแ แแขแแแ.
แแแแแกแแแแแ 1: แแแแแช แจแแแแแฎแแ แกแแ แแแขแ แกแแ แแแ แแ, แแแแ แแ แแแแแแแแ แแ แแแ แงแแแแ แฏแแ แแ scp
. แจแแแแแ แแแแแแจแแ แแแ แแแจแแแแแแ ssh
แแ แจแแแกแ แฃแแแ แกแแ แแแขแ แกแแญแแ แ แแ แแฃแแแแขแแแแ.
Cons:
- แแ แ แฅแแแแแแ แแ แแแก แแแชแแแแ
- แจแแแซแแแแ แแ แแงแแก แแแแแแ, แกแแแแช แแแแแ แแแ, แแ แแ แแงแแก แฌแแแแแ แแแกแแ, แแ แกแแ แแแขแ แจแแกแ แฃแแแแก แฉแแแแชแแแแแแก แแ แแก.
- แแแแแแจแแฌแแแแแแ แกแแแฃแแแ แ แแแแแก แแแกแฃแคแแแแแแ (แฌแแจแแแแ แกแแ แแแขแ).
- แฃแแแ แกแแแ แแแฅแแแแแแ.
แแแแแกแแแแแ 2:
- แจแแแแแฎแแ แแฎแแแแ แคแฃแแฅแชแแแแแก แแแแแแ แขแแแแแ แกแแ แแแขแจแ แแ แกแแแ แแแ แแ แแคแแ แ แแแฃแจแแแ
- แแ แแแ
sed
แแแแแแขแ แคแฃแแฅแชแแแก แแแแแซแแฎแแแ แแแแแแแ - แแแแแแแแแ แแก แงแแแแแคแแ แ แแแ แแแแแ แจแจ-แแ แแแแแก แกแแจแฃแแแแแแ (
|
)
แแแแแแแแ:
- แญแแจแแแ แแขแแ แแแฅแแแแฅแแแแแก แแ แแฅแแแ
- แฅแแแแแก แแแแแแแแ แแแ แแ แแ แแก
- แแแแก แแแแ แแ แแ แซแแแแก
แแแแแ แแแแแแแแแ แแก 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