Бессерверные кластеры и Terraform HashiCorp на AWS

Давайте обсудим отличную установку - создание кластера Kubernetes на вершине AWS с использованием сервиса EKS. Также мы добавляем кластер Фаргейт (бессерверный). Это полное решение всех проблем, с которыми мы столкнулись в моей последней настройке. Мы создадим всю структуру с помощью Terraform. В конце концов, нам нужно будет запустить только одну команду, чтобы создать и уничтожить всю инфраструктуру.

Перед тем, как продолжить, необходимо выполнить несколько предварительных условий:

  1. Знай про базовый Terraform.
  2. Знать основную терминологию ЭКС и Фаргейт кластер
  3. Познакомьтесь с Kubernetes

Повестка дня

Мы создали хорошо управляемое, автоматически масштабируемое и автоматически развертываемое веб-приложение, которое модули Fargate должны запускать в частной подсети, взаимодействуя с внешним миром через балансировщик нагрузки, размещенный в общедоступной подсети. За кулисами все данные хранятся в базе данных в частной лаборатории, доступной только администратору.

Вовлеченные шаги

  • Сети
  • Узлы EKS, Fargate и управляемые узлы
  • Kubernetes
  • База данных
  • Управление кодом

1. Сначала мы создаем VPC и настраиваем в нем подключение.

Я не буду здесь подробно описывать этот шаг, поскольку он практически такой же, как и в моей предыдущей настройке. Вы можете увидеть это здесь.

Мы создаем VPC, общедоступные и частные подсети, шлюзы Интернета и NAT, а также таблицу маршрутов точно так же, как мы это делали в моей предыдущей статье.

Нам просто нужно добавить специальные теги к подсетям, чтобы Kubernetes знал, для чего следует использовать подсети:

  • Тег kubernetes.io/cluster/{clustername}: shared необходимо добавить ко всем подсетям, которые кластер должен иметь возможность использовать.
  • Тегkubernetes.io/role/elb: 1 необходимо добавить в общедоступные подсети, чтобы Kubernetes знал использовать только эти подсети для общедоступных балансировщиков нагрузки.
  • Тегkubernetes.io/role/internal-elb: 1 необходимо добавить в частные подсети, чтобы Kubernetes знал, как использовать эти подсети для внутренних балансировщиков нагрузки.

Полный код Terraform для сетей VPC можно увидеть здесь.

2. Создайте кластер EKS

Для Terraform необходима следующая настройка:

resource "aws_eks_cluster" "eks_cluster" {
  name     = "${var.cluster_name}-${var.environment}"
   
  role_arn = aws_iam_role.eks_cluster_role.arn
  enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
  
   vpc_config {
    subnet_ids =  concat(var.public_subnets, var.private_subnets)
  }
   
   timeouts {
     delete    = "30m"
   }
}

Как видите, нам также нужно прикрепить к кластеру роль, которая даст ему необходимое разрешение для взаимодействия с узлами. Настройка выглядит следующим образом:

Мы также добавим в этот кластер метрики CloudWatch. Для этого нам нужно добавить группу журналов CloudWatch, которая настроена следующим образом:

Создайте группу узлов

Теперь у нас есть кластер, настроенный и готовый, но у нас еще нет узлов, на которых можно было бы запускать наши модули. Поды можно запускать без каких-либо узлов. Но вам необходимо внести некоторые изменения в развертывание CoreDNS (подробнее об этом здесь и здесь).

Поэтому вместо этого мы создадим группу узлов для пространства имен kube-system, которое будет использоваться для запуска любых модулей, необходимых для работы кластера Kubernetes. Мы можем запустить группу узлов в публичных / частных подсетях. Настройка выглядит следующим образом:

resource "aws_eks_node_group" "eks_node_group" {
  cluster_name    = aws_eks_cluster.eks_cluster.name
  node_group_name = "${var.cluster_name}-${var.environment}-  node_group"
  node_role_arn   = aws_iam_role.eks_node_group_role.arn
  subnet_ids      = var.public_subnets

  scaling_config {
    desired_size = 2
    max_size     = 3
    min_size     = 2
  }

  instance_types  = ["${var.eks_node_group_instance_types}"]
}

Здесь вы видите, что для desired size я поставил 2, потому что мне нужно как минимум два узла во время запуска, а внутри одного узла вы можете запускать только ограниченное количество модулей - как в t2.micro, вы можете запускать только два модуля в узел из-за ENI.

То же самое и с кластером EKS. Группа узлов также требует присоединенной роли для связи с запущенными на ней модулями, которая настроена следующим образом:

В AWS за кулисами запускается группа узлов в сервисе EC2.

Наконец, мы создали профиль Fargate

Чтобы запускать поды в конфигурации Fargate (без сервера), нам сначала нужно создать профиль Fargate. Этот профиль определяет пространства имен и селекторы, которые используются для определения того, какие поды должны запускаться на узлах Fargate. Убедитесь, что поды Fargate могут работать только в частных подсетях. Настройка выглядит следующим образом:

