Интеграция Ansible с Jenkins: Автоматизация вашего CI/CD конвейера

Интегрируйте Ansible с Jenkins для запуска плейбуков из CI/CD конвейеров, управления SSH-учетными данными и последовательного развертывания.

Интеграция Ansible с Jenkins: автоматизация CI/CD конвейера

Интеграция Ansible с Jenkins позволяет вашему CI/CD конвейеру собирать артефакты, запускать тесты и развертывать их с помощью тех же плейбуков, которые вы используете вне Jenkins. Основная задача — настроить учетные данные, инвентаризацию и выполнение плейбуков, не превращая конвейер в набор shell-команд.

Это руководство показывает практическую настройку: Jenkins оркестрирует конвейер, а Ansible отвечает за развертывание и настройку на целевых хостах. Примеры предполагают развертывание на Linux через SSH, но тот же подход работает с инвентаризациями и ролями для разных сред.

Как Jenkins и Ansible работают вместе

Прежде чем писать конвейер, разделите обязанности:

  • Jenkins: Проверяет код, собирает артефакты, запускает тесты, собирает логи и решает, когда запускать этапы развертывания.
  • Ansible: Подключается к целевым хостам, копирует артефакты, записывает конфигурацию, управляет сервисами и делает шаги развертывания идемпотентными.

Зачем интегрировать Jenkins с Ansible?

Такое разделение дает вашей команде четкие границы:

  1. Jenkins хранит процесс релиза в Jenkinsfile.
  2. Ansible хранит действия с инфраструктурой в плейбуках и ролях.
  3. Файлы инвентаризации определяют, какие хосты относятся к staging, production или другим средам.
  4. Учетные данные Jenkins защищают SSH-ключи, пароли Vault и токены.

Предварительные требования для интеграции

Перед началом убедитесь, что у вас есть:

  • Работающий контроллер Jenkins и хотя бы один агент, способный выполнять задачи развертывания.
  • Ansible, установленный на агенте Jenkins, который будет запускать ansible-playbook.
  • Целевые машины, доступные с этого агента по SSH.
  • Выделенная пара SSH-ключей для автоматизации.
  • Код приложения, инвентаризация, плейбуки и роли в системе контроля версий.

Настройка Jenkins для работы с Ansible

Jenkins нужно две вещи, прежде чем он сможет безопасно запускать Ansible: среда выполнения Ansible на агенте и учетные данные для целевых хостов.

1. Установка плагина Ansible

Вы можете вызывать ansible-playbook напрямую из шага shell, но плагин Ansible для Jenkins упрощает управление конфигурацией и выводом.

  1. Перейдите в Manage Jenkins > Manage Plugins.
  2. Перейдите на вкладку Available и найдите "Ansible".
  3. Выберите плагин "Ansible" и нажмите Install without restart или Download now and install after restart.

2. Настройка SSH-учетных данных

Ansible будет использовать SSH для подключения к целевым серверам. Храните закрытый ключ в учетных данных Jenkins, а не в репозитории.

  1. Перейдите в Manage Jenkins > Manage Credentials.
  2. Выберите хранилище учетных данных, используемое вашими задачами.
  3. Нажмите Add Credentials.
  4. Выберите SSH Username with private key.
  5. Установите стабильный ID, например ansible-ssh-key.
  6. Введите имя пользователя SSH, используемое на целевых машинах.
  7. Добавьте закрытый ключ и сохраните учетные данные.

Совет: Лучшие практики управления ключами

  • Никогда не жестко кодируйте SSH-ключи в Jenkinsfile, инвентаризации или плейбуке.
  • Используйте выделенные ключи для автоматизации, а не личные ключи.
  • Регулярно меняйте ключи в соответствии с политикой безопасности вашей команды.

Разработка конвейера Jenkins с Ansible

Используйте декларативный конвейер в Jenkinsfile, чтобы логика развертывания проверялась вместе с кодом приложения.

Базовая структура конвейера

Вот компактный пример для сборки, развертывания на staging и развертывания на production с проверкой:

