αα
αααα»αα’ααααααααααΎαααααΎ
α αΌααα·ααΆαααΆα’αααααΆααααααα·ααΈαααααΆααααα’αα·αα·ααααΆα αααΎααααα»αααααΎααΆααααΆαααααα α αΎαααΆαα·αααΆααααΆαααααΌααααααΆααααΆαααααΈαααΈαα·ααΆααΈαααααα α αΎαβα’αααβαα·αβααΆβααααΌαβααΆααβα ααβααΆαβα’αΆαααααβαααααΆααα ααΆαβαα½ααα»αβαααα αΆ α¬βαα»αααΆαβαα·αααβααααΈα αααα»αααααΆαααΆαααααααΆ α’αααααΉαααααΌααααααααααααα·ααΈ αααα½αααΆ α αΎαα αΆααααααΎαααΆαααααααα αααα»αααααΈ docker ααααΌαα’αααα’αΆα αααα½αααΆ αααααΆααααα αΆααααααΎαααΆα‘αΎααα·α ααα»ααααααΆααΉααα ααααΆααααααααααααααΎαα ααΆαααααααα·ααΈααΉααα·αααααΎαααΆααα αααααααΆααααααΆαααααα·ααΈααααΌαα αααΆααααααααααΎααααΈααααΎαααΆαααααΌαα α α»αβααΎβααΆβα αΆααβααααΎα ααα»ααααβααΆβαα·αβα’αΆα βααααΎβααΆα? αααβααΆβαααα αΆ ααΌαβαααααααΆαβααΆβαααβαααααααΆαβαα·α αα½α βαααα»α αα·αβααΎαααΆαβααΆαβαααβα’αΆα βααααΎβαα βααΆαα
ααΆαααα·αααα α’αααααααΆαα αααΎαααααΌαααΆααααα αΆαααΆαααααααα·ααααα - ααΆααααααααααΆααααααααα»αααΌαα αααααΉαααΆαααααΉααα·ααα·ααΆααααααααα α αΎααααααΌαααΉαα αααααααααααα½αααΆαααααααααΆααα αααααΆααααα·ααΆααΆα ααΌααααααααΆ ααΆααααααα·ααααααααΆααααααΆα’αααααααΌαααα»ααααααα ααα»ααααααΆαααααΆαααΈααΌαααα "ααα"α
αα
αα
ααααααα½αα±ααα
αΆααα’αΆααααααααααα·ααΆααααααΆαα Google αααααααΆααααα’αΆαααΌαααααΌαααΆααα·αααααΆαα
ααΎααααααααΈαα½ααα ααααα·αααΎααΆαα’αααΈαααααααααα·αα
αααΆααααΆαα, google ααΆα αΎααα·αα·αααααΎαααΆα
ααα
αααα αΆααααααΎαα
$ mkdir blue-green-deployment && cd $_
RΠRΞΌSΠRΠRΡSΠ
α αΌαααΎααααααΎαααααΆαααααα·ααααα α αΎαααΆααααΆαα αααα»ααα»ααα½αα
αα αα ααααααα½αα±ααα αΆααα’αΆαααααα
cat << EOF > file-name
(α―αααΆααα ααΈααα +ααΆααααααΌααααα I/O ) ααΊααΆαα·ααΈαα½αααΎααααΈαααααΎαα―αααΆααα α»αααααΆαααααααααΎααΆααααααααΆαα½αα α’αααΈααααααααΆαααα bash α’αΆαααΈ/dev/stdin
αααααΆααααΈαααααΆααααα αα·ααα»ααααααΆααEOF
ααΉαααααΌαααΆααααααααΆαα αααα»αfile-name
.wget -qO- URL
(ααααααααα ) β αααα ααα―αααΆααααααΆαααα½αααΆαααα HTTP αα/dev/stdout
(α’αΆααΆα‘αΌαcurl URL
).
ααααα»ααα
ααΆαα·ααα αααα»ααααααΆαααααααΆααααααα ααΎααααΈααΎαααΆαααααα·α αααααΆαα Python α αα α α»ααααα ααααΉαααΆαααααααα½ααααααΌα αααα αα·α αΆαααΆααΆαα ααααααααΆααααα αααααΆαααααΌαααΆαααΆααααΎααααΈααααΎαα αααααααααα·α (ααααααααΌαααααΌαααΆαααΆαααααααααααΆαα½αα§αααααααααα·α ) α αΎααααααΆαααααααααααΆαααααααααΌαααΆαααα’α·αααΆααα
$ cat << EOF > uptimer.py
from http.server import BaseHTTPRequestHandler, HTTPServer
from time import monotonic
app_version = 1
app_name = f'Uptimer v{app_version}.0'
loading_seconds = 15 - app_version * 5
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
try:
t = monotonic() - server_start
if t < loading_seconds:
self.send_error(503)
else:
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
response = f'<h2>{app_name} is running for {t:3.1f} seconds.</h2>n'
self.wfile.write(response.encode('utf-8'))
except Exception:
self.send_error(500)
else:
self.send_error(404)
httpd = HTTPServer(('', 8080), Handler)
server_start = monotonic()
print(f'{app_name} (loads in {loading_seconds} sec.) started.')
httpd.serve_forever()
EOF
$ cat << EOF > Dockerfile
FROM python:alpine
EXPOSE 8080
COPY uptimer.py app.py
CMD [ "python", "-u", "./app.py" ]
EOF
$ docker build --tag uptimer .
Sending build context to Docker daemon 39.42kB
Step 1/4 : FROM python:alpine
---> 8ecf5a48c789
Step 2/4 : EXPOSE 8080
---> Using cache
---> cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
---> a7fbb33d6b7e
Step 4/4 : CMD [ "python", "-u", "./app.py" ]
---> Running in 1906b4bd9fdf
Removing intermediate container 1906b4bd9fdf
---> c1655b996fe8
Successfully built c1655b996fe8
Successfully tagged uptimer:latest
$ docker run --rm --detach --name uptimer --publish 8080:8080 uptimer
8f88c944b8bf78974a5727070a94c76aa0b9bb2b3ecf6324b784e782614b2fbf
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8f88c944b8bf uptimer "python -u ./app.py" 3 seconds ago Up 5 seconds 0.0.0.0:8080->8080/tcp uptimer
$ docker logs uptimer
Uptimer v1.0 (loads in 10 sec.) started.
$ wget -qSO- http://localhost:8080
HTTP/1.0 503 Service Unavailable
Server: BaseHTTP/0.6 Python/3.8.3
Date: Sat, 22 Aug 2020 19:52:40 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 484
$ wget -qSO- http://localhost:8080
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.8.3
Date: Sat, 22 Aug 2020 19:52:45 GMT
Content-Type: text/html
<h2>Uptimer v1.0 is running for 15.4 seconds.</h2>
$ docker rm --force uptimer
uptimer
ααααΌααααΈαααα αααΆα
ααΎααααΈα±αααααααα·ααΈααααααΎαα’αΆα
ααααΆααααααΌαααααα·αααΆαααααΆααααααααΆαα ααΆα
αΆαααΆα
αααΆααΆαα’αααααΆααααααααααα
ααΈαα»αααΆαααααΉαααΆααααΆααααα½αααααααΆα ααΆα’αΆα
ααΆ web server
αααααα·ααΈ αα·αααααΌααααΈαααα
αααΆαα’αΆα
ααααΌαααΆαααααΆαααα
ααΆααααα»α docker αααααααΎ
ααααα·αααΎααααΌααααΈαααα
αααΆαααααα
ααΎαααΆαααΈααααααααα α’αααααΉαααααΌαααααααα
αααααααΆα docker α αΎαααααΆαααααααα·ααΈαα
ααααΌααααΈαααα
αααΆαααΆαααααααααΆααααΆαααΈα ααααααααΌαααααα
αααα αααααα·ααΈ αααΆαααΆαααααα --publish
ααΌα
αααα
αΆααααααΎαααααΌα αα·αααΌα
ααααΆααΉαααααΌααααΈαααα
αααΆααααα
ααΎαααΉαααααΎαααΆαααααΌααααΈαααα
αααΆααα
ααΎα
ααα 80 ααΈαααααααααα·αααΆα’αααααΆαααααα½αααααΆαααααααΆαααΆααααα
α ααααα·αααΎα
ααα 80 ααΆαααααααα
ααΎαααΆαααΈαααΆαααααααααα’ααα ααΌαααααΌααααΆαααΆαααααα --publish 80:80
αα
ααΎ --publish ANY_FREE_PORT:80
.
αα αα ααααααα½αα±ααα αΆααα’αΆαααααα
- "αα
αααα»ααααααΆα Docker ααααααααΎααααα’αααααααΎααααΆαα α’αααα’αΆα
ααααΆααααααααΆαα½ααα»αααΊααααα·αααααΉαααααΆααααα’αΆααααααΆα IP ααα»αααααααα ααααααα»αααΊαααααααααΌαααΆααααααααΆααα
αααα»αα’αΆααααααΆα IP ααααααΆααααα"(
α’ααααα "ααΆαααααΆαααααααΆαααΆαα½ααα»αααΆα ααααα‘αα" ααααα "ααααΎαααααΆαααααΆαααααααααα’αααααααΎααααΆαα" α ααα»α ααΈ 5 ααααΌα docker) α
ααααα»ααα
$ 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 daemon ααΈαααΆαααΈαααααααααα
- ααα·ααααΆααααααααα½α
DOCKER_HOST
. - αααααΎααααααΆααααΆααααααααΆ
-H
α¬--host
α§αααααdocker-compose
. docker context
- ααα·ααααΆααααααααα½α
αα·ααΈααΆαααααααΈααΈα (ααΆαα½αααΉααααααΎαααΈαααααΆααααΆαα’αα»ααααααααααΆ) ααααΌαααΆααα·αααααΆαααΆαααα’αα
αααα»αα’ααααα
deploy.sh
α₯α‘αΌαααα α αΌαααΎααααααΌαα’αααΈααααααααΆααααααΎαααΆαααααΎααααααα αααα»αααααααΈααααα½αα α αΌαα αΆααααααΎαααΆαα½αααΉααα»αααΆαααααα·αααααΌα α αΎααααααΆααααααΎααα»αααΆααααααααααααααΆαααααΎαα αααα»αααΆα
αα αα ααααααα½αα±ααα αΆααα’αΆαααααα
${parameter?err_msg}
- αα½αααα’αααααΆαα·αα»αααααααααα bash (akaααΆααααα½ααααΆαααΆαααααα ) ααααα·αααΎparameter
αα·αααΆααααααΆαα, αα·αααααerr_msg
α αΎαα ααααααααααΌα 1 αdocker --log-driver journald
- ααΆαααααΆαααΎα αααααα·ααΈαααααΆααΆααααααααΆ docker ααΊααΆα―αααΆαα’ααααααααααααΆαααΆαααααα·αααΆαα½αα‘αΎαα ααΆαα½αααΉααα·ααΈααΆαααααααα αααααα ααα»α’αΆα αααααααΆαααΆααααΌαααΆααααΆαααΆαααα αα ααΌα αααααααααΆααααα·ααΆααΆαααα·ααααα α αΆαααΆα αααααΌαααααΆααααααΌααααααα·ααΈαααααΆα±ααααΆααααααααΆαααα
ααααααΈαααΆααα±ααααααΎααααΆαα
deploy() {
local usage_msg="Usage: ${FUNCNAME[0]} image_name"
local image_name=${1?$usage_msg}
ensure-reverse-proxy || return 2
if get-active-slot $image_name
then
local OLD=${image_name}_BLUE
local new_slot=GREEN
else
local OLD=${image_name}_GREEN
local new_slot=BLUE
fi
local NEW=${image_name}_${new_slot}
echo "Deploying '$NEW' in place of '$OLD'..."
docker run
--detach
--restart always
--log-driver journald
--name $NEW
--network web-gateway
$image_name || return 3
echo "Container started. Checking health..."
for i in {1..20}
do
sleep 1
if get-service-status $image_name $new_slot
then
echo "New '$NEW' service seems OK. Switching heads..."
sleep 2 # Ensure service is ready
set-active-slot $image_name $new_slot || return 4
echo "'$NEW' service is live!"
sleep 2 # Ensure all requests were processed
echo "Killing '$OLD'..."
docker rm -f $OLD
docker image prune -f
echo "Deployment successful!"
return 0
fi
echo "New '$NEW' service is not ready yet. Waiting ($i)..."
done
echo "New '$NEW' service did not raise, killing it. Failed to deploy T_T"
docker rm -f $NEW
return 5
}
αααααααα·ααααααααΆαααααΎα
ensure-reverse-proxy
- ααααΌαααααΆααααΆααααΌααααΈαααα αααΆααααα»αααααΎαααΆα (ααΆααααααααααααααΆααααΆαααΆαααααααΆαααααΌα)get-active-slot service_name
- αααααααΆααΎααααααααα½αααΆαααααααααααααΆααααααΆαααααααααΆααααααα±αα (BLUE
α¬GREEN
)get-service-status service_name deployment_slot
- αααααααΆααΎααααΆααααααααα½α ααΆααααΎααααΈααααΎαααΆαααααΎα αΌαα¬α’ααset-active-slot service_name deployment_slot
- ααααΆααααααΌαααΆαααααααα ααΆαααααααα nginx αα αααα»ααα»αααααΌααααΈαααα αααΆα
αα βαααα»αβααααΆαα:
ensure-reverse-proxy() {
is-container-up reverse-proxy && return 0
echo "Deploying reverse-proxy..."
docker network create web-gateway
docker run
--detach
--restart always
--log-driver journald
--name reverse-proxy
--network web-gateway
--publish 80:80
nginx:alpine || return 1
docker exec --interactive reverse-proxy sh -c "> /etc/nginx/conf.d/default.conf"
docker exec reverse-proxy nginx -s reload
}
is-container-up() {
local container=${1?"Usage: ${FUNCNAME[0]} container_name"}
[ -n "$(docker ps -f name=${container} -q)" ]
return $?
}
get-active-slot() {
local service=${1?"Usage: ${FUNCNAME[0]} service_name"}
if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then
echo "Collision detected! Stopping ${service}_GREEN..."
docker rm -f ${service}_GREEN
return 0 # BLUE
fi
if is-container-up ${service}_BLUE && ! is-container-up ${service}_GREEN; then
return 0 # BLUE
fi
if ! is-container-up ${service}_BLUE; then
return 1 # GREEN
fi
}
get-service-status() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?usage_msg}
local slot=${2?$usage_msg}
case $service in
# Add specific healthcheck paths for your services here
*) local health_check_port_path=":8080/" ;;
esac
local health_check_address="http://${service}_${slot}${health_check_port_path}"
echo "Requesting '$health_check_address' within the 'web-gateway' docker network:"
docker run --rm --network web-gateway alpine
wget --timeout=1 --quiet --server-response $health_check_address
return $?
}
set-active-slot() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
get-nginx-config $service $slot | docker exec --interactive reverse-proxy sh -c "cat > /etc/nginx/conf.d/$service.conf"
docker exec reverse-proxy nginx -t || return 2
docker exec reverse-proxy nginx -s reload
}
αα»αααΆα get-active-slot
ααΆαααΆαααΆααααααααα·α
αα½α
α
α ααα»α’αααΈααΆαααΆααΆαααα‘ααααα α αΎααα·αα αααααα?
ααααααΆαααΆααααα αα
αααα»ααα»αααΆαα α
ααΌααααα ααΎααα·αα·αααααΎαααααααααααΆαααΆαααααααΆ α αΎαααΆααα·αα·αααααΎααααααΌαα
αααααααααΎ bash ααΊααΆααααα½αααΆαααΆααα·αα·αααααΎαααααα’ααααα ααΎαααΈααααααααΆαααα½αααΆαααααααΈααΆααΊααΆααααααΆαα:
get-active-slot service && echo BLUE || echo GREEN
.
ααΎααααααααααΈαα·αααΆαααααααααΆααααΎααααΈαααα ααααααααΆααα’ααααααα?
ααΌααααΈααααΈαααΉααααααααααΆααα αΎα α
α»ααααααααΊαα
ααΈααααααααΆααααΆααααααα ααΎααααΈαα»αα±ααααααα else
.
ααΆααααα»αααΆαααααααα‘ααααΆαααααααα
ααΆαααααααα nginx ααα»αααααααααα·αααααΌαααΆααααααα get-nginx-config service_name deployment_slot
. αααααΆαααααααααααΆααΆαα½αααΉαααΆααα·αα·ααααα»αααΆα αα
ααΈαααα’αααα’αΆα
αααααααΆααααααααΆαα½ααααααΆααααααΆααααααΆαα½αα ααα’αααΈααααα½αα±ααα
αΆααα’αΆαααααα - ααααα»ααααα cat <<- EOF
αααα’αα»ααααΆαα±ααα’ααααα»αααααΆααααΆααα’αααα
ααΎαα αα·αα αΎα αααααααααΆαααααΎαααααααααΆαααα’ααΊααΆααααΆααα
αααα»αααΆαα½αα
ααααα ααααααααααααααααααΌαααΆαα
αΆαααα»αααΆααΆααααααα’αΆαααααααΆααα ααα»ααααααααΆαααααααααΆαα bash α αΎαααΆααααα’ααααααααα»αααΆαααααΎαααααααααΆαααααααΆαα
αααα»αααΆαααααααα
ααΆαααααααα nginx α ααα»ααα ααΆαααΆαααααΆααααΆαα½αααααααΆαα
ααΈααααα·αααΆα αΆααααΌα
ααΆαααααααααΆαααααα’αααα»αα
ααααΈα’αααΈαααα’αΆααααααααα»αα αααααΆαααΆαααΆααααα α’αααααΉααα·αααΎαααΆαα
αααα»ααααααααΆαααααααα α
αΆααααΆααααΈ Habr "ααααΎααΆααΆαααα’" αααααΆαααααΆααααααΌαααααΆααααΆααα’αααα
ααΆ 4 spaces αα·αααααΎα±αα EOF αα·αααααΉαααααΌαα
ααΎααααΈαα»αα±αααααααα‘αΎαααΈααα αααα»αααΉαααααΆααα’αααααααΆααα’αααΈ
cat << 'EOF'
αααααΉααα½αααααααα ααααααααα ααααα·αααΎα’αααααααααααΆαααΆααααcat << EOF
αααααΆαααααα ααΆααααα»α heredoc ααααα’ααααααααΌαααΆα interpolated (α’αααααααΌαααΆααααααΈα ($foo
), ααΆαα α ααΆααααααααΆ ($(bar)
) ααα) α αΎαααααα·αααΎα’αααααααΆααα α»ααααα ααααα―αααΆααααα»ααααααααααα½α αααααΆαααααααΆαααααΌαααΆααα·α α αΎααα·αα·ααααααααΆ$
ααααΌαααΆααααα αΆαααΌα α α’αααΈαααα’αααααααΌαααΆαααΎααααΈαααα αΌαααααααΈααα ααΆααααα»αααααααΈαααααααααα
get-nginx-config() {
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
local container_name=${service}_${slot}
case $service in
# Add specific nginx configs for your services here
*) nginx-config-simple-service $container_name:8080 ;;
esac
}
nginx-config-simple-service() {
local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass"
local proxy_pass=${1?$usage_msg}
cat << EOF
server {
listen 80;
location / {
proxy_pass http://$proxy_pass;
}
}
EOF
}
αααααΊααΆααααααΈαααΆααααΌαα α αΎαβααΌα
αααα
ααααΎαααΆαααααααΈααααααΆαααααααααΆαααΆαααααααα ααΎαααΆαααΈαααααΈα ααααΆα
ααΆααααααα αΎαααΎααααΈαααααΎαααΆαααΈαααααααα
α αααααα localhost
ααααααααΆααα
$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
himura@localhost's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.
ααΎαααΆααααααααααααΈαααΆαααααααΆααααααΆαααααΌαααΆααααααΆααααααΎαααΆαα»ααα ααΆαααααΆαααΈαααααααα α αΎααααα½ααααααααααα»αααααΆαααααααΆααααΌα ααα»ααααααΎααΎαα’αΆα ααααα·ααααα·ααΆαααααααααΆαα ααΎαααΆαααΈαααΈα ααααΆα? ααααααΈαααΆαα’αΆαα»ααααα αααααααΆααΆαααααααααΆααα α αΎαα’αΆα ααααΎααααΆααααααΆααααααΆα αααΎααααα»αααααααα½ααααααααααΌααααΈαααα αααΆααα½α (α’αααα’αΆα ααααΎ nginx configs ααΎααααΈααααα url αα½αααΆααΆααααΆαααααα½αααΆ)α ααααααΈααα·αα’αΆα αααααΆαα»ααα ααΎαααΆαααΈαααααΆααα ααααααααα»αααααΈααα ααΎαααΉααα·αα’αΆα α’αΆαααααααΆαααααααααααααααα·ααΆααα (αααααΆααααααααααα½ααα»αααα α»α αα·αααααααααααΆααααααααΈ) α αΎαααΆααΌαα ααααΆαααΆα = α’αΆαααααα
αααααααααΆαααΈ 1: αα
αααααααΆαα»αααααααΈααα
ααΎαααΆαααΈααα ααα»ααααα
ααααααΆααΆαααααααααααΆαα scp
. αααααΆααααααααΆααααΆαααα ssh
αα·αααααα·ααααα·ααααααΈαααΆαα½αααΉαα’αΆαα»αααααα
αΆαααΆα
αα
αα»ααα·ααααα·:
- αααααααΆαααΈααααα½αα±αααα½αα
- αααα ααααΆαα·αααΆααααααααααα’αααα αααα α¬αααα ααααΆαα·αααΆααα·αααα·α αΌαααααΎααΆ α¬ααααααΈαα’αΆα ααααΌαααΆαααααα·ααααα·αα ααααααα½αα
- ααΆααααΌαααΆαααααΆαα±αααααα’αΆααααααΆααααΈαααα½αα’ααα (αα»αααααααΈα) α
- αααααααΆαααΈαα½α α αΎαα
αααααααααΆα 2:
- αα»ααααα·ααααααα»αααΆααα αααα»αααααααΈα α αΎααα·αααααΎαααΆαα’αααΈααΆααα’ααα
- αααααΆααααα½αααΈ
sed
αααααααα»αααΆαα α αα α α»ααααα αα - ααααΎααΆααΆααα’αααααααααΆαααα
shh ααΆααααααααα (
|
)
αααα»α:
- αα·αααΆααααΆααααα
- αα·αααΆαααΆαα» boilerplate
- ααΆαα’αΆαααααααααααΆαα
α αΌαααΎαααααΎααΆαααααααΆα Ansible α ααΆα α’αααΈαααααΌαααΆααααααΎααα½α α αΎαα ααΆα αααα ααβααΎαβααΆβααΎβαααΌααΌβαααβααΆαβααααααβααΆαααα ααΎαααΆα αα·αβααΌα βααα»ααααΆα
$ cat << 'EOF' > deploy.sh
#!/bin/bash
usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}
echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPT
EOF
$ chmod +x deploy.sh
$ ./deploy.sh localhost magic-porridge-pot
Connecting to localhost...
Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'
ααααααΆαααΆααααα ααΎααα·αα’αΆα
ααααΆααααΆαααΆαααΈαααΈα
ααααΆαααΆα bash αααααααααΆαααα ααΌα
ααααααΎαααΉαααααααααΆααααα½ααα·αα·αααααΌα
αα½ααα
ααΎαααααΌα (αααααΊαααα½αα±αα
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!
α₯α‘αΌααααα’αααα’αΆα
ααΎα
αα»αβααααα βαααα’αΆαβαααααβαααβααααΎβααΆαα α£
$ 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