Terraform vs. Helm для управления объектами K8s

Когда я начал переходить на Kubernetes (K8s), я обнаружил, что могу использовать Terraform для управления не только инфраструктурой, но и определять в нем объекты K8s, но я также могу использовать Helm для этого. Но что было бы хорошим способом справиться с этим?

В этом посте мы рассмотрим Terraform и Helm для управления кластерами Kubernetes с некоторыми фрагментами кода и идеей о том, как их можно использовать вместе для начала работы.

Структура поста:

  1. Что такое Terraform?
  2. Управление ресурсами Kubernetes с помощью Terraform
  3. Что такое Helm?
  4. Управление ресурсами Kubernetes с помощью Helm
  5. Совместное использование Helm и Terraform

Что такое Terraform?

HashiCorp Terraform — это инструмент инфраструктуры как кода, позволяющий определять облачные и локальные ресурсы в человекочитаемых конфигурационных файлах, которые можно редактировать, повторно использовать и совместно использовать.

Он может управлять низкоуровневыми компонентами, такими как вычислительные ресурсы, ресурсы хранения и сетевые ресурсы, а также высокоуровневыми компонентами, такими как записи DNS и функции SaaS.

Terraform рассматривает инфраструктуру как код (IaC), то есть команды управляют настройкой инфраструктуры с помощью конфигурационных файлов, а не графического пользовательского интерфейса (вспомните Azure Portal и тому подобное).

Итак, зачем вам использовать Terraform?

Некоторые из преимуществ включают:

  • Он позволяет командам создавать, изменять и управлять инфраструктурой безопасным, последовательным и повторяемым способом, определяя конфигурации ресурсов, которые можно версионировать, повторно использовать и совместно использовать.

  • Поддерживаются все основные облачные провайдеры: Azure, AWS, GCP и многие другие, которые вы можете найти, просмотрев их реестр.

Провайдеры определяют отдельные единицы инфраструктуры, например, вычислительные экземпляры или частные сети, как ресурсы. Вы можете объединять ресурсы от разных провайдеров в конфигурации Terraform, которые можно использовать повторно, называемые модулями, и управлять ими с помощью согласованного языка и рабочего процесса.

Вы определяете провайдеров в коде Terraform следующим образом:

