Выражаем благодарность за подготовку статьи Михаилу Тряхову (@PerseptronYar) из компании Akvelon (Ярославль) за помощь в написании данной статьи. Михаил работает в команде разработчиков Microsoft Azure CLI (Command Line Interface) со специализацией на Networking Services.

Приветствую Вас, дорогие читатели!


Продолжим описание основных средств разработки Microsoft Azure, начатый месяц назад в статье о первых шагах в Azure Resource Manager (ARM). Мы успели поговорить об основных отличиях классического (Azure Service Management) подхода от нового режима ARM. Рассмотрели способы работы с JSON-шаблонами (templates), позволяющие более простым образом разворачивать и модифицировать архитектуру. При первой же возможности продемонстрировали способы настройки политики безопасности на примере трехуровневого приложения.
Я очень благодарю за фидбек, полученный пусть и в скромном объеме, но в конструктивном ключе. Ваши вопросы натолкнули меня на необходимость описания процесса создания гибридных решений — и уже в этой статье мы затронем некоторые кейсы этой предметной области. Напомню, мы рассматривали пример архитектуры, схематически изображенной ниже.



Для начала мне хотелось бы продолжить работу с Networking сервисами в ARM. Итак, давайте рассмотрим взаимодействие между внутренними уровнями приложения.



Напомню, мы уже прописали политику безопасности через Network Security Groups, запретив, в частности, доступ к сервисам баз данных (backend) напрямую из интернета.


{
   "name": "Block_Internet",
   "properties": {
      "description": "Block Internet",
      "protocol": "*",
      "sourcePortRange": "*",
      "destinationPortRange": "*",
      "sourceAddressPrefix": "*",
      "destinationAddressPrefix": "Internet",
      "access": "Deny",
      "priority": 200,
      "direction": "Outbound"
   }
}

Помимо этого, предметная область может потребовать от нас и других ратных подвигов. Для того, чтобы обеспечить корректное взаимодействие между уровнями нашего приложения, нам необходимо сконфигурировать routing. Большинство необходимых кейсов прекрасно покрывается предоставляемыми по умолчанию System Routes. Это позволяет не мучиться настройкой связей между виртуальными машинами в рамках virtual network, безотносительно подсетей (subnets), к которым они относятся. Системные маршруты, к тому же, обеспечивают обмен данных и вне созданной сети (в интернет, в другие сети через VPN). Автоматически создаются связи с таблицей маршрутов (route table). Для того, чтобы снизить размер нашего шаблона, предлагаю вынести данную задачу в несколько упрощенном виде.



Но мы здесь не для того, чтобы тратить много времени на данные по умолчанию сервисы. Рассмотрим менее тривиальные кейсы, когда нам не обойтись базовой системной маршрутизацией. Для таких целей может использоваться User Defined Routing (UDR). С его помощью мы сможем создать маршруты, определенные пользователем и реализовать более сложный кастомный сценарий. К примеру, UDR поможет использовать виртуальные устройства в архитектуре Azure, обеспечить доступ к интернету через вашу локальную сеть.



Реализация подобной задачи может решаться именно за счет User Defined Routes. Другими случаями, где нужность UDR очевидна — это настройка Firewall-a, построение более глубокого анализа передаваемых по сети данных. Подобный способ построения маршрутизации может также помочь в более полезной кастомизации системы логгирования.

Итак, рассмотрим пример построения UDR. Мы модифицируем дефолтный случай с System Routes и добавляем третью виртуалку, через которую будем прогонять наш трафик для выполнения поставленных задач.



Предлагаю вновь производить развертывание инфраструктуры с помощь JSON-шаблона, который мы решили считать наиболее наглядным. Работать с ним будем в одном из уже привычных редакторов. Первым делом мы, естественно, запустим Visual Studio, но для приведенного нами примера столь мощная IDE — достаточно "тяжелое" решение. В качестве альтернативы можно использовать любой редактор, поддерживающий работу с JSON. Одним из относительно новых выходов из ситуации потенциальных синтаксических ошибок в JSON является VS Code, доступный, например, в бета-версии marketplace.

Его код доступен по этой ссылке. Полагаю, если вы заглянете в него, то его объем даст понимание, почему я малодушно вынес этот таск из первоначальной трехуровневой схемы. Как и прежде, не пугаемся и продолжаем. В шаблоне мы создаем виртуальную сеть и три подсети: frontend, backend и промежуточная между ними Virtual Appliance (subnet3). Frontend подсеть (subnet1) мы, помимо NSG, соотносим с таблицей маршрутов (route table), которая будет направлять исходящий трафик. Отмечу, что User Defined Routes пригодны для конфигурирования только исходящего трафика, к тому же маршрутизация должна происходить вне рамкок одной и той же подсети.

В каждой подсети мы разворачиваем виртуальные машины и ставим им в соответствие Public IP адреса, конфигурируем network security groups, добавляя к дефолтным правилам своё, разрешая доступ по RDP. Ну и на десерт – добавляем упоминавшуюся выше route table, в которой прописываем правило, отражающее пункт назначение нашего маршрута (destination route).


{
  "type": "Microsoft.Network/routeTables",
  "name": "[variables('routeTableName')]",
  "apiVersion": "2015-05-01-preview",
  "location": "[parameters('location')]",
  "properties": {
    "routes": [
      {
        "name": "VirtualApplianceRouteToSubnet3",
        "properties": {
          "addressPrefix": "[parameters('subnet3Prefix')]",
          "nextHopType": "VirtualAppliance",
          "nextHopIpAddress": "[variables('NvmPrivateIPAddress')]"
        }
      }
    ]
  }
}


