ãã®èšäºã§äœ¿çšããã®ã¯ã
å€ãã®ã¯ã©ã€ã¢ã³ããã¢ã¯ãã£ãã«åäœããŠãã Web ã¢ããªã±ãŒã·ã§ã³ãããããããæ°ç§éåæ¢ããããšã¯çµ¶å¯Ÿã«ããããªããšããŸãã ãããŠãã©ã€ãã©ãªã®æŽæ°ããã°ä¿®æ£ããŸãã¯æ°ããåªããæ©èœãå®éã«å±éããå¿ èŠããããŸãã éåžžã®ç¶æ³ã§ã¯ãã¢ããªã±ãŒã·ã§ã³ãåæ¢ãã眮ãæããŠãå床起åããå¿ èŠããããŸãã docker ã®å Žåãæåã«çœ®ãæããŠããåèµ·åã§ããŸãããéåžžãã¢ããªã±ãŒã·ã§ã³ã®æåã®ããŒãã«ã¯æéãããããããã¢ããªã±ãŒã·ã§ã³ãžã®ãªã¯ãšã¹ããåŠçãããªãæéãæ®ããŸãã èµ·åã¯ãããã®ã®ãåäœäžèœã«ãªã£ãå Žåã¯ã©ããªããŸãã? ãããåé¡ã§ããæå°éã®æ段ã§ãã§ããã ããšã¬ã¬ã³ãã«è§£æ±ºããŸãããã
å 責äºé : èšäºã®å€§éšåã¯å®éšçãªåœ¢åŒãã€ãŸãã³ã³ãœãŒã« ã»ãã·ã§ã³ã®èšé²ã®åœ¢åŒã§æäŸãããŠããŸãã ãããç解ããã®ãããã»ã©é£ãããªããã³ãŒãèªäœãååã«ææžåãããããšãé¡ã£ãŠããŸãã é°å²æ°ãšããŠãããããåãªãã³ãŒãã®æçã§ã¯ãªãããéã®ããã¬ã¿ã€ãã®çŽã§ãããšæ³åããŠãã ããã
ã³ãŒããèªãã ãã§ã¯ã°ã°ãã®ãé£ããèå³æ·±ããã¯ããã¯ãåã»ã¯ã·ã§ã³ã®åé ã§èª¬æãããŠããŸãã ä»ã«äžæãªç¹ãããå Žåã¯ãGoogle ã§èª¿ã¹ãŠãã ããã
å§ããŸããã
$ mkdir blue-green-deployment && cd $_
ãµãŒãã¹
å®éšçãªãµãŒãã¹ãäœæããŠã³ã³ããã«é 眮ããŠã¿ãŸãããã
èå³æ·±ããã¯ããã¯
cat << EOF > file-name
(ãã¢ããã¥ã¡ã³ã +I / Oãªãã€ã¬ã¯ã ) ã¯ãXNUMX ã€ã®ã³ãã³ãã§è€æ°è¡ã®ãã¡ã€ã«ãäœæããæ¹æ³ã§ãã 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ãµãŒããŒã§ããå¯èœæ§ããããŸã
ã¢ããªã±ãŒã·ã§ã³ãšãªããŒã¹ ãããã·ã¯ã次ã䜿çšã㊠Docker å
ã§ãªã³ã¯ã§ããŸãã
ãªããŒã¹ ãããã·ãå¥ã®ãã¹ãäžã«ååšããå Žåã¯ãDocker ãããã¯ãŒã¯ãæŸæ£ãããã¹ã ãããã¯ãŒã¯çµç±ã§ã¢ããªã±ãŒã·ã§ã³ããªããŒã¹ ãããã·ã«æ¥ç¶ããããŒãã転éããå¿
èŠããããŸãã ã¢ããªã±ãŒã·ã§ã³ ãã©ã¡ãŒã¿ --publish
ãæåã®èµ·åæãšãªããŒã¹ ãããã·ã®å Žåãšåæ§ã§ãã
ãªããŒã¹ ãããã·ãããŒã 80 ã§å®è¡ããŸããããã¯ããŸãã«å€éšãããã¯ãŒã¯ããªãã¹ã³ããå¿
èŠããããšã³ãã£ãã£ã§ããããã§ãã ãã¹ã ãã¹ãã§ããŒã 80 ã䜿çšäžã®å Žåã¯ããã©ã¡ãŒã¿ãå€æŽããŸãã --publish 80:80
Ма --publish ANY_FREE_PORT:80
.
èå³æ·±ããã¯ããã¯
- ããŠãŒã¶ãŒãäœæãã Docker ãããã¯ãŒã¯ã§ã¯ãIP ã¢ãã¬ã¹ã ãã§ãªãã³ã³ãããšéä¿¡ã§ããŸãã ã³ã³ããåããã® IP ã¢ãã¬ã¹ã«è§£æ±ºãããŸãã" (
èšäºãã¹ã¿ã³ãã¢ãã³ ã³ã³ããã䜿çšãããããã¯ãŒã¯ããããŒãããŠãŒã¶ãŒå®çŸ©ã®ããªããž ãããã¯ãŒã¯ã®äœ¿çšã ãdocker ã³ãŒãã®ãã€ã³ã 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>
ã·ãŒã ã¬ã¹ãªå±é
ã¢ããªã±ãŒã·ã§ã³ã®æ°ããããŒãžã§ã³ (èµ·åããã©ãŒãã³ã¹ã XNUMX ååäž) ãããŒã«ã¢ãŠãããã·ãŒã ã¬ã¹ã«å±éããŠã¿ãŸãããã
èå³æ·±ããã¯ããã¯
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 ãåãã XNUMX ã€ã®ãã¹ããããå Žåã«ã®ã¿æ€èšã§ããŸãã å°ãªããšã次ã®ããã«ãªããŸãã
$ 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 ããŒã¢ã³ ãµãŒããŒã«æ¥ç¶ããŸãã
- ç°å¢å€æ°
DOCKER_HOST
. - ã³ãã³ãã©ã€ã³ãªãã·ã§ã³
-H
ãŸãã¯--host
èšåšdocker-compose
. docker context
- ç°å¢å€æ°
XNUMX çªç®ã®æ¹æ³ (å®è£
ã«ã¯ XNUMX ã€ã®ãªãã·ã§ã³ããããŸã) ã«ã€ããŠã¯ãèšäºã§è©³ãã説æãããŠããŸãã
deploy.sh
次ã«ãæåã§è¡ã£ããã¹ãŠã®äœæ¥ã XNUMX ã€ã®ã¹ã¯ãªããã«ãŸãšããŠã¿ãŸãããã ãããã¬ãã«ã®é¢æ°ããå§ããŠã次ã«ãã®äžã§äœ¿çšãããŠããä»ã®é¢æ°ãèŠãŠã¿ãŸãããã
èå³æ·±ããã¯ããã¯
${parameter?err_msg}
- ããã·ã¥éæ³ã®åªæã® XNUMX 〠(å¥å)ãã©ã¡ãŒã¿çœ®æ ïŒã ãã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
.
ãã¹ãŠã®ç¶æ ãåºå¥ããã«ã¯ãæ¬åœã« XNUMX ã€ã®æ¡ä»¶ã§ååãªã®ã§ãããã?
XNUMX ã€ã§ãååã§ããæåŸã® XNUMX ã€ã¯å®å
šãæãããã ãã«ããã«ãããŸãã else
.
nginx æ§æãè¿ãé¢æ°ã®ã¿ãæªå®çŸ©ã®ãŸãŸã§ãã get-nginx-config service_name deployment_slot
ã ãã«ã¹ãã§ãã¯ãšåæ§ã«ãããã§ã¯ä»»æã®ãµãŒãã¹ã®ä»»æã®æ§æãèšå®ã§ããŸãã èå³æ·±ããã®ã®ãã¡ - ã ã cat <<- EOF
ã䜿çšãããšãæåã®ãã¹ãŠã®ã¿ããåé€ã§ããŸãã 確ãã«ãé©åãªæžåŒèšå®ã®ä»£åãšããŠã¿ããšã¹ããŒã¹ãæ··åšããããšã«ãªããŸãããããã¯ä»æ¥ã§ã¯éåžžã«æªã圢åŒãšèããããŠããŸãã ãã ããbash ã§ã¯ã¿ãã匷å¶ããããããnginx èšå®ã§éåžžã®æžåŒèšå®ãè¡ãããšãã§ããŸãã ã€ãŸããããã§ã¿ããšã¹ããŒã¹ãçµã¿åãããããšããææªã®è§£æ±ºçã®ãã¡ã®æè¯ã®è§£æ±ºçã®ããã«æããŸãã ãã ãã以äžã®ã¹ããããã§ã¯ããã¯èŠãããŸãããããã¯ãHabr ããã¹ãŠã®ã¿ãã 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.
äºåã«æ§ç¯ãããã€ã¡ãŒãžãã¿ãŒã²ãã ãµãŒããŒã«ããŠã³ããŒããããµãŒãã¹ ã³ã³ãããã·ãŒã ã¬ã¹ã«çœ®ãæãããããã€ã¡ã³ã ã¹ã¯ãªãããäœæããŸããããããããªã¢ãŒã ãã·ã³ã§å®è¡ããã«ã¯ã©ãããã°ããã§ãããã? ãã®ã¹ã¯ãªããã¯ãŠãããŒãµã«ã§ãããXNUMX ã€ã®ãªããŒã¹ ãããã·ã®äžã§äžåºŠã«è€æ°ã®ãµãŒãã¹ããããã€ã§ãããããã¹ã¯ãªããã«ã¯åŒæ°ããããŸã (nginx æ§æã䜿çšããŠãã©ã® URL ãã©ã®ãµãŒãã¹ã«ãªããã決å®ã§ããŸã)ã ã¹ã¯ãªããããµãŒããŒã«ä¿åããããšã¯ã§ããŸããããã®å Žåã(ãã°ä¿®æ£ãæ°ãããµãŒãã¹ã®è¿œå ã®ç®çã§) ã¹ã¯ãªãããèªåçã«æŽæ°ããããšãã§ãããäžè¬ã«ç¶æ = æªãšãªãããã§ãã
解決ç 1: ã¹ã¯ãªããã¯åŒãç¶ããµãŒããŒã«ä¿åããŸãããæ¯åã³ããŒããŸãã scp
ã 次ã«ãçµç±ã§æ¥ç¶ããŸã ssh
å¿
èŠãªåŒæ°ãæå®ããŠã¹ã¯ãªãããå®è¡ããŸãã
çæïŒ
- XNUMX ã€ã®ã¢ã¯ã·ã§ã³ã§ã¯ãªã XNUMX ã€ã®ã¢ã¯ã·ã§ã³
- ã³ããŒããå Žæããªãã£ãããã¢ã¯ã»ã¹ã§ããªãã£ããã眮ææã«ã¹ã¯ãªãããå®è¡ããããããå¯èœæ§ããããŸãã
- èªåã§ã¯ãªãŒã³ã¢ãããã (ã¹ã¯ãªãããåé€ãã) ããšããå§ãããŸãã
- ãã§ã«ã¢ã¯ã·ã§ã³ãXNUMXã€ãããŸãã
解決ç2ïŒè§£æ±ºçXNUMXïŒ
- ã¹ã¯ãªããå ã«é¢æ°å®çŸ©ã®ã¿ãä¿æããäœãå®è¡ããªã
- ãšãšãââã«
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'
ãã ãããªã¢ãŒã ãã¹ãã«é©å㪠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
åºæïŒ habr.com