Cómo implementar una aplicación ASP.NET con cero tiempo de inactividad

127

Para implementar una nueva versión de nuestro sitio web, hacemos lo siguiente:

  1. Comprime el nuevo código y súbelo al servidor.
  2. En el servidor en vivo, elimine todo el código en vivo del directorio del sitio web de IIS.
  3. Extraiga el nuevo archivo zip de código en el directorio IIS ahora vacío

Este proceso está totalmente programado y ocurre con bastante rapidez, pero aún puede haber un tiempo de inactividad de 10-20 segundos cuando se eliminan los archivos antiguos y se implementan los archivos nuevos.

¿Alguna sugerencia sobre un método de tiempo de inactividad de 0 segundos?

Karl Glennon
fuente
¿No debería estar esto en ServerFault?
Daniel Rodriguez el
49
Quizás, pero ServerFault no existía en septiembre del 2008
Karl Glennon
3
¿IIS puede apuntar a una carpeta de enlace simbólico? ¿Cambiar el enlace simbólico hará que el proceso de IIS se recicle?
Neil McGuigan,
¿alguna solución final con una muestra de script de código fuente completo?
Kiquenet
¿No es posible tener múltiples grupos de aplicaciones y cambiar el tráfico de un grupo de aplicaciones a otro?
Lucas el

Respuestas:

79

Necesita 2 servidores y un equilibrador de carga. Aquí hay pasos:

  1. Convierta todo el tráfico en el Servidor 2
  2. Implementar en el servidor 1
  3. Servidor de prueba 1
  4. Convierta todo el tráfico en el Servidor 1
  5. Implementar en el servidor 2
  6. Servidor de prueba 2
  7. Gire el tráfico en ambos servidores

La cosa es que, incluso en este caso, aún tendrá reinicios de la aplicación y pérdida de sesiones si está utilizando "sesiones fijas". Si tiene sesiones de base de datos o un servidor de estado, entonces todo debería estar bien.

Sklivvz
fuente
44
También puede configurar el equilibrador de carga para que preste servicio a las sesiones existentes para un servidor determinado, pero no acepte otras nuevas. Eso le permite evitar perder sesiones. Sin embargo, esta técnica requiere esperar a que finalicen las sesiones, y en general querrás escribir esto.
35
Este método tiende a caerse cuando el rollo de código tiene cambios estructurales en la base de datos. Una vez que actualice la base de datos para el servidor 1, el servidor 2 explotará. Ahora puede hacer una copia de seguridad / restaurar la base de datos para probar en el servidor 1, pero luego tiene el problema de ordenar los datos que cambiaron en la base de datos en vivo mientras se ejecutaba la copia paralela.
EBarr
1
@AndreiRinea: ¿cómo supone que esto funcionaría en un sistema OLTP de alto volumen? O el sistema se desincroniza y pierde datos cuando se corta, o necesita pausar la entrada de datos y escribir un script para identificar y migrar los datos transitorios a la nueva estructura de base de datos.
EBarr
9
@EBarr: y, en cualquier caso, técnicamente aún tiene cero tiempo de inactividad en la aplicación ASP.NET: la pregunta no es "cómo implementar en un servidor SQL DB con cero tiempo de inactividad".
Sklivvz
66
La clave es desarrollar de una manera que sus cambios sql no sean destructivos. A menudo tiene que hacer cambios destructivos de SQL en la siguiente versión una vez que ya no se usa. No es difícil de hacer con la práctica.
Bealer
60

La herramienta de implementación web de Microsoft admite esto hasta cierto punto:

Habilita la compatibilidad con el Sistema de archivos transaccionales de Windows (TxF). Cuando el soporte TxF está habilitado, las operaciones de archivo son atómicas; es decir, tienen éxito o fracasan por completo. Esto garantiza la integridad de los datos y evita que los datos o archivos existan en un estado "a mitad de camino" o dañado. En MS Deploy, TxF está deshabilitado de forma predeterminada.

Parece que la transacción es para toda la sincronización. Además, TxF es una característica de Windows Server 2008, por lo que esta característica de transacción no funcionará con versiones anteriores.

Creo que es posible modificar su script para el tiempo de inactividad 0 utilizando carpetas como versiones y la metabase de IIS:

  • para una ruta / url existente:
  • Copie el sitio web nuevo (o modificado) al servidor en
    • \ web \ app \ v2.1 \
  • Modificar la metabase de IIS para cambiar la ruta del sitio web
    • desde \ web \ app \ 2.0 \
    • a \ web \ app \ v2.1 \

