Последние две недели я экспериментировал с Azure Kubernetes Service (AKS) и его публичным аналогом — acs-engine. Вот немного моего опыта, особенно с учетом того, что ранее я с этими инструментами не работал. Я пытаюсь взглянуть на всё это с точки зрения инфраструктуры, а не с позиции разработчиков.

Контейнеры, облако и приватные окружения

Несколько моментов, которые стоит учитывать при работе с AKS

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

Kubernetes — это оркестратор. Он управляет контейнерами на разных узлах. Есть мастер-узлы и рабочие узлы. Мастер-узлы управляют кластером и выполняют основную часть управляющей плоскости. Рабочие узлы запускают рабочие нагрузки — контейнеры, которыми управляют специальные агенты — тоже часть управляющей плоскости. Однако Kubernetes не запускает контейнеры сам по себе, он управляет подложкой, такой как Docker. В случае AKS Microsoft управляет мастер-узлами и предоставляет публичную конечную точку для управления кластером.

Еще один важный момент:

Kubernetes предъявляет следующие базовые требования к сетевой реализации (если не учитывать намеренные политики сегментации сети):

  • все контейнеры могут общаться между собой без NAT
  • все узлы могут общаться с контейнерами (и наоборот) без NAT
  • IP-адрес, который видит контейнер, совпадает с тем, что видят другие

Сетевой стек Kubernetes требует понимания CNI. Это программный компонент, позволяющий контейнерам взаимодействовать. В Azure доступны два варианта CNI — kubenet и Azure CNI. Kubenet — это встроенный компонент Kubernetes. Azure CNI — решение от Microsoft. По умолчанию AKS использует kubenet. Чтобы включить Azure CNI, нужно явно указать это в разделе networkProfile шаблона:

"networkProfile": {
    "networkPlugin": "azure",
    "serviceCidr":"172.66.0.0/16",
    "DNSServiceIP":"172.66.0.10",
    "dockerBridgeCidr":"172.22.0.1/16"
}

Также была задача разместить кластер в приватной VNET, доступной только из корпоративной среды. В таких сценариях часто используется дизайн hub-and-spoke:

hub and spoke

В начале часто используется “Forced tunneling” — принудительная маршрутизация трафика в On-Prem. Это может мешать созданию AKS. Чтобы всё работало, VNET должен иметь доступ к следующим доменам:

Эти домены используются при создании и пост-обработке. Их недоступность приведет к сбою развертывания.

Также встречается зависимость от 8.8.8.8:53 или 8.8.4.4:53 — эти DNS тоже должны быть доступны.

Важно понимать, что вы запускаете только рабочие узлы, а мастер-узлы управляются Microsoft. Вы получаете публичную конечную точку, её можно найти в портале. Команда az aks get-credentials создаст файл конфигурации config в папке .kube и вы сможете управлять кластером через интернет.

Если у вас есть внутренние зависимости — DNS, PKI, репозитории — нужно предусмотреть их до развертывания.

После развертывания вы получите объект кластера и отдельную Resource Group с именем MC_<имяРГ>_<регион>. Если используется Azure CNI, каждая ВМ получит несколько IP, каждый POD будет иметь собственный IP внутри VNET. Эти ВМ не стоит настраивать вручную, потому что они пересоздаются при обновлении. Настройку DNS лучше выполнять на уровне VNET.

Само развертывание — это просто. Вы создаете ARM-шаблон и запускаете его в нужной Resource Group.

Пример:

... (оставлено без изменений)

Что важно:

Azure Kubernetes Service vs acs-engine

AKS и acs-engine похожи, но не идентичны. Оба создают Kubernetes-кластер, но acs-engine создает всё — включая мастер-узлы. Он также требует доступ к тем же доменам. Главное преимущество — поддержка кастомных образов и VNETов, если они совместимы с dpkg. По умолчанию — Ubuntu и RHEL. acs-engine принимает JSON в своём формате и генерирует ARM-шаблон.

Пример шаблона:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "clusterName": {
      "type": "string"
    },
    "kubernetesVersion": {
      "type": "string",
      "defaultValue": "1.10.6",
      "allowedValues": [
        "1.10.6",
        "1.10.5",
        "1.10.3"
      ],
    },
    "vnetSubnetID": {
      "type": "string"
    },
    "dnsPrefix": {
      "type": "string"
    },
    "workerCount": {
      "type": "int"
    },
    "servicePrincipalClientId": {
      "type": "string",
      "metadata": {
        "description": "generate with the command line: "
      }
    },
    "servicePrincipalSecret": {
      "type": "securestring"
    },
    "vmSize": {
      "type": "string"
    }
  },
  "variables": {},
  "resources": [
    {
      "name": "[parameters('clusterName')]",
      "type": "Microsoft.ContainerService/managedClusters",
      "apiVersion": "2018-03-31",
      "location": "[resourceGroup().location]",
      "tags": {},
      "properties": {
        "kubernetesVersion": "[parameters('kubernetesVersion')]",
        "dnsPrefix": "[parameters('dnsPrefix')]",
        "agentPoolProfiles": [
          {
            "name": "[concat(parameters('clusterName'))]",
            "count": "[parameters('workerCount')]",
            "vmSize": "[parameters('vmSize')]",
            "vnetSubnetID": "[parameters('vnetSubnetID')]",
            "maxPods": 15
          }
        ],
        "linuxProfile": {
          "adminUsername": "azureuser",
          "ssh": {
            "publicKeys": [
              {
                "keyData": "ssh-rsa <key goes here>"
              }
            ]
          }
        },
        "networkProfile": {
          "networkPlugin": "azure",
          "serviceCidr":"172.66.0.0/16",
          "DNSServiceIP":"172.66.0.10",
          "dockerBridgeCidr":"172.33.0.1/16"
        },
        "servicePrincipalProfile": {
          "clientId": "[parameters('servicePrincipalClientId')]",
          "secret": "[parameters('servicePrincipalSecret')]"
        },
        "enableRBAC": false
      }
    }
  ],
  "outputs": {}
}

Как видно, формат похож на AKS. Можно заключить, что они используют схожую кодовую базу.