Gitlab CI + Ansible + Alpine IaaC или как не надо делать

Или как не надо делать.
1. Заводим в Gitlab проект
2. Проект - это у нас один большой плейбук, структура моего плейбука выглядит примерно так
infra/
├── README.md
├── group_vars
│   └── all.yml
├── host_vars
│   └── specific_host.yml
├── inventory
│   └── inventory.ini
├── meta
│   └── main.yml
├── requirements.yml
├── roles
│   ├── deploy
│   │   └── tasks
│   │       ├── helloworld.yml
│   │       └── main.yml
│   ├── prepare
│   │   └── tasks
│   │       ├── apt.yml
│   │       ├── hostname.yml
│   │       └── main.yml
└── site.yml
- requirements.yml - список зависимостей, особая благодарность 
geerlingguy.github-usersиgeerlingguy.security; - Разбиваю логически этапы на единообразные роли;
 - Внутри единообразных ролей main.yml делает только 
include_tasks; - Всё, что можно засунуть в Ansible Loop - оказывается в Ansible Loop;
 - В inventory добавляем свои хосты
 
3. В разделе Settings(1), CI/CD(2), Variables(3) создаём секрет с произвольным именем, например SSH_PRIVATE_KEY
Есть два типа секретов - переменная и файл. В первом случае при вызове этой переменной возвращается её значение, а во втором - абсолютный путь до файла
Делаем её защищённой
Внутрь пишем приватную часть ssh-ключа. Не забываем про пустую строку на конце. Сохраняем.

4. Создаём .gitlab-ci.yml
---
image: alpine:latest  # берём за основу последний alpine
before_script:
  - 'apk add openssh-client ansible'  # ставим глобально нужные пакеты
  - eval $(ssh-agent -s)  # используем ssh-agent для подключения
  - mkdir -p ~/.ssh  # готовим shh-конфиг
  - echo 'StrictHostKeyChecking no' > ~/.ssh/config
  - chmod 0600 ${id_rsa_path}
  - ssh-add ${id_rsa_path}
stages:  # разбиваем процесс на этапы тестов и деплоя.
  - test
  - deploy
yaml_linter:  # Имя для джобы
  stage: test  # К какому этапу относится
  script:
    - apk add yamllint  # ставим пакеты
    - yamllint .  # запускаем
ansible_linter:
  stage: test
  script:
    - apk add ansible-lint
    - ansible-lint site.yml
ansible_check_mode:
  stage: test
  script:
    - ansible-galaxy install -r requirements.yml
    - ansible-playbook --diff -i inventory/inventory.ini site.yml --check
deploy_job:
  stage: deploy
  script:
    - ansible-galaxy install -r requirements.yml
    - ansible-playbook --diff -i inventory/inventory.ini site.yml
  only:  # запускаем только в следующих бранчах
    - main
Не забудьте положить слепок ключа в ~/.ssh/authorized_keys.
Стремитесь к индемпотентности плейбука. Любые изменения должны накатываться один раз.
Придумай, как ты будешь бекапить состояния и артефакты.
Почему не надо делать так:
Катится при всяком коммите, нужна проверка катим ли мы на продОтсутствуют тесты- Игнорируются состояния
 - Если что-то надо удалить из старого - надо удалять явно
 - Всегда надо помнить про состояния
 - Можно всё сломать и чинить придётся чинить руками