Este método ofrece los siguientes beneficios:

  • En caso de que la nueva versión tenga un problema, puede retroceder fácilmente a v2.0
  • Para implementar en múltiples servidores físicos o virtuales, puede usar su script para la implementación de archivos. Una vez que todos los servidores tengan la nueva versión, puede cambiar simultáneamente todas las metabases de los servidores con la Herramienta de implementación web de Microsoft.
George Tsiokos
fuente
55
Implementé este enfoque adaptando nuestros scripts de implementación de powershell. Puede ver la parte del script que cambia la carpeta del sitio IIS aquí: stackoverflow.com/questions/330608/… Gracias por el puntero.
Karl Glennon
17
Desafortunadamente, este método no tiene en cuenta los cambios estructurales en la base de datos. Una vez que actualice la base de datos para v2.1, entonces v.2.0 explota.
EBarr
8
Usar TxF es excesivo aquí, en mi opinión. No hace daño tener v2.0 y v2.1 en el sistema de archivos al mismo tiempo. El gran cambio ocurre cuando v2.1 se conecta y, para ese momento, la transacción TxF se ha confirmado. El tiempo de inactividad cero realmente ocurre debido a la forma en que IIS se mueve de un antiguo AppPool a uno nuevo, no por TxF.
RickNZ
55
Otro problema con esto es si se almacena una gran cantidad de datos de usuario en subcarpetas de las carpetas de la aplicación.
Kenny Evitt
44
Esta no es una implementación de 0 segundos porque la nueva aplicación necesita iniciarse.
Usr
12

Puede lograr una implementación de tiempo de inactividad cero en un solo servidor utilizando el Enrutamiento de solicitud de aplicaciones en IIS como un equilibrador de carga de software entre dos sitios IIS locales en puertos diferentes. Esto se conoce como una estrategia de implementación verde azulada en la que solo uno de los dos sitios está disponible en el equilibrador de carga en un momento dado. Implemente en el sitio que está "inactivo", caliéntelo y llévelo al equilibrador de carga (generalmente pasando una verificación de estado de Enrutamiento de solicitud de aplicación), luego saque el sitio original que estaba activo, fuera del "grupo" (nuevamente haciendo que su comprobación de estado falle).

Un tutorial completo se puede encontrar aquí.

kavun
fuente
7

Revisé esto recientemente y la solución que se me ocurrió fue tener dos sitios configurados en IIS y cambiar entre ellos.

Para mi configuración, tenía un directorio web para cada sitio A y B como este: c: \ Intranet \ Live A \ Interface c: \ Intranet \ Live B \ Interface

En IIS, tengo dos sitios idénticos (mismos puertos, autenticación, etc.), cada uno con su propio grupo de aplicaciones. Uno de los sitios se está ejecutando (A) y el otro se detiene (B). el live también tiene el encabezado de host en vivo.

Cuando se trata de implementar para vivir, simplemente publico en la ubicación del sitio DETENIDO. Como puedo acceder al sitio B usando su puerto, puedo precalentar el sitio para que el primer usuario no provoque el inicio de una aplicación. Luego, usando un archivo por lotes, copio el encabezado del host en vivo en B, detengo A e inicio B.

Rob King
fuente
1
Esto ayuda con el tiempo de inactividad debido a la copia del archivo, pero tiene el mismo problema que @Sklivvz: tan pronto como el rollo de código tiene cambios estructurales en la base de datos, el sitio se dispara.
EBarr
Esto también me pareció la forma intuitiva, pero ¿por qué no hay una forma fácil e integrada de hacer esto?
Petrus Theron
3
@Ebarr no despliega cambios destructivos de sql. Por ejemplo, si necesita eliminar una columna, hágalo en la próxima versión cuando ya no sea utilizada por A o B.
Bealer
@Bealer: de acuerdo (con una advertencia). Hay una serie completa de estas preguntas sobre el "tiempo de inactividad durante los roles de código". Todavía tengo que encontrar uno que realmente discuta las realidades de la evolución de un esquema de base de datos. Advertencia: hay una variedad de complicaciones que vienen junto con cambios de dos fases en el esquema. Un ejemplo: muchos de los ORM barf si la definición de la tabla difiere de la definición tal como la entiende (columnas nuevas o faltantes).
EBarr
2
@Rob, ¿cómo puede "precalentar" el sitio si se detiene?
Andrew Gee
7

Con la clase ServerManager de Microsoft.Web.Administration puede desarrollar su propio agente de implementación.

El truco consiste en cambiar la ruta física del directorio virtual, lo que da como resultado un cambio atómico en línea entre las aplicaciones web antiguas y las nuevas.

¡Tenga en cuenta que esto puede resultar en que los AppDomains antiguos y nuevos se ejecuten en paralelo!

El problema es cómo sincronizar los cambios en las bases de datos, etc.

