git add .gitignore
git commit -m "Add a .gitignore file"
Чтобы поделиться этим кодом со своими коллегами, вам необходимо создать общий Git-репозиторий, к которому вы все можете обращаться. Для этого можно использовать GitHub. Перейдите на сайт https://github.com, зарегистрируйтесь (если вы этого еще не сделали), создайте новый репозиторий и укажите его в качестве удаленной точки входа под названием origin для своего локального Git-репозитория:
git remote add origin git@github.com:
Теперь, когда вы хотите поделиться своими изменениями с коллегами, можете загрузить изменения в репозиторий origin:
git push origin master
Если вы хотите посмотреть, какие изменения внесли ваши коллеги, можете загрузить изменения из репозитория origin:
git pull origin master
При дальнейшем чтении этой книги и в целом при применении Terraform не забывайте регулярно фиксировать (gitcommit) и загружать (gitpush) свои изменения. Это позволит работать над кодом совместно с членами вашей команды. К тому же все изменения вашей инфраструктуры будут записываться в журнал фиксаций, что придется очень кстати в случае отладки. Больше о командной работе с Terraform вы узнаете в главе 8.
Развертывание одного веб-сервера
Следующим шагом будет развертывание на этом инстансе веб-сервера. Мы попытаемся развернуть простейшую веб-архитектуру: один веб-сервер, способный отвечать на HTTP-запросы (рис. 2.6).
В реальных условиях для создания веб-сервера использовался бы веб-фреймворк наподобие Ruby on Rails или Django. Чтобы не усложнять этот пример, мы запустим простейший веб-сервер, который всегда возвращает текст Hello,World29:
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
Рис. 2.6. Начнем с простой архитектуры: одного веб-сервера, запущенного в AWS, который отвечает на HTTP-запросы
Это bash-скрипт, записывающий текст Hello, World в файл index.html, который затем раздается на порте 8080 с помощью веб-сервера, запущенного с использованием инструмента под названием busybox (https://busybox.net/) (в Ubuntu установлен по умолчанию). Я указал команду busybox между nohup и &, чтобы веб-сервер постоянно работал в фоне, даже после завершения bash-скрипта.
Номера портов
В этом примере вместо стандартного HTTP-порта 80 используется 8080. Дело в том, что все порты меньше 1024 требуют администраторских привилегий. Это потенциальный риск безопасности, поскольку любой злоумышленник, которому удастся взломать ваш сервер, тоже получит привилегии администратора.
В связи с этим веб-сервер рекомендуется запускать от имени обычного пользователя с ограниченными правами доступа. Это означает, что вам следует прослушивать порты с более высокими номерами, но, как вы увидите позже в этой главе, вы можете настроить балансировщик нагрузки так, чтобы он работал на порте 80 и перенаправлял трафик на более высокие порты ваших серверов.
Как запустить этот скрипт на сервере EC2? Как уже упоминалось в подразделе «Средства шаблонизации серверов» на с. 31, для этого обычно применяется инструмент вроде Packer, который создает пользовательский образ AMI с предустановленным веб-сервером. Поскольку в этом примере в качестве веб-сервера используется однострочный скрипт на основе busybox, вы можете обойтись стандартным образом Ubuntu 18.04 и запустить скрипт Hello, World в рамках конфигурации пользовательских данных вашего сервера EC2. Вы можете передать параметру user_data в коде Terraform то, что будет выполнено при загрузке сервера: либо скрипт командной оболочки, либо директиву cloud-init. Вариант со скриптом показан ниже:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-example"
}
}
<<-EOF и EOF — элементы синтаксиса heredoc, который позволяет создавать многострочные строковые литералы без использования множества символов перехода на новую строку.
Перед запуском этого веб-сервера нужно сделать еще кое-что. AWS по умолчанию закрывает для сервера EC2 весь входящий и исходящий трафик. Чтобы ваш сервер мог принимать запросы на порте 8080, необходимо создать группу безопасности:
resource "aws_security_group" "instance" {
name = "terraform-example-instance"
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Этот код создает новый ресурс под названием aws_security_group (обратите внимание, что все ресурсы в AWS начинаются с aws_) и делает так, чтобы эта группа разрешала принимать на порте 8080 TCP-запросы из блока CIDR 0.0.0.0/0. Блок CIDR — это краткая запись диапазона IP-адресов. Например, блок CIDR 10.0.0.0/24 представляет все IP-адреса между 10.0.0.0 и 10.0.0.255. Блок CIDR 0.0.0.0/0 охватывает диапазон всех возможных IP-адресов, поэтому данная группа безопасности разрешает принимать на порте 8080 запросы с любого IP30.
Создания группы безопасности как таковой будет недостаточно. Нужно сделать так, чтобы сервер EC2 ее использовал. Для этого вы должны передать ее идентификатор аргументу vpc_security_group_ids ресурса aws_instance. Но сначала необходимо познакомиться с выражениями Terraform.
В Terraform выражением является все, что возвращает значение. Вы уже видели простейший тип выражений — литералы, такие как строки (например, "ami-0c55b159cbfafe1f0") и числа (скажем, 5). Terraform поддерживает много других разновидностей выражений, которые будут встречаться на страницах этой книги.
Особенно полезным типом выражений является ссылка, которая позволяет обращаться к значениям с других участков кода. Чтобы указать ID группы безопасности, нужно сослаться на атрибут ресурса с помощью такого синтаксиса:
PROVIDER — это имя провайдера (например, aws), TYPE — это тип ресурса (вроде security_group), NAME — имя этого ресурса (в нашем случае группа безопасности называется "instance"), а ATTRIBUTE — это либо один из аргументов ресурса (скажем, name), либо один из атрибутов, которые он экспортировал (список доступных атрибутов можно найти в документации каждого ресурса). Группа безопасности экспортирует атрибут под названием id, поэтому к нему можно обратиться с помощью такого выражения:
aws_security_group.instance.id
Вы можете использовать идентификатор этой группы безопасности в аргументе vpc_security_group_ids ресурса aws_instance:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-example"
}
}
Ссылаясь в одном ресурсе на другой, вы создаете неявную зависимость. Terraform анализирует такие зависимости, строит из них граф и применяет его для автоматического определения порядка, в котором должны создаваться ресурсы. Например, если бы этот код развертывался с нуля, система Terraform знала бы о том, что группу безопасности нужно создать раньше, чем сервер EC2, поскольку последний использует ID этой группы. Вы можете даже вывести граф зависимостей с помощью команды graph:
$ terraform graph
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] aws_instance.example"
[label = "aws_instance.example", shape = "box"]
"[root] aws_security_group.instance"
[label = "aws_security_group.instance", shape = "box"]
"[root] provider.aws"
[label = "provider.aws", shape = "diamond"]
"[root] aws_instance.example" ->
"[root] aws_security_group.instance"
"[root] aws_security_group.instance" ->
"[root] provider.aws"
"[root] meta.count-boundary (EachMode fixup)" ->
"[root] aws_instance.example"
"[root] provider.aws (close)" ->
"[root] aws_instance.example"
"[root] root" ->
"[root] meta.count-boundary (EachMode fixup)"
"[root] root" ->
"[root] provider.aws (close)"
}
}
Вывод выполнен на языке описания графов под названием DOT. Сам граф можно отобразить, как это сделано на рис. 2.7, с использованием настольного приложенияGraphviz или его веб-версии GraphvizOnline (bit.ly/2mPbxmg).
При прохождении по дереву зависимостей Terraform пытается как можно сильнее распараллелить создание ресурсов, что приводит к довольно эффективному применению изменений. В этом прелесть декларативного языка: вы просто описываете то, что вам нужно, а Terraform определяет наиболее эффективный способ реализации.
Рис. 2.7. Граф зависимостей для сервера EC2 и его группы безопасности
Выполнив команду apply, вы увидите, что Terraform хочет создать группу безопасности и заменить имеющийся сервер EC2 другим — с новыми пользовательскими данными:
$ terraform apply
(...)
Terraform will perform the following actions:
# aws_instance.example must be replaced
-/+ resource "aws_instance" "example" {