У меня структура проекта с ansible выглядит вот так:
Ansible # Группа в gitlab.
Roles # Группа внутри Ansible, в которой проекты по названию роли.
Playbooks # Проект с файликами playbook-ов.
Inventory # Проект с инвентарем. Тут происходит все управление за счет переменных.
Для реализации сиай мы приходим к модели, где любая роль может быть раскатана с теми или иными значениями и поведением за счет управления ее из инвентори, следовательно инвентори это то место, где должна быть реализована сиай.
Есть только один нюанс, с такой точки зрения инвентарь по-сути - это монорепа. Для удобства сделаем сиай динамическим:
default:
tags: [ <tags> ]
include:
- project: "<project_path"
ref: <ci_version>
file:
- "<template_file>"
git:get:diff:
stage: .pre
extends: [".ansible_git_diff"]
image: <ansible_image>
artifacts:
reports:
dotenv: deploy_var.env
expire_in: 24 hours
paths:
- changed_inventories.json
expire_in: 24 hours
create:deploy:pipeline:
stage: render
needs: ["git:get:diff"]
image: <gomplate_image>
script:
- .gitlab/scripts/render.sh
variables:
TEMPLATE_FILE: ".gitlab/templates/deploy.yml.tmpl"
RENDERED_FILE: ".gitlab/dynamic/deploy.yml"
COLLAPSE_NAME: "Deploy job template"
COLLAPSE_CAT_FILE: ".gitlab/dynamic/deploy.yml"
artifacts:
paths:
- .gitlab/dynamic
expire_in: 24 hours
deploy:trigger:
stage: prepare
needs: ["git:get:diff", "create:deploy:pipeline"]
trigger:
include:
- artifact: .gitlab/dynamic/deploy.yml
job: create:deploy:pipeline
Это основной
.gitlab-ci.yml в корне инвенторя. Сами темплейты в удаленной репе показаны не будут, они не влезут да и тут только общий подход в конечном проекте.В джобе
create:deploy:pipeline мы рендерим с помощью gomplate вот такой шаблон:default:
tags: [ <tags> ]
include:
- project: "<project_path>"
ref: <ci_version>
file:
- "<template_file>"
{{- if (eq (env.Getenv "SKIP_DEPLOY") "true") }}
nothing:to:deploy:
stage: deploy
script:
- echo "No inventory found for deploy"
{{- else }}
{{- $inventory_dirs := (datasource "inventory_dirs" "changed_inventories.json") }}
{{- range $path := $inventory_dirs }}
deploy:{{ $path | strings.ReplaceAll "/" "-" }}:
stage: deploy
extends: [".ansible_run_playbook"]
image: <ansible_image>
variables:
INVENTORY_PATH: {{ $path }}
PLAYBOOK: {{ index (strings.Split "/" (print $path)) 0 }}.yml
{{- end }}
{{- end }}
Переменную SKIP_DEPLOY мы получали на шаге
git:get:diff, как и changed_inventories.json (в нем сравнение изменений по git).В этой модели предполагается, что директории в инвенторе и имена плэйбуков одинаковы. Это видно по переменным в шаблонной джобе.
Скрипт для рендера выглядит так:
#!/bin/bash
TEMPLATE_DIR="."
function collapse () {
echo -e "\e[0Ksection_start:`date +%s`:templates[collapsed=true]\r\e[0K$1"
cat $2
echo -e "\e[0Ksection_end:`date +%s`:templates\r\e[0K"
}
gomplate -f $TEMPLATE_FILE -o $RENDERED_FILE -d inventory_dirs=$TEMPLATE_DIR
collapse "${COLLAPSE_NAME}" "${COLLAPSE_CAT_FILE}"
Ну и последним шагом
deploy:trigger просто вызываем отрендеренный ранее шаблон через trigger job.В итоге за счет шаблонизации мы получаем динамически создаваемое кол-во джобов в зависимости от изменений в inventory.
#gitlab