Статьи

Непрерывная доставка в облаке: динамическая настройка

В первой части этой серии я представил конвейер Continuous Delivery (CD) для приложения Manatee Tracking . Во второй части я рассказал о том, как мы используем этот конвейер CD для доставки программного обеспечения от регистрации до производства. В третьей части мы сосредоточились на том, как CloudFormation используется для создания сценариев виртуальных компонентов AWS, которые создают инфраструктуру Manatee. Список тем для каждой из статей приводится ниже:

Часть 1: Введение — Введение в непрерывную доставку в облаке и остальные статьи;
Часть 2: CD Pipeline — углубленный взгляд на CD Pipeline;
Часть 3: CloudFormation — подготовка виртуальных ресурсов по сценарию ;
Часть 4: Динамическая настройка — что вы читаете сейчас;
Часть 5. Автоматизация развертывания. Скриптовое управление развертыванием;
Часть 6. Автоматизация инфраструктуры. Предоставление сценариев для среды (Автоматизация инфраструктуры)

В этой части серии я собираюсь объяснить, как мы динамически генерируем нашу конфигурацию и по возможности избегаем файлов свойств. Вместо того чтобы использовать файлы свойств, мы сохраняем и извлекаем конфигурацию на лету — как часть конвейера CD — без предварительного определения этих значений в статическом файле (то есть файле свойств) заранее. Мы делаем это, используя два метода: AWS SimpleDB и CloudFormation .

SimpleDB — это высокодоступный нереляционный сервис хранения данных, который хранит только строки в парах ключ-значение. CloudFormation, как обсуждалось в третьей части серии, является языком сценариев для распределения и настройки виртуальных ресурсов AWS.

Использование SimpleDB

На всем протяжении конвейера CD нам часто нужно управлять состоянием в нескольких заданиях Jenkins. Для этого мы используем SimpleDB. По мере выполнения конвейера значения, которые потребуются последующим заданиям, сохраняются в SimpleDB как свойства. Когда свойства необходимы, мы используем простой скрипт сценария Ruby, чтобы вернуть пару ключ / значение из SimpleDB, а затем использовать его как часть работы. Сохраняемые и извлекаемые значения варьируются от IP-адресов и доменных имен до идентификаторов AMI (образов машин).

Так что же делает это динамичным? Когда выполняются задания Jenkins или шаблоны CloudFormation, мы часто получаем свойства, которые необходимо использовать где-либо еще. Вместо жесткого кодирования всех значений, которые будут использоваться в файле свойств, мы создаем, храним и извлекаем их по мере выполнения конвейера.

if [ $deployToProduction ] == true
then
SSH_KEY=production
else
SSH_KEY=development
fi

# Create Cloudformaton Stack
ruby /usr/share/tomcat6/scripts/aws/create_stack.rb ${STACK_NAME} ${WORKSPACE}/production.template ${HOST} ${JENKINSIP} ${SSH_KEY} ${SGID} ${SNS_TOPIC}

# Load SimpleDB Domain with Key/Value Pairs
ruby /usr/share/tomcat6/scripts/aws/load_domain.rb ${STACK_NAME}

# Pull and store variables from SimpleDB
host=`ruby /usr/share/tomcat6/scripts/aws/showback_domain.rb ${STACK_NAME} InstanceIPAddress`

# Run Acceptance Tests
cucumber features/production.feature host=${host} user=ec2-user key=/usr/share/tomcat6/.ssh/id_rsa

Упомянутый выше в
фрагменте кода CreateTargetEnvironment . Это
скрипт
load_domain.rb, который перебирает файл и отправляет пары ключ / значение в SimpleDB.

require 'rubygems'
require 'aws-sdk'
load File.expand_path('../../config/aws.config', __FILE__)

stackname=ARGV[0]

file = File.open("/tmp/properties", "r")

sdb = AWS::SimpleDB.new

AWS::SimpleDB.consistent_reads do
  domain = sdb.domains["stacks"]
  item = domain.items["#{stackname}"]

  file.each_line do|line|
    key,value = line.split '='
    item.attributes.set(
      "#{key}" => "#{value}")
  end
end

