Despliegue en el servidor: qué significa en palabras simples

Lanzamos el código en producción. ¿Cómo se hace esto? Hablamos sobre la automatización y el despliegue sin "tiempo de inactividad".
El despliegue es el proceso de "implementación" de un servicio web, como un sitio web, en un entorno de trabajo. El entorno de trabajo es el lugar donde el sitio web se ejecuta y está disponible para las solicitudes. Puede ser un hosting listo para usar o su propia infraestructura de servidor.

No solo los servicios web se despliegan, sino también cualquier servicio accesible a través de la red. Incluso si esta red es interna y no está disponible para solicitudes a través de Internet.

Para comprender el despliegue, es necesario comprender el ciclo de vida del código. El código de la aplicación se desarrolla en la máquina de trabajo del desarrollador, pero se ejecuta en otro lugar llamado producción. Producción es el entorno de ejecución (a veces se le llama entorno de producción). En el caso de una aplicación simple, puede consistir en un solo servidor, pero puede haber miles o decenas de miles en el caso de aplicaciones realmente complejas.

Cómo sucede esto. Los desarrolladores agregan código al repositorio. En algún momento, deciden que es hora de llevarlo a producción. Esto puede suceder según un horario regular, como una vez cada dos semanas, o simplemente según sea necesario, incluso después de cada cambio. En gran medida, la cantidad de despliegues depende del nivel de automatización, es decir, de lo fácil que sea el proceso de implementación y reversión en caso de problemas. En Códica, los despliegues se realizan casi después de cada cambio, alrededor de 3 despliegues al día.
Cada vez que los desarrolladores deciden que todo está listo, crean una versión. Por lo general, una versión se entiende como una etiqueta en Git que registra qué se implementará. Es decir, los cambios agregados al maestro después de crear la etiqueta no afectarán la etiqueta en sí, por lo que estamos seguros de lo que estamos implementando.

{/* image */}

Para sitios web estáticos o front-end independientes (solo HTML, CSS y archivos estáticos), el despliegue se reduce a actualizar el código en el servidor. En el caso del back-end, al menos se conecta una base de datos. En general, el despliegue puede ser un procedimiento complicado que lleva mucho tiempo. En sistemas distribuidos que constan de muchos servicios web independientes, no hay un despliegue general: cada parte de la aplicación se implementa por separado.

Vale la pena mencionar que las plataformas PaaS, como Heroku, se encargan completamente del despliegue. Solo necesita hacer un commit y todo lo demás sucederá automáticamente. El precio por esto es el costo de la plataforma misma.
Pasos del despliegue
Entrega de código al servidor

Existen diferentes formas de entregar código al servidor según la forma en que se empaqueta. A veces, simplemente copian el código en el servidor como un conjunto de archivos, pero esto es raro, más a menudo se actualiza a través de Git. Anteriormente, el método de implementación a través de los administradores de paquetes estándar de las distribuciones de Linux era popular. Ahora también se encuentra y es el más adecuado para situaciones específicas.
  • Git: git checkout tag-name
  • Docker: docker pull image-name:tag-name
  • Apt (Paquete): apt-install application-package-name
Actualización de la base de datos

Por lo general, una nueva versión de la aplicación requiere cambios en la base de datos. Para esto, durante (o antes) del despliegue, se ejecutan migraciones, que son scripts especiales que contienen reglas para actualizar la base de datos. Por ejemplo, scripts SQL:
CREATE TABLE car (
    id INT NOT NULL PRIMARY KEY,
    license_plate VARCHAR NOT NULL,
    color VARCHAR NOT NULL
);

ALTER TABLE owner ADD driver_license_id VARCHAR;
Inicio y detención

En algún momento de este proceso, se detiene la versión anterior y se inicia la nueva. Si primero detiene la versión anterior, luego ejecuta las migraciones y luego inicia la nueva, obtendrá un tiempo de inactividad en el servicio. Así es como funcionan muchos proyectos, pero esto puede ser perjudicial para el negocio y los despliegues frecuentes. Por lo tanto, los proyectos más avanzados no se detienen durante el despliegue. Sobre cómo hacer esto, a continuación.
Automatización
El despliegue debe ser automatizado al máximo, esto afecta el Time To Market, una característica clave de las aplicaciones orientadas al negocio. Cuanto más rápido y con más frecuencia entreguemos cambios al usuario, mejor. Verificamos hipótesis más rápido, realizamos correcciones más rápido, justificamos el dinero invertido en desarrollo más rápido. Sin automatización, los desarrolladores temen realizar despliegues, se convierte en una carga, lo que lleva a una disminución en el número de despliegues y estrés regular para todo el equipo, con quedarse en el trabajo hasta altas horas de la noche.

