Pull to refresh
70.47
Слёрм
Учебный центр для тех, кто работает в IT

Amazon + Ansible

Reading time 5 min
Views 13K
image + image

В данной заметке хочу рассказать о некоторых приемах решений задач по связке ansible + amazon, может кому-то будет полезно или кто-то подскажет решения лучше. О установке/настройке ansible информации уже много, так что её я пропущу. О работе с amazon тоже ничего оригинального добавить не смогу. Итак, приступим.

Введение


Попросили помочь с разовой настройкой проекта, конкретно часть ansible + amazon, а так же конфигурация серверов на базе ubuntu и сервисов. Требования к проекты были выставлены такие:
  • Сервера балансировщики на nginx
  • N-е количество бэкендов nginx + nodejs
  • Сервер redis master
  • N-е количество redis slave
  • N-е количество windows servers ( что-то для криптографии, с условием готового шаблона)
  • Создавать очереди SQS
  • Создавать security groups


Примечание

Заказчик пожелал, что не нужно вносить правки на уже работающие сервера, если надо что-то поменять — меняют скрипты создания, сносят старое и создают новое… Хозяин — барин.

Работаем

Для большинства задач можно использовать модуль ec2. Модуль замечательный :), с большим количеством “подмодулей” почитать их описание можно тут: /usr/share/ansible/cloud (для ubuntu/debian).
В инвентарный файл ansible (hosts) нужно прописать всего 1 хост:

[local]
localhost


Я начал с самого простого, с windows server. Приведу пример playbook:

- hosts: localhost
  connection: local
  gather_facts: False
  vars:
    hostname: Windows
    ec2_access_key: "Secret"
    ec2_secret_key: "Secret_key”
    instance_type: "t2.micro"
    image: "ami-xxxxxxxx"
    group: "launch-wizard-1"
    region: "us-west-2"
  tasks:
    - name: make one instance
      ec2: image={{ image }}
           instance_type={{ instance_type }}
           aws_access_key={{ ec2_access_key }}
           aws_secret_key={{ ec2_secret_key }}
           instance_tags='{ "Name":"{{ hostname }}" }'
           region={{ region }}
           group={{ group }}
           wait=true


Тут все просто, ansible используя ваши aws_access_key и aws_secret_key посылает запрос в amazon на создание машинки с именем hostname=”Windows” типа instance_type=«t2.micro» из ранее созданого образа image=«ami-xxxxxxxx» в регионе region=«us-west-2» и присваивает sequrity group group=«launch-wizard-1» и больше ничего.

Далее сложнее, будем делать бэкенды nginx. Для начала все то же самое, создаем машину, но нужно с ней работать в этом же плэйбуке.
Создадим в кабинете amazon keypair, назовем его, например, aws_ansible. Скачиваем ключик и копируем его в ~/.ssh/id_rsa пользователя из под которого запускаете плэйбуки.

Приведу в пример плэйбук создания бэкенда:

- hosts: localhost
  connection: local
  gather_facts: False
  vars:
    hostname: Nginx_nodejs
    ec2_access_key: “Secret"
    ec2_secret_key: "Secret_key"
    keypair: "aws_ansible"
    instance_type: "t2.micro"
    image: "ami-33db9803"
    group: "launch-wizard-1"
    region: "us-west-2"
  tasks:
    - name: make one instance
      ec2: image={{ image }}
           instance_type={{ instance_type }}
           aws_access_key={{ ec2_access_key }}
           aws_secret_key={{ ec2_secret_key }}
           keypair={{ keypair }}
           instance_tags='{ "Name":"{{ hostname }}" , "Group":"nginx_backend" }'
           region={{ region }}
           group={{ group }}
           wait=true
      register: ec2_info

    - debug: var=ec2_info
    - debug: var=item
      with_items: ec2_info.instance_ids

    - add_host: hostname={{ item.public_ip }} groupname=ec2hosts
      with_items: ec2_info.instances

    - name: wait for instances to listen on port:22
      wait_for:
        state=started
        host={{ item.public_dns_name }}
        port=22
      with_items: ec2_info.instances

- hosts: ec2hosts
  gather_facts: True
  user: ubuntu
  sudo: True
  vars:
     connections : "4096"

  tasks:
     - include: nginx/tasks/setup.yml
  handlers:
     - name: restart nginx
       action: service name=nginx state=restarted

- hosts: ec2hosts
  gather_facts: True
  user: ubuntu
  sudo: True
  
  tasks:
 - include: nodejs/tasks/setup.yml


Теперь, что поменялось:
Мы указали, что для этой машинки использовать наш ключ keypair: «aws_ansible»
Указали образ чистой ubuntu image: «ami-33db9803».
С помощью registr и debug мы получили public_ip новой машинки и записали её во временную инвентаризацию в группу ec2hosts, записывать в hosts файл из плэйбука нельзя ( я не нашел как).
Следующим действием “wait for instances to listen on port:22” ждем когда станет доступен ssh.
И после всего этого выполняем обычные сценарии с обычным сервером, в моем случае установка/настройка nginx и nodejs

