Implementación de un Pipeline de Despliegue Automatizado con Docker, GitLab y Ansible

Configuración del Entorno Base

Para orquestar un flujo de integración y despliegue continuo, el primer paso es preparar la infraestructura subyacente. Asegúrese de que los archivos /etc/hostname y /etc/hosts del servidor estén correctamente definidos.

Instale Docker siguiendo la documentación oficial para su distribución. Posteriormente, instale Docker Compose descargando el binario estable:

sudo curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Para optimizar la descarga de imágenes, configure un mirror de registro en /etc/docker/daemon.json.

Definición de la Infraestructura con Docker Compose

La siguiente configuración levanta un proxy inverso con generación automática de certificados SSL, el servidor GitLab y su respectivo agente de ejecución (Runner). Es crucial no depender de la red bridge por defecto, ya que posteriormente el contenedor de Ansible necesitará comunicarse directamente con GitLab sin conflictos de ruteo entre múltiples redes aisladas.

version: '3.8'
services:
  reverse-proxy:
    image: jwilder/nginx-proxy:alpine
    container_name: proxy_nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./proxy/certs:/etc/nginx/certs:ro
      - ./proxy/vhost:/etc/nginx/vhost.d
      - ./proxy/html:/usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
    labels:
      com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: 'true'

  ssl-companion:
    image: nginxproxy/acme-companion
    container_name: proxy_ssl
    restart: unless-stopped
    volumes:
      - ./proxy/certs:/etc/nginx/certs:rw
      - ./proxy/vhost:/etc/nginx/vhost.d
      - ./proxy/html:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro

  gitlab-core:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab_core
    restart: unless-stopped
    hostname: 'gitlab.miempresa.local'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.miempresa.local'
      VIRTUAL_HOST: gitlab.miempresa.local
      VIRTUAL_PORT: 80
      LETSENCRYPT_HOST: gitlab.miempresa.local
      LETSENCRYPT_EMAIL: admin@miempresa.local
    ports:
      - '2222:22'
    volumes:
      - '/data/gitlab/config:/etc/gitlab'
      - '/data/gitlab/logs:/var/log/gitlab'
      - '/data/gitlab/data:/var/opt/gitlab'

  ci-runner:
    image: gitlab/gitlab-runner:alpine
    container_name: runner_ci
    restart: unless-stopped
    volumes:
      - '/data/runner/config:/etc/gitlab-runner'
      - '/var/run/docker.sock:/var/run/docker.sock'

Registro y Ajuste del Runner

Una vez que los contenedores estén operativos, registre el agente de CI/CD utilizando el token proporcionado en el panel de administración de GitLab:

docker exec -it runner_ci gitlab-runner register \
  --non-interactive \
  --url "http://gitlab.miempresa.local/" \
  --registration-token "TOKEN_DE_REGISTRO" \
  --executor "docker" \
  --docker-image "alpine:latest" \
  --description "Agente de Despliegue" \
  --docker-privileged \
  --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"

Edite el archivo de configuración generado en /data/runner/config/config.toml para evitar descargas redundantes de imágenes:

[[runners.docker]]
  pull_policy = ["if-not-present"]
  shm_size = 0

Gestión de Autenticación SSH

Genere un par de claves SSH en el servidor anfitrión. La clave pública debe ser agregada a la sección de claves SSH en la configuración de su cuenta de GitLab para permitir clonaciones automatizadas.

ssh-keygen -t rsa -b 4096 -C "deployer@miempresa.local"
cat ~/.ssh/id_rsa.pub

Estructura del Repositorio de Ansible

Almacene las definiciones de Ansible en un proyecto dedicado dentro de GitLab para que puedan ser inyectadas en la imagen de despliegue durante la fase de construcción.

Cree un archivo de inventario inventory/servidores_prod:

[produccion]
192.168.1.50 ansible_user=deploy

Defina el libro de jugadas principal playbooks/deploy.yml:

- hosts: produccion
  vars:
    app_name: "servicio_web"
    target_env: "prod"
    build_artifact: "/tmp/builds/package.tar.gz"
  tasks:
    - name: Crear directorio base de la aplicación
      file: 
        path: "/opt/apps/{{ app_name }}-{{ target_env }}" 
        state: directory
        mode: '0755'

    - name: Limpiar código fuente anterior
      file:
        state: absent
        path: "/opt/apps/{{ app_name }}-{{ target_env }}/src"

    - name: Descomprimir nuevo artefacto
      unarchive:
        src: "{{ build_artifact }}"
        dest: "/opt/apps/{{ app_name }}-{{ target_env }}/"
        remote_src: yes

    - name: Detener contenedores en ejecución
      shell: docker-compose -f docker-compose.yml -f docker-compose.{{ target_env }}.yml down
      args:
        chdir: "/opt/apps/{{ app_name }}-{{ target_env }}"
      ignore_errors: yes

    - name: Desplegar nueva versión
      shell: docker-compose -f docker-compose.yml -f docker-compose.{{ target_env }}.yml up -d --build
      args:
        chdir: "/opt/apps/{{ app_name }}-{{ target_env }}"

Configure el comportamiento de Ansible mediante ansible.cfg:

[defaults]
transport = ssh
host_key_checking = False
inventory = inventory/servidores_prod
private_key_file = ~/.ssh/id_rsa
retry_files_enabled = False

[ssh_connection]
ssh_args = -o ForwardAgent=yes -o StrictHostKeyChecking=no

Construcción de la Imagen de Despliegue

El pipeline de GitLab utilizará un contenedor personalizado con Ansible preinstalado para ejecutar las tareas en los servidores de destino.

Defina el Dockerfile:

FROM alpine:3.18
LABEL maintainer="equipo-devops"

RUN apk add --no-cache \
    ansible \
    git \
    openssh-client \
    bash

RUN mkdir -p /root/.ssh
COPY id_rsa /root/.ssh/id_rsa
RUN chmod 600 /root/.ssh/id_rsa && \
    echo -e "Host *\n\tStrictHostKeyChecking no\n" > /root/.ssh/config

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

Cree el script de inicialziación entrypoint.sh:

#!/bin/bash
REPO_URL="git@gitlab.miempresa.local:infra/ansible-inventory.git"
DEST_DIR="/ansible"

if [ ! -d "$DEST_DIR" ]; then
     git clone "$REPO_URL" "$DEST_DIR"
fi

cd "$DEST_DIR"
git pull origin main

exec "$@"

Asegúrese de que la clave pública del conteneder de despliegue esté registrada en el archivo ~/.ssh/authorized_keys del servidor de producción objetivo, con los permisos adecuados (chmod 600 ~/.ssh/authorized_keys).

Resolución de Problemas Comunes

Límite de tamaño de carga en GitLab:
Si el proceso de construcción falla al subir artefactos pesados, ajuste la configuración de Nginx interno de GitLab. Modifique /data/gitlab/config/gitlab.rb:

nginx['enable'] = true
nginx['client_max_body_size'] = '2048m'

Aplique los cambios reiniciando el servicio: docker exec gitlab_core gitlab-ctl reconfigure.

Aislamiento de Redes:
Si los contenedores no logran comunicarse entre sí debido a segmentaciones de red creadas por Docker Compose, genere una red externa dedicada:

docker network create infra_network

Posteriormente, asigne esta red a los servicios correspondientes en sus archivos de configuración, verificando la topología con docker network ls y brctl show para asegurar que las interfaces virtuales estén correctamente enlazadas.

Etiquetas: Docker GitLab Ansible docker-compose nginx-proxy

Publicado el 7-14 11:27