Hay tres formas principales de automatización:
  • 1
    Con herramientas creadas para lenguajes específicos. Por ejemplo, en Ruby, esto es Capistrano, una de las primeras y más conocidas herramientas de este tipo, que se hizo popular mucho más allá de Ruby. El principal problema con tales herramientas es su fuerte dependencia del lenguaje.
  • 2
    Con Ansible, que ya tiene un módulo incorporado para el despliegue. Es ideal para la mayoría de las situaciones de implementación en servidores administrados.
  • 3
    Sistemas de orquestación como Kubernetes. Si se utilizan, no hay despliegue automático sin ellos.
Pero incluso si la automatización está completa, todavía queda la tarea de "iniciar el despliegue". El inicio también se automatiza. Existe todo un enfoque llamado Entrega Continua (continuous delivery). Es difícil de implementar y no es adecuado en todas partes, pero si tiene éxito, se olvidan del despliegue. Se ejecuta completamente por sí mismo sin intervención humana. Lo más importante en este caso es tener un buen monitoreo y un sistema de alerta para reaccionar a los errores.
# https://docs.ansible.com/ansible/latest/collections/community/general/deploy_helper_module.html#examples
- name: Initialize the deploy root and gather facts
  community.general.deploy_helper:
    path: /path/to/root
- name: Clone the project to the new release folder
  ansible.builtin.git:
    repo: ansible.builtin.git://foosball.example.org/path/to/repo.git
    dest: '{{ deploy_helper.new_release_path }}'
    version: v1.1.1
- name: Add an unfinished file, to allow cleanup on successful finalize
  ansible.builtin.file:
    path: '{{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }}'
    state: touch
Despliegue sin tiempo de inactividad
Si no se toman medidas especiales, cada despliegue resultará en una interrupción (posiblemente parcial) del servicio. Durante este tiempo, los usuarios verán un error o un mensaje sobre la actualización en curso. Pero esto no sucede en la mayoría de los servicios web importantes en Internet. ¿Por qué? Debido a la implementación del enfoque "despliegue sin tiempo de inactividad" (downtime - tiempo de inactividad del servicio).

El despliegue sin tiempo de inactividad parece como si el servicio nunca se detuviera, pero aún se actualiza. Esto se logra ejecutando simultáneamente las versiones antigua y nueva del código. Es decir, cuando se implementa una aplicación, primero se inicia una nueva versión junto con la antigua. Y solo cuando la automatización se asegura de que la nueva versión se ha iniciado y está funcionando, se detiene la versión antigua. Para realizar este procedimiento, necesitará lo siguiente:
  • 1
    Infraestructura. Necesita un balanceador de carga que pueda cambiar el tráfico (conexiones entrantes de navegadores u otros sistemas) entre las versiones antigua y nueva del código. Y es deseable tener al menos dos servidores, aunque no es obligatorio.
  • 2
    Despliegue. El proceso de despliegue sin tiempo de inactividad es mucho más complicado que con una parada. La forma más fácil de hacer esto es en sistemas de orquestación como Kubernetes.
  • 3
    Cultura de código. No se puede lograr un funcionamiento sin interrupciones sin una cultura específica de escritura de código. Para que las versiones antigua y nueva puedan funcionar al mismo tiempo, es necesario controlar todas las interfaces. Es importante cumplir con la compatibilidad inversa (trabajar con API, base de datos, colas y, en general, cualquier almacenamiento).
  • 4
    Base de datos. Debe ser compatible hacia atrás entre la versión antigua y la nueva. Todas las migraciones solo hacia adelante (¡no se pueden revertir!) y solo para agregar. No se pueden eliminar ni actualizar (renombrar, cambiar el tipo) columnas y tablas.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: tomcat-deployment-${TARGET_ROLE}
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: tomcat
        role: ${TARGET_ROLE}
    spec:
      containers:
      - name: tomcat-container
        image: tomcat:${TOMCAT_VERSION}
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /
            port: 8080
Recursos adicionales
Leer otros artículos de Guías
Lea otros artículos relevantes del mundo de la tecnología y el espíritu empresarial.