Al sondear la existencia de AppDomains con PhysicalPaths antiguos o nuevos, es posible detectar cuándo los antiguos AppDomain (s) han finalizado y si los nuevos AppDomain (s) se han iniciado.

Para forzar el inicio de un dominio de aplicación, debe realizar una solicitud HTTP (IIS 7.5 admite la función de inicio automático)

Ahora necesita una forma de bloquear solicitudes para el nuevo AppDomain. Utilizo un mutex con nombre, que es creado y propiedad del agente de implementación, atendido por Application_Start de la nueva aplicación web y luego liberado por el agente de implementación una vez que se han realizado las actualizaciones de la base de datos.

(Uso un archivo de marcador en la aplicación web para habilitar el comportamiento de espera de mutex) Una vez que se ejecuta la nueva aplicación web, elimino el archivo de marcador.

Jack
fuente
6

Está bien, ya que todos están votando la respuesta que escribí en 2008 * ...

Le diré cómo lo hacemos ahora en 2014. Ya no usamos sitios web porque estamos usando ASP.NET MVC ahora.

Ciertamente no necesitamos un equilibrador de carga y dos servidores para hacerlo, está bien si tiene 3 servidores para cada sitio web que mantiene, pero es una exageración total para la mayoría de los sitios web.

Además, no confiamos en el último asistente de Microsoft: demasiado lento, demasiada magia oculta y demasiado propenso a cambiar su nombre.

Así es como lo hacemos:

  1. Tenemos un paso posterior a la compilación que copia las DLL generadas en una carpeta 'bin-pub'.

  2. Utilizamos Beyond Compare (que es excelente **) para verificar y sincronizar archivos modificados (a través de FTP porque eso es ampliamente compatible) hasta el servidor de producción

  3. Tenemos una URL segura en el sitio web que contiene un botón que copia todo en 'bin-pub' a 'bin' (tomar una copia de seguridad primero para permitir una reversión rápida). En este punto, la aplicación se reinicia sola. Luego, nuestro ORM verifica si hay tablas o columnas que deben agregarse y las crea.

Eso es solo un tiempo de inactividad de milisegundos. El reinicio de la aplicación puede tomar uno o dos segundos, pero durante el reinicio, las solicitudes se almacenan temporalmente, por lo que efectivamente no hay tiempo de inactividad.

Todo el proceso de implementación demora entre 5 segundos y 30 minutos, dependiendo de cuántos archivos se cambien y cuántos cambios se revisen.

De esta manera, no tiene que copiar un sitio web completo a un directorio diferente, sino solo la carpeta bin. También tiene control completo sobre el proceso y sabe exactamente qué está cambiando.

** Siempre hacemos un vistazo rápido de los cambios que estamos implementando, como una verificación doble de último minuto, para saber qué probar y si algo se rompe, estamos listos. Usamos Beyond Compare porque te permite diferenciar fácilmente archivos a través de FTP. Nunca haría esto sin BC, no tienes idea de lo que estás sobrescribiendo.

