– destroy
Terraform will perform the following actions:
– google_compute_instance.terraform
Plan: 0 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
google_compute_instance.terraform: Destroying… (ID: terraform)
google_compute_instance.terraform: Still destroying… (ID: terraform, 10s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 20s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 30s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 40s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 50s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m0s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m10s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m20s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m30s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m40s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 1m50s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 2m0s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 2m10s elapsed)
google_compute_instance.terraform: Still destroying… (ID: terraform, 2m20s elapsed)
google_compute_instance.terraform: Destruction complete after 2m30s
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
Создание инфраструктуры в AWS
Для создания конфигурации AWS кластера, создадим под него отдельную папку, а предыдущую – в параллельную:
esschtolts@cloudshell:~/terraform (agil7e-aleph-20391)$ mkdir gcp
esschtolts@cloudshell:~/terraform (agil7e-aleph-20391)$ mv main.tf gcp/main.tf
esschtolts@cloudshell:~/terraform (agil7e-aleph-20391)$ mkdir aws
esschtolts@cloudshell:~/terraform (agil7e-aleph-20391)$ cd aws
Role – аналог пользователя, только не для людей, а для сервисов, таких как AWS, а в нашем случае – это сервера EKS. Но мне видится аналогом ролей не пользователи, а группы, например, группа создания кластера, группа работы с БД и т.д. К серверу можно прикрепить только одну роль, при этом роль может содержать множество прав (Polices). В результате нам не нужно работать ни с логинами и паролями, ни с токенами, ни с сертификатами: хранить, передавать, ограничивать доступ, передавать – мы только указываем в WEB панели инструментов (IMA) или с помощью API (а производно в конфигурации) права. Нашему кластеру необходимы эти права, чтобы он смог само настраиваться и реплицироваться, так как он состоит из стандартных сервисов AWS. Для управления самими компонентами кластера AWS EC2 (сервера), AWS ELB (Elastic Load Balancer, балансировщик) и AWS KMS (Key Management Service, менеджер и шифрование ключей) нужен доступ AmazonEKSClusterPolicy, для мониторинга AmazonEKSServicePolicy с помощью компонентов CloudWatch Logs (мониторинг по логам), Route 53 (создание сети в зоне), IAM (управления правами). Я не стал описывать роль в конфиге и создал её через IAM по документации: https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role. html #create-service-role.
Для большей надёжности ноды Kubernetes кластера должны располагаться в разных зонах, то есть дата центрах. В каждом регионе, содержится несколько зон для поддержания отказоустойчивости, при этот сохраняя минимальных летенси (время ответа сервера) для местного населения. Важно заметить, что некоторые регионы могут быть представлены в нескольких экземплярах в рамках одной страны, например, US-east-1 в US East (N. Virginia) и US-east-2 в US East (Ohio) – обозначение регионов ведётся цифрами. Пока создание кластера EKS доступно только US-east зоне.
VPC для разработчика, простейшем варианте, сводится к наименованию подсети как конкретный ресурс.
Напишем конфигурацию в соответствии с документацией www.terraform.io/docs/providers/aws/r/eks_cluster. html :
esschtolts@cloudshell:~/terraform/aws (agile-aleph-203917)$ cat main.tf
provider "aws" {
access_key = "${var.token}"
secret_key = "${var.key}"
region = "us-east-1"
}
# Params
variable "token" {
default = ""
}
variable "key" {
default = ""
}
# EKS
resource "aws_eks_cluster" "example" {
enabled_cluster_log_types = ["api", "audit"]
name = "exapmle"
role_arn = "arn:aws:iam::177510963163:role/ServiceRoleForAmazonEKS2"
vpc_config {
subnet_ids = ["${aws_subnet.subnet_1.id}", "${aws_subnet.subnet_2.id}"]
}
}
output "endpoint" {
value = "${aws_eks_cluster.example.endpoint}"
}
output "kubeconfig-certificate-authority-data" {
value = "${aws_eks_cluster.example.certificate_authority.0.data}"
}
# Role
data "aws_iam_policy_document" "eks-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["eks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "tf_role" {
name = "tf_role"
assume_role_policy = "${data.aws_iam_policy_document.eks-role-policy.json}"
tags = {
tag-key = "tag-value"
}
}
resource "aws_iam_role_policy_attachment" "attach-cluster" {
role = "tf_role"
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
}
resource "aws_iam_role_policy_attachment" "attach-service" {
role = "tf_role"
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
}
# Subnet
resource "aws_subnet" "subnet_1" {
vpc_id = "${aws_vpc.main.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
tags = {
Name = "Main"
}
}
resource "aws_subnet" "subnet_2" {
vpc_id = "${aws_vpc.main.id}"
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
tags = {
Name = "Main"
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
Через 9 минут 44 секунды я получил готовую самоподдерживающуюся инфраструктуру для кластера Kubernetes:
esschtolts@cloudshell:~/terraform/aws (agile-aleph-203917)$ ./../terraform apply -var="token=AKIAJ4SYCNH2XVSHNN3A" -var="key=huEWRslEluynCXBspsul3AkKlinAlR9+MoU1ViY7"
Теперь удалим (у меня заняло 10 минуты 23 секунды):
esschtolts@cloudshell:~/terraform/aws (agile-aleph-203917)$ ./../terraform destroy -var="token=AKIAJ4SYCNH2XVSHNN3A" -var="key=huEWRslEluynCXBspsul3AkKlinAlR9+MoU1ViY7"
Destroy complete! Resources: 7 destroyed.
Налаживание процесса CI/CD
Amazon предоставляет (aws.amazon.com/ru/devops/) большой спектр DevOps инструментов, оформленных в облачную инфраструктуру:
* AWS Code Pipeline – сервис позволяет создать из набора сервисов в визуальном редакторе цепочку этапов, через которые должен пройти код, прежде чем он попадёт на продакшн, например, сборку и тестирование.
* AWS Code Build – сервис предоставляет авто масштабирующую очередь сборки, что может потребоваться для компилируемых языков программирования, когда при добавлении фич или внесения изменений необходимо длительная пере компиляция всего приложения, при использовании одного сервера становится узким местом при выкатке изменений.
* AWS Code Deploy – автоматизирует развёртывание и возвращение к предыдущему состоянию продакшна в случаи ошибок.
* AWS CodeStar – сервис объединяет в себе основные возможности предыдущих сервисов.
Настраиваем удалённое управление
сервер артефактов
aws s3 ls s3://name_backet aws s3 sync s3://name_backet name_fonder –exclude *.tmp # в папку будет скачены файлы из бакета, например, сайт
Теперь, нам нужно скачать плагин работы с AWS:
esschtolts@cloudshell:~/terraform/aws (agile-aleph-203917)$ ./../terraform init | grep success
Terraform has been successfully initialized!
Теперь нам нужно получить доступы к AWS, для того кликаем по имени вашего пользователя шапки WEB-интерфейса, кроме My account, появится пункт My Security Credentials, выбрав который, переходим Access Key –> Create New Access Key. Создадим EKS (Elastic Kuberntes Service):
esschtolts@cloudshell:~/terraform/aws (agile-aleph-203917)$ ./../terraform apply
–var="token=AKIAJ4SYCNH2XVSHNN3A" -var="key=huEWRslEluynCXBspsul3AkKlinAlR9+MoU1ViY7"
Удаляем все:
$ ../terraform destroy
Создание кластера в GCP
node pool – объединение нод в кластер с
resource "google_container_cluster" "primary" {
name = "tf"
location = "us-central1"
$ cat main.tf # состояние конфигурации
terraform {
required_version = "> 0.10.0"
}
terraform {
backend "s3" {
bucket = "foo-terraform"
key = "bucket/terraform.tfstate"
region = "us-east-1"
encrypt = "true"
}
}
$ cat cloud.tf# конфигурации облака
provider "google" {
token = "${var.hcloud_token}"
}
$ cat variables.tf# переменные и получение токенов
variable "hcloud_token" {}
$ cat instances.tf# создание ресурсов
resource "hcloud_server" "server" { ....
$ terraform import aws_acm_certificate.cert arn:aws:acm:eu-central-1:123456789012:certificate/7e7a28d2-163f-4b8f-b9cd-822f96c08d6a
$ terraform init # Инициализация конфигов
$ terraform plan # Проверка действий
$ terraform apply # Запуск действий
Отладка:
essh@kubernetes-master:~/graylog$ sudo docker run –name graylog –link graylog_mongo:mongo –link graylog_elasticsearch:elasticsearch \
–p 9000:9000 -p 12201:12201 -p 1514:1514 \
–e GRAYLOG_HTTP_EXTERNAL_URI="http://127.0.0.1:9000/" \
–d graylog/graylog:3.0
0f21f39192440d9a8be96890f624c1d409883f2e350ead58a5c4ce0e91e54c9d
docker: Error response from daemon: driver failed programming external connectivity on endpoint graylog (714a6083b878e2737bd4d4577d1157504e261c03cb503b6394cb844466fb4781): Bind for 0.0.0.0:9000 failed: port is already allocated.
essh@kubernetes-master:~/graylog$ sudo netstat -nlp | grep 9000
tcp6 0 0 :::9000:::* LISTEN 2505/docker-proxy
essh@kubernetes-master:~/graylog$ docker rm graylog
graylog
essh@kubernetes-master:~/graylog$ sudo docker run –name graylog –link graylog_mongo:mongo –link graylog_elasticsearch:elasticsearch \
–p 9001:9000 -p 12201:12201 -p 1514:1514 \
–e GRAYLOG_HTTP_EXTERNAL_URI="http://127.0.0.1:9001/" \
–d graylog/graylog:3.0
e5aefd6d630a935887f494550513d46e54947f897e4a64b0703d8f7094562875
https://blog.maddevs.io/terrafom-hetzner-a2f22534514b
Для примера, создадим один инстанс:
$ cat aws/provider.tf
provider "aws" {
region = "us-west-1"
}
resource "aws_instance" "my_ec2" {
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t2.micro"
}
$ cd aws
$ aws configure
$ terraform init
$ terraform apply –auto-approve
$ cd ..
provider "aws" {
region = "us-west-1"
}
resource "aws_sqs_queue" "terraform_queue" {
name = "terraform-queue"
delay_seconds = 90
max_message_size = 2048
message_retention_seconds = 86400
receive_wait_time_seconds = 10
}
data "aws_route53_zone" "vuejs_phalcon" {
name = "test.com."
private_zone = true
}
resource "aws_route53_record" "www" {
zone_id = "${data.aws_route53_zone.vuejs_phalcon.zone_id}"
name = "www.${data.aws_route53_zone.selected.name}"
type = "A"
ttl = "300"
records = ["10.0.0.1"]
}
resource "aws_elasticsearch_domain" "example" {
domain_name = "example"
elasticsearch_version = "1.5"
cluster_config {
instance_type = "r4.large.elasticsearch"
}
snapshot_options {
automated_snapshot_start_hour = 23
}
}
resource "aws_eks_cluster" "eks_vuejs_phalcon" {
name = "eks_vuejs_phalcon"
role_arn = "${aws_iam_role.eks_vuejs_phalcon.arn}"
vpc_config {
subnet_ids = ["${aws_subnet.eks_vuejs_phalcon.id}", "${aws_subnet.example2.id}"]
}
}
output "endpoint" {
value = "${aws_eks_cluster.eks_vuejs_phalcon.endpoint}"
}
output "kubeconfig-certificate-authority-data" {
value = "${aws_eks_cluster.eks_vuejs_phalcon.certificate_authority.0.data}"
}
provider "google" {
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-central1"
}
resource "google_container_cluster" "primary" {
name = "my-gke-cluster"
location = "us-central1"
remove_default_node_pool = true
initial_node_count = 1
master_auth {
username = ""
password = ""
}
}
output "client_certificate" {
value = "${google_container_cluster.primary.master_auth.0.client_certificate}"
}
output "client_key" {
value = "${google_container_cluster.primary.master_auth.0.client_key}"
}
output "cluster_ca_certificate" {
value = "${google_container_cluster.primary.master_auth.0.cluster_ca_certificate}"
}
$ cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: phalcon_vuejs
namespace: development
spec:
selector:
matchLabels:
app: vuejs
replicas: 1
template:
metadata:
labels:
app: vuejs
spec:
initContainers:
– name: vuejs_build
image: vuejs/ci
volumeMounts:
– name: app
mountPath: /app/public
command:
– /bin/bash
– -c
– |
cd /app/public
git clone essch/vuejs_phalcon:1.0 .
npm test
npm build
containers:
– name: healtcheck
image: mileschou/phalcon:7.2-cli
args:
– /bin/sh
– -c
– cd /usr/src/app && git clone essch/app_phalcon:1.0 && touch /tmp/healthy && sleep 10 && php script.php
readinessProbe:
exec:
command:
– cat
– /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
exec:
command:
– cat
– /tmp/healthy
initialDelaySeconds: 15
periodSeconds: 5
voumes:
– name: app
emptyDir: {}
Вот и мы создали AWS EC2 инстанс. Мы опустили указание ключей, так как AWS API уже авторизован и эта авторизация будет использоваться Terraform.
Также, для использования кода, Terraform поддерживает переменные, данные и модули.
Создадим отдельную сеть:
resource "aws_vpc" "my_vpc" {
cidr_block = "190.160.0.0/16"
instance_target = "default"
}
resource "aws_subnet" "my_subnet" {
vpc_id = "${aws_vpc.my_vpc.id}"
cidr_block = "190.160.1.0/24"
}
$ cat gce/provider.tf
provider "google" {
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-central1"
}
resource "google_compute_instance" "default" {
name = "test"
machine_type = "n1-standard-1"
zone = "us-central1-a"
}
$ cd gce
$ terraform init
$ terraform apply
$ cd ..
Для распределенной работы поместим состояние в AWS S3 состояние инфраструктуры (так же можно помещать другие данные), но для безопасности в другой регион:
terraform {
backend "s3" {
bucket = "tfstate"
key = "terraform.tfstate"
region = "us-state-2"
}
}
provider "kubernetes" {
host = "https://104.196.242.174"
username = "ClusterMaster"
password = "MindTheGap"
}
resource "kubernetes_pod" "my_pod" {
spec {
container {
image = "Nginx:1.7.9"
name = "Nginx"
port {
container_port = 80
}
}
}
}
Команды:
terraform init # скачивание зависимостей в соответствии с конфигами, проверка их
terraform validate # проверка синтаксиса
terraform plan # детально посмотреть, как будет изменена инфраструктура и почему именно так, например,
будет ли изменена только мета информация у сервиса или будет пересоздан сам сервис, что часто недопустимо для баз данных.
terraform apply # применение изменений
Общая часть для всех провайдеров – ядро.
$ which aws
$ aws fonfigure # https://www.youtube.com/watch?v=IxA1IPypzHs
$ cat aws.tf
# https://www.terraform.io/docs/providers/aws/r/instance.html
resource "aws_instance" "ec2instance" {
ami = "${var.ami}"
instance_type = "t2.micro"
}
resource "aws_security_group" "instance_gc" {
…
}
$cat run.js
export AWS_ACCESS_KEY_ID="anaccesskey"
export AWS_SECRET_ACCESS_KEY="asecretkey"
export AWS_DEFAULT_REGION="us-west-2"
terraform plan
terraform apply
$ cat gce.tf # https://www.terraform.io/docs/providers/google/index.html#
# Google Cloud Platform Provider
provider "google" {
credentials = "${file("account.json")}"
project = "phalcon"
region = "us-central1"
}
#https://www.terraform.io/docs/providers/google/r/app_engine_application.html
resource "google_project" "my_project" {
name = "My Project"
project_id = "your-project-id"
org_id = "1234567"
}
resource "google_app_engine_application" "app" {
project = "${google_project.my_project.project_id}"
location_id = "us-central"
}
# google_compute_instance
resource "google_compute_instance" "default" {
name = "test"
machine_type = "n1-standard-1"
zone = "us-central1-a"
tags = ["foo", "bar"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
// Local SSD disk
scratch_disk {
}
network_interface {
network = "default"
access_config {
// Ephemeral IP
}
}
metadata = {
foo = "bar"
}
metadata_startup_script = "echo hi > /test.txt"
service_account {
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
}
}
Расширяемость с помощью external resource, в качестве которого может быть скрипт на BASH:
data "external" "python3" {
program = ["Python3"]
}
Создание кластера машин с помощью Terraform
Создание кластера с помощью Terraform рассматривается в Создание инфраструктуры в GCP. Сейчас уделим больше внимания самому кластеру, а не инструментам по его созданию. Создам через панель администратора GCE проект (отображается в шапке интерфейса) node-cluster. Ключ для Kubernetes я скачал IAM и администрирование –> Сервисные аккаунты –> Создать сервисный аккаунт и при создании выбрал роль Владелец и положил в проект под названием kubernetes_key.JSON:
eSSH@Kubernetes-master:~/node-cluster$ cp ~/Downloads/node-cluster-243923-bbec410e0a83.JSON ./kubernetes_key.JSON
Скачал terraform:
essh@kubernetes-master:~/node-cluster$ wget https://releases.hashicorp.com/terraform/0.12.2/terraform_0.12.2_linux_amd64.zip >/dev/null 2>/dev/null
essh@kubernetes-master:~/node-cluster$ unzip terraform_0.12.2_linux_amd64.zip && rm -f terraform_0.12.2_linux_amd64.zip
Archive: terraform_0.12.2_linux_amd64.zip
inflating: terraform
essh@kubernetes-master:~/node-cluster$ ./terraform version
Terraform v0.12.2
Добавили провайдера GCE и запустил скачивание "драйверов" к нему:
essh@kubernetes-master:~/node-cluster$ cat main.tf
provider "google" {
credentials = "${file("kubernetes_key.json")}"
project = "node-cluster"
region = "us-central1"
}essh@kubernetes-master:~/node-cluster$ ./terraform init
Initializing the backend…
Initializing provider plugins…
– Checking for available provider plugins…
– Downloading plugin for provider "google" (terraform-providers/google) 2.8.0…
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "…" constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.google: version = "~> 2.8"
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Добавлю виртуальную машину:
essh@kubernetes-master:~/node-cluster$ cat main.tf
provider "google" {
credentials = "${file("kubernetes_key.json")}"
project = "node-cluster-243923"
region = "europe-north1"
}
resource "google_compute_instance" "cluster" {
name = "cluster"
zone = "europe-north1-a"
machine_type = "f1-micro"
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
network_interface {
network = "default"
access_config {}
}
essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# google_compute_instance.cluster will be created
+ resource "google_compute_instance" "cluster" {
+ can_ip_forward = false
+ cpu_platform = (known after apply)
+ deletion_protection = false
+ guest_accelerator = (known after apply)
+ id = (known after apply)
+ instance_id = (known after apply)
+ label_fingerprint = (known after apply)
+ machine_type = "f1-micro"
+ metadata_fingerprint = (known after apply)
+ name= "cluster"
+ project = (known after apply)
+ self_link = (known after apply)
+ tags_fingerprint = (known after apply)
+ zone= "europe-north1-a"
+ boot_disk {
+ auto_delete = true
+ device_name = (known after apply)
+ disk_encryption_key_sha256 = (known after apply)
+ source = (known after apply)
+ initialize_params {
+ image = "debian-cloud/debian-9"
+ size = (known after apply)
+ type = (known after apply)
}
}
+ network_interface {
+ address = (known after apply)
+ name = (known after apply)
+ network = "default"
+ network_ip = (known after apply)
+ subnetwork = (known after apply)
+ subnetwork_project = (known after apply)
+ access_config {
+ assigned_nat_ip = (known after apply)
+ nat_ip = (known after apply)
+ network_tier = (known after apply)
}
}
+ scheduling {
+ automatic_restart = (known after apply)
+ on_host_maintenance = (known after apply)
+ preemptible = (known after apply)
+ node_affinities {
+ key = (known after apply)
+ operator = (known after apply)
+ values = (known after apply)
}
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
google_compute_instance.cluster: Creating…
google_compute_instance.cluster: Still creating… [10s elapsed]
google_compute_instance.cluster: Creation complete after 11s [id=cluster]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Добавлю к ноде публичный статический IP-адрес и SSH-ключ:
essh@kubernetes-master:~/node-cluster$ ssh-keygen -f node-cluster
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in node-cluster.