Еще я добавил Tag «Group»:«nginx_backend», это нужно для того что бы работать со всеми бэкендами сразу. Как? Есть скрипт подходящий для инвентаризации серверов amazon в ansible. Почитать о нем, а так же скачать его можно тут docs.ansible.com/intro_dynamic_inventory.html#id6.

Отлично, но у меня ситуация не много иная, мне нужно сделать upstream в nginx с неизвестным заранее количеством бэкендов. Побороздив просторы документации по ansible, не нашел как делать динамические списки. То есть подставлять динамически ip бэкенда — пожалуйста, а вот их количество менять… Как всегда на выручку пришел старый способ, написал велосипед ан python. не большой скрипт который вызывается из плэйбука до настройки nginx и генерирует конфиг с upstream.

Листинг:

#!/usr/bin/env python

import  sys, os
from commands import *

group = '"tag_Group_nginx_backend": ['
template = "/etc/ansible/playbooks/nginx/templates/balance.conf.j2"
list_ip = []
#Create ec2_list
data = getoutput("/etc/ansible/ec2.py --refresh-cache")

flag = 0
for line in data.split("\n"):
    if flag:
        if line.strip() != "],":
            list_ip.append(line.strip().strip(",").strip("\""))
        else:
            break
    if line.strip() == group:
        flag = 1

f = open(template, 'w')
f.write('''# upstream list
upstream backend {''')
f.close()
for ip in list_ip:
    f = open(template, 'a')
    f.write('''
    server '''+ip+''':80 weight=3 fail_timeout=15s;''')
    f.close()
f = open(template, 'a')
f.write('''
}''')
f.close()


Еще одна проблема была с redis master, его ip нужно было прописывать на каждый слэйв. Решил сделать с помощью include_vars.

При создании мастера до проверки доступности ssh делаю так:

   - replace: dest={{ redis_master_ip }} regexp='^(\s+)(master\:)\s(.*)$' replace='\1\2 {{ item.public_ip }}'
      with_items: ec2_info.instances

В переменных указал:

  redis_master_ip: "/etc/ansible/playbooks/redis/files/master_ip.yml"

Сам файл изначально должен быть и выглядит примерно так:

 master: 1.2.3.4

Затем в плейбуке настройки redis slave добавляем:

- name: Get master IP
  include_vars: "{{ redis_master_ip }}"


Используем переменную {{ master }} в шаблоне.

Sequrity group создается просто, используем модуль ec2_group:

- hosts: localhost
  connection: local

  tasks:
    - name: nginx  ec2 group
      local_action:
        module: ec2_group
        name: nginx
        description: an nginx EC2 group
        region: us-west-2
        aws_secret_key: "Secret"
        aws_access_key: "Secret"
        rules:
          - proto: tcp
            from_port: 80
            to_port: 80
            cidr_ip: 192.168.0.0/24
          - proto: tcp
            from_port: 22
            to_port: 22
            cidr_ip: 0.0.0.0/0
        rules_egress:
          - proto: all
            cidr_ip: 0.0.0.0/0


Сложнее оказалось с очередями, модуля для них не нашлось, пытался даже доделать чьи-то попытки его написать. Но быстро опомнился и сделал через cloudformation.

Так выглядит плэйбук:

- hosts: localhost
  connection: local
  gather_facts: False
  vars:
    sqs_access_key: “Secret"
    sqs_secret_key: "Secret"
    region: "us-west-2”
  tasks:
  - name: launch some aws services
    cloudformation: >
      stack_name="TEST"
      region={{ region }}
      template=files/cloudformation.json
<\code>

А так template:

<code>
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS CloudFormation SQS”
  "Resources" : {
    "MyQueue" : {
      "Type" : "AWS::SQS::Queue"
    }
  },
  "Outputs" : {
    "QueueURL" : {
      "Description" : "URL of newly created SQS Queue",
      "Value" : { "Ref" : "MyQueue" }
    },
    "QueueARN" : {
      "Description" : "ARN of newly created SQS Queue",
      "Value" : { "Fn::GetAtt" : ["MyQueue", "Arn"]}
    }
  }
}


Резюме

Популярность облачных решений и систем управления конфигурациями растет, но я потратил порядочно времени выискивая варианты того или иного решения, собирая информацию и тд. Отсюда и родилась идея написать эту статью.

Автор: Бурнашев Роман, главный системный администратор компании centos-admin.ru
Tags:
Hubs:
+4
Comments 2
Comments Comments 2

Articles

Information

Website
slurm.io
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Антон Скобин