В этой статье мы с вами познакомимся на базовом уровне с Ansible, и развернем с помощью него на сервере PHP проект
Знакомство с Ansible
Что это за чудо-инструмент такой?
Ansible - это инструмент каждого YAML-чемпиона, с помощью него можно развертывать приложения, настраивать конфиги и автоматизации задач через ssh
Возможно, вы слышали про него вместе с фразой - Инфраструктура как код (IaC), потому что с помощью него в большинстве своем настраивают инфраструктуру
Основные концепции:
Playbook - это yaml файл который содержит набор задач для последовательного их выполнения
--- - name: Update web servers hosts: webservers tasks: ... - name: Update db servers hosts: databases tasks: ...
В примере указан сценарий обновление веб сервера и базы данных
В каждом сценарии мы описываем:
name- имя сценарияhosts- название хоста/хостов из inventory файлаtasks- набор задач, можно указывать напрямую инструкции или подключать отдельные файлы с самой задачей
Для детального ознакомление, перейдите по ссылке
Task - это yaml файл с неким набором команд, который отвечает за свою область (установка пакетов, настройка веб-сервера и т.д)
--- - name: Update web servers hosts: webservers tasks: - name: Ensure apache is at the latest version ansible.builtin.yum: name: httpd state: latest - name: Write the apache config file ansible.builtin.template: src: /srv/httpd.j2 dest: /etc/httpd.conf - name: Update db servers hosts: databases tasks: - name: Ensure postgresql is at the latest version ansible.builtin.yum: name: postgresql state: latest - name: Ensure that postgresql is started ansible.builtin.service: name: postgresql state: started
Здесь уже явно смотрим выполнения задач в playbook
В задаче мы можем описать:
name- название задачиansible.builtin.- модуль и его параметры
Модуль в задаче облегчает выполнения операций путем заготовленного сценария внутри модуля, а взаимодействие происходит с помощью передачи аргументов модулю
Для детального ознакомление, перейдите по ссылке
Vars - это yaml файл в котором содержится набор переменных, которые мы можем использовать в task и template файлах
--- repo_url: "https://github.com/deniskorbakov/laravel-12-frankenphp-docker.git" path_to_remote_directory: "/var/www/laravel"
В файле мы описывает переменные, которые мы хотим использовать в наших task и template файлах
- hosts: app_servers vars: app_path: "{{ path_to_remote_directory }}/22"
Чтобы использовать переменные, мы открываем и закрываем птички, а в них указываем имя нашей переменой
Для детального ознакомление, перейдите по ссылке
Template - это j2 файл, который мы можем переиспользовать в task файлах, например для копирования конфигурационных файлов с заготовленными переменными
server { listen 80; listen [::]:80; server_name {{ domain }}; server_tokens off; root {{ path_to_remote_directory }}/public; ... }
Данный файл содержит переменные, которые будут определены в ходе выполнения playbook, тем самым позволяют гибко настраивать различные файлы
Для детального ознакомление перейдите по ссылке
Inventrory - это ini файл, который содержит список хостов которыми мы можем управлять через Ansible
[web] host1 host2 ansible_port=222 # defined inline, interpreted as an integer [web:vars] http_port=8080 # all members of 'web' will inherit these myvar=23 # defined in a :vars section, interpreted as a string
С помощью этого файлы мы описываем наши хосты, которые в дальнейшем мы сможем указывать в playbook
Для детального ознакомление перейдите по ссылке
Заключение по Ansible:
С помощью данных концептов мы можем описывать сценарии задач в плейбуках, добавлять переиспользуемые переменные и шаблонные файлы, а так же иметь возможность выполнять задачи на нескольких хостах
Про проект на PHP
Данный шаблон содержит frankenphp, docker-compose окружение, веб сокеты через centrifugo, Open Api Doc и готовую авторизацию
В нем уже заранее описан playbook для развертывания на проде
Пишем Playbook
Описание того, что предстоит сделать:
На проде нам надо будет установить нужные пакеты, настроить nginx для проксирования нашего проекта, выпустить сертификаты для домена, развернуть и настроить сам проект
Настраиваем inventory:
[webservers] 144.124.249.213 [all:vars] ansible_connection=ssh ansible_user=champion
Заполняем доступы ssh для сервера, на котором будем разворачивать проект
Указываем переменные:
--- repo_url: "https://github.com/deniskorbakov/laravel-12-frankenphp-docker.git" path_to_remote_directory: "/var/www/laravel" domain: "v543323.hosted-by-vdsina.com" url: "https://{{ domain }}" os_environment: - key: APP_URL value: "{{ url }}" - key: APP_ENV value: "production" - key: APP_DEBUG value: "false" - key: OCTANE_HTTPS value: "true"
Заполняем следующие переменные:
repo_url- url нашего проекта в гитхабеpath_to_remote_directory- путь где будет лежать наш проект на сервереdomain- указываем который привязан к нашему серверуurl- формируется самостоятельно из domainos_environment- заполняем переменные для env которые заменим на проде
Создаем Playbook:
- name: Expand the environment hosts: webservers vars_files: - ../vars/default.yml tasks: - name: Init Packages ansible.builtin.include_tasks: ../tasks/packages/init.yml - name: Setup Docker ansible.builtin.include_tasks: ../tasks/docker/setup.yml - name: Clone Project ansible.builtin.include_tasks: ../tasks/sync/copy.yml - name: Init App ansible.builtin.include_tasks: ../tasks/app/init.yml - name: Configure Nginx ansible.builtin.include_tasks: ../tasks/system/nginx.yml - name: Produce Certificates ansible.builtin.include_tasks: ../tasks/system/cert.yml - name: Rebuild App ansible.builtin.include_tasks: ../tasks/app/rebuild.yml
Здесь мы указывает алиас наших хостов из inventory.ini в hosts, добавляем файл с переменными и указываем задачи, которые выполняться по очереди при запуске
Описываем Tasks:
Дальше по порядку рассмотрим каждую задачу
Init Packages
--- - name: Install required packages ansible.builtin.apt: name: - apt-transport-https - ca-certificates - curl - software-properties-common - gnupg - make - git - nginx - socat - certbot - python3-certbot-nginx state: present update_cache: yes
Здесь мы устанавливаем все необходимые нам пакеты для работы
Setup Docker
--- - name: Add Docker GPG key ansible.builtin.apt_key: url: https://download.docker.com/linux/ubuntu/gpg state: present - name: Add Docker repository ansible.builtin.apt_repository: repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable state: present filename: docker - name: Install Docker ansible.builtin.apt: name: - docker-ce - docker-ce-cli - containerd.io state: present update_cache: yes - name: Start and enable Docker service ansible.builtin.service: name: docker state: started enabled: yes - name: Add user to docker group ansible.builtin.user: name: "{{ ansible_user | default('ansible') }}" groups: docker append: yes - name: Install Docker Compose ansible.builtin.get_url: url: https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64 dest: /usr/local/bin/docker-compose mode: '0755' - name: Create symbolic link for Docker Compose ansible.builtin.file: src: /usr/local/bin/docker-compose dest: /usr/bin/docker-compose state: link - name: Verify Docker Compose installation ansible.builtin.command: docker-compose --version register: docker_compose_version changed_when: false
В данной задаче мы устанавливаем docker и docker-compose для дальнейшей работы
Clone Project
--- - name: Check if directory exists ansible.builtin.stat: path: "{{ path_to_remote_directory }}" register: project_dir_stat - name: Create project dir ansible.builtin.file: path: "{{ path_to_remote_directory }}" state: directory mode: 0755 when: not project_dir_stat.stat.exists - name: Git clone block: - name: Clone repository ansible.builtin.git: repo: "{{ repo_url }}" dest: "{{ path_to_remote_directory }}" version: "{{ branch | default('main') }}" register: clone_result retries: 3 delay: 5 until: clone_result is succeeded when: not project_dir_stat.stat.exists
Здесь мы создаем директорию для проекта, если она еще не создана, и клонируем наш проект, который мы указывали в файле переменных
Init App
--- - name: Create Storage Public Dir ansible.builtin.file: path: "{{ path_to_remote_directory }}/storage/app/public" state: directory mode: 0755 - name: Copy env.example ansible.builtin.copy: src: "{{ path_to_remote_directory }}/.env.example" dest: "{{ path_to_remote_directory }}/.env" remote_src: yes - name: Set vars in ENV lineinfile: path: "{{ path_to_remote_directory }}/.env" state: present regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value}}" with_items: "{{ os_environment }}" become: yes - name: Init Project ansible.builtin.command: cmd: make init-prod chdir: "{{ path_to_remote_directory }}" register: command_result failed_when: "'FAILED' in command_result.stderr"
Здесь мы создаем public директорию, копируем env.example в env и заменяем определенные переменные, которые у нас явно указаны в файле с переменными для ansible и запускаем make init-prod для инициализации проекта на проде
Configure Nginx
--- - name: Delete default dir ansible.builtin.file: state: absent path: /var/www/html - name: Copy config ansible.builtin.template: src: ../templates/nginx_conf.j2 dest: "/etc/nginx/sites-enabled/{{ domain }}" - name: Reload Nginx ansible.builtin.systemd: state: reloaded name: nginx
В данной задаче удаляем дефолтную директорию, копируем config для nginx и перезапускаем процесс nginx
Produce Certificates
--- - name: Obtain SSL certificate with certbot ansible.builtin.command: | certbot \ --force-renewal \ --nginx \ --noninteractive \ --agree-tos \ --cert-name {{ domain }} \ -d {{ domain }} \ -m test@gmail.com \ --verbose args: creates: "/etc/letsencrypt/live/{{ domain }}/cert.pem" become: yes register: certbot_result
Здесь уже мы выпускаем сертификаты для нашего домена через certbot
Rebuild App
--- - name: Pause for 2 min ansible.builtin.pause: minutes: 2 - name: Restart app ansible.builtin.command: cmd: make restart chdir: "{{ path_to_remote_directory }}" become: yes register: restart_result - name: Pause for 2 min ansible.builtin.pause: minutes: 2 - name: Update project ansible.builtin.command: cmd: make update-project chdir: "{{ path_to_remote_directory }}" become: yes register: update_result
В данной задаче мы перезапускаем наши контейнеры и обновляем данные проекты, чтобы все заработало наверняка !
Результат проделанной работы:
Теперь мы с вами написали ваш первый playbook и познакомились с чудо-инструментом ansible для YAML чемпионов
Жду комментарии под постом о том, что можно улучшить или как бы вы писали данный playbook ;)
Итог
Сегодня с вами узнали немного об Ansible, Изучили его базовые концепции, написали с вами Playbook и развернули проект на проде
Благодарю вас за то, что прочитали данную статью