* Desplácese hacia abajo para verlo: (Por cierto, ya no recomendaría sitios web porque son más lentos de construir y pueden fallar con archivos temporales a medio compilar. Los usamos en el pasado porque permitieron un archivo más ágil por archivo despliegue. Muy rápido para solucionar un problema menor y puedes ver exactamente lo que estás implementando (si usas Beyond Compare, por supuesto, de lo contrario olvídalo).

Mike Nelson
fuente
Sin embargo, seguirá teniendo tiempo de inactividad porque el grupo de aplicaciones se recicla.
testpattern
No, no hay tiempo de inactividad debido a las peticiones se almacenan de forma automática por IIS durante el reinicio de aplicaciones
micrófono nelson
5

Los únicos métodos de tiempo de inactividad cero en los que puedo pensar incluyen el alojamiento en al menos 2 servidores.

Sam Meldrum
fuente
1

Refinaría un poco la respuesta de George, como sigue, para un solo servidor:

  1. Use un proyecto de implementación web para precompilar el sitio en una sola DLL
  2. Comprime el nuevo sitio y súbelo al servidor
  3. Descomprímalo en una nueva carpeta ubicada en una carpeta con los permisos correctos para el sitio, de modo que los archivos descomprimidos hereden los permisos correctamente (tal vez e: \ web, con subcarpetas v20090901, v20090916, etc.)
  4. Use el Administrador IIS para cambiar el nombre de la carpeta que contiene el sitio
  5. Mantenga la carpeta antigua por un tiempo, para que pueda recurrir a ella en caso de problemas.

El paso 4 hará que el proceso de trabajo de IIS se recicle.

Esto es solo cero tiempo de inactividad si no está utilizando sesiones InProc; use el modo SQL en su lugar si puede (aún mejor, evite el estado de sesión por completo).

Por supuesto, es un poco más complicado cuando hay múltiples servidores y / o cambios en la base de datos ...

RickNZ
fuente
1
Mismo problema que @Sklivvz: este método se cae tan pronto como el rollo de código tiene cambios estructurales en la base de datos.
EBarr
3
Es por eso que dije que estaba más involucrado cuando hay cambios en la base de datos ... La implementación de código con cambios estructurales en la base de datos no es solo un problema de implementación; También tiene que haber soporte en el código, y probablemente también en el DB.
RickNZ
1

Para ampliar la respuesta de sklivvz, que dependía de tener algún tipo de equilibrador de carga (o simplemente una copia en espera en el mismo servidor)

  1. Dirija todo el tráfico al Sitio / Servidor 2
  2. Opcionalmente, espere un poco para asegurarse de que el menor número posible de usuarios tenga flujos de trabajo pendientes en la versión implementada
  3. Implemente en el sitio / servidor 1 y caliéntelo tanto como sea posible
  4. Ejecute migraciones de bases de datos transaccionalmente (esfuércese por hacer esto posible)
  5. Dirija inmediatamente todo el tráfico al Sitio / Servidor 1
  6. Implementar en el sitio / servidor 2
  7. Tráfico directo a ambos sitios / servidores

Es posible introducir un poco de prueba de humo, creando una instantánea / copia de la base de datos, pero eso no siempre es factible.

Si es posible y necesario, use "diferencias de enrutamiento", como diferentes URL de inquilino: s (customerX.myapp.net) o diferentes usuarios, para implementar primero en un grupo desconocido de conejillos de indias. Si nada falla, liberar a todos.

Dado que las migraciones de bases de datos están involucradas, volver a una versión anterior a menudo es imposible.

Hay formas de hacer que las aplicaciones funcionen mejor en estos escenarios, como el uso de colas de eventos y mecanismos de reproducción, pero como estamos hablando de implementar cambios en algo que está en uso, realmente no hay una manera infalible.

gliljas
fuente
1

Así es como lo hago:

Requisitos mínimos absolutos del sistema:
1 servidor con

  • 1 equilibrador de carga / proxy inverso (por ejemplo, nginx) ejecutándose en el puerto 80
  • 2 ASP.NET-Core / mono reverse-proxy / fastcgi chroot-jails o docker-container que escuchan en 2 puertos TCP diferentes
    (o incluso solo dos aplicaciones de proxy inverso en 2 puertos TCP diferentes sin ninguna caja de arena)

Flujo de trabajo:

iniciar transacción myupdate

try
    Web-Service: Tell all applications on all web-servers to go into primary read-only mode 
    Application switch to primary read-only mode, and responds 
    Web sockets begin notifying all clients 
    Wait for all applications to respond

    wait (custom short interval)

    Web-Service: Tell all applications on all web-servers to go into secondary read-only mode 
    Application switch to secondary read-only mode (data-entry fuse)
    Updatedb - secondary read-only mode (switches database to read-only)

    Web-Service: Create backup of database 
    Web-Service: Restore backup to new database
    Web-Service: Update new database with new schema 

    Deploy new application to apt-repository 
    (for windows, you will have to write your own custom deployment web-service)
    ssh into every machine in array_of_new_webapps
    run apt-get update
    then either 
    apt-get dist-upgrade
    OR
    apt-get install <packagename>
    OR 
    apt-get install --only-upgrade <packagename>
    depending on what you need
    -- This deploys the new application to all new chroots (or servers/VMs)

    Test: Test new application under test.domain.xxx
    -- everything that fails should throw an exception here
    commit myupdate;

    Web-Service: Tell all applications to send web-socket request to reload the pages to all clients at time x (+/- random number)
    @client: notify of reload and that this causes loss of unsafed data, with option to abort 

    @ time x:  Switch load balancer from array_of_old_webapps to array_of_new_webapps 
    Decomission/Recycle array_of_old_webapps, etc.

catch
        rollback myupdate 
        switch to read-write mode
        Web-Service: Tell all applications to send web-socket request to unblock read-only mode
end try 
Stefan Steiger
fuente
-7

Sugeriría mantener los archivos antiguos allí y simplemente sobrescribirlos. De esta forma, el tiempo de inactividad se limita a los tiempos de sobrescritura de un solo archivo y solo falta un archivo a la vez.

Sin embargo, no estoy seguro de que esto ayude en una "aplicación web" (creo que estás diciendo que eso es lo que estás usando), por lo que siempre usamos "sitios web". Además, la implementación de "sitios web" no reinicia su sitio y elimina todas las sesiones de usuario.

Mike Nelson
fuente
Hola Mike, quizás quieras eliminar esta respuesta.
Sohail Ahmed