En este artículo, vamos a , , y organizar un despliegue sin interrupciones de una aplicación web. es una técnica que permite actualizar una aplicación de inmediato sin rechazar ninguna solicitud. Es una de las estrategias de implementación sin tiempo de inactividad y es más adecuada para aplicaciones con una instancia única, pero que tienen la posibilidad de cargar una segunda instancia preparada.
Supongamos que tienes una aplicación web que está siendo utilizada activamente por muchos clientes y no puede parar incluso por unos segundos. Y necesitas implementar una actualización de biblioteca, corregir un error o una nueva característica emocionante. En una situación normal, tendrías que detener la aplicación, reemplazarla y volver a iniciarla. En el caso de Docker, puedes reemplazar primero y luego reiniciar, pero aún así habrá un período en el que las solicitudes a la aplicación no serán procesadas, ya que generalmente se requiere algo de tiempo para la carga inicial de la aplicación. ¿Y si arranca pero resulta no estar operativo? Así que, vamos a resolver este problema con la menor cantidad de recursos y de la manera más elegante posible.
ADVERTENCIA: La mayor parte del artículo se presenta en un formato experimental: como un registro de sesión de consola. Espero que no sea demasiado difícil de seguir, y que este código se documente suficientemente por sí mismo. Para añadir atmósfera, imagina que no son solo fragmentos de código, sino papel de un telégrafo "de hierro".
Técnicas interesantes que son difíciles de encontrar simplemente leyendo el código están descritas al inicio de cada sección. Si algo no está claro, busca y verifica en (afortunadamente, está funcionando de nuevo gracias al desbloqueo de Telegram). Lo que no se puede buscar, pregúntalo en los comentarios. Estaré encantado de añadir la sección correspondiente "Técnicas interesantes".
Comencemos.
$ mkdir blue-green-deployment && cd $_El servicio
Crearemos un servicio de prueba y lo colocaremos en un contenedor.
Técnicas interesantes
cat < file-name( + ) es un método para crear un archivo de varias líneas con un solo comando. Todo lo que bash lea de/dev/stdindespués de esta línea y hasta la líneaEOFse escribirá enfile-name.wget -qO- URL() imprime el documento recibido por HTTP en/dev/stdout(similar acurl URL).
Impresión
Estoy rompiendo intencionadamente el snippet para incluir el resaltado para Python. Al final habrá otro fragmento similar. Consideren que en estos lugares el papel fue cortado para ser enviado al departamento de resaltado (donde se coloreaba el código manualmente con marcadores), y luego estas piezas se pegaban de nuevo.
$ cat < uptimer.pydesde http.server importar BaseHTTPRequestHandler, HTTPServer
desde time importar monotonic
version_app = 1
nombre_app = f'Uptimer v{version_app}.0'
segundos_cargando = 15 - version_app * 5
clase Handler(BaseHTTPRequestHandler):
def do_GET(self):
si self.path == '':
try:
t = monotonic() - inicio_servidor
si t < segundos_cargando:
self.send_error(503)
más:
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
respuesta = f'<h2>{nombre_app} está corriendo durante {t:3.1f} segundos.</h2>n'
self.wfile.write(respuesta.encode('utf-8'))
except Exception:
self.send_error(500)
más:
self.send_error(404)
httpd = HTTPServer(('', 8080), Handler)
inicio_servidor = monotonic()
print(f'{nombre_app} (carga en {segundos_cargando} seg.) iniciado.')
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 (carga en 10 seg.) iniciado.
$ wget -qSO- http://localhost:8080
HTTP/1.0 503 Servicio No Disponible
Server: BaseHTTP/0.6 Python/3.8.3
Fecha: Sáb, 22 Ago 2020 19:52:40 GMT
Conexión: cerrar
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
Fecha: Sáb, 22 Ago 2020 19:52:45 GMT
Content-Type: text/html
<h2>Uptimer v1.0 está en ejecución desde hace 15.4 segundos.</h2>
$ docker rm --force uptimer
uptimerProxy inverso
Para que nuestra aplicación tenga la capacidad de cambiar de forma imperceptible, es necesario que haya alguna entidad delante de ella que oculta su reemplazo. Esto puede ser un servidor web en . El proxy inverso se coloca entre el cliente y la aplicación. Toma solicitudes de los clientes y las redirige a la aplicación, mientras que las respuestas de la aplicación se envían a los clientes.
La aplicación y el proxy inverso se pueden vincular dentro de Docker utilizando . De esta manera, ni siquiera es necesario abrir un puerto para el contenedor con la aplicación en el sistema anfitrión, lo que permite aislar al máximo la aplicación de amenazas externas.
Si el proxy inverso va a estar en otro host, se deberá renunciar a docker network y vincular la aplicación al proxy inverso a través de la red del host, abriendo el puerto aplicación parámetro --publish, como en el primer lanzamiento y como en el proxy inverso.
El proxy inverso se ejecutará en el puerto 80, ya que es la entidad que debería escuchar el exterior. Si el puerto 80 está ocupado en su host de prueba, cambie el parámetro --publish 80:80 en --publish ANY_FREE_PORT:80.
Técnicas interesantes
- «En las redes de Docker creadas por el usuario, se puede vincular a los contenedores no solo por la dirección IP. El nombre del contenedor también se resuelve en su dirección IP» (, punto 5 del código de Docker).
Impresión
$ 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 está en ejecución desde hace 11.5 segundos.</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.…" hace 27 segundos En funcionamiento 25 segundos 0.0.0.0:80->80\/tcp reverse-proxy
a1105f1b583d uptimer "python -u .\/app.py" hace aproximadamente un minuto En funcionamiento hace aproximadamente un minuto 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 está en ejecución desde hace 104.1 segundos.</h2>Despliegue sin interrupciones
Lanzaremos una nueva versión de la aplicación (con un aumento del rendimiento de inicio de dos veces) y trataremos de desplegarla sin interrupciones.
Técnicas interesantes
echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt'— Escribir textomy texten el archivo/my-file.txtdentro del contenedormy-container.cat > /my-file.txt— Escribir el contenido de la entrada estándar en un archivo/dev/stdin.
Impresión
$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py
$ docker build --tag uptimer .
Enviando contexto de construcción al demonio de Docker 39.94kB
Paso 1/4 : FROM python:alpine
---> 8ecf5a48c789
Paso 2/4 : EXPOSE 8080
---> Usando caché
---> cf92d174c9d3
Paso 3/4 : COPY uptimer.py app.py
---> 3eca6a51cb2d
Paso 4/4 : CMD [ "python", "-u", ".\/app.py" ]
---> Ejecutando en 8f13c6d3d9e7
Eliminando contenedor intermedio 8f13c6d3d9e7
---> 1d56897841ec
Construcción exitosa 1d56897841ec
Etiqueta exitosa uptimer:latest
$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer
96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02
$ docker logs uptimer_BLUE
Uptimer v2.0 (se carga en 5 seg.) iniciado.
$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 está en funcionamiento desde hace 23.9 segundos.</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
servidor {
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: señal de proceso iniciada
$ wget -qO- http://localhost
<h2>Uptimer v2.0 está en funcionamiento desde hace 63.4 segundos.</h2>
$ docker rm -f uptimer
uptimer
$ wget -qO- http://localhost
<h2>Uptimer v2.0 está en funcionamiento desde hace 84.8 segundos.</h2>
$ docker ps
ID DEL CONTENEDOR IMAGEN COMANDO CREADO ESTADO PUERTOS NOMBRES
96932d4ca97a uptimer "python -u .\/app.py" Hace aproximadamente un minuto En funcionamiento Desde hace un minuto 8080/tcp uptimer_BLUE
80695a822c19 nginx:alpine "\/docker-entrypoint.…" Hace 8 minutos En funcionamiento desde hace 8 minutos 0.0.0.0:80->80/tcp reverse-proxyEn esta etapa, la imagen se construye directamente en el servidor, lo que requiere que allí se encuentren los archivos fuente de la aplicación, además de sobrecargar el servidor con trabajo adicional. El siguiente paso será separar la construcción de la imagen en una máquina diferente (por ejemplo, en un sistema CI) para luego transferirla al servidor.
Transferencia de imágenes
Desafortunadamente, no tiene sentido transferir imágenes de localhost a localhost, así que esta sección solo se puede probar teniendo a mano dos hosts con Docker. En lo básico, se ve algo así:
$ 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 hace 5 minutos 78.9MBComando docker save guarda los datos de la imagen en un archivo .tar, es decir, pesa aproximadamente 1.5 veces más de lo que podría pesar en formato comprimido. Así que comprimámoslo en nombre de la economía de tiempo y tráficos:
$ docker image save uptimer | gzip | ssh production-server 'zcat | docker image load'
Loaded image: uptimer:latestY además, se puede monitorear el proceso de transferencia (aunque para esto se necesita una herramienta de terceros):
$ docker image save uptimer | gzip | pv | ssh production-server 'zcat | docker image load'
25.7MiB 0:01:01 [ 425KiB/s] [ ]
Loaded image: uptimer:latestConsejo: Si necesita un montón de parámetros para conectarse al servidor por SSH, es posible que no esté utilizando un archivo
~/.ssh/config.
La transferencia de imágenes a través de docker image save/load es el método más minimalista, pero no el único. Hay otros:
- Container Registry (estándar de la industria).
- Conectarse al daemon de docker del servidor desde otro host:
- Variable de entorno
DOCKER_HOST. - Parámetro de línea de comandos
-Ho--hostla herramientadocker-compose. docker context
- Variable de entorno
El segundo método (con tres variantes de implementación) está bien descrito en el artículo .
deploy.sh
Ahora uniremos todo lo que hicimos manualmente en un solo script. Comencemos con la función de nivel superior y luego veamos las otras que se utilizan en él.
Técnicas interesantes
${parameter?err_msg}es uno de los hechizos de la magia bash (aka ). Siel parámetrono está definido, se muestrael err_msgy se sale con código 1.docker --log-driver journald— por defecto, el controlador de registros de Docker es un archivo de texto sin ninguna rotación. Con este enfoque, los registros ocupan rápidamente todo el disco, por lo que para un entorno de producción es necesario cambiar el controlador por uno más inteligente.
Script de despliegue
deploy() {
local usage_msg="Uso: ${FUNCNAME[0]} nombre_de_imagen"
local image_name=${1?$usage_msg}
ensure-reverse-proxy || return 2
if get-active-slot $image_name
then
local OLD=${image_name}_AZUL
local new_slot=VERDE
else
local OLD=${image_name}_VERDE
local new_slot=AZUL
fi
local NEW=${image_name}_${new_slot}
echo "Desplegando '$NEW' en lugar de '$OLD'..."
docker run
--detach
--restart siempre
--log-driver journald
--name $NEW
--network web-gateway
$image_name || return 3
echo "Contenedor iniciado. Comprobando salud..."
for i in {1..20}
do
sleep 1
if get-service-status $image_name $new_slot
then
echo "Nuevo servicio '$NEW' parece estar OK. Cambiando..."
sleep 2 # Asegurarse de que el servicio esté listo
set-active-slot $image_name $new_slot || return 4
echo "¡El servicio '$NEW' está activo!"
sleep 2 # Asegurarse de que todas las solicitudes fueron procesadas
echo "Eliminando '$OLD'..."
docker rm -f $OLD
docker image prune -f
echo "¡Despliegue exitoso!"
return 0
fi
echo "El nuevo servicio '$NEW' aún no está listo. Esperando ($i)..."
done
echo "El nuevo servicio '$NEW' no se levantó, eliminándolo. Falló el despliegue T_T"
docker rm -f $NEW
return 5
}Funciones utilizadas:
ensure-reverse-proxy— Verifica que el proxy inverso esté funcionando (útil para el primer despliegue)get-active-slot service_name— Determina qué ranura está activa para el servicio dado (AZULoVERDE)get-service-status service_name deployment_slot— Verifica si el servicio está listo para procesar solicitudes entrantesset-active-slot service_name deployment_slot— Cambia la configuración de nginx en el contenedor del proxy inverso
En orden:
asegurar-proxy-inverso() {
está-contenedor-activo proxy-inverso && return 0
echo "Desplegando proxy-inverso..."
docker red crear puerta-web
docker ejecutar
--desprender
--reiniciar siempre
--controlador-registro journald
--nombre proxy-inverso
--red puerta-web
--publicar 80:80
nginx:alpine || return 1
docker exec --interactivo proxy-inverso sh -c "> /etc/nginx/conf.d/default.conf"
docker exec proxy-inverso nginx -s recargar
}
está-contenedor-activo() {
local contenedor=${1?"Uso: ${FUNCNAME[0]} nombre_contenedor"}
[ -n "$(docker ps -f name=${contenedor} -q)" ]
return $?
}
obtener-slot-activo() {
local servicio=${1?"Uso: ${FUNCNAME[0]} nombre_servicio"}
si está-contenedor-activo ${servicio}_AZUL && está-contenedor-activo ${servicio}_VERDE; entonces
echo "¡Colisión detectada! Deteniendo ${servicio}_VERDE..."
docker rm -f ${servicio}_VERDE
return 0 # AZUL
fi
si está-contenedor-activo ${servicio}_AZUL && ! está-contenedor-activo ${servicio}_VERDE; entonces
return 0 # AZUL
fi
si ! está-contenedor-activo ${servicio}_AZUL; entonces
return 1 # VERDE
fi
}
obtener-estado-servicio() {
local msg_uso="Uso: ${FUNCNAME[0]} nombre_servicio slot_despliegue"
local servicio=${1?msg_uso}
local slot=${2?$msg_uso}
caso $servicio en
# Agrega rutas de verificación de estado específicas para tus servicios aquí
*) local ruta_verificacion_estado=":8080/" ;;
esac
local direccion_verificacion_estado="http://${servicio}_${slot}${ruta_verificacion_estado}"
echo "Solicitando '$direccion_verificacion_estado' dentro de la red docker 'puerta-web':"
docker ejecutar --rm --red puerta-web alpine
wget --tiempo-espera=1 --silencio --respuesta-servidor $direccion_verificacion_estado
return $?
}
establecer-slot-activo() {
local msg_uso="Uso: ${FUNCNAME[0]} nombre_servicio slot_despliegue"
local servicio=${1?$msg_uso}
local slot=${2?$msg_uso}
[ "$slot" == AZUL ] || [ "$slot" == VERDE ] || return 1
obtener-configuracion-nginx $servicio $slot | docker exec --interactivo proxy-inverso sh -c "cat > /etc/nginx/conf.d/$servicio.conf"
docker exec proxy-inverso nginx -t || return 2
docker exec proxy-inverso nginx -s recargar
}La función obtener-slot-activo requiere una pequeña explicación:
¿Por qué devuelve un número y no imprime una cadena?
Aún así, en la función llamada verificamos el resultado de su trabajo, y comprobar el código de salida con bash es mucho más fácil que con una cadena. Además, obtener una cadena de ella es muy fácil:
obtener-slot-activo servicio && echo AZUL || echo VERDE.
¿Son suficientes tres condiciones para distinguir todos los estados?
Incluso con dos es suficiente; la última aquí es simplemente para completitud, para no escribir else.
Sólo queda indefinida la función que devuelve las configuraciones de nginx: obtener-configuracion-nginx nombre_servicio slot_despliegue. De manera similar a la verificación de estado, aquí se puede establecer cualquier configuración para cualquier servicio. De lo interesante, sólo cat <<- EOF, lo que permite eliminar todas las tabulaciones al principio. Sin embargo, el precio de un formato limpio es la mezcla de tabulaciones con espacios, lo que hoy se considera de muy mal gusto. Pero bash obliga las tabulaciones, y en la configuración de nginx también sería bueno tener un formato adecuado. En resumen, aquí la mezcla de tabulaciones con espacios parece ser realmente la mejor solución de las peores. Sin embargo, en el snippet a continuación no lo verá, ya que Habr 'hace bien' al cambiar todas las tabulaciones por 4 espacios, haciendo que EOF sea no válido. .
Para no tener que repetir, les contaré sobre
cat << 'EOF', que aparecerá más adelante. Si simplemente se escribecat << EOF, dentro del heredoc se realiza la interpolación de la cadena (se revelan las variables ($foo), llamadas de comandos ($(bar)) etc.), pero si se rodea el símbolo de fin de documento con comillas simples, la interpolación se desactiva y el símbolo$se imprime tal como es. Lo que se necesita para insertar un script dentro de otro script.
get-nginx-config() {
local usage_msg="Uso: ${FUNCNAME[0]} nombre_servicio slot_despliegue"
local service=${1?$usage_msg}
local slot=${2?$usage_msg}
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1
local container_name=${service}_${slot}
case $service in
# Agregue configuraciones específicas de nginx para sus servicios aquí
*) nginx-config-simple-service $container_name:8080 ;;
esac
}
nginx-config-simple-service() {
local usage_msg="Uso: ${FUNCNAME[0]} proxy_pass"
local proxy_pass=${1?$usage_msg}
cat << EOF
server {
listen 80;
location / {
proxy_pass http://$proxy_pass;
}
}
EOF
}Este es todo el script. Y aquí para descargarlo a través de wget o curl.
Ejecución de scripts parametrizados en un servidor remoto
Ha llegado el momento de conectarse al servidor de destino. Esta vez localhost será adecuado:
$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: intentando iniciar sesión con la(s) nueva(s) clave(s), para filtrar cualquier clave que ya esté instalada
/usr/bin/ssh-copy-id: INFO: 1 clave(s) quedan por instalar -- si se le solicita, es para instalar las nuevas claves
la contraseña de himura@localhost:
Número de clave(s) añadida(s): 1
Ahora intente iniciar sesión en la máquina con: "ssh 'localhost'"
y verifique que solo se hayan añadido las clave(s) que deseaba.Hemos escrito un script de despliegue que transfiere una imagen compilada previamente al servidor de destino y sustituye el contenedor del servicio de manera fluida, pero ¿cómo lo ejecutamos en una máquina remota? El script tiene argumentos, ya que es versátil y puede desplegar varios servicios bajo un solo reverse proxy (los archivos de configuración de nginx pueden resolver qué servicio se manejará por qué URL). No se puede almacenar el script en el servidor, ya que en ese caso no podremos actualizarlo automáticamente (para correcciones de errores y adición de nuevos servicios), y, en general, el estado es malo.
Solución 1: Almacenar el script en el servidor, pero copiarlo cada vez a través de scp. Luego conectarse a través de ssh y ejecutar el script con los argumentos necesarios.
Desventajas:
- Dos acciones en lugar de una.
- El lugar donde copias puede no existir, no tener acceso, o el script puede ejecutarse en el momento de la sustitución.
- Es recomendable limpiar (eliminar el script) después de usarlo.
- Ya son tres acciones.
Solución 2:
- Mantener solo las definiciones de funciones en el script y no ejecutar nada.
- Con
sedAgregar al final la llamada a la función. - Enviar todo esto directamente a shh a través de pipe (
|)
Pros:
- Verdaderamente sin estado.
- Sin entidades plantillas.
- Sintiendo genial.
Pero hagámoslo sin Ansible. Sí, ya todo está inventado. Sí, es un ciclo. Miren qué bicicleta tan simple, elegante y minimalista:
$ cat < 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
Conectando a localhost...
¡Genial! La función 'deploy' se está ejecutando en 'hut' con el argumento 'magic-porridge-pot'.Sin embargo, no podemos estar seguros de que en el host remoto haya un bash adecuado, así que agregaremos una pequeña verificación al principio (esto en lugar de ):
if [ "$SHELL" != "/bin/bash" ]
then
echo "El shell '$SHELL' no es compatible con 'deploy.sh'. Establezca un shell '/bin/bash' para '$USER@$HOSTNAME'."
exit 1
fiY ahora todo es real:
$ 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
Enviando la imagen comprimida 'uptimer' a 'localhost' a través de ssh...
Imagen cargada: uptimer:latest
Conectando a 'localhost' a través de ssh para desplegar 'uptimer' de manera ininterrumpida...
Desplegando 'uptimer_GREEN' en lugar de 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Contenedor iniciado. Comprobando el estado...
Solicitando 'http://uptimer_GREEN:8080/' dentro de la red docker 'web-gateway':
HTTP/1.0 503 Servicio No Disponible
wget: el servidor devolvió error: HTTP/1.0 503 Servicio No Disponible
El nuevo servicio 'uptimer_GREEN' aún no está listo. Esperando (1)...
Solicitando 'http://uptimer_GREEN:8080/' dentro de la red docker 'web-gateway':
HTTP/1.0 503 Servicio No Disponible
wget: el servidor devolvió error: HTTP/1.0 503 Servicio No Disponible
El nuevo servicio 'uptimer_GREEN' aún no está listo. Esperando (2)...
Solicitando 'http://uptimer_GREEN:8080/' dentro de la red docker 'web-gateway':
HTTP/1.0 200 OK
Servidor: BaseHTTP/0.6 Python/3.8.3
Fecha: Sab, 22 Ago 2020 20:15:50 GMT
Tipo de Contenido: text/html
El nuevo servicio 'uptimer_GREEN' parece estar OK. Cambiando entre versiones...
nginx: el archivo de configuración /etc/nginx/nginx.conf tiene una sintaxis correcta
nginx: la prueba del archivo de configuración /etc/nginx/nginx.conf fue exitosa
2020/08/22 20:15:54 [aviso] 97#97: proceso de señal iniciado
¡El servicio 'uptimer_GREEN' está activo!
Matando 'uptimer_BLUE'...
uptimer_BLUE
Espacio total reclamado: 0B
¡Despliegue exitoso!Ahora puedes abrir en el navegador, volver a ejecutar el despliegue y asegurarte de que se realiza sin inconvenientes actualizando la página de manera continua durante el despliegue.
No olvides limpiar después de trabajar :3
$ docker rm -f uptimer_GREEN reverse-proxy
uptimer_GREEN
reverse-proxy
$ docker network rm web-gateway
web-gateway
$ cd ..
$ rm -r blue-green-deploymentFuente: habr.com