resource "aws_eks_fargate_profile" "eks_fargate" {
  cluster_name           = aws_eks_cluster.eks_cluster.name
  fargate_profile_name   = "${var.cluster_name}-${var.environment}-fargate-profile"
  pod_execution_role_arn = aws_iam_role.eks_fargate_role.arn
  subnet_ids             = var.private_subnets

  selector {
    namespace = "${var.fargate_namespace}"
  }

  timeouts {
    create   = "30m"
    delete   = "30m"
  }
}

То же, что и выше - для профиля Fargate также требуется прикрепленная роль, которая позволяет контроллеру Fargate выполнять вызовы API AWS на вашем устройстве, которое настраивается следующим образом:

3. Настройте Kubernetes с помощью Terraform.

Сначала мы сообщаем Terraform, где работает наш кластер Kubernetes. Для этого нам нужно добавить kubernetes провайдера, например:

Теперь создайте пространство имен, развертывание и службу для нашего приложения.

Пространство имен

resource "kubernetes_namespace" "fargate" {
  metadata {
    labels = {
      app = "my-app"
    }
    name = "fargate-node"
  }
}

Затем мы создаем развертывание для нашего приложения с помощью образа Docker для запуска модулей.

Развертывание

resource "kubernetes_deployment" "app" {
  metadata {
    name      = "owncloud-server"
    namespace = "fargate-node"
    labels    = {
      app = "owncloud"
    }
  }

  spec {
    replicas = 2

    selector {
      match_labels = {
        app = "owncloud"
      }
    }

    template {
      metadata {
        labels = {
          app = "owncloud"
        }
      }

      spec {
        container {
          image = "owncloud"
          name  = "owncloud-server"

          port {
            container_port = 80
          }
        }
      }
    }
  }
   depends_on = [kubernetes_namespace.fargate]

}

Также создайте сервис для балансировки нагрузки.

"Услуга"

resource "kubernetes_service" "app" {
  metadata {
    name      = "owncloud-service"
    namespace = "fargate-node"
  }
  spec {
    selector = {
      app = "owncloud"
    }

    port {
      port        = 80
      target_port = 80
      protocol    = "TCP"
    }

    type = "NodePort"
  }

  depends_on = [kubernetes_deployment.app]
}

Примечание. Здесь есть одна хитрость. Если вы хотите получить доступ к этому веб-приложению из общедоступного мира, доступны три балансировщика нагрузки: CLB, NLB и ALB . Вы можете выбрать любой из них. Вы можете легко создать CLB или NLB. Но создание ALB - очень типичный выбор в этой настройке. Я расскажу вам обо всех балансировщиках нагрузки.

Если вы хотите создать CLB, используйте службу LoadBalancer, а для создания NLB используйте:

Создание ALB немного сложнее. Нам нужен ALB для подключения к любому работающему модулю, а также для регистрации доступных целевых модулей с помощью ALB. Для этого нам понадобится Ingress-контроллер.

Чтобы контроллер Ingress имел права доступа для создания ALB и регистрации целевых модулей на ALB, нам необходимо создать политику, позволяющую это.

Теперь создайте роль и присоедините эту политику к роли.

Теперь нам также нужна роль кластера для контроллера Ingress, учетная запись службы, привязанная к этой роли, к которой привязана ранее созданная роль IAM.

Теперь мы можем развернуть контроллер Ingress в нашем кластере.

Теперь, когда контроллер Ingress развернут, мы можем создать ALB для веб-приложения с помощью Kubernetes Ingress.

resource "kubernetes_ingress" "app" {
  metadata {
    name      = "owncloud-lb"
    namespace = "fargate-node"
    annotations = {
      "kubernetes.io/ingress.class"           = "alb"
      "alb.ingress.kubernetes.io/scheme"      = "internet-facing"
      "alb.ingress.kubernetes.io/target-type" = "ip"
    }
    labels = {
        "app" = "owncloud"
    }
  }

  spec {
      backend {
        service_name = "owncloud-service"
        service_port = 80
      }
    rule {
      http {
        path {
          path = "/"
          backend {
            service_name = "owncloud-service"
            service_port = 80
          }
        }
      }
    }
  }

  depends_on = [kubernetes_service.app]
}

После этого вы также можете вручную проверить, создан он или нет.

Откройте DNS-имя в своем браузере и убедитесь, что ваше веб-приложение там отлично работает.

4. Создание базы данных с помощью экземпляра RDS в AWS

Я не буду здесь подробно описывать этот шаг, поскольку он практически такой же, как и в моей предыдущей настройке. Вы можете увидеть это здесь.

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

5. Управление кодом

Это самый важный шаг, потому что самое важное - это управление кодом. Для хорошего управления я использую module в Terraform и создаю отдельную папку для каждой службы. Как и в нашей последней настройке, мы будем использовать файл .tfvars для получения значений переменных.

Теперь просто запустите одну команду, чтобы создать всю установку:

terraform apply -var-file={variable file name}

Нет сомнений в том, что это отличная установка. Но компании в основном хранят свои данные в собственных помещениях или используют частное облако, такое как OpenStack. Я постараюсь написать об этом статью в будущем.

Для справки я загрузил свой код в свой репозиторий GitHub:



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