Я прошу обратить внимание на важный момент – мы указываем тип и адрес следующего получателя нашего трафика. В нашем случае мы указываем, что если трафик пришел в Subnet 3 (Virtual Appliance), то мы перенаправляем его на следующий (приватный) IP-адрес.
Дополнительный шаг, который требуется, это установить необходимую связь между финальной Backend подсетью, ее приватным IP-адресом и нашей таблицей маршрутов. Для этого мы создаем Network Interfaces (NIC) для каждой подсети. Если для Frontend подсети все праздно и малоинтересно, то в конфигурации Backend-a все не так просто. В ней мы указываем связь публичного и приватного IP-адресов, а также допускаем пересылку IP. Получили вожделенный маршрутизатор.


"properties": {
  "ipConfigurations": [
    {
      "name": "ipconfig1",
      "properties": {
        "privateIPAllocationMethod": "Static",
        "privateIPAddress": "[variables('NvmPrivateIPAddress')]",
        "publicIPAddress": {
          "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PublicIPNameForVM2'))]"
        },
        "subnet": {
          "id": "[variables('subnet2Ref')]"
        }
      }
    }
  ],
  "enableIPForwarding": true
}


Остальные особенности конфигурации (Network interfaces и прочее), опять же, убедительно прошу отследить в доступном шаблоне. Там же вносим все необходимые параметры для создания виртуальных машин, указав их Network Interfaces. Видим также очевидные в контексте описанного выше настройки виртуальных машин на ОС Windows.

Итак, развернув желанную инфраструктуру, мы можем потестировать, что же в итоге получилось. Например, можем отследить, что Virtual Appliance (VM3) имеет свободный доступ к Backend-уровню (VM2). Обратившись же с первой виртуаки (Frontend Layer, VM1), мы можем отследить как происходит ожидаемый редирект.



Надеюсь, этот уже не столь простой кейс добавил вам некоторого понимания о методах создания User Defined Routing решений. И уж если данная статья и не подтолкнет Вас к немедленным экспериментам, то уж точно окажется в закладках на скорое будущее.

Нельзя не сказать пару слов про использование Express Routes, базовым кейсом для использования которых является потребность в подключении сервисов вашей сети к Azure и другим облачным сервисам Microsoft (Office 365, CRM Online). Существующая инфраструктура может располагаться в вашем дата-центре (on-premises), или размещена в разных местах. Что важно — установленное подключение при этом изолировано от общего интернет-соединения, а производиться будет по выделенному каналу (Express route circuit). Исключено соединение не только с другими интернет ресурсами, но даже с сервисами Microsoft Azure (Storage, SQL database). Динамическая маршрутизация будет проходить по стандартным протоколам (BGP). Существует несколько способов установить подключение локальной сети к облачным сервисам Microsoft.



В случае Cloud Exchange co-location, подключение производится через Ethernet Exchange поставщика совместного размещения. Данный способ установки подключения позволит, к примеру, обеспечить виртуальное кросс-соединение с Azure.

Подключение типа point-to-point обеспечивает Ethernet сеть между локальным дата-центром вашей компании и Azure. Доступ по-прежнему осуществляется по выделенным каналам и доступ через публичный интернет исключена.

Сети типа any-to-any позволяют осуществить интеграцию с облаком Azure глобальной вычислительной сети WAN (например, кампус крупного университета, офис, практикующий удаленную работу). Производится это с помощью VPN-сети на основе MPLS. В контексте Azure это называется IPVPN, что позволяет в рамках WAN воспринимать Azure как еще один кампус или удаленный офис.

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

Под конец же хочу сказать пару слов, как описанные выше способы осуществления подключений взаимодействуют друг с другом. Когда мы явно не указываем route table для подсети (subnet), по умолчанию используются System Routes. Если же таблица указана и связь установлена, routing производится по совпадению наиболее длинного префикса (LPM) среди UDR и System Routes. При наличии нескольких маршрутов с совпадающими значениями LPM, маршрут выбирается по источнику в следующем порядке:


  • UDR
  • маршрут BGP (если используется ExpressRoute);
  • Системные маршруты

    Надеюсь, эта статья добавила понимания в вопросах маршрутизации средствами Microsoft Azure в новом Azure Resource Manager. Впереди — балансировка нагрузки и гибридные решения ARM, ASM, on-premises. По-прежнему, очень жду Ваших вопросов и пожеланий. Спасибо за внимание!

Комментарии (4)


  1. InfiniteCode
    05.05.2016 18:57

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


    1. PerseptronYar
      08.05.2016 22:18

      Речь в приведенной архитектуре идет о группе виртуалок с некоторым количеством баз данных, в том числе распределенных по различным локациям. Internal Load Balancer позволяет удобным способом оптимизировать процесс обращения к backend-у и обеспечить отказоустойчивость системы очевидным и человеколюбивым образом.
      Я впервые услышал о способностях серверов БД распределять запросы между различными базами данных. Но, по слухам, балансировка запросов в подобных инфраструктурах — задача нетривиальная, не имеющая единого решения и требующая серьезных кастомизаций. Load Balancer — серьезное обезболивающее.


      1. InfiniteCode
        09.05.2016 19:24

        Ниже hodzanassredin привел пример, это то о чем я говорил. Тоже самое и в MongoDB. Да и в целом логично, что для разных шардов нужно сразу идти на шард, а не гулять по всяким промежуточным нодам. Никакой кастомизаций серъезных не требуется, и решение одно — на уровне драйвера базы данных. Иначе вообще, тогда теряется смысл ключей шардирования.


    1. hodzanassredin
      09.05.2016 18:00

      Да это частая ошибка. Например в туториалах по кассандре предлагают ставить лоад балансер blog.z-proj.com/setting-up-a-cassandra-cluster-on-azure. Хотя в манах четко указано что не надо blog.tomas.cat/en/2013/05/13/cassandra-frequent-mistakes.