Автоматизация облачной инфраструктуры с помощью SparrowCI

Пайплайн SparrowCI — это дерево задач, где одна задача может зависеть от других и так далее. У задач также есть состояния, которые доступны из других задач. Эта архитектура очень хорошо соответствует природе иерархических облачных ресурсов.

Рассмотрим этот простой пример идемпотентного создания экземпляра ec2:

tasks:
  - 
    name: create-ec2
    default: true
    config:
      name: host_01
      image: ami-0e322da50e0e90e21
    if:
       language: Bash
       code: |
        name=$(config name)
        instance_id=$(aws ec2 describe-instances \
           --filters "Name=tag:Name,Values=$name" \
           | yq ‘.Reservations | .[0] | .Instances \
           | .[0]| .InstanceId’)
        if $instance_id != “null”; then
           echo "{ \"status\": \"skip\" }" \
           > $cache_root_dir/state.json 
        fi
    language: Raku
    init: |
      run_task "ec2", %(
        sn => config()<tasks><state><create-sn><state><sn-id>
      );
    subtasks:
      - 
         name: ec2
         language: Bash
         code: |
           set -e

           name=$(config name)
           image=$(config image)

           id=$(aws ec2 run-instances \
           --image-id $image \
           --count 1 \
           --instance-type t2.micro  \
           --subnet-id $sn \
           | yq ‘.Instances | .[0] | .InstanceId’)

           aws ec2 create-tags \
            --resources $id \
            --region $region  \
            --tags "Name=$name"
    depends:
      - 
         # create subnet
         name: create-sn
         config:
           sb_name: subnet_01
  - 
     name: create-sn
     config:
        cidr_block: 10.0.0.0/24
        vpc_id: vpc-000111222
     language: Bash
     code: |
       set -e
       sb_name=$(config sb_name)
       cidr_block=$(config cidr_block)
       vpc_id=$(config vpc_id)

       SubnetId=$(aws ec2 describe-subnets \
       --filters "Name=tag:Name,Values=$sb_name" \
       | yq ‘.subnets | .[0] | .SubnetId’)

       if $SubnetId != "null"; then
         echo "{ \"sn-id\": \"$SubnetId\" }" \
         > $cache_root_dir/state.json
       else
         SubnetId=(aws ec2 create-subnet \
           --vpc-id $vpc_id \
           --cidr-block $cidr_block \
           --tag-specifications \
           “ResourceType=subnet,Tags=[{Key=Name,Value=$sb_name}]” \
         | yq ‘.Subnet.SubnetId’)

          echo "{ \"sn-id\": \"$SubnetId\" }" \
          > $cache_root_dir/state.json
       fi

В этом сценарии задача, создающая экземпляр ec2, зависит от задачи, которая сначала создает подсеть. Идентификатор подсети доступен в задаче создания экземпляра ec2 через синтаксис хэша Raku config()<tasks><create-sn><sn-id>, где create-sn — это имя задачи, создающей подсеть.

Модификатор «Если» первой задачи предотвращает выполнение задачи, если экземпляр ec2 с заданным именем host_01 существует, объявляя статус как "status": "skip" .

Задачи Bash объявляют о своем состоянии, выгружая данные в файл state.json, другие языки могут вызывать функцию updates_state(hash) напрямую.

Создание нескольких экземпляров

Приведенный выше пример может быть легко расширен для развертывания множества экземпляров ec2. Все, что нам нужно сделать, это использовать синтаксис hub tasks для многократного выполнения одного и того же шаблона задачи:

tasks:
  - 
    name: create-ec2
    default: true
    config:
      image: ami-0e322da50e0e90e21
    hub:
      language: Raku
      code: |
        update_state %(
          list => [
             { config => { image => config()<image>, name => “host_01” } },
             { config => { image => config()<image>, name => “host_02” } },
             { config => { image => config()<image>, name => “host_03” } },
          ]
        );
      if:
        language: Bash
        code: |
          name=$(config name)
          instance_id=$(aws ec2 describe-instances \
           --filters "Name=tag:Name,Values=$name" \
           | yq ‘.Reservations | .[0] | .Instances \
           | .[0].InstanceId’)
          if $instance_id != “null”; then
             echo "{ \"status\": \"skip\" }" \
             > $cache_root_dir/state.json 
          fi
     language: Raku
     init: |
       # the rest is the same
       # as in the snippet above 
        

Этот простой пример демонстрирует гибкость SparrowCI, позволяющую пользователям создавать различные сценарии для управления сложной облачной инфраструктурой.

Спасибо за чтение!

PS. Поразмыслив над этим примером, я придумал лучшее решение — “https://github.com/melezhik/sparrowci-aws-example/blob/main/sparrow.yaml» — но это, наверное, тема к следующему посту))