Переиспользование плейбука

Иногда приходишь к тому, что плейбук становится универсальным или однотипным для каких-то нужд и при этом ты постоянно его копируешь. Копировать в целом неплохо, но хочется сократить количество открытых файлов и вносимых изменений.

Суть этого трюка в двух моментах:

  • вынести общие таски деплоя в отдельный файл, чтобы импортировать
  • кастомизировать отдельные таски деплоя в переменных роли

Вот древо файлов для этого трюка:

├── app │   ├── defaults │   │   └── main.yml │   └── tasks │   └── main.yml └── common └── app.yml

Как обычно, исходники можно найти тут - src/00_sources/06_tricks/02_playbook_reuse.

Предположим есть такой плейбук:

# 01_example/common/app.yml --- - name: Create the domain in Cloudflare community.general.cloudflare_dns: zone: "{{ base_domain }}" record: "{{ app_name }}" type: A value: "{{ server_ipv4 }}" account_email: "{{ cloudflare_email }}" account_api_key: "{{ cloudflare_api_key }}" when: app_init - name: Create directories ansible.builtin.file: path: "{{ item }}" state: directory with_items: - "{{ app_directory }}" - "{{ app_source_directory }}" when: app_init - name: Download files via git ansible.builtin.git: repo: "{{ app_repository }}" dest: "{{ app_source_directory }}" force: true accept_hostkey: true key_file: "{{ git_ssh_key_path }}" - name: Build docker image community.general.docker_image: build: path: "{{ app_source_directory }}" name: "{{ app_name }}" tag: latest source: build state: present - name: Start docker container community.docker.docker_container: name: "{{ app_name }}" image: "{{ app_name }}:latest" state: started pull: false recreate: true restart_policy: "unless-stopped" command: "{{ app_command }}" labels: "{{ app_labels | items2dict }}" - name: Wait for the app to be online uri: url: "https://{{ app_host }}" status_code: 200 register: result until: result.status == 200 retries: 60 delay: 1 - name: "Done" debug: msg: "https://{{ app_host }}"

Который можно кастомизировать с помощью переменных вот так:

--- app_init: no app_directory: "/path/to/myapp" app_source_directory: "{{ app_directory }}/src" app_repository: "ssh://git@mygit.com/myapp.git" app_name: "myapp" app_domain: "{{ app_name }}" app_host: "{{ app_domain }}.{{ base_domain }}" app_internal_port: "8080" app_command: - "--db-uri=mongodb://mongodb:27017" - "server" - "--bind=:{{ app_internal_port }}" app_labels: - key: "traefik.enable" value: "true" - key: "traefik.http.routers.{{ app_name }}.entrypoints" value: "https" - key: "traefik.http.routers.{{ app_name }}.rule" value: "Host(`{{ app_host }}`)" - key: "traefik.http.routers.{{ app_name }}.tls" value: "true" - key: "traefik.http.routers.{{ app_name }}.tls.certresolver" value: "letsencrypt" - key: "traefik.http.services.{{ app_name }}.loadbalancer.server.port" value: "{{ app_internal_port }}"

Из интересных вещей тут следующие:

  • команды для запуска внутри контейнера в одной переменной в виде списка
app_command: - "--db-uri=mongodb://mongodb:27017" - "server" - "--bind=:{{ app_internal_port }}"
  • лейблы для контейнера разложены на key/value, которые дальше в плейбуке раскладываются с помощью items2dict
# 01_example/app/defaults/main.yml app_labels: - key: "traefik.enable" value: "true" - key: "traefik.http.routers.{{ app_name }}.entrypoints" value: "https" - key: "traefik.http.routers.{{ app_name }}.rule" value: "Host(`{{ app_host }}`)" - key: "traefik.http.routers.{{ app_name }}.tls" value: "true" - key: "traefik.http.routers.{{ app_name }}.tls.certresolver" value: "letsencrypt" - key: "traefik.http.services.{{ app_name }}.loadbalancer.server.port" value: "{{ app_internal_port }}"
# 01_example/common/app.yml labels: "{{ app_labels | items2dict }}"

И теперь всё, что остаётся для каждого нового приложения - это заимпортировать из common/app.yml и изменить переменные по вкусу:

# 01_example/app/tasks/main.yml --- - name: Generic app tasks ansible.builtin.include_tasks: "../../common/tasks/app.yml"