Упомянутый выше в
фрагменте кода CreateTargetEnvironment . Это
скрипт
showback_domain.rb, который подключается к SimpleDB и возвращает пару ключ / значение.

load File.expand_path('../../config/aws.config', __FILE__)

item_name=ARGV[0]
key=ARGV[1]

sdb = AWS::SimpleDB.new

AWS::SimpleDB.consistent_reads do
  domain = sdb.domains["stacks"]
  item = domain.items["#{item_name}"]

  item.attributes.each_value do |name, value|
    if name == "#{key}"
      puts "#{value}".chomp
    end
  end
end

В приведенном выше фрагменте кода CreateTargetEnvironment мы сохраняем выходные данные стека CloudFormation во временном файле. Затем мы перебираем файл с помощью скрипта load_domain.rb и сохраняем пары ключ / значение в SimpleDB.

После этого мы вызываем SimpleDB со скриптом showback_domain.rb, возвращаем IP-адрес экземпляра (созданный в шаблоне CloudFormation) и сохраняем его в hostпеременной. hostзатем используется огурцом для ssh в целевой экземпляр и запуска приемочных тестов.

Использование CloudFormation

В наших шаблонах CloudFormation мы выделяем несколько ресурсов AWS. Каждый раз, когда мы запускаем шаблон, используется другой ресурс. Например, в нашем jenkins.templateмы создаем нового пользователя IAM . Каждый раз, когда мы запускаем шаблон, создается новый пользователь IAM с разными учетными данными. Нам нужен способ ссылки на эти ресурсы. Именно здесь вступает CloudFormation. Вы можете ссылаться на ресурсы в других ресурсах по всему сценарию. Вы можете определить ссылку на другой ресурс, используя функцию Ref в CloudFormation. Используя Ref, вы можете динамически ссылаться на значения других ресурсов, таких как IP-адрес, имя домена и т. Д.

В этом сценарии мы создаем пользователя IAM, ссылаемся на пользователя IAM, чтобы создать ключи доступа AWS, а затем сохраняем их в переменной среды.

"CfnUser" : {
  "Type" : "AWS::IAM::User",
  "Properties" : {
    "Path": "/",
    "Policies": [{
      "PolicyName": "root",
      "PolicyDocument": {
        "Statement":[{
          "Effect":"Allow",
          "Action":"*",
          "Resource":"*"
        }
      ]}
    }]
  }
},

"HostKeys" : {
  "Type" : "AWS::IAM::AccessKey",
  "Properties" : {
    "UserName" : { "Ref": "CfnUser" }
  }
},

"# Add AWS Credentials to Tomcat\n",
"echo \"AWS_ACCESS_KEY=", { "Ref" : "HostKeys" }, "\" >> /etc/sysconfig/tomcat6\n",
"echo \"AWS_SECRET_ACCESS_KEY=", {"Fn::GetAtt": ["HostKeys", "SecretAccessKey"]}, "\" >> /etc/sysconfig/tomcat6\n",

Затем мы можем использовать эти ключи доступа в других сценариях, ссылаясь на переменные среды $AWS_ACCESS_KEYи $AWS_SECRET_ACCESS_KEY.

Чем это отличается от типичного управления конфигурацией?

Как правило, во многих организациях имеется большое свойство с жестко закодированными парами ключ / значение, которое передается в конвейер. Конвейер выполняется с использованием заданных параметров и не может масштабироваться или изменяться без изменения пользователем файла свойств. Они не могут масштабироваться или адаптироваться, потому что все свойства жестко запрограммированы, если файл свойств жестко кодирует IP для экземпляра EC2, и он по какой-либо причине отключается, их конвейер не работает, пока кто-то не исправит файл свойств. Есть более эффективные способы сделать это при использовании облака. Облако предоставляет ресурсы по требованию, которые будут постоянно меняться. Эти ресурсы каждый раз будут иметь разные IP-адреса, доменные имена и т. Д.

С динамической конфигурацией нет файлов свойств, каждое свойство генерируется как часть конвейера.

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

В следующей части нашей серии, посвященной автоматизации развертывания, мы рассмотрим создание сценариев и тестирование развертывания с использованием стандартных инструментов. В следующей статье вы узнаете, как организовать последовательности развертывания и настройки с помощью Capistrano.