// Jenkinsfile
pipeline {
    agent any

    environment {
        // Определите переменные окружения при необходимости
        ANSIBLE_HOST_KEY_CHECKING = 'False' // Будьте осторожны в production, предпочитайте known_hosts
    }

    stages {
        stage('Checkout Source') {
            steps {
                git 'https://your-scm-url/your-repo.git'
            }
        }

        stage('Build Application') {
            // Этот этап может собирать JAR, WAR, Docker-образ и т.д.
            // Пример: сборка Spring Boot JAR
            steps {
                sh 'mvn clean package'
            }
        }

        stage('Run Unit Tests') {
            steps {
                sh 'mvn test'
            }
        }

        stage('Deploy to Staging') {
            steps {
                script {
                    // Используйте SSH-агент, чтобы сделать закрытый ключ доступным для Ansible
                    sshagent(credentials: ['ansible-ssh-key']) {
                        // Выполнение плейбука Ansible
                        sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \
                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }

        stage('Deploy to Production') {
            // Этот этап может требовать ручного подтверждения
            input {
                message "Продолжить развертывание на Production?"
                ok "Развернуть на Production"
            }
            steps {
                script {
                    sshagent(credentials: ['ansible-ssh-key']) {
                        sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \
                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }
    }

    post {
        always {
            echo 'Конвейер завершен.'
        }
        success {
            echo 'Конвейер выполнен успешно!'
            // slackSend channel: '#deployments', message: "Развертывание успешно: ${env.BUILD_URL}"
        }
        failure {
            echo 'Конвейер завершился ошибкой!'
            // slackSend channel: '#deployments', message: "Развертывание не удалось: ${env.BUILD_URL}"
        }
    }
}

Ключевые детали конвейера

  • agent any подходит для демонстрации. В production используйте метку агента, гарантирующую установку Ansible.
  • sshagent(credentials: ['ansible-ssh-key']) делает закрытый ключ доступным только внутри этого блока.
  • ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml явно указывает целевую среду.
  • Блок input для production добавляет ручной шлюз перед чувствительным развертыванием.
  • Избегайте отключения проверки ключа хоста в production. Вместо этого управляйте known_hosts на агенте Jenkins.

Пример плейбука Ansible: развертывание веб-приложения

Вот упрощенные inventory/staging.ini и playbooks/deploy_app.yml для Java-веб-приложения. Имя вашего артефакта, менеджер сервисов и пути могут отличаться.

inventory/staging.ini

[web_servers]
web1.example.com
web2.example.com

[database_servers]
db1.example.com

[all:vars]
ansible_user=ubuntu

playbooks/deploy_app.yml

---
- name: Развертывание веб-приложения
  hosts: web_servers
  become: yes
  vars:
    app_name: my-webapp
    app_path: /opt/{{ app_name }}
    app_port: 8080
    app_version: "{{ app_version | default('1.0.0') }}"

  tasks:
    - name: Убедиться, что директория приложения существует
      ansible.builtin.file:
        path: "{{ app_path }}"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'

    - name: Скопировать JAR-файл приложения на целевой хост
      ansible.builtin.copy:
        src: "target/{{ app_name }}-{{ app_version }}.jar"
        dest: "{{ app_path }}/{{ app_name }}.jar"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      notify: restart app service

    - name: Убедиться, что файл сервиса systemd существует
      ansible.builtin.template:
        src: templates/my-webapp.service.j2
        dest: /etc/systemd/system/{{ app_name }}.service
        owner: root
        group: root
        mode: '0644'
      notify: restart app service

    - name: Убедиться, что сервис приложения запущен и включен
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes

  handlers:
    - name: restart app service
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes

templates/my-webapp.service.j2 (шаблон systemd-сервиса)

[Unit]
Description={{ app_name }} Application
After=network.target

[Service]
User={{ ansible_user }}
ExecStart=/usr/bin/java -jar {{ app_path }}/{{ app_name }}.jar --server.port={{ app_port }}
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

Этот плейбук создает директорию приложения, копирует собранный JAR, записывает systemd-юнит и запускает сервис. Обработчик перезапускает сервис только при изменении артефакта или файла юнита.

Лучшие практики и советы

  • Делайте плейбуки идемпотентными, чтобы повторный запуск неудачного конвейера не нанес дополнительного ущерба.
  • Храните пароли Vault в учетных данных Jenkins и передавайте их через временный файл паролей или одобренную привязку учетных данных.
  • Организуйте логику развертывания в роли, такие как webserver, database и app_deploy.
  • Используйте отдельные инвентаризации или group_vars для staging и production.
  • Запускайте Ansible на выделенных агентах Jenkins, а не на контроллере.
  • Используйте -v или -vv только при необходимости; избегайте утечки секретов в логи.
  • Тестируйте плейбуки до того, как Jenkins их запустит, особенно для ролей production.

Когда обращаться к профессионалу

Привлекайте администратора Jenkins или платформы, когда конвейер развертывает на production, управляет общими учетными данными, записывает на множество хостов или требует контроля аудита. Обработка учетных данных, проверка ключа хоста и поведение отката заслуживают повторной проверки перед первым запуском в production.

Вывод

Используйте Jenkins для оркестрации и Ansible для состояния развертывания. Храните учетные данные в Jenkins, плейбуки — в системе контроля версий, делайте инвентаризацию явной и тестируйте те же команды Ansible вне конвейера, прежде чем автоматизировать развертывание в production.