Переиспользование плейбука
Иногда приходишь к тому, что плейбук становится универсальным или однотипным для каких-то нужд и при этом ты постоянно его копируешь. Копировать в целом неплохо, но хочется сократить количество открытых файлов и вносимых изменений.
Суть этого трюка в двух моментах:
- вынести общие таски деплоя в отдельный файл, чтобы импортировать
- кастомизировать отдельные таски деплоя в переменных роли
Вот древо файлов для этого трюка:
├── 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://[email protected]/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"