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-ключа. Не забываем про пустую строку на конце. Сохраняем. Alt Text

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.

Стремитесь к индемпотентности плейбука. Любые изменения должны накатываться один раз.

Придумай, как ты будешь бекапить состояния и артефакты.

Почему не надо делать так:

  • Катится при всяком коммите, нужна проверка катим ли мы на прод
  • Отсутствуют тесты
  • Игнорируются состояния
  • Если что-то надо удалить из старого - надо удалять явно
  • Всегда надо помнить про состояния
  • Можно всё сломать и чинить придётся чинить руками