terraform {
  required_providers {
    helm = {
      version = "2.5.1"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "2.23.0"
    }
    azurerm = {
      source = "hashicorp/azurerm"
    }
  }
Вход в полноэкранный режим Выход из полноэкранного режима
  • Язык конфигурации является декларативным:

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

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

  • Состояние конфигурации позволяет отслеживать изменения ресурсов в ходе развертывания.

  • Все конфигурации подлежат контролю версий для безопасной совместной работы над инфраструктурой.

Вы можете узнать больше о преимуществах этой системы, ознакомившись с официальными примерами использования из документации Terraform.

Terraform — как это работает?

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

Инициализирует бэкенд, выполнив команду terraform init, которая установит плагины, необходимые Terraform для управления инфраструктурой.

Выходные данные будут выглядеть примерно так:

Initializing modules...
(...)
- module1 in ../../modules/module1
- module2 in ../../modules/module2

Initializing the backend...

Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Reusing previous version of hashicorp/kubernetes from the dependency lock file
- Reusing previous version of hashicorp/azuread from the dependency lock file
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Reusing previous version of hashicorp/helm from the dependency lock file
- Installing hashicorp/helm v2.5.1...
- Installed hashicorp/helm v2.5.1 (signed by HashiCorp)
- Installing hashicorp/kubernetes v2.10.0...
- Installed hashicorp/kubernetes v2.10.0 (signed by HashiCorp)
- Installing hashicorp/azuread v2.23.0...
- Installed hashicorp/azuread v2.23.0 (signed by HashiCorp)
- Installing hashicorp/azurerm v3.10.0...
- Installed hashicorp/azurerm v3.10.0 (signed by HashiCorp)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

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.
Войти в полноэкранный режим Выход из полноэкранного режима

В приведенном примере мы инициализируем бэкенд, в нашем случае мы храним файл состояния в контейнере в azure, провайдеров (azurerm, azuread, helm, kubernetes) и получаем сообщение об успешном завершении.

Запланируйте изменения, которые необходимо внести, выполнив команду terraform plan, которая предоставит обзор «запланированных» изменений, которые Terraform внесет в соответствии с вашей конфигурацией.

Во время планирования Terraform будет отмечать, какие ресурсы будут:

  • добавлены знаком ‘+’,
  • обновлены с помощью знака ‘~’ или
  • удалены с помощью знака ‘-‘.

В этом примере Terraform создает совершенно новый ресурс, как вы видите, все атрибуты отмечены знаком +:

  # module.monitoring.helm_release.prometheus_agent[0] will be created
  + resource "helm_release" "prometheus_agent" {
      + atomic                     = false
      + chart                      = "../../charts/prometheus-agent"
      + cleanup_on_fail            = false
      + create_namespace           = false
      + dependency_update          = false
      + disable_crd_hooks          = false
      + disable_openapi_validation = false
      + disable_webhooks           = false
      + force_update               = false
(...)
Войти в полноэкранный режим Выйти из полноэкранного режима

А в этом примере ресурс уже был создан ранее, и Terraform просто обновляет кое-что в нем, что отмечено знаком ~:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:
  # module.grafana.grafana_organization.org will be updated in-place
  ~ resource "grafana_organization" "org" {
      ~ admins       = [
          + "new_admin@grafana.admin",
        ]
        id           = "1"
        name         = "MyOrg"
        # (5 unchanged attributes hidden)
    }
Войти в полноэкранный режим Выход из полноэкранного режима

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

Это также полезно для обнаружения дрейфа инфраструктуры между желаемым состоянием и текущим.

Примените желаемые изменения, выполнив команду terraform apply.

Вид из официальной документации Terraform:

Итак, это и есть Terraform в двух словах.

Управление ресурсами Kubernetes с помощью Terraform

Провайдер Kubernetes (K8S) в Terraform используется для взаимодействия с ресурсами, поддерживаемыми Kubernetes, и предлагает множество преимуществ, но важно отметить, что эта возможность все еще является новой. Это означает, что в провайдере могут быть доступны не все ресурсы или могут существовать некоторые открытые ошибки.

Итак, как это будет выглядеть?
Давайте рассмотрим пример для кластера AKS в Terraform:

resource "azurerm_kubernetes_cluster" "main" {
  name                              = "aks-${var.prefix}-${var.env}"
  location                          = azurerm_resource_group.main.location
  resource_group_name               = azurerm_resource_group.main.name
  dns_prefix                        = "${var.prefix}-${var.env}"
  role_based_access_control_enabled = var.ad_admin_group == "" ? false : true
  kubernetes_version                = var.kubernetes_version

  dynamic "azure_active_directory_role_based_access_control" {
    for_each = var.ad_admin_group == "" ? [] : [1]
    content {
      admin_group_object_ids = [var.ad_admin_group]
      azure_rbac_enabled     = true
    }
  }

  dynamic "oms_agent" {
    for_each = var.oms_agent_enabled == true ? [1] : []
    content {
      log_analytics_workspace_id = var.log_analytics_workspace_id
    }
  }
  azure_policy_enabled             = var.azure_policy_enabled
  http_application_routing_enabled = false
  api_server_authorized_ip_ranges  = var.api_server_authorized_ip_ranges

  default_node_pool {
    name                 = "default"
    enable_auto_scaling  = var.enable_auto_scaling
    max_count            = var.enable_auto_scaling ? var.max_count : null
    min_count            = var.enable_auto_scaling ? var.min_count : null
    node_count           = var.node_count
    type                 = "VirtualMachineScaleSets"
    vm_size              = var.node_size
    tags                 = var.tags
    orchestrator_version = var.node_pool_orchestrator_version
  }

  service_principal {
    client_id     = azuread_application.main.application_id
    client_secret = azuread_service_principal_password.main.value
  }

  tags = var.tags
}
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, вы создаете ресурсы, запускаете terraform apply, и он предоставит вашу инфраструктуру.

Для развертывания мы создаем отдельный файл Terraform, используя ресурс развертывания kubernetes:

resource "kubernetes_deployment" "example" {
  metadata {
    name = "example"
    labels = {
      App = "Example"
    }
  }

  spec {
    replicas = 2
    selector {
      match_labels = {
        App = "Example"
      }
    }
    template {
      metadata {
        labels = {
          App = "Example"
        }
      }
      spec {
        container {
          image = "nginx:1.7.8"
          name  = "example"

          port {
            container_port = 80
          }

          resources {
            limits = {
              cpu    = "0.5"
              memory = "512Mi"
            }
            requests = {
              cpu    = "250m"
              memory = "50Mi"
            }
          }
        }
      }
    }
  }
}

Вход в полноэкранный режим Выйти из полноэкранного режима

Для его создания снова запускаем terraform apply и подтверждаем изменения.

Тот же подход применим и к созданию службы:

resource "kubernetes_service" "example" {
  metadata {
    name = "example"
  }
  spec {
    selector = {
      App = kubernetes_deployment.example.spec.0.template.0.metadata[0].labels.App
    }
    port {
      port        = 80
      target_port = 80
    }

    type = "LoadBalancer"
  }
}

Войти в полноэкранный режим Выйти из полноэкранного режима

А если вы хотите масштабировать эту установку, то подход следующий:

  • Внесите изменения в количество реплик
  spec {
    replicas = 3
    selector {
      match_labels = {
        App = "Example"
      }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима
  • Примените код терраформы и подтвердите изменения.

Кроме того, мы можем сохранить ресурс kubernetes_namespace:

resource "kubernetes_namespace" "example" {
  metadata {
    annotations = {
      name = "example"
    }
    name = "example"
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

И любые секреты, которые вам могут понадобиться:

resource "kubernetes_secret" "example" {
  metadata {
    name      = "example"
    namespace = "example"
  }

  data = {
    "some_setting" = "false"
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Использование этого подхода означает, что вы можете воспользоваться всеми преимуществами Terraform, включая:

  • вы можете использовать один инструмент для управления ресурсами инфраструктуры, а также для управления кластером
  • вы используете один язык для всех ваших инфраструктурных ресурсов, а также объектов k8s
  • вы можете видеть план ваших изменений до предоставления ресурсов.

Недостатки: конечно, вы должны быть знакомы с языком hcl, и если команда работает недавно, это займет немного времени.

Провайдер K8s Terraform может не полностью поддерживать все объекты бета-версии, поэтому вам, возможно, придется подождать.

Если вы заинтересованы в инициализации кластера и всех объектов K8s через Terraform, пожалуйста, ознакомьтесь с официальной документацией для пошаговых настроек.

Что такое Helm?

Helm — это инструмент управления пакетами, который помогает вам управлять приложениями Kubernetes. Helm использует диаграммы Helm Charts для определения, установки и обновления приложений Kubernetes.

Давайте рассмотрим некоторые термины при работе с Helm:

Helm: это интерфейс командной строки, который помогает вам определять, устанавливать и обновлять приложения Kubernetes с помощью диаграмм.

Диаграммы: это формат пакета приложения Helm. Диаграмма — это пакет информации, необходимой для создания экземпляра приложения Kubernetes. По сути, это пакет файлов и шаблонов, который преобразуется в объекты Kubernetes во время развертывания.

Репозиторий диаграмм: это место, где вы можете хранить и совместно использовать упакованные диаграммы.

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

Релиз: это запущенный экземпляр графика, объединенный с определенной конфигурацией. Он создается Helm для отслеживания установки графиков, которые вы создали/определили.

Для получения более подробной информации об архитектуре Helm.

Некоторые из преимуществ Helm:

  • Диаграммы имеют формат YAML и являются многоразовыми, поскольку обеспечивают повторяемость установки приложения. Благодаря этому вы можете использовать их в нескольких средах (считайте, что dev, staging и prod — это одно и то же).
  • Поскольку диаграммы создают повторяемый процесс, это упрощает развертывание.
  • Многие диаграммы уже доступны, но вы можете создавать и свои собственные — пользовательские диаграммы.
  • Вы можете создавать зависимости между диаграммами, а также использовать вложенные диаграммы для повышения гибкости вашей настройки.
  • Графики служат единым центром управления.
  • Релизы отслеживаются.
  • Вы можете обновлять или откатывать несколько объектов K8s вместе.
  • Диаграммы можно легко устанавливать и удалять.

Управление ресурсами Kubernetes с помощью Helm

Мы рассмотрели основную терминологию при использовании Helm, но давайте посмотрим, как это будет выглядеть.

Первое, что нам нужно сделать, это в репозитории, где находится наш код, выполнить команду helm create <app_name>. Эта команда создает каталог графика вместе с общими файлами и каталогами, используемыми в графике. Подробнее о команде.

В результате будет создана следующая структура:

.
└── example
    ├── Chart.yaml
    ├── charts
    ├── templates
    │   ├── NOTES.txt
    │   ├── _helpers.tpl
    │   ├── deployment.yaml
    │   ├── hpa.yaml
    │   ├── ingress.yaml
    │   ├── service.yaml
    │   ├── serviceaccount.yaml
    │   └── tests
    │       └── test-connection.yaml
    └── values.yaml

Вход в полноэкранный режим Выход из полноэкранного режима

Мы замечаем, что она создана:
Файл Chart.yaml, который просто содержит информацию о графике.

apiVersion: v2
name: example
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

Вход в полноэкранный режим Выйти из полноэкранного режима

Директория charts, куда вы можете добавить любые графики, от которых зависит ваш график.

Каталог шаблонов:
Каталог для хранения partials и helpers. Файл под названием _helpers.tpl — это место по умолчанию для партиций шаблонов, на которые, как мы увидим, опираются остальные yaml-файлы.

Как это работает?

Давайте рассмотрим небольшой пример, в этом файле мы определяем fullname:

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "example.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
Войти в полноэкранный режим Выйти из полноэкранного режима

И в нашем файле service.yaml мы можем включить определенное fullname, например:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "example.fullname" . }}
  labels:
    {{- include "example.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "example.selectorLabels" . | nindent 4 }}
Вход в полноэкранный режим Выход из полноэкранного режима

А в файле values.yaml мы также можем переопределить его:

# Default values for example.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: ClusterIP
  port: 80
(...)
Войти в полноэкранный режим Выйти из полноэкранного режима

В приведенном примере fullname будет именем Chart, поскольку мы не передали никакого значения в файл values.yaml для переопределения, основанного на определении параметра в файле _helpers.tpl.

Файл values.yaml, который содержит значения по умолчанию для ваших шаблонов. На данном этапе вы можете разделить файл values для каждого окружения: один для staging, другой для production и т.д.

С этого момента вы можете начать конфигурирование в соответствии с вашими потребностями. Если вы посмотрите yaml-файлы, то заметите, что структура очень похожа на ту, которую мы определили в коде терраформы объектов K8s.

Для установки графиков вы можете либо идти по одному и использовать команды helm install/upgrade, либо добавить это в ваши CI/CD конвейеры.

Установка с помощью командной строки может выглядеть примерно так:

where:

  • infra/charts/example — расположение вашего файла Chart.yaml.
  • values=infra/charts/example/values.yaml — расположение файла значений.

Ознакомьтесь с полным описанием команды здесь.

helm install или helm upgrade —install?

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

Для простоты вы всегда можете использовать подкоманду upgrade.

Совместное использование Helm и Terraform

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

В потенциальной установке, в которой вы будете использовать оба варианта, вы могли бы структурировать ее следующим образом:

  • использовать Terraform для создания и управления ресурсами: кластером K8s, пространством имен K8s и секретами K8s (если таковые имеются).
  • использовать диаграммы Helm для развертывания приложений.

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

Стоит отметить, что вы также можете использовать Terraform для управления развертыванием Helm, используя ресурс helm_release.

При таком подходе вы будете иметь и инфраструктуру, и инициализацию в одном месте — в Terraform. Я не буду в этом посте описывать различия между ними, но отмечу, что выбор этого подхода должен зависеть от того, насколько часто вам нужно вносить изменения в инфраструктуру, потому что в этом случае во время операции terraform apply будет происходить релиз Helm.

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

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

Оцените статью
devanswers.ru
